librw/src/gl/gl3device.cpp

631 lines
12 KiB
C++
Raw Normal View History

2016-07-05 11:36:43 +02:00
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "../rwbase.h"
#include "../rwerror.h"
#include "../rwplg.h"
2017-08-29 10:12:56 +02:00
#include "../rwrender.h"
#include "../rwengine.h"
2016-07-05 11:36:43 +02:00
#include "../rwpipeline.h"
#include "../rwobjects.h"
#ifdef RW_OPENGL
#include <GL/glew.h>
2017-08-09 10:57:32 +02:00
#include <GLFW/glfw3.h>
2016-07-05 11:36:43 +02:00
#include "rwgl3.h"
#include "rwgl3shader.h"
#include "rwgl3impl.h"
2016-07-05 11:36:43 +02:00
2017-08-09 10:57:32 +02:00
#define PLUGIN_ID 0
2016-07-05 11:36:43 +02:00
namespace rw {
namespace gl3 {
struct UniformState
{
2016-07-05 18:22:22 +02:00
int32 alphaFunc;
2016-07-05 11:36:43 +02:00
float32 alphaRef;
2016-07-05 18:22:22 +02:00
int32 fogEnable;
float32 fogStart;
float32 fogEnd;
int32 pad[3];
RGBAf fogColor;
2016-07-05 11:36:43 +02:00
};
struct UniformScene
{
float32 proj[16];
float32 view[16];
};
struct UniformLight
{
V3d position;
float32 w;
V3d direction;
int32 pad1;
RGBAf color;
float32 radius;
float32 minusCosAngle;
int32 pad2[2];
};
#define MAX_LIGHTS 8
struct UniformObject
{
2017-08-04 19:54:03 +02:00
RawMatrix world;
RGBAf ambLight;
int32 numLights;
int32 pad[3];
2016-07-05 11:36:43 +02:00
UniformLight lights[MAX_LIGHTS];
};
static GLuint vao;
static GLuint ubo_state, ubo_scene, ubo_object;
static GLuint whitetex;
static UniformState uniformState;
static UniformScene uniformScene;
static UniformObject uniformObject;
int32 u_matColor;
int32 u_surfaceProps;
2016-07-05 11:36:43 +02:00
Shader *simpleShader;
static bool32 stateDirty = 1;
static bool32 sceneDirty = 1;
static bool32 objectDirty = 1;
// cached render states
static bool32 vertexAlpha;
static bool32 textureAlpha;
static uint32 srcblend, destblend;
static uint32 zwrite;
static uint32 ztest;
2017-08-09 10:57:32 +02:00
static int32 activeTexture;
static uint32 blendMap[] = {
2016-07-05 11:36:43 +02:00
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,
};
2017-08-09 10:57:32 +02:00
static void
setVertexAlpha(bool32 enable)
2016-07-05 11:36:43 +02:00
{
2017-08-09 10:57:32 +02:00
if(vertexAlpha != enable){
vertexAlpha = enable;
if(!textureAlpha){
if(enable)
2016-07-05 11:36:43 +02:00
glEnable(GL_BLEND);
2017-08-09 10:57:32 +02:00
else
2016-07-05 11:36:43 +02:00
glDisable(GL_BLEND);
}
2017-08-09 10:57:32 +02:00
}
}
static void
setRenderState(int32 state, uint32 value)
{
switch(state){
case VERTEXALPHA:
setVertexAlpha(value);
2016-07-05 11:36:43 +02:00
break;
case SRCBLEND:
if(srcblend != value){
srcblend = value;
glBlendFunc(blendMap[srcblend], blendMap[destblend]);
}
break;
case DESTBLEND:
if(destblend != value){
destblend = value;
glBlendFunc(blendMap[srcblend], blendMap[destblend]);
}
break;
case ZTESTENABLE:
if(ztest != value){
ztest = value;
if(ztest)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
}
break;
case ZWRITEENABLE:
if(zwrite != (value ? GL_TRUE : GL_FALSE)){
zwrite = value ? GL_TRUE : GL_FALSE;
glDepthMask(zwrite);
}
break;
2016-07-05 18:22:22 +02:00
case FOGENABLE:
if(uniformState.fogEnable != value){
uniformState.fogEnable = value;
stateDirty = 1;
}
break;
case FOGCOLOR:
// no cache check here...too lazy
convColor(&uniformState.fogColor, (RGBA*)&value);
stateDirty = 1;
break;
2016-07-05 11:36:43 +02:00
case ALPHATESTFUNC:
2016-07-05 18:22:22 +02:00
if(uniformState.alphaFunc != value){
uniformState.alphaFunc = value;
stateDirty = 1;
}
2016-07-05 11:36:43 +02:00
break;
case ALPHATESTREF:
2016-07-05 18:22:22 +02:00
if(uniformState.alphaRef != value/255.0f){
uniformState.alphaRef = value/255.0f;
stateDirty = 1;
}
2016-07-05 11:36:43 +02:00
break;
}
}
2017-08-09 10:57:32 +02:00
static uint32
2016-07-05 11:36:43 +02:00
getRenderState(int32 state)
{
2016-07-05 18:22:22 +02:00
RGBA rgba;
2016-07-05 11:36:43 +02:00
switch(state){
case VERTEXALPHA:
return vertexAlpha;
case SRCBLEND:
return srcblend;
case DESTBLEND:
return destblend;
case ZTESTENABLE:
return ztest;
case ZWRITEENABLE:
return zwrite;
2016-07-05 18:22:22 +02:00
case FOGENABLE:
return uniformState.fogEnable;
case FOGCOLOR:
convColor(&rgba, &uniformState.fogColor);
return *(uint32*)&rgba;
2016-07-05 11:36:43 +02:00
case ALPHATESTFUNC:
return uniformState.alphaFunc;
case ALPHATESTREF:
2017-08-09 10:57:32 +02:00
return (uint32)(uniformState.alphaRef*255.0f);
2016-07-05 11:36:43 +02:00
}
2016-07-05 18:22:22 +02:00
return 0;
2016-07-05 11:36:43 +02:00
}
2017-08-09 10:57:32 +02:00
static void
2016-07-05 11:36:43 +02:00
resetRenderState(void)
{
2017-08-09 10:57:32 +02:00
uniformState.alphaFunc = ALPHAGREATEREQUAL;
2016-07-05 11:36:43 +02:00
uniformState.alphaRef = 10.0f/255.0f;
2016-07-05 18:22:22 +02:00
uniformState.fogEnable = 0;
uniformState.fogStart = 0.0f;
uniformState.fogColor = { 1.0f, 1.0f, 1.0f, 1.0f };
2016-07-05 11:36:43 +02:00
stateDirty = 1;
vertexAlpha = 0;
textureAlpha = 0;
glDisable(GL_BLEND);
srcblend = BLENDSRCALPHA;
destblend = BLENDINVSRCALPHA;
glBlendFunc(blendMap[srcblend], blendMap[destblend]);
zwrite = GL_TRUE;
glDepthMask(zwrite);
ztest = 1;
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
for(int i = 0; i < 8; i++){
glActiveTexture(GL_TEXTURE0+i);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
void
setWorldMatrix(Matrix *mat)
{
2017-08-04 19:54:03 +02:00
convMatrix(&uniformObject.world, mat);
2016-07-05 11:36:43 +02:00
objectDirty = 1;
}
void
setAmbientLight(RGBAf *amb)
{
uniformObject.ambLight = *amb;
objectDirty = 1;
}
void
setNumLights(int32 n)
{
uniformObject.numLights = n;
objectDirty = 1;
}
void
setLight(int32 n, Light *light)
{
UniformLight *l;
Frame *f;
Matrix *m;
l = &uniformObject.lights[n];
f = light->getFrame();
if(f){
m = f->getLTM();
l->position = m->pos;
l->direction = m->at;
}
// light has position
2017-08-09 10:57:32 +02:00
l->w = light->getType() >= Light::POINT ? 1.0f : 0.0f;
2016-07-05 11:36:43 +02:00
l->color = light->color;
l->radius = light->radius;
l->minusCosAngle = light->minusCosAngle;
objectDirty = 1;
}
void
setProjectionMatrix(float32 *mat)
{
memcpy(&uniformScene.proj, mat, 64);
sceneDirty = 1;
}
void
setViewMatrix(float32 *mat)
{
memcpy(&uniformScene.view, mat, 64);
sceneDirty = 1;
}
2017-08-09 10:57:32 +02:00
static void
setActiveTexture(int32 n)
{
if(activeTexture != n){
activeTexture = n;
glActiveTexture(n);
}
}
2016-07-05 11:36:43 +02:00
void
setTexture(int32 n, Texture *tex)
{
// TODO: support mipmaps
static GLint filternomip[] = {
0, GL_NEAREST, GL_LINEAR,
GL_NEAREST, GL_LINEAR,
GL_NEAREST, GL_LINEAR
};
static GLint wrap[] = {
0, GL_REPEAT, GL_MIRRORED_REPEAT,
GL_CLAMP, GL_CLAMP_TO_BORDER
};
2016-07-05 11:36:43 +02:00
bool32 alpha;
2017-08-09 10:57:32 +02:00
setActiveTexture(GL_TEXTURE0+n);
2016-07-15 11:55:52 +02:00
if(tex == nil || tex->raster->platform != PLATFORM_GL3 ||
tex->raster->width == 0){
2016-07-05 11:36:43 +02:00
glBindTexture(GL_TEXTURE_2D, whitetex);
alpha = 0;
}else{
Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, tex->raster,
nativeRasterOffset);
glBindTexture(GL_TEXTURE_2D, natras->texid);
alpha = natras->hasAlpha;
if(tex->filterAddressing != natras->filterAddressing){
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filternomip[tex->getFilter()]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filternomip[tex->getFilter()]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap[tex->getAddressU()]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap[tex->getAddressV()]);
natras->filterAddressing = tex->filterAddressing;
}
2016-07-05 11:36:43 +02:00
}
2017-08-09 10:57:32 +02:00
if(n == 0){
if(alpha != textureAlpha){
textureAlpha = alpha;
if(!vertexAlpha){
if(alpha)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
}
}
2016-07-05 11:36:43 +02:00
}
}
void
flushCache(void)
{
if(objectDirty){
glBindBuffer(GL_UNIFORM_BUFFER, ubo_object);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformObject),
&uniformObject);
objectDirty = 0;
}
if(sceneDirty){
glBindBuffer(GL_UNIFORM_BUFFER, ubo_scene);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformScene),
&uniformScene);
sceneDirty = 0;
}
if(stateDirty){
glBindBuffer(GL_UNIFORM_BUFFER, ubo_state);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformState),
&uniformState);
stateDirty = 0;
}
}
2017-08-09 10:57:32 +02:00
static void
2016-07-05 18:22:22 +02:00
clearCamera(Camera *cam, RGBA *col, uint32 mode)
{
RGBAf colf;
GLbitfield mask;
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;
glClear(mask);
}
2017-08-09 10:57:32 +02:00
static GLFWwindow *glfwwindow;
static void
showRaster(Raster *raster)
{
// TODO: do this properly!
glfwSwapBuffers(glfwwindow);
}
static void
2016-07-05 11:36:43 +02:00
beginUpdate(Camera *cam)
{
float view[16], proj[16];
// View Matrix
Matrix inv;
2017-08-04 19:54:03 +02:00
Matrix::invert(&inv, cam->getFrame()->getLTM());
2016-07-05 11:36:43 +02:00
// 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;
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;
2016-07-06 11:44:59 +02:00
proj[8] = cam->viewOffset.x*invwx;
proj[9] = cam->viewOffset.y*invwy;
proj[12] = -proj[8];
proj[13] = -proj[9];
2016-07-05 11:36:43 +02:00
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{
2016-07-06 11:44:59 +02:00
proj[10] = -(cam->farPlane+cam->nearPlane)*invz;
proj[11] = 0.0f;
proj[14] = -2.0f*invz;
proj[15] = 1.0f;
2016-07-05 11:36:43 +02:00
}
setProjectionMatrix(proj);
2016-07-05 18:22:22 +02:00
if(uniformState.fogStart != cam->fogPlane){
uniformState.fogStart = cam->fogPlane;
stateDirty = 1;
}
if(uniformState.fogEnd != cam->farPlane){
uniformState.fogEnd = cam->farPlane;
stateDirty = 1;
}
2016-07-05 11:36:43 +02:00
}
2017-08-09 10:57:32 +02:00
static int
2017-08-29 10:12:56 +02:00
openGLFW(EngineStartParams *startparams)
2017-08-09 10:57:32 +02:00
{
GLenum status;
GLFWwindow *win;
/* Init GLFW */
if(!glfwInit()){
RWERROR((ERR_GENERAL, "glfwInit() failed"));
return 0;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
win = glfwCreateWindow(startparams->width, startparams->height, startparams->windowtitle, 0, 0);
if(win == nil){
RWERROR((ERR_GENERAL, "glfwCreateWindow() failed"));
glfwTerminate();
return 0;
}
glfwMakeContextCurrent(win);
/* Init GLEW */
glewExperimental = GL_TRUE;
status = glewInit();
if(status != GLEW_OK){
RWERROR((ERR_GENERAL, glewGetErrorString(status)));
glfwDestroyWindow(win);
glfwTerminate();
return 0;
}
if(!GLEW_VERSION_3_3){
RWERROR((ERR_GENERAL, "OpenGL 3.3 needed"));
glfwDestroyWindow(win);
glfwTerminate();
return 0;
}
glfwwindow = win;
*startparams->window = win;
return 1;
}
static int
2017-08-29 10:12:56 +02:00
closeGLFW(void)
2016-07-05 11:36:43 +02:00
{
2017-08-09 10:57:32 +02:00
glfwDestroyWindow(glfwwindow);
glfwTerminate();
return 1;
}
2016-07-05 11:36:43 +02:00
2017-08-09 10:57:32 +02:00
static int
initOpenGL(void)
{
registerBlock("Scene");
registerBlock("Object");
registerBlock("State");
u_matColor = registerUniform("u_matColor");
u_surfaceProps = registerUniform("u_surfaceProps");
2016-07-05 11:36:43 +02:00
glClearColor(0.25, 0.25, 0.25, 1.0);
resetRenderState();
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
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_DYNAMIC_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_DYNAMIC_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_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 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_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1,
0, GL_RGBA, GL_UNSIGNED_BYTE, &whitepixel);
2017-08-29 10:12:56 +02:00
#include "shaders/simple_vs_gl3.inc"
#include "shaders/simple_fs_gl3.inc"
simpleShader = Shader::fromStrings(simple_vert_src, simple_frag_src);
openIm2D();
2017-08-29 10:12:56 +02:00
openIm3D();
return 1;
}
2017-08-29 10:12:56 +02:00
static int
termOpenGL(void)
{
closeIm3D();
closeIm2D();
return 1;
}
static int
finalizeOpenGL(void)
{
2017-08-09 10:57:32 +02:00
return 1;
2016-07-05 11:36:43 +02:00
}
2017-08-09 10:57:32 +02:00
static int
deviceSystem(DeviceReq req, void *arg0)
{
switch(req){
2017-08-29 10:12:56 +02:00
case DEVICEOPEN:
return openGLFW((EngineStartParams*)arg0);
case DEVICECLOSE:
return closeGLFW();
2017-08-09 10:57:32 +02:00
case DEVICEINIT:
return initOpenGL();
2017-08-29 10:12:56 +02:00
case DEVICETERM:
return termOpenGL();
case DEVICEFINALIZE:
return finalizeOpenGL();
2017-08-09 10:57:32 +02:00
}
return 1;
}
Device renderdevice = {
-1.0f, 1.0f,
gl3::beginUpdate,
null::endUpdate,
gl3::clearCamera,
gl3::showRaster,
gl3::setRenderState,
gl3::getRenderState,
gl3::im2DRenderIndexedPrimitive,
2017-08-29 10:12:56 +02:00
gl3::im3DTransform,
gl3::im3DRenderIndexed,
gl3::im3DEnd,
2017-08-09 10:57:32 +02:00
gl3::deviceSystem
};
2016-07-05 11:36:43 +02:00
}
}
#endif