mirror of
https://github.com/aap/librw.git
synced 2025-03-13 05:14:43 +00:00
2088 lines
51 KiB
C++
2088 lines
51 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "../rwbase.h"
|
|
#include "../rwerror.h"
|
|
#include "../rwplg.h"
|
|
#include "../rwrender.h"
|
|
#include "../rwengine.h"
|
|
#include "../rwpipeline.h"
|
|
#include "../rwobjects.h"
|
|
#ifdef RW_OPENGL
|
|
#include <GL/glew.h>
|
|
#ifdef LIBRW_SDL2
|
|
#include <SDL.h>
|
|
#else
|
|
#include <GLFW/glfw3.h>
|
|
#endif
|
|
#include "rwgl3.h"
|
|
#include "rwgl3shader.h"
|
|
#include "rwgl3impl.h"
|
|
|
|
#define PLUGIN_ID 0
|
|
|
|
namespace rw {
|
|
namespace gl3 {
|
|
|
|
GlGlobals glGlobals;
|
|
|
|
Gl3Caps gl3Caps;
|
|
// terrible hack for GLES
|
|
bool32 needToReadBackTextures;
|
|
|
|
int32 alphaFunc;
|
|
float32 alphaRef;
|
|
|
|
struct UniformState
|
|
{
|
|
float32 alphaRefLow;
|
|
float32 alphaRefHigh;
|
|
int32 pad[2];
|
|
|
|
float32 fogStart;
|
|
float32 fogEnd;
|
|
float32 fogRange;
|
|
float32 fogDisable;
|
|
|
|
RGBAf fogColor;
|
|
};
|
|
|
|
struct UniformScene
|
|
{
|
|
float32 proj[16];
|
|
float32 view[16];
|
|
};
|
|
|
|
#define MAX_LIGHTS 8
|
|
|
|
struct UniformObject
|
|
{
|
|
RawMatrix world;
|
|
RGBAf ambLight;
|
|
struct {
|
|
float type;
|
|
float radius;
|
|
float minusCosAngle;
|
|
float hardSpot;
|
|
} lightParams[MAX_LIGHTS];
|
|
V4d lightPosition[MAX_LIGHTS];
|
|
V4d lightDirection[MAX_LIGHTS];
|
|
RGBAf lightColor[MAX_LIGHTS];
|
|
};
|
|
|
|
struct GLShaderState
|
|
{
|
|
RGBA matColor;
|
|
SurfaceProperties surfProps;
|
|
float extraSurfProp;
|
|
};
|
|
|
|
const char *shaderDecl120 =
|
|
"#version 120\n"
|
|
"#define GL2\n"
|
|
"#define texture texture2D\n"
|
|
"#define VSIN(index) attribute\n"
|
|
"#define VSOUT varying\n"
|
|
"#define FSIN varying\n"
|
|
"#define FRAGCOLOR(c) (gl_FragColor = c)\n";
|
|
const char *shaderDecl330 =
|
|
"#version 330\n"
|
|
"#define VSIN(index) layout(location = index) in\n"
|
|
"#define VSOUT out\n"
|
|
"#define FSIN in\n"
|
|
"#define FRAGCOLOR(c) (fragColor = c)\n";
|
|
const char *shaderDecl100es =
|
|
"#version 100\n"
|
|
"#define GL2\n"
|
|
"#define texture texture2D\n"
|
|
"#define VSIN(index) attribute\n"
|
|
"#define VSOUT varying\n"
|
|
"#define FSIN varying\n"
|
|
"#define FRAGCOLOR(c) (gl_FragColor = c)\n"
|
|
"precision highp float;\n"
|
|
"precision highp int;\n";
|
|
const char *shaderDecl310es =
|
|
"#version 310 es\n"
|
|
"#define VSIN(index) layout(location = index) in\n"
|
|
"#define VSOUT out\n"
|
|
"#define FSIN in\n"
|
|
"#define FRAGCOLOR(c) (fragColor = c)\n"
|
|
"precision highp float;\n"
|
|
"precision highp int;\n";
|
|
|
|
const char *shaderDecl;
|
|
|
|
// this needs a define in the shaders as well!
|
|
//#define RW_GL_USE_UBOS
|
|
|
|
static GLuint vao;
|
|
#ifdef RW_GL_USE_UBOS
|
|
static GLuint ubo_state, ubo_scene, ubo_object;
|
|
#endif
|
|
static GLuint whitetex;
|
|
static UniformState uniformState;
|
|
static UniformScene uniformScene;
|
|
static UniformObject uniformObject;
|
|
static GLShaderState shaderState;
|
|
|
|
#ifndef RW_GL_USE_UBOS
|
|
// State
|
|
int32 u_alphaRef;
|
|
int32 u_fogData;
|
|
//int32 u_fogStart;
|
|
//int32 u_fogEnd;
|
|
//int32 u_fogRange;
|
|
//int32 u_fogDisable;
|
|
int32 u_fogColor;
|
|
|
|
// Scene
|
|
int32 u_proj;
|
|
int32 u_view;
|
|
|
|
// Object
|
|
int32 u_world;
|
|
int32 u_ambLight;
|
|
int32 u_lightParams;
|
|
int32 u_lightPosition;
|
|
int32 u_lightDirection;
|
|
int32 u_lightColor;
|
|
#endif
|
|
|
|
int32 u_matColor;
|
|
int32 u_surfProps;
|
|
|
|
Shader *defaultShader;
|
|
|
|
static bool32 stateDirty = 1;
|
|
static bool32 sceneDirty = 1;
|
|
static bool32 objectDirty = 1;
|
|
|
|
struct RwRasterStateCache {
|
|
Raster *raster;
|
|
Texture::Addressing addressingU;
|
|
Texture::Addressing addressingV;
|
|
Texture::FilterMode filter;
|
|
};
|
|
|
|
#define MAXNUMSTAGES 8
|
|
|
|
// cached RW render states
|
|
struct RwStateCache {
|
|
bool32 vertexAlpha;
|
|
uint32 alphaTestEnable;
|
|
uint32 alphaFunc;
|
|
bool32 textureAlpha;
|
|
bool32 blendEnable;
|
|
uint32 srcblend, destblend;
|
|
uint32 zwrite;
|
|
uint32 ztest;
|
|
uint32 cullmode;
|
|
uint32 stencilenable;
|
|
uint32 stencilpass;
|
|
uint32 stencilfail;
|
|
uint32 stencilzfail;
|
|
uint32 stencilfunc;
|
|
uint32 stencilref;
|
|
uint32 stencilmask;
|
|
uint32 stencilwritemask;
|
|
uint32 fogEnable;
|
|
float32 fogStart;
|
|
float32 fogEnd;
|
|
|
|
// emulation of PS2 GS
|
|
bool32 gsalpha;
|
|
uint32 gsalpharef;
|
|
|
|
RwRasterStateCache texstage[MAXNUMSTAGES];
|
|
};
|
|
static RwStateCache rwStateCache;
|
|
|
|
enum
|
|
{
|
|
// actual gl states
|
|
RWGL_BLEND,
|
|
RWGL_SRCBLEND,
|
|
RWGL_DESTBLEND,
|
|
RWGL_DEPTHTEST,
|
|
RWGL_DEPTHFUNC,
|
|
RWGL_DEPTHMASK,
|
|
RWGL_CULL,
|
|
RWGL_CULLFACE,
|
|
RWGL_STENCIL,
|
|
RWGL_STENCILFUNC,
|
|
RWGL_STENCILFAIL,
|
|
RWGL_STENCILZFAIL,
|
|
RWGL_STENCILPASS,
|
|
RWGL_STENCILREF,
|
|
RWGL_STENCILMASK,
|
|
RWGL_STENCILWRITEMASK,
|
|
|
|
// uniforms
|
|
RWGL_ALPHAFUNC,
|
|
RWGL_ALPHAREF,
|
|
RWGL_FOG,
|
|
RWGL_FOGSTART,
|
|
RWGL_FOGEND,
|
|
RWGL_FOGCOLOR,
|
|
|
|
RWGL_NUM_STATES
|
|
};
|
|
static bool uniformStateDirty[RWGL_NUM_STATES];
|
|
|
|
struct GlState {
|
|
bool32 blendEnable;
|
|
uint32 srcblend, destblend;
|
|
|
|
bool32 depthTest;
|
|
uint32 depthFunc;
|
|
|
|
uint32 depthMask;
|
|
|
|
bool32 cullEnable;
|
|
uint32 cullFace;
|
|
|
|
bool32 stencilEnable;
|
|
// glStencilFunc
|
|
uint32 stencilFunc;
|
|
uint32 stencilRef;
|
|
uint32 stencilMask;
|
|
// glStencilOp
|
|
uint32 stencilPass;
|
|
uint32 stencilFail;
|
|
uint32 stencilZFail;
|
|
// glStencilMask
|
|
uint32 stencilWriteMask;
|
|
};
|
|
static GlState curGlState, oldGlState;
|
|
|
|
static int32 activeTexture;
|
|
static uint32 boundTexture[MAXNUMSTAGES];
|
|
|
|
static uint32 currentFramebuffer;
|
|
|
|
static uint32 blendMap[] = {
|
|
GL_ZERO, // actually invalid
|
|
GL_ZERO,
|
|
GL_ONE,
|
|
GL_SRC_COLOR,
|
|
GL_ONE_MINUS_SRC_COLOR,
|
|
GL_SRC_ALPHA,
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
|
GL_DST_ALPHA,
|
|
GL_ONE_MINUS_DST_ALPHA,
|
|
GL_DST_COLOR,
|
|
GL_ONE_MINUS_DST_COLOR,
|
|
GL_SRC_ALPHA_SATURATE,
|
|
};
|
|
|
|
static uint32 stencilOpMap[] = {
|
|
GL_KEEP, // actually invalid
|
|
GL_KEEP,
|
|
GL_ZERO,
|
|
GL_REPLACE,
|
|
GL_INCR,
|
|
GL_DECR,
|
|
GL_INVERT,
|
|
GL_INCR_WRAP,
|
|
GL_DECR_WRAP
|
|
};
|
|
|
|
static uint32 stencilFuncMap[] = {
|
|
GL_NEVER, // actually invalid
|
|
GL_NEVER,
|
|
GL_LESS,
|
|
GL_EQUAL,
|
|
GL_LEQUAL,
|
|
GL_GREATER,
|
|
GL_NOTEQUAL,
|
|
GL_GEQUAL,
|
|
GL_ALWAYS
|
|
};
|
|
|
|
static float maxAnisotropy;
|
|
|
|
/*
|
|
* GL state cache
|
|
*/
|
|
|
|
void
|
|
setGlRenderState(uint32 state, uint32 value)
|
|
{
|
|
switch(state){
|
|
case RWGL_BLEND: curGlState.blendEnable = value; break;
|
|
case RWGL_SRCBLEND: curGlState.srcblend = value; break;
|
|
case RWGL_DESTBLEND: curGlState.destblend = value; break;
|
|
case RWGL_DEPTHTEST: curGlState.depthTest = value; break;
|
|
case RWGL_DEPTHFUNC: curGlState.depthFunc = value; break;
|
|
case RWGL_DEPTHMASK: curGlState.depthMask = value; break;
|
|
case RWGL_CULL: curGlState.cullEnable = value; break;
|
|
case RWGL_CULLFACE: curGlState.cullFace = value; break;
|
|
case RWGL_STENCIL: curGlState.stencilEnable = value; break;
|
|
case RWGL_STENCILFUNC: curGlState.stencilFunc = value; break;
|
|
case RWGL_STENCILFAIL: curGlState.stencilFail = value; break;
|
|
case RWGL_STENCILZFAIL: curGlState.stencilZFail = value; break;
|
|
case RWGL_STENCILPASS: curGlState.stencilPass = value; break;
|
|
case RWGL_STENCILREF: curGlState.stencilRef = value; break;
|
|
case RWGL_STENCILMASK: curGlState.stencilMask = value; break;
|
|
case RWGL_STENCILWRITEMASK: curGlState.stencilWriteMask = value; break;
|
|
}
|
|
}
|
|
|
|
void
|
|
flushGlRenderState(void)
|
|
{
|
|
if(oldGlState.blendEnable != curGlState.blendEnable){
|
|
oldGlState.blendEnable = curGlState.blendEnable;
|
|
(oldGlState.blendEnable ? glEnable : glDisable)(GL_BLEND);
|
|
}
|
|
|
|
if(oldGlState.srcblend != curGlState.srcblend ||
|
|
oldGlState.destblend != curGlState.destblend){
|
|
oldGlState.srcblend = curGlState.srcblend;
|
|
oldGlState.destblend = curGlState.destblend;
|
|
glBlendFunc(oldGlState.srcblend, oldGlState.destblend);
|
|
}
|
|
|
|
if(oldGlState.depthTest != curGlState.depthTest){
|
|
oldGlState.depthTest = curGlState.depthTest;
|
|
(oldGlState.depthTest ? glEnable : glDisable)(GL_DEPTH_TEST);
|
|
}
|
|
if(oldGlState.depthFunc != curGlState.depthFunc){
|
|
oldGlState.depthFunc = curGlState.depthFunc;
|
|
glDepthFunc(oldGlState.depthFunc);
|
|
}
|
|
if(oldGlState.depthMask != curGlState.depthMask){
|
|
oldGlState.depthMask = curGlState.depthMask;
|
|
glDepthMask(oldGlState.depthMask);
|
|
}
|
|
|
|
if(oldGlState.stencilEnable != curGlState.stencilEnable){
|
|
oldGlState.stencilEnable = curGlState.stencilEnable;
|
|
(oldGlState.stencilEnable ? glEnable : glDisable)(GL_STENCIL_TEST);
|
|
}
|
|
if(oldGlState.stencilFunc != curGlState.stencilFunc ||
|
|
oldGlState.stencilRef != curGlState.stencilRef ||
|
|
oldGlState.stencilMask != curGlState.stencilMask){
|
|
oldGlState.stencilFunc = curGlState.stencilFunc;
|
|
oldGlState.stencilRef = curGlState.stencilRef;
|
|
oldGlState.stencilMask = curGlState.stencilMask;
|
|
glStencilFunc(oldGlState.stencilFunc, oldGlState.stencilRef, oldGlState.stencilMask);
|
|
}
|
|
if(oldGlState.stencilPass != curGlState.stencilPass ||
|
|
oldGlState.stencilFail != curGlState.stencilFail ||
|
|
oldGlState.stencilZFail != curGlState.stencilZFail){
|
|
oldGlState.stencilPass = curGlState.stencilPass;
|
|
oldGlState.stencilFail = curGlState.stencilFail;
|
|
oldGlState.stencilZFail = curGlState.stencilZFail;
|
|
glStencilOp(oldGlState.stencilFail, oldGlState.stencilZFail, oldGlState.stencilPass);
|
|
}
|
|
if(oldGlState.stencilWriteMask != curGlState.stencilWriteMask){
|
|
oldGlState.stencilWriteMask = curGlState.stencilWriteMask;
|
|
glStencilMask(oldGlState.stencilWriteMask);
|
|
}
|
|
|
|
if(oldGlState.cullEnable != curGlState.cullEnable){
|
|
oldGlState.cullEnable = curGlState.cullEnable;
|
|
(oldGlState.cullEnable ? glEnable : glDisable)(GL_CULL_FACE);
|
|
}
|
|
if(oldGlState.cullFace != curGlState.cullFace){
|
|
oldGlState.cullFace = curGlState.cullFace;
|
|
glCullFace(oldGlState.cullFace);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
setAlphaBlend(bool32 enable)
|
|
{
|
|
if(rwStateCache.blendEnable != enable){
|
|
rwStateCache.blendEnable = enable;
|
|
setGlRenderState(RWGL_BLEND, enable);
|
|
}
|
|
}
|
|
|
|
bool32
|
|
getAlphaBlend(void)
|
|
{
|
|
return rwStateCache.blendEnable;
|
|
}
|
|
|
|
static void
|
|
setDepthTest(bool32 enable)
|
|
{
|
|
if(rwStateCache.ztest != enable){
|
|
rwStateCache.ztest = enable;
|
|
if(rwStateCache.zwrite && !enable){
|
|
// If we still want to write, enable but set mode to always
|
|
setGlRenderState(RWGL_DEPTHTEST, true);
|
|
setGlRenderState(RWGL_DEPTHFUNC, GL_ALWAYS);
|
|
}else{
|
|
setGlRenderState(RWGL_DEPTHTEST, rwStateCache.ztest);
|
|
setGlRenderState(RWGL_DEPTHFUNC, GL_LEQUAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setDepthWrite(bool32 enable)
|
|
{
|
|
enable = enable ? GL_TRUE : GL_FALSE;
|
|
if(rwStateCache.zwrite != enable){
|
|
rwStateCache.zwrite = enable;
|
|
if(enable && !rwStateCache.ztest){
|
|
// Have to switch on ztest so writing can work
|
|
setGlRenderState(RWGL_DEPTHTEST, true);
|
|
setGlRenderState(RWGL_DEPTHFUNC, GL_ALWAYS);
|
|
}
|
|
setGlRenderState(RWGL_DEPTHMASK, rwStateCache.zwrite);
|
|
}
|
|
}
|
|
|
|
static void
|
|
setAlphaTest(bool32 enable)
|
|
{
|
|
uint32 shaderfunc;
|
|
if(rwStateCache.alphaTestEnable != enable){
|
|
rwStateCache.alphaTestEnable = enable;
|
|
shaderfunc = rwStateCache.alphaTestEnable ? rwStateCache.alphaFunc : ALPHAALWAYS;
|
|
if(alphaFunc != shaderfunc){
|
|
alphaFunc = shaderfunc;
|
|
uniformStateDirty[RWGL_ALPHAFUNC] = true;
|
|
stateDirty = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setAlphaTestFunction(uint32 function)
|
|
{
|
|
uint32 shaderfunc;
|
|
if(rwStateCache.alphaFunc != function){
|
|
rwStateCache.alphaFunc = function;
|
|
shaderfunc = rwStateCache.alphaTestEnable ? rwStateCache.alphaFunc : ALPHAALWAYS;
|
|
if(alphaFunc != shaderfunc){
|
|
alphaFunc = shaderfunc;
|
|
uniformStateDirty[RWGL_ALPHAFUNC] = true;
|
|
stateDirty = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setVertexAlpha(bool32 enable)
|
|
{
|
|
if(rwStateCache.vertexAlpha != enable){
|
|
if(!rwStateCache.textureAlpha){
|
|
setAlphaBlend(enable);
|
|
setAlphaTest(enable);
|
|
}
|
|
rwStateCache.vertexAlpha = enable;
|
|
}
|
|
}
|
|
|
|
static void
|
|
setActiveTexture(int32 n)
|
|
{
|
|
if(activeTexture != n){
|
|
activeTexture = n;
|
|
glActiveTexture(GL_TEXTURE0+n);
|
|
}
|
|
}
|
|
|
|
uint32
|
|
bindTexture(uint32 texid)
|
|
{
|
|
uint32 prev = boundTexture[activeTexture];
|
|
boundTexture[activeTexture] = texid;
|
|
glBindTexture(GL_TEXTURE_2D, texid);
|
|
return prev;
|
|
}
|
|
|
|
void
|
|
bindFramebuffer(uint32 fbo)
|
|
{
|
|
if(currentFramebuffer != fbo){
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
currentFramebuffer = fbo;
|
|
}
|
|
}
|
|
|
|
static GLint filterConvMap_NoMIP[] = {
|
|
0, GL_NEAREST, GL_LINEAR,
|
|
GL_NEAREST, GL_LINEAR,
|
|
GL_NEAREST, GL_LINEAR
|
|
};
|
|
static GLint filterConvMap_MIP[] = {
|
|
0, GL_NEAREST, GL_LINEAR,
|
|
GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST,
|
|
GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR
|
|
};
|
|
|
|
static GLint addressConvMap[] = {
|
|
0, GL_REPEAT, GL_MIRRORED_REPEAT,
|
|
GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER
|
|
};
|
|
|
|
static void
|
|
setFilterMode(uint32 stage, int32 filter)
|
|
{
|
|
if(rwStateCache.texstage[stage].filter != (Texture::FilterMode)filter){
|
|
rwStateCache.texstage[stage].filter = (Texture::FilterMode)filter;
|
|
Raster *raster = rwStateCache.texstage[stage].raster;
|
|
if(raster){
|
|
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, rwStateCache.texstage[stage].raster, nativeRasterOffset);
|
|
if(natras->filterMode != filter){
|
|
setActiveTexture(stage);
|
|
if(natras->autogenMipmap || natras->numLevels > 1){
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_MIP[filter]);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_MIP[filter]);
|
|
}else{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_NoMIP[filter]);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_NoMIP[filter]);
|
|
}
|
|
natras->filterMode = filter;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setAddressU(uint32 stage, int32 addressing)
|
|
{
|
|
if(rwStateCache.texstage[stage].addressingU != (Texture::Addressing)addressing){
|
|
rwStateCache.texstage[stage].addressingU = (Texture::Addressing)addressing;
|
|
Raster *raster = rwStateCache.texstage[stage].raster;
|
|
if(raster){
|
|
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
|
|
if(natras->addressU == addressing){
|
|
setActiveTexture(stage);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, addressConvMap[addressing]);
|
|
natras->addressU = addressing;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setAddressV(uint32 stage, int32 addressing)
|
|
{
|
|
if(rwStateCache.texstage[stage].addressingV != (Texture::Addressing)addressing){
|
|
rwStateCache.texstage[stage].addressingV = (Texture::Addressing)addressing;
|
|
Raster *raster = rwStateCache.texstage[stage].raster;
|
|
if(raster){
|
|
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, rwStateCache.texstage[stage].raster, nativeRasterOffset);
|
|
if(natras->addressV == addressing){
|
|
setActiveTexture(stage);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, addressConvMap[addressing]);
|
|
natras->addressV = addressing;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setRasterStageOnly(uint32 stage, Raster *raster)
|
|
{
|
|
bool32 alpha;
|
|
if(raster != rwStateCache.texstage[stage].raster){
|
|
rwStateCache.texstage[stage].raster = raster;
|
|
setActiveTexture(stage);
|
|
if(raster){
|
|
assert(raster->platform == PLATFORM_GL3);
|
|
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
|
|
bindTexture(natras->texid);
|
|
|
|
rwStateCache.texstage[stage].filter = (rw::Texture::FilterMode)natras->filterMode;
|
|
rwStateCache.texstage[stage].addressingU = (rw::Texture::Addressing)natras->addressU;
|
|
rwStateCache.texstage[stage].addressingV = (rw::Texture::Addressing)natras->addressV;
|
|
|
|
alpha = natras->hasAlpha;
|
|
}else{
|
|
bindTexture(whitetex);
|
|
alpha = 0;
|
|
}
|
|
|
|
if(stage == 0){
|
|
if(alpha != rwStateCache.textureAlpha){
|
|
rwStateCache.textureAlpha = alpha;
|
|
if(!rwStateCache.vertexAlpha){
|
|
setAlphaBlend(alpha);
|
|
setAlphaTest(alpha);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setRasterStage(uint32 stage, Raster *raster)
|
|
{
|
|
bool32 alpha;
|
|
if(raster != rwStateCache.texstage[stage].raster){
|
|
rwStateCache.texstage[stage].raster = raster;
|
|
setActiveTexture(stage);
|
|
if(raster){
|
|
assert(raster->platform == PLATFORM_GL3);
|
|
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
|
|
bindTexture(natras->texid);
|
|
uint32 filter = rwStateCache.texstage[stage].filter;
|
|
uint32 addrU = rwStateCache.texstage[stage].addressingU;
|
|
uint32 addrV = rwStateCache.texstage[stage].addressingV;
|
|
if(natras->filterMode != filter){
|
|
if(natras->autogenMipmap || natras->numLevels > 1){
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_MIP[filter]);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_MIP[filter]);
|
|
}else{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_NoMIP[filter]);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_NoMIP[filter]);
|
|
}
|
|
natras->filterMode = filter;
|
|
}
|
|
if(natras->addressU != addrU){
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, addressConvMap[addrU]);
|
|
natras->addressU = addrU;
|
|
}
|
|
if(natras->addressV != addrV){
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, addressConvMap[addrV]);
|
|
natras->addressV = addrV;
|
|
}
|
|
alpha = natras->hasAlpha;
|
|
}else{
|
|
bindTexture(whitetex);
|
|
alpha = 0;
|
|
}
|
|
|
|
if(stage == 0){
|
|
if(alpha != rwStateCache.textureAlpha){
|
|
rwStateCache.textureAlpha = alpha;
|
|
if(!rwStateCache.vertexAlpha){
|
|
setAlphaBlend(alpha);
|
|
setAlphaTest(alpha);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
setTexture(int32 stage, Texture *tex)
|
|
{
|
|
if(tex == nil || tex->raster == nil){
|
|
setRasterStage(stage, nil);
|
|
return;
|
|
}
|
|
setRasterStageOnly(stage, tex->raster);
|
|
setFilterMode(stage, tex->getFilter());
|
|
setAddressU(stage, tex->getAddressU());
|
|
setAddressV(stage, tex->getAddressV());
|
|
}
|
|
|
|
static void
|
|
setRenderState(int32 state, void *pvalue)
|
|
{
|
|
uint32 value = (uint32)(uintptr)pvalue;
|
|
switch(state){
|
|
case TEXTURERASTER:
|
|
setRasterStage(0, (Raster*)pvalue);
|
|
break;
|
|
case TEXTUREADDRESS:
|
|
setAddressU(0, value);
|
|
setAddressV(0, value);
|
|
break;
|
|
case TEXTUREADDRESSU:
|
|
setAddressU(0, value);
|
|
break;
|
|
case TEXTUREADDRESSV:
|
|
setAddressV(0, value);
|
|
break;
|
|
case TEXTUREFILTER:
|
|
setFilterMode(0, value);
|
|
break;
|
|
case VERTEXALPHA:
|
|
setVertexAlpha(value);
|
|
break;
|
|
case SRCBLEND:
|
|
if(rwStateCache.srcblend != value){
|
|
rwStateCache.srcblend = value;
|
|
setGlRenderState(RWGL_SRCBLEND, blendMap[rwStateCache.srcblend]);
|
|
}
|
|
break;
|
|
case DESTBLEND:
|
|
if(rwStateCache.destblend != value){
|
|
rwStateCache.destblend = value;
|
|
setGlRenderState(RWGL_DESTBLEND, blendMap[rwStateCache.destblend]);
|
|
}
|
|
break;
|
|
case ZTESTENABLE:
|
|
setDepthTest(value);
|
|
break;
|
|
case ZWRITEENABLE:
|
|
setDepthWrite(value);
|
|
break;
|
|
case FOGENABLE:
|
|
if(rwStateCache.fogEnable != value){
|
|
rwStateCache.fogEnable = value;
|
|
uniformStateDirty[RWGL_FOG] = true;
|
|
stateDirty = 1;
|
|
}
|
|
break;
|
|
case FOGCOLOR:
|
|
// no cache check here...too lazy
|
|
RGBA c;
|
|
c.red = value;
|
|
c.green = value>>8;
|
|
c.blue = value>>16;
|
|
c.alpha = value>>24;
|
|
convColor(&uniformState.fogColor, &c);
|
|
uniformStateDirty[RWGL_FOGCOLOR] = true;
|
|
stateDirty = 1;
|
|
break;
|
|
case CULLMODE:
|
|
if(rwStateCache.cullmode != value){
|
|
rwStateCache.cullmode = value;
|
|
if(rwStateCache.cullmode == CULLNONE)
|
|
setGlRenderState(RWGL_CULL, false);
|
|
else{
|
|
setGlRenderState(RWGL_CULL, true);
|
|
setGlRenderState(RWGL_CULLFACE, rwStateCache.cullmode == CULLBACK ? GL_BACK : GL_FRONT);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STENCILENABLE:
|
|
if(rwStateCache.stencilenable != value){
|
|
rwStateCache.stencilenable = value;
|
|
setGlRenderState(RWGL_STENCIL, value);
|
|
}
|
|
break;
|
|
case STENCILFAIL:
|
|
if(rwStateCache.stencilfail != value){
|
|
rwStateCache.stencilfail = value;
|
|
setGlRenderState(RWGL_STENCILFAIL, stencilOpMap[value]);
|
|
}
|
|
break;
|
|
case STENCILZFAIL:
|
|
if(rwStateCache.stencilzfail != value){
|
|
rwStateCache.stencilzfail = value;
|
|
setGlRenderState(RWGL_STENCILZFAIL, stencilOpMap[value]);
|
|
}
|
|
break;
|
|
case STENCILPASS:
|
|
if(rwStateCache.stencilpass != value){
|
|
rwStateCache.stencilpass = value;
|
|
setGlRenderState(RWGL_STENCILPASS, stencilOpMap[value]);
|
|
}
|
|
break;
|
|
case STENCILFUNCTION:
|
|
if(rwStateCache.stencilfunc != value){
|
|
rwStateCache.stencilfunc = value;
|
|
setGlRenderState(RWGL_STENCILFUNC, stencilFuncMap[value]);
|
|
}
|
|
break;
|
|
case STENCILFUNCTIONREF:
|
|
if(rwStateCache.stencilref != value){
|
|
rwStateCache.stencilref = value;
|
|
setGlRenderState(RWGL_STENCILREF, value);
|
|
}
|
|
break;
|
|
case STENCILFUNCTIONMASK:
|
|
if(rwStateCache.stencilmask != value){
|
|
rwStateCache.stencilmask = value;
|
|
setGlRenderState(RWGL_STENCILMASK, value);
|
|
}
|
|
break;
|
|
case STENCILFUNCTIONWRITEMASK:
|
|
if(rwStateCache.stencilwritemask != value){
|
|
rwStateCache.stencilwritemask = value;
|
|
setGlRenderState(RWGL_STENCILWRITEMASK, value);
|
|
}
|
|
break;
|
|
|
|
case ALPHATESTFUNC:
|
|
setAlphaTestFunction(value);
|
|
break;
|
|
case ALPHATESTREF:
|
|
if(alphaRef != value/255.0f){
|
|
alphaRef = value/255.0f;
|
|
uniformStateDirty[RWGL_ALPHAREF] = true;
|
|
stateDirty = 1;
|
|
}
|
|
break;
|
|
case GSALPHATEST:
|
|
rwStateCache.gsalpha = value;
|
|
break;
|
|
case GSALPHATESTREF:
|
|
rwStateCache.gsalpharef = value;
|
|
}
|
|
}
|
|
|
|
static void*
|
|
getRenderState(int32 state)
|
|
{
|
|
uint32 val;
|
|
RGBA rgba;
|
|
switch(state){
|
|
case TEXTURERASTER:
|
|
return rwStateCache.texstage[0].raster;
|
|
case TEXTUREADDRESS:
|
|
if(rwStateCache.texstage[0].addressingU == rwStateCache.texstage[0].addressingV)
|
|
val = rwStateCache.texstage[0].addressingU;
|
|
else
|
|
val = 0; // invalid
|
|
break;
|
|
case TEXTUREADDRESSU:
|
|
val = rwStateCache.texstage[0].addressingU;
|
|
break;
|
|
case TEXTUREADDRESSV:
|
|
val = rwStateCache.texstage[0].addressingV;
|
|
break;
|
|
case TEXTUREFILTER:
|
|
val = rwStateCache.texstage[0].filter;
|
|
break;
|
|
|
|
case VERTEXALPHA:
|
|
val = rwStateCache.vertexAlpha;
|
|
break;
|
|
case SRCBLEND:
|
|
val = rwStateCache.srcblend;
|
|
break;
|
|
case DESTBLEND:
|
|
val = rwStateCache.destblend;
|
|
break;
|
|
case ZTESTENABLE:
|
|
val = rwStateCache.ztest;
|
|
break;
|
|
case ZWRITEENABLE:
|
|
val = rwStateCache.zwrite;
|
|
break;
|
|
case FOGENABLE:
|
|
val = rwStateCache.fogEnable;
|
|
break;
|
|
case FOGCOLOR:
|
|
convColor(&rgba, &uniformState.fogColor);
|
|
val = RWRGBAINT(rgba.red, rgba.green, rgba.blue, rgba.alpha);
|
|
break;
|
|
case CULLMODE:
|
|
val = rwStateCache.cullmode;
|
|
break;
|
|
|
|
case STENCILENABLE:
|
|
val = rwStateCache.stencilenable;
|
|
break;
|
|
case STENCILFAIL:
|
|
val = rwStateCache.stencilfail;
|
|
break;
|
|
case STENCILZFAIL:
|
|
val = rwStateCache.stencilzfail;
|
|
break;
|
|
case STENCILPASS:
|
|
val = rwStateCache.stencilpass;
|
|
break;
|
|
case STENCILFUNCTION:
|
|
val = rwStateCache.stencilfunc;
|
|
break;
|
|
case STENCILFUNCTIONREF:
|
|
val = rwStateCache.stencilref;
|
|
break;
|
|
case STENCILFUNCTIONMASK:
|
|
val = rwStateCache.stencilmask;
|
|
break;
|
|
case STENCILFUNCTIONWRITEMASK:
|
|
val = rwStateCache.stencilwritemask;
|
|
break;
|
|
|
|
case ALPHATESTFUNC:
|
|
val = rwStateCache.alphaFunc;
|
|
break;
|
|
case ALPHATESTREF:
|
|
val = (uint32)(alphaRef*255.0f);
|
|
break;
|
|
case GSALPHATEST:
|
|
val = rwStateCache.gsalpha;
|
|
break;
|
|
case GSALPHATESTREF:
|
|
val = rwStateCache.gsalpharef;
|
|
break;
|
|
default:
|
|
val = 0;
|
|
}
|
|
return (void*)(uintptr)val;
|
|
}
|
|
|
|
static void
|
|
resetRenderState(void)
|
|
{
|
|
rwStateCache.alphaFunc = ALPHAGREATEREQUAL;
|
|
alphaFunc = 0;
|
|
alphaRef = 10.0f/255.0f;
|
|
uniformState.fogDisable = 1.0f;
|
|
uniformState.fogStart = 0.0f;
|
|
uniformState.fogEnd = 0.0f;
|
|
uniformState.fogRange = 0.0f;
|
|
uniformState.fogColor = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
rwStateCache.gsalpha = 0;
|
|
rwStateCache.gsalpharef = 128;
|
|
stateDirty = 1;
|
|
|
|
rwStateCache.vertexAlpha = 0;
|
|
rwStateCache.textureAlpha = 0;
|
|
rwStateCache.alphaTestEnable = 0;
|
|
|
|
memset(&oldGlState, 0xFE, sizeof(oldGlState));
|
|
|
|
rwStateCache.blendEnable = 0;
|
|
setGlRenderState(RWGL_BLEND, false);
|
|
rwStateCache.srcblend = BLENDSRCALPHA;
|
|
rwStateCache.destblend = BLENDINVSRCALPHA;
|
|
setGlRenderState(RWGL_SRCBLEND, blendMap[rwStateCache.srcblend]);
|
|
setGlRenderState(RWGL_DESTBLEND, blendMap[rwStateCache.destblend]);
|
|
|
|
rwStateCache.zwrite = GL_TRUE;
|
|
setGlRenderState(RWGL_DEPTHMASK, rwStateCache.zwrite);
|
|
|
|
rwStateCache.ztest = 1;
|
|
setGlRenderState(RWGL_DEPTHTEST, true);
|
|
setGlRenderState(RWGL_DEPTHFUNC, GL_LEQUAL);
|
|
|
|
rwStateCache.cullmode = CULLNONE;
|
|
setGlRenderState(RWGL_CULL, false);
|
|
setGlRenderState(RWGL_CULLFACE, GL_BACK);
|
|
|
|
rwStateCache.stencilenable = 0;
|
|
setGlRenderState(RWGL_STENCIL, GL_FALSE);
|
|
rwStateCache.stencilfail = STENCILKEEP;
|
|
setGlRenderState(RWGL_STENCILFAIL, GL_KEEP);
|
|
rwStateCache.stencilzfail = STENCILKEEP;
|
|
setGlRenderState(RWGL_STENCILZFAIL, GL_KEEP);
|
|
rwStateCache.stencilpass = STENCILKEEP;
|
|
setGlRenderState(RWGL_STENCILPASS, GL_KEEP);
|
|
rwStateCache.stencilfunc = STENCILALWAYS;
|
|
setGlRenderState(RWGL_STENCILFUNC, GL_ALWAYS);
|
|
rwStateCache.stencilref = 0;
|
|
setGlRenderState(RWGL_STENCILREF, 0);
|
|
rwStateCache.stencilmask = 0xFFFFFFFF;
|
|
setGlRenderState(RWGL_STENCILMASK, 0xFFFFFFFF);
|
|
rwStateCache.stencilwritemask = 0xFFFFFFFF;
|
|
setGlRenderState(RWGL_STENCILWRITEMASK, 0xFFFFFFFF);
|
|
|
|
activeTexture = -1;
|
|
for(int i = 0; i < MAXNUMSTAGES; i++){
|
|
setActiveTexture(i);
|
|
bindTexture(whitetex);
|
|
}
|
|
setActiveTexture(0);
|
|
}
|
|
|
|
void
|
|
setWorldMatrix(Matrix *mat)
|
|
{
|
|
convMatrix(&uniformObject.world, mat);
|
|
objectDirty = 1;
|
|
}
|
|
|
|
int32
|
|
setLights(WorldLights *lightData)
|
|
{
|
|
int i, n;
|
|
Light *l;
|
|
int32 bits;
|
|
|
|
uniformObject.ambLight = lightData->ambient;
|
|
|
|
bits = 0;
|
|
|
|
if(lightData->numAmbients)
|
|
bits |= VSLIGHT_AMBIENT;
|
|
|
|
n = 0;
|
|
for(i = 0; i < lightData->numDirectionals && i < 8; i++){
|
|
l = lightData->directionals[i];
|
|
uniformObject.lightParams[n].type = 1.0f;
|
|
uniformObject.lightColor[n] = l->color;
|
|
memcpy(&uniformObject.lightDirection[n], &l->getFrame()->getLTM()->at, sizeof(V3d));
|
|
bits |= VSLIGHT_POINT;
|
|
n++;
|
|
if(n >= MAX_LIGHTS)
|
|
goto out;
|
|
}
|
|
|
|
for(i = 0; i < lightData->numLocals; i++){
|
|
Light *l = lightData->locals[i];
|
|
|
|
switch(l->getType()){
|
|
case Light::POINT:
|
|
uniformObject.lightParams[n].type = 2.0f;
|
|
uniformObject.lightParams[n].radius = l->radius;
|
|
uniformObject.lightColor[n] = l->color;
|
|
memcpy(&uniformObject.lightPosition[n], &l->getFrame()->getLTM()->pos, sizeof(V3d));
|
|
bits |= VSLIGHT_POINT;
|
|
n++;
|
|
if(n >= MAX_LIGHTS)
|
|
goto out;
|
|
break;
|
|
case Light::SPOT:
|
|
case Light::SOFTSPOT:
|
|
uniformObject.lightParams[n].type = 3.0f;
|
|
uniformObject.lightParams[n].minusCosAngle = l->minusCosAngle;
|
|
uniformObject.lightParams[n].radius = l->radius;
|
|
uniformObject.lightColor[n] = l->color;
|
|
memcpy(&uniformObject.lightPosition[n], &l->getFrame()->getLTM()->pos, sizeof(V3d));
|
|
memcpy(&uniformObject.lightDirection[n], &l->getFrame()->getLTM()->at, sizeof(V3d));
|
|
// lower bound of falloff
|
|
if(l->getType() == Light::SOFTSPOT)
|
|
uniformObject.lightParams[n].hardSpot = 0.0f;
|
|
else
|
|
uniformObject.lightParams[n].hardSpot = 1.0f;
|
|
bits |= VSLIGHT_SPOT;
|
|
n++;
|
|
if(n >= MAX_LIGHTS)
|
|
goto out;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uniformObject.lightParams[n].type = 0.0f;
|
|
out:
|
|
objectDirty = 1;
|
|
return bits;
|
|
}
|
|
|
|
void
|
|
setProjectionMatrix(float32 *mat)
|
|
{
|
|
memcpy(&uniformScene.proj, mat, 64);
|
|
sceneDirty = 1;
|
|
}
|
|
|
|
void
|
|
setViewMatrix(float32 *mat)
|
|
{
|
|
memcpy(&uniformScene.view, mat, 64);
|
|
sceneDirty = 1;
|
|
}
|
|
|
|
Shader *lastShaderUploaded;
|
|
|
|
#define U(i) currentShader->uniformLocations[i]
|
|
|
|
void
|
|
setMaterial(const RGBA &color, const SurfaceProperties &surfaceprops, float extraSurfProp)
|
|
{
|
|
bool force = lastShaderUploaded != currentShader;
|
|
if(force || !equal(shaderState.matColor, color)){
|
|
rw::RGBAf col;
|
|
convColor(&col, &color);
|
|
glUniform4fv(U(u_matColor), 1, (GLfloat*)&col);
|
|
shaderState.matColor = color;
|
|
}
|
|
|
|
if(force ||
|
|
shaderState.surfProps.ambient != surfaceprops.ambient ||
|
|
shaderState.surfProps.specular != surfaceprops.specular ||
|
|
shaderState.surfProps.diffuse != surfaceprops.diffuse ||
|
|
shaderState.extraSurfProp != extraSurfProp){
|
|
float surfProps[4];
|
|
surfProps[0] = surfaceprops.ambient;
|
|
surfProps[1] = surfaceprops.specular;
|
|
surfProps[2] = surfaceprops.diffuse;
|
|
surfProps[3] = extraSurfProp;
|
|
glUniform4fv(U(u_surfProps), 1, surfProps);
|
|
shaderState.surfProps = surfaceprops;
|
|
}
|
|
}
|
|
|
|
void
|
|
flushCache(void)
|
|
{
|
|
flushGlRenderState();
|
|
|
|
#ifndef RW_GL_USE_UBOS
|
|
|
|
// TODO: this is probably a stupid way to do it without UBOs
|
|
if(lastShaderUploaded != currentShader){
|
|
lastShaderUploaded = currentShader;
|
|
objectDirty = 1;
|
|
sceneDirty = 1;
|
|
stateDirty = 1;
|
|
|
|
int i;
|
|
for(i = 0; i < RWGL_NUM_STATES; i++)
|
|
uniformStateDirty[i] = true;
|
|
}
|
|
|
|
if(sceneDirty){
|
|
glUniformMatrix4fv(U(u_proj), 1, 0, uniformScene.proj);
|
|
glUniformMatrix4fv(U(u_view), 1, 0, uniformScene.view);
|
|
sceneDirty = 0;
|
|
}
|
|
|
|
if(objectDirty){
|
|
glUniformMatrix4fv(U(u_world), 1, 0, (float*)&uniformObject.world);
|
|
glUniform4fv(U(u_ambLight), 1, (float*)&uniformObject.ambLight);
|
|
glUniform4fv(U(u_lightParams), MAX_LIGHTS, (float*)uniformObject.lightParams);
|
|
glUniform4fv(U(u_lightPosition), MAX_LIGHTS, (float*)uniformObject.lightPosition);
|
|
glUniform4fv(U(u_lightDirection), MAX_LIGHTS, (float*)uniformObject.lightDirection);
|
|
glUniform4fv(U(u_lightColor), MAX_LIGHTS, (float*)uniformObject.lightColor);
|
|
objectDirty = 0;
|
|
}
|
|
|
|
// if(stateDirty){
|
|
|
|
uniformState.fogDisable = rwStateCache.fogEnable ? 0.0f : 1.0f;
|
|
uniformState.fogStart = rwStateCache.fogStart;
|
|
uniformState.fogEnd = rwStateCache.fogEnd;
|
|
uniformState.fogRange = 1.0f/(rwStateCache.fogStart - rwStateCache.fogEnd);
|
|
|
|
if(uniformStateDirty[RWGL_ALPHAFUNC] || uniformStateDirty[RWGL_ALPHAREF]){
|
|
switch(alphaFunc){
|
|
case ALPHAALWAYS:
|
|
default:
|
|
glUniform2f(U(u_alphaRef), -1000.0f, 1000.0f);
|
|
break;
|
|
case ALPHAGREATEREQUAL:
|
|
glUniform2f(U(u_alphaRef), alphaRef, 1000.0f);
|
|
break;
|
|
case ALPHALESS:
|
|
glUniform2f(U(u_alphaRef), -1000.0f, alphaRef);
|
|
break;
|
|
}
|
|
uniformStateDirty[RWGL_ALPHAFUNC] = false;
|
|
uniformStateDirty[RWGL_ALPHAREF] = false;
|
|
}
|
|
|
|
if(uniformStateDirty[RWGL_FOG] ||
|
|
uniformStateDirty[RWGL_FOGSTART] ||
|
|
uniformStateDirty[RWGL_FOGEND]){
|
|
float fogData[4] = {
|
|
uniformState.fogStart,
|
|
uniformState.fogEnd,
|
|
uniformState.fogRange,
|
|
uniformState.fogDisable
|
|
};
|
|
glUniform4fv(U(u_fogData), 1, fogData);
|
|
uniformStateDirty[RWGL_FOG] = false;
|
|
uniformStateDirty[RWGL_FOGSTART] = false;
|
|
uniformStateDirty[RWGL_FOGEND] = false;
|
|
}
|
|
|
|
if(uniformStateDirty[RWGL_FOGCOLOR]){
|
|
glUniform4fv(U(u_fogColor), 1, (float*)&uniformState.fogColor);
|
|
uniformStateDirty[RWGL_FOGCOLOR] = false;
|
|
}
|
|
|
|
// stateDirty = 0;
|
|
// }
|
|
#else
|
|
if(objectDirty){
|
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_object);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformObject), nil, GL_STREAM_DRAW);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformObject), &uniformObject, GL_STREAM_DRAW);
|
|
objectDirty = 0;
|
|
}
|
|
if(sceneDirty){
|
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_scene);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformScene), nil, GL_STREAM_DRAW);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformScene), &uniformScene, GL_STREAM_DRAW);
|
|
sceneDirty = 0;
|
|
}
|
|
if(stateDirty){
|
|
switch(alphaFunc){
|
|
case ALPHAALWAYS:
|
|
default:
|
|
uniformState.alphaRefLow = -1000.0f;
|
|
uniformState.alphaRefHigh = 1000.0f;
|
|
break;
|
|
case ALPHAGREATEREQUAL:
|
|
uniformState.alphaRefLow = alphaRef;
|
|
uniformState.alphaRefHigh = 1000.0f;
|
|
break;
|
|
case ALPHALESS:
|
|
uniformState.alphaRefLow = -1000.0f;
|
|
uniformState.alphaRefHigh = alphaRef;
|
|
break;
|
|
}
|
|
uniformState.fogDisable = rwStateCache.fogEnable ? 0.0f : 1.0f;
|
|
uniformState.fogStart = rwStateCache.fogStart;
|
|
uniformState.fogEnd = rwStateCache.fogEnd;
|
|
uniformState.fogRange = 1.0f/(rwStateCache.fogStart - rwStateCache.fogEnd);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_state);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformState), nil, GL_STREAM_DRAW);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformState), &uniformState, GL_STREAM_DRAW);
|
|
stateDirty = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
setFrameBuffer(Camera *cam)
|
|
{
|
|
Raster *fbuf = cam->frameBuffer->parent;
|
|
Raster *zbuf = cam->zBuffer->parent;
|
|
assert(fbuf);
|
|
|
|
Gl3Raster *natfb = PLUGINOFFSET(Gl3Raster, fbuf, nativeRasterOffset);
|
|
Gl3Raster *natzb = PLUGINOFFSET(Gl3Raster, zbuf, nativeRasterOffset);
|
|
assert(fbuf->type == Raster::CAMERA || fbuf->type == Raster::CAMERATEXTURE);
|
|
|
|
// Have to make sure depth buffer is attached to FB's fbo
|
|
bindFramebuffer(natfb->fbo);
|
|
if(zbuf){
|
|
if(natfb->fboMate == zbuf){
|
|
// all good
|
|
assert(natzb->fboMate == fbuf);
|
|
}else{
|
|
if(natzb->fboMate){
|
|
// have to detatch from fbo first!
|
|
Gl3Raster *oldfb = PLUGINOFFSET(Gl3Raster, natzb->fboMate, nativeRasterOffset);
|
|
if(oldfb->fbo){
|
|
bindFramebuffer(oldfb->fbo);
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0);
|
|
bindFramebuffer(natfb->fbo);
|
|
}
|
|
oldfb->fboMate = nil;
|
|
}
|
|
natfb->fboMate = zbuf;
|
|
natzb->fboMate = fbuf;
|
|
if(natfb->fbo){
|
|
if(gl3Caps.gles)
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, natzb->texid);
|
|
else
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, natzb->texid, 0);
|
|
}
|
|
}
|
|
}else{
|
|
// remove z-buffer
|
|
if(natfb->fboMate && natfb->fbo)
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0);
|
|
natfb->fboMate = nil;
|
|
}
|
|
}
|
|
|
|
static void
|
|
beginUpdate(Camera *cam)
|
|
{
|
|
float view[16], proj[16];
|
|
// View Matrix
|
|
Matrix inv;
|
|
Matrix::invert(&inv, cam->getFrame()->getLTM());
|
|
// Since we're looking into positive Z,
|
|
// flip X to ge a left handed view space.
|
|
view[0] = -inv.right.x;
|
|
view[1] = inv.right.y;
|
|
view[2] = inv.right.z;
|
|
view[3] = 0.0f;
|
|
view[4] = -inv.up.x;
|
|
view[5] = inv.up.y;
|
|
view[6] = inv.up.z;
|
|
view[7] = 0.0f;
|
|
view[8] = -inv.at.x;
|
|
view[9] = inv.at.y;
|
|
view[10] = inv.at.z;
|
|
view[11] = 0.0f;
|
|
view[12] = -inv.pos.x;
|
|
view[13] = inv.pos.y;
|
|
view[14] = inv.pos.z;
|
|
view[15] = 1.0f;
|
|
memcpy(&cam->devView, &view, sizeof(RawMatrix));
|
|
setViewMatrix(view);
|
|
|
|
// Projection Matrix
|
|
float32 invwx = 1.0f/cam->viewWindow.x;
|
|
float32 invwy = 1.0f/cam->viewWindow.y;
|
|
float32 invz = 1.0f/(cam->farPlane-cam->nearPlane);
|
|
|
|
proj[0] = invwx;
|
|
proj[1] = 0.0f;
|
|
proj[2] = 0.0f;
|
|
proj[3] = 0.0f;
|
|
|
|
proj[4] = 0.0f;
|
|
proj[5] = invwy;
|
|
proj[6] = 0.0f;
|
|
proj[7] = 0.0f;
|
|
|
|
proj[8] = cam->viewOffset.x*invwx;
|
|
proj[9] = cam->viewOffset.y*invwy;
|
|
proj[12] = -proj[8];
|
|
proj[13] = -proj[9];
|
|
if(cam->projection == Camera::PERSPECTIVE){
|
|
proj[10] = (cam->farPlane+cam->nearPlane)*invz;
|
|
proj[11] = 1.0f;
|
|
|
|
proj[14] = -2.0f*cam->nearPlane*cam->farPlane*invz;
|
|
proj[15] = 0.0f;
|
|
}else{
|
|
proj[10] = -(cam->farPlane+cam->nearPlane)*invz;
|
|
proj[11] = 0.0f;
|
|
|
|
proj[14] = 2.0f*invz;
|
|
proj[15] = 1.0f;
|
|
}
|
|
memcpy(&cam->devProj, &proj, sizeof(RawMatrix));
|
|
setProjectionMatrix(proj);
|
|
|
|
if(rwStateCache.fogStart != cam->fogPlane){
|
|
rwStateCache.fogStart = cam->fogPlane;
|
|
uniformStateDirty[RWGL_FOGSTART] = true;
|
|
stateDirty = 1;
|
|
}
|
|
if(rwStateCache.fogEnd != cam->farPlane){
|
|
rwStateCache.fogEnd = cam->farPlane;
|
|
uniformStateDirty[RWGL_FOGEND] = true;
|
|
stateDirty = 1;
|
|
}
|
|
|
|
setFrameBuffer(cam);
|
|
|
|
int w, h;
|
|
int x, y;
|
|
Raster *fb = cam->frameBuffer->parent;
|
|
if(fb->type == Raster::CAMERA){
|
|
#ifdef LIBRW_SDL2
|
|
SDL_GetWindowSize(glGlobals.window, &w, &h);
|
|
#else
|
|
glfwGetWindowSize(glGlobals.window, &w, &h);
|
|
#endif
|
|
}else{
|
|
w = fb->width;
|
|
h = fb->height;
|
|
}
|
|
x = 0;
|
|
y = 0;
|
|
|
|
// Got a subraster
|
|
if(cam->frameBuffer != fb){
|
|
x = cam->frameBuffer->offsetX;
|
|
// GL y offset is from bottom
|
|
y = h - cam->frameBuffer->height - cam->frameBuffer->offsetY;
|
|
w = cam->frameBuffer->width;
|
|
h = cam->frameBuffer->height;
|
|
}
|
|
|
|
if(w != glGlobals.presentWidth || h != glGlobals.presentHeight ||
|
|
x != glGlobals.presentOffX || y != glGlobals.presentOffY){
|
|
glViewport(x, y, w, h);
|
|
glGlobals.presentWidth = w;
|
|
glGlobals.presentHeight = h;
|
|
glGlobals.presentOffX = x;
|
|
glGlobals.presentOffY = y;
|
|
}
|
|
}
|
|
|
|
static void
|
|
endUpdate(Camera *cam)
|
|
{
|
|
}
|
|
|
|
static void
|
|
clearCamera(Camera *cam, RGBA *col, uint32 mode)
|
|
{
|
|
RGBAf colf;
|
|
GLbitfield mask;
|
|
|
|
setFrameBuffer(cam);
|
|
|
|
convColor(&colf, col);
|
|
glClearColor(colf.red, colf.green, colf.blue, colf.alpha);
|
|
mask = 0;
|
|
if(mode & Camera::CLEARIMAGE)
|
|
mask |= GL_COLOR_BUFFER_BIT;
|
|
if(mode & Camera::CLEARZ)
|
|
mask |= GL_DEPTH_BUFFER_BIT;
|
|
if(mode & Camera::CLEARSTENCIL)
|
|
mask |= GL_STENCIL_BUFFER_BIT;
|
|
glDepthMask(GL_TRUE);
|
|
glClear(mask);
|
|
glDepthMask(rwStateCache.zwrite);
|
|
}
|
|
|
|
static void
|
|
showRaster(Raster *raster, uint32 flags)
|
|
{
|
|
// TODO: do this properly!
|
|
#ifdef LIBRW_SDL2
|
|
if(flags & Raster::FLIPWAITVSYNCH)
|
|
SDL_GL_SetSwapInterval(1);
|
|
else
|
|
SDL_GL_SetSwapInterval(0);
|
|
SDL_GL_SwapWindow(glGlobals.window);
|
|
#else
|
|
if(flags & Raster::FLIPWAITVSYNCH)
|
|
glfwSwapInterval(1);
|
|
else
|
|
glfwSwapInterval(0);
|
|
glfwSwapBuffers(glGlobals.window);
|
|
#endif
|
|
}
|
|
|
|
static bool32
|
|
rasterRenderFast(Raster *raster, int32 x, int32 y)
|
|
{
|
|
Raster *src = raster;
|
|
Raster *dst = Raster::getCurrentContext();
|
|
Gl3Raster *natdst = PLUGINOFFSET(Gl3Raster, dst, nativeRasterOffset);
|
|
Gl3Raster *natsrc = PLUGINOFFSET(Gl3Raster, src, nativeRasterOffset);
|
|
|
|
switch(dst->type){
|
|
case Raster::NORMAL:
|
|
case Raster::TEXTURE:
|
|
case Raster::CAMERATEXTURE:
|
|
switch(src->type){
|
|
case Raster::CAMERA:
|
|
setActiveTexture(0);
|
|
glBindTexture(GL_TEXTURE_2D, natdst->texid);
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, (dst->height-src->height)-y,
|
|
0, 0, src->width, src->height);
|
|
glBindTexture(GL_TEXTURE_2D, boundTexture[0]);
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef LIBRW_SDL2
|
|
|
|
static void
|
|
addVideoMode(int displayIndex, int modeIndex)
|
|
{
|
|
int i;
|
|
SDL_DisplayMode mode;
|
|
|
|
SDL_GetDisplayMode(displayIndex, modeIndex, &mode);
|
|
|
|
for(i = 1; i < glGlobals.numModes; i++){
|
|
if(glGlobals.modes[i].mode.w == mode.w &&
|
|
glGlobals.modes[i].mode.h == mode.h &&
|
|
glGlobals.modes[i].mode.format == mode.format){
|
|
// had this mode already, remember highest refresh rate
|
|
if(mode.refresh_rate > glGlobals.modes[i].mode.refresh_rate)
|
|
glGlobals.modes[i].mode.refresh_rate = mode.refresh_rate;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// none found, add
|
|
glGlobals.modes[glGlobals.numModes].mode = mode;
|
|
glGlobals.modes[glGlobals.numModes].flags = VIDEOMODEEXCLUSIVE;
|
|
glGlobals.numModes++;
|
|
}
|
|
|
|
static void
|
|
makeVideoModeList(int displayIndex)
|
|
{
|
|
int i, num, depth;
|
|
|
|
num = SDL_GetNumDisplayModes(displayIndex);
|
|
rwFree(glGlobals.modes);
|
|
glGlobals.modes = rwNewT(DisplayMode, num+1, ID_DRIVER | MEMDUR_EVENT);
|
|
|
|
SDL_GetCurrentDisplayMode(displayIndex, &glGlobals.modes[0].mode);
|
|
glGlobals.modes[0].flags = 0;
|
|
glGlobals.numModes = 1;
|
|
|
|
for(i = 0; i < num; i++)
|
|
addVideoMode(displayIndex, i);
|
|
|
|
for(i = 0; i < glGlobals.numModes; i++){
|
|
depth = SDL_BITSPERPIXEL(glGlobals.modes[i].mode.format);
|
|
// set depth to power of two
|
|
for(glGlobals.modes[i].depth = 1; glGlobals.modes[i].depth < depth; glGlobals.modes[i].depth <<= 1);
|
|
}
|
|
}
|
|
|
|
static int
|
|
openSDL2(EngineOpenParams *openparams)
|
|
{
|
|
glGlobals.winWidth = openparams->width;
|
|
glGlobals.winHeight = openparams->height;
|
|
glGlobals.winTitle = openparams->windowtitle;
|
|
glGlobals.pWindow = openparams->window;
|
|
|
|
memset(&gl3Caps, 0, sizeof(gl3Caps));
|
|
|
|
/* Init SDL */
|
|
if(SDL_InitSubSystem(SDL_INIT_VIDEO)){
|
|
RWERROR((ERR_GENERAL, SDL_GetError()));
|
|
return 0;
|
|
}
|
|
|
|
makeVideoModeList(0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
closeSDL2(void)
|
|
{
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
return 1;
|
|
}
|
|
|
|
static struct {
|
|
int gl;
|
|
int major, minor;
|
|
} profiles[] = {
|
|
{ SDL_GL_CONTEXT_PROFILE_CORE, 3, 3 },
|
|
{ SDL_GL_CONTEXT_PROFILE_CORE, 2, 1 },
|
|
{ SDL_GL_CONTEXT_PROFILE_ES, 3, 1 },
|
|
{ SDL_GL_CONTEXT_PROFILE_ES, 2, 0 },
|
|
{ 0, 0, 0 },
|
|
};
|
|
|
|
static int
|
|
startSDL2(void)
|
|
{
|
|
GLenum status;
|
|
SDL_Window *win;
|
|
SDL_GLContext ctx;
|
|
DisplayMode *mode;
|
|
|
|
mode = &glGlobals.modes[glGlobals.currentMode];
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, glGlobals.numSamples);
|
|
|
|
int i;
|
|
for(i = 0; profiles[i].gl; i++){
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profiles[i].gl);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, profiles[i].major);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, profiles[i].minor);
|
|
|
|
if(mode->flags & VIDEOMODEEXCLUSIVE) {
|
|
win = SDL_CreateWindow(glGlobals.winTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, mode->mode.w, mode->mode.h, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
|
|
if (win)
|
|
SDL_SetWindowDisplayMode(win, &mode->mode);
|
|
} else {
|
|
win = SDL_CreateWindow(glGlobals.winTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, glGlobals.winWidth, glGlobals.winHeight, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
|
|
if (win)
|
|
SDL_SetWindowDisplayMode(win, NULL);
|
|
}
|
|
if(win){
|
|
gl3Caps.gles = profiles[i].gl == SDL_GL_CONTEXT_PROFILE_ES;
|
|
gl3Caps.glversion = profiles[i].major*10 + profiles[i].minor;
|
|
break;
|
|
}
|
|
}
|
|
if(win == nil){
|
|
RWERROR((ERR_GENERAL, SDL_GetError()));
|
|
return 0;
|
|
}
|
|
ctx = SDL_GL_CreateContext(win);
|
|
printf("OpenGL version: %s\n", glGetString(GL_VERSION));
|
|
|
|
/* Init GLEW */
|
|
glewExperimental = GL_TRUE;
|
|
status = glewInit();
|
|
if(status != GLEW_OK){
|
|
RWERROR((ERR_GENERAL, glewGetErrorString(status)));
|
|
SDL_GL_DeleteContext(ctx);
|
|
SDL_DestroyWindow(win);
|
|
return 0;
|
|
}
|
|
if(!GLEW_VERSION_3_3){
|
|
RWERROR((ERR_GENERAL, "OpenGL 3.3 needed"));
|
|
SDL_GL_DeleteContext(ctx);
|
|
SDL_DestroyWindow(win);
|
|
return 0;
|
|
}
|
|
glGlobals.window = win;
|
|
glGlobals.glcontext = ctx;
|
|
*glGlobals.pWindow = win;
|
|
glGlobals.presentWidth = 0;
|
|
glGlobals.presentHeight = 0;
|
|
glGlobals.presentOffX = 0;
|
|
glGlobals.presentOffY = 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
stopSDL2(void)
|
|
{
|
|
SDL_GL_DeleteContext(glGlobals.glcontext);
|
|
SDL_DestroyWindow(glGlobals.window);
|
|
return 1;
|
|
}
|
|
#else
|
|
|
|
static void
|
|
addVideoMode(const GLFWvidmode *mode)
|
|
{
|
|
int i;
|
|
|
|
for(i = 1; i < glGlobals.numModes; i++){
|
|
if(glGlobals.modes[i].mode.width == mode->width &&
|
|
glGlobals.modes[i].mode.height == mode->height &&
|
|
glGlobals.modes[i].mode.redBits == mode->redBits &&
|
|
glGlobals.modes[i].mode.greenBits == mode->greenBits &&
|
|
glGlobals.modes[i].mode.blueBits == mode->blueBits){
|
|
// had this mode already, remember highest refresh rate
|
|
if(mode->refreshRate > glGlobals.modes[i].mode.refreshRate)
|
|
glGlobals.modes[i].mode.refreshRate = mode->refreshRate;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// none found, add
|
|
glGlobals.modes[glGlobals.numModes].mode = *mode;
|
|
glGlobals.modes[glGlobals.numModes].flags = VIDEOMODEEXCLUSIVE;
|
|
glGlobals.numModes++;
|
|
}
|
|
|
|
static void
|
|
makeVideoModeList(void)
|
|
{
|
|
int i, num;
|
|
const GLFWvidmode *modes;
|
|
|
|
modes = glfwGetVideoModes(glGlobals.monitor, &num);
|
|
rwFree(glGlobals.modes);
|
|
glGlobals.modes = rwNewT(DisplayMode, num+1, ID_DRIVER | MEMDUR_EVENT);
|
|
|
|
glGlobals.modes[0].mode = *glfwGetVideoMode(glGlobals.monitor);
|
|
glGlobals.modes[0].flags = 0;
|
|
glGlobals.numModes = 1;
|
|
|
|
for(i = 0; i < num; i++)
|
|
addVideoMode(&modes[i]);
|
|
|
|
for(i = 0; i < glGlobals.numModes; i++){
|
|
num = glGlobals.modes[i].mode.redBits +
|
|
glGlobals.modes[i].mode.greenBits +
|
|
glGlobals.modes[i].mode.blueBits;
|
|
// set depth to power of two
|
|
for(glGlobals.modes[i].depth = 1; glGlobals.modes[i].depth < num; glGlobals.modes[i].depth <<= 1);
|
|
}
|
|
}
|
|
|
|
static int
|
|
openGLFW(EngineOpenParams *openparams)
|
|
{
|
|
glGlobals.winWidth = openparams->width;
|
|
glGlobals.winHeight = openparams->height;
|
|
glGlobals.winTitle = openparams->windowtitle;
|
|
glGlobals.pWindow = openparams->window;
|
|
|
|
memset(&gl3Caps, 0, sizeof(gl3Caps));
|
|
|
|
/* Init GLFW */
|
|
if(!glfwInit()){
|
|
RWERROR((ERR_GENERAL, "glfwInit() failed"));
|
|
return 0;
|
|
}
|
|
|
|
glGlobals.monitor = glfwGetMonitors(&glGlobals.numMonitors)[0];
|
|
|
|
makeVideoModeList();
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
closeGLFW(void)
|
|
{
|
|
glfwTerminate();
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
glfwerr(int error, const char *desc)
|
|
{
|
|
fprintf(stderr, "GLFW Error: %s\n", desc);
|
|
}
|
|
|
|
static struct {
|
|
int gl;
|
|
int major, minor;
|
|
} profiles[] = {
|
|
{ GLFW_OPENGL_API, 3, 3 },
|
|
{ GLFW_OPENGL_API, 2, 1 },
|
|
{ GLFW_OPENGL_ES_API, 3, 1 },
|
|
{ GLFW_OPENGL_ES_API, 2, 0 },
|
|
{ 0, 0, 0 },
|
|
};
|
|
|
|
static int
|
|
startGLFW(void)
|
|
{
|
|
GLenum status;
|
|
GLFWwindow *win;
|
|
DisplayMode *mode;
|
|
|
|
mode = &glGlobals.modes[glGlobals.currentMode];
|
|
|
|
glfwSetErrorCallback(glfwerr);
|
|
glfwWindowHint(GLFW_RED_BITS, mode->mode.redBits);
|
|
glfwWindowHint(GLFW_GREEN_BITS, mode->mode.greenBits);
|
|
glfwWindowHint(GLFW_BLUE_BITS, mode->mode.blueBits);
|
|
glfwWindowHint(GLFW_REFRESH_RATE, mode->mode.refreshRate);
|
|
glfwWindowHint(GLFW_SAMPLES, glGlobals.numSamples);
|
|
|
|
int i;
|
|
for(i = 0; profiles[i].gl; i++){
|
|
glfwWindowHint(GLFW_CLIENT_API, profiles[i].gl);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, profiles[i].major);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, profiles[i].minor);
|
|
|
|
if(mode->flags & VIDEOMODEEXCLUSIVE)
|
|
win = glfwCreateWindow(mode->mode.width, mode->mode.height, glGlobals.winTitle, glGlobals.monitor, nil);
|
|
else
|
|
win = glfwCreateWindow(glGlobals.winWidth, glGlobals.winHeight, glGlobals.winTitle, nil, nil);
|
|
if(win){
|
|
gl3Caps.gles = profiles[i].gl == GLFW_OPENGL_ES_API;
|
|
gl3Caps.glversion = profiles[i].major*10 + profiles[i].minor;
|
|
break;
|
|
}
|
|
}
|
|
if(win == nil){
|
|
RWERROR((ERR_GENERAL, "glfwCreateWindow() failed"));
|
|
return 0;
|
|
}
|
|
glfwMakeContextCurrent(win);
|
|
printf("OpenGL version: %s\n", glGetString(GL_VERSION));
|
|
|
|
/* Init GLEW */
|
|
glewExperimental = GL_TRUE;
|
|
status = glewInit();
|
|
if(status != GLEW_OK){
|
|
RWERROR((ERR_GENERAL, glewGetErrorString(status)));
|
|
glfwDestroyWindow(win);
|
|
return 0;
|
|
}
|
|
if(!GLEW_VERSION_3_3){
|
|
RWERROR((ERR_GENERAL, "OpenGL 3.3 needed"));
|
|
glfwDestroyWindow(win);
|
|
return 0;
|
|
}
|
|
glGlobals.window = win;
|
|
*glGlobals.pWindow = win;
|
|
glGlobals.presentWidth = 0;
|
|
glGlobals.presentHeight = 0;
|
|
glGlobals.presentOffX = 0;
|
|
glGlobals.presentOffY = 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
stopGLFW(void)
|
|
{
|
|
glfwDestroyWindow(glGlobals.window);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
initOpenGL(void)
|
|
{
|
|
int numExt;
|
|
glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
|
|
for(int i = 0; i < numExt; i++){
|
|
const char *ext = (const char*)glGetStringi(GL_EXTENSIONS, i);
|
|
if(strcmp(ext, "GL_EXT_texture_compression_s3tc") == 0)
|
|
gl3Caps.dxtSupported = true;
|
|
else if(strcmp(ext, "GL_KHR_texture_compression_astc_ldr") == 0)
|
|
gl3Caps.astcSupported = true;
|
|
// printf("%d %s\n", i, ext);
|
|
}
|
|
|
|
if(gl3Caps.gles){
|
|
if(gl3Caps.glversion >= 30)
|
|
shaderDecl = shaderDecl310es;
|
|
else
|
|
shaderDecl = shaderDecl100es;
|
|
}else{
|
|
if(gl3Caps.glversion >= 30)
|
|
shaderDecl = shaderDecl330;
|
|
else
|
|
shaderDecl = shaderDecl120;
|
|
}
|
|
|
|
#ifndef RW_GL_USE_UBOS
|
|
u_alphaRef = registerUniform("u_alphaRef");
|
|
u_fogData = registerUniform("u_fogData");
|
|
// u_fogStart = registerUniform("u_fogStart");
|
|
// u_fogEnd = registerUniform("u_fogEnd");
|
|
// u_fogRange = registerUniform("u_fogRange");
|
|
// u_fogDisable = registerUniform("u_fogDisable");
|
|
u_fogColor = registerUniform("u_fogColor");
|
|
u_proj = registerUniform("u_proj");
|
|
u_view = registerUniform("u_view");
|
|
u_world = registerUniform("u_world");
|
|
u_ambLight = registerUniform("u_ambLight");
|
|
u_lightParams = registerUniform("u_lightParams");
|
|
u_lightPosition = registerUniform("u_lightPosition");
|
|
u_lightDirection = registerUniform("u_lightDirection");
|
|
u_lightColor = registerUniform("u_lightColor");
|
|
lastShaderUploaded = nil;
|
|
#else
|
|
registerBlock("Scene");
|
|
registerBlock("Object");
|
|
registerBlock("State");
|
|
#endif
|
|
u_matColor = registerUniform("u_matColor");
|
|
u_surfProps = registerUniform("u_surfProps");
|
|
|
|
glClearColor(0.25, 0.25, 0.25, 1.0);
|
|
|
|
byte whitepixel[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
|
glGenTextures(1, &whitetex);
|
|
glBindTexture(GL_TEXTURE_2D, whitetex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1,
|
|
0, GL_RGBA, GL_UNSIGNED_BYTE, &whitepixel);
|
|
|
|
resetRenderState();
|
|
|
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
|
|
|
|
if(gl3Caps.glversion >= 30){
|
|
glGenVertexArrays(1, &vao);
|
|
glBindVertexArray(vao);
|
|
}
|
|
|
|
#ifdef RW_GL_USE_UBOS
|
|
glGenBuffers(1, &ubo_state);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_state);
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("State"), ubo_state);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformState), &uniformState,
|
|
GL_STREAM_DRAW);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
|
|
glGenBuffers(1, &ubo_scene);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_scene);
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("Scene"), ubo_scene);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformScene), &uniformScene,
|
|
GL_STREAM_DRAW);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
|
|
glGenBuffers(1, &ubo_object);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo_object);
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("Object"), ubo_object);
|
|
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformObject), &uniformObject,
|
|
GL_STREAM_DRAW);
|
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
#endif
|
|
|
|
#include "shaders/default_vs_gl.inc"
|
|
#include "shaders/simple_fs_gl.inc"
|
|
const char *vs[] = { shaderDecl, header_vert_src, default_vert_src, nil };
|
|
const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil };
|
|
defaultShader = Shader::create(vs, fs);
|
|
assert(defaultShader);
|
|
|
|
openIm2D();
|
|
openIm3D();
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
termOpenGL(void)
|
|
{
|
|
closeIm3D();
|
|
closeIm2D();
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
finalizeOpenGL(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
#ifdef LIBRW_SDL2
|
|
static int
|
|
deviceSystemSDL2(DeviceReq req, void *arg, int32 n)
|
|
{
|
|
VideoMode *rwmode;
|
|
|
|
switch(req){
|
|
case DEVICEOPEN:
|
|
return openSDL2((EngineOpenParams*)arg);
|
|
case DEVICECLOSE:
|
|
return closeSDL2();
|
|
|
|
case DEVICEINIT:
|
|
return startSDL2() && initOpenGL();
|
|
case DEVICETERM:
|
|
return termOpenGL() && stopSDL2();
|
|
|
|
case DEVICEFINALIZE:
|
|
return finalizeOpenGL();
|
|
|
|
// TODO: implement subsystems
|
|
|
|
case DEVICEGETNUMVIDEOMODES:
|
|
return glGlobals.numModes;
|
|
|
|
case DEVICEGETCURRENTVIDEOMODE:
|
|
return glGlobals.currentMode;
|
|
|
|
case DEVICESETVIDEOMODE:
|
|
if(n >= glGlobals.numModes)
|
|
return 0;
|
|
glGlobals.currentMode = n;
|
|
return 1;
|
|
|
|
case DEVICEGETVIDEOMODEINFO:
|
|
rwmode = (VideoMode*)arg;
|
|
rwmode->width = glGlobals.modes[n].mode.w;
|
|
rwmode->height = glGlobals.modes[n].mode.h;
|
|
rwmode->depth = glGlobals.modes[n].depth;
|
|
rwmode->flags = glGlobals.modes[n].flags;
|
|
return 1;
|
|
|
|
case DEVICEGETMAXMULTISAMPLINGLEVELS:
|
|
{
|
|
GLint maxSamples;
|
|
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
|
|
if(maxSamples == 0)
|
|
return 1;
|
|
return maxSamples;
|
|
}
|
|
case DEVICEGETMULTISAMPLINGLEVELS:
|
|
if(glGlobals.numSamples == 0)
|
|
return 1;
|
|
return glGlobals.numSamples;
|
|
case DEVICESETMULTISAMPLINGLEVELS:
|
|
glGlobals.numSamples = (uint32)n;
|
|
return 1;
|
|
default:
|
|
assert(0 && "not implemented");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#else
|
|
|
|
static int
|
|
deviceSystemGLFW(DeviceReq req, void *arg, int32 n)
|
|
{
|
|
GLFWmonitor **monitors;
|
|
VideoMode *rwmode;
|
|
|
|
switch(req){
|
|
case DEVICEOPEN:
|
|
return openGLFW((EngineOpenParams*)arg);
|
|
case DEVICECLOSE:
|
|
return closeGLFW();
|
|
|
|
case DEVICEINIT:
|
|
return startGLFW() && initOpenGL();
|
|
case DEVICETERM:
|
|
return termOpenGL() && stopGLFW();
|
|
|
|
case DEVICEFINALIZE:
|
|
return finalizeOpenGL();
|
|
|
|
|
|
case DEVICEGETNUMSUBSYSTEMS:
|
|
return glGlobals.numMonitors;
|
|
|
|
case DEVICEGETCURRENTSUBSYSTEM:
|
|
return glGlobals.currentMonitor;
|
|
|
|
case DEVICESETSUBSYSTEM:
|
|
monitors = glfwGetMonitors(&glGlobals.numMonitors);
|
|
if(n >= glGlobals.numMonitors)
|
|
return 0;
|
|
glGlobals.currentMonitor = n;
|
|
glGlobals.monitor = monitors[glGlobals.currentMonitor];
|
|
return 1;
|
|
|
|
case DEVICEGETSUBSSYSTEMINFO:
|
|
monitors = glfwGetMonitors(&glGlobals.numMonitors);
|
|
if(n >= glGlobals.numMonitors)
|
|
return 0;
|
|
strncpy(((SubSystemInfo*)arg)->name, glfwGetMonitorName(monitors[n]), sizeof(SubSystemInfo::name));
|
|
return 1;
|
|
|
|
|
|
case DEVICEGETNUMVIDEOMODES:
|
|
return glGlobals.numModes;
|
|
|
|
case DEVICEGETCURRENTVIDEOMODE:
|
|
return glGlobals.currentMode;
|
|
|
|
case DEVICESETVIDEOMODE:
|
|
if(n >= glGlobals.numModes)
|
|
return 0;
|
|
glGlobals.currentMode = n;
|
|
return 1;
|
|
|
|
case DEVICEGETVIDEOMODEINFO:
|
|
rwmode = (VideoMode*)arg;
|
|
rwmode->width = glGlobals.modes[n].mode.width;
|
|
rwmode->height = glGlobals.modes[n].mode.height;
|
|
rwmode->depth = glGlobals.modes[n].depth;
|
|
rwmode->flags = glGlobals.modes[n].flags;
|
|
return 1;
|
|
|
|
case DEVICEGETMAXMULTISAMPLINGLEVELS:
|
|
{
|
|
GLint maxSamples;
|
|
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
|
|
if(maxSamples == 0)
|
|
return 1;
|
|
return maxSamples;
|
|
}
|
|
case DEVICEGETMULTISAMPLINGLEVELS:
|
|
if(glGlobals.numSamples == 0)
|
|
return 1;
|
|
return glGlobals.numSamples;
|
|
case DEVICESETMULTISAMPLINGLEVELS:
|
|
glGlobals.numSamples = (uint32)n;
|
|
return 1;
|
|
default:
|
|
assert(0 && "not implemented");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
Device renderdevice = {
|
|
-1.0f, 1.0f,
|
|
gl3::beginUpdate,
|
|
gl3::endUpdate,
|
|
gl3::clearCamera,
|
|
gl3::showRaster,
|
|
gl3::rasterRenderFast,
|
|
gl3::setRenderState,
|
|
gl3::getRenderState,
|
|
gl3::im2DRenderLine,
|
|
gl3::im2DRenderTriangle,
|
|
gl3::im2DRenderPrimitive,
|
|
gl3::im2DRenderIndexedPrimitive,
|
|
gl3::im3DTransform,
|
|
gl3::im3DRenderPrimitive,
|
|
gl3::im3DRenderIndexedPrimitive,
|
|
gl3::im3DEnd,
|
|
#ifdef LIBRW_SDL2
|
|
gl3::deviceSystemSDL2
|
|
#else
|
|
gl3::deviceSystemGLFW
|
|
#endif
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
#else
|
|
// urgh, probably should get rid of that eventually
|
|
#include "rwgl3.h"
|
|
namespace rw {
|
|
namespace gl3 {
|
|
Gl3Caps gl3Caps;
|
|
bool32 needToReadBackTextures;
|
|
}
|
|
}
|
|
#endif
|