forked from Mirrors/openclonk
784 lines
25 KiB
C++
784 lines
25 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
|
|
/* OpenGL implementation of NewGfx */
|
|
|
|
#include "C4Include.h"
|
|
#include <C4DrawGL.h>
|
|
|
|
#include <C4Surface.h>
|
|
#include <C4Window.h>
|
|
#include <C4FoWRegion.h>
|
|
#include "C4Rect.h"
|
|
#include "C4Config.h"
|
|
#include "C4Application.h"
|
|
|
|
#ifndef USE_CONSOLE
|
|
|
|
// MSVC doesn't define M_PI in math.h unless requested
|
|
#ifdef _MSC_VER
|
|
#define _USE_MATH_DEFINES
|
|
#endif /* _MSC_VER */
|
|
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <limits.h>
|
|
|
|
C4DrawGLShader::C4DrawGLShader(Type shader_type)
|
|
{
|
|
GLint gl_type;
|
|
switch(shader_type)
|
|
{
|
|
case FRAGMENT: gl_type = GL_FRAGMENT_SHADER_ARB; break;
|
|
case VERTEX: gl_type = GL_VERTEX_SHADER_ARB; break;
|
|
case GEOMETRY: gl_type = GL_GEOMETRY_SHADER_ARB; break;
|
|
default: assert(false); break;
|
|
}
|
|
|
|
Shader = glCreateShaderObjectARB(gl_type);
|
|
if(!Shader) throw C4DrawGLError(FormatString("Failed to create shader")); // TODO: custom error class?
|
|
}
|
|
|
|
C4DrawGLShader::~C4DrawGLShader()
|
|
{
|
|
glDeleteObjectARB(Shader);
|
|
}
|
|
|
|
void C4DrawGLShader::Load(const char* code)
|
|
{
|
|
glShaderSourceARB(Shader, 1, &code, NULL);
|
|
glCompileShaderARB(Shader);
|
|
|
|
GLint compile_status;
|
|
glGetObjectParameterivARB(Shader, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);
|
|
if(compile_status != GL_TRUE)
|
|
{
|
|
const char* shader_type_str;
|
|
switch(GetType())
|
|
{
|
|
case VERTEX: shader_type_str = "vertex"; break;
|
|
case FRAGMENT: shader_type_str = "fragment"; break;
|
|
case GEOMETRY: shader_type_str = "geometry"; break;
|
|
default: assert(false); break;
|
|
}
|
|
|
|
GLint length;
|
|
glGetObjectParameterivARB(Shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
|
|
if(length > 0)
|
|
{
|
|
std::vector<char> error_message(length);
|
|
glGetInfoLogARB(Shader, length, NULL, &error_message[0]);
|
|
throw C4DrawGLError(FormatString("Failed to compile %s shader: %s", shader_type_str, &error_message[0]));
|
|
}
|
|
else
|
|
{
|
|
throw C4DrawGLError(FormatString("Failed to compile %s shader", shader_type_str));
|
|
}
|
|
}
|
|
}
|
|
|
|
StdMeshMaterialShader::Type C4DrawGLShader::GetType() const
|
|
{
|
|
GLint shader_type;
|
|
glGetObjectParameterivARB(Shader, GL_OBJECT_SUBTYPE_ARB, &shader_type);
|
|
|
|
switch(shader_type)
|
|
{
|
|
case GL_FRAGMENT_SHADER_ARB: return FRAGMENT;
|
|
case GL_VERTEX_SHADER_ARB: return VERTEX;
|
|
case GL_GEOMETRY_SHADER_ARB: return GEOMETRY;
|
|
default: assert(false); return static_cast<StdMeshMaterialShader::Type>(-1);
|
|
}
|
|
}
|
|
|
|
C4DrawGLProgram::C4DrawGLProgram(const C4DrawGLShader* fragment_shader, const C4DrawGLShader* vertex_shader, const C4DrawGLShader* geometry_shader)
|
|
{
|
|
Program = glCreateProgramObjectARB();
|
|
if(fragment_shader != NULL)
|
|
glAttachObjectARB(Program, fragment_shader->Shader);
|
|
if(vertex_shader != NULL)
|
|
glAttachObjectARB(Program, vertex_shader->Shader);
|
|
if(geometry_shader != NULL)
|
|
glAttachObjectARB(Program, geometry_shader->Shader);
|
|
glLinkProgramARB(Program);
|
|
|
|
GLint link_status;
|
|
glGetObjectParameterivARB(Program, GL_OBJECT_LINK_STATUS_ARB, &link_status);
|
|
if(link_status != GL_TRUE)
|
|
{
|
|
GLint length;
|
|
glGetObjectParameterivARB(Program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
|
|
if(length > 0)
|
|
{
|
|
std::vector<char> error_message(length);
|
|
glGetInfoLogARB(Program, length, NULL, &error_message[0]);
|
|
glDeleteObjectARB(Program);
|
|
throw C4DrawGLError(FormatString("Failed to link program: %s", &error_message[0]));
|
|
}
|
|
else
|
|
{
|
|
glDeleteObjectARB(Program);
|
|
throw C4DrawGLError(StdStrBuf("Failed to link program"));
|
|
}
|
|
}
|
|
}
|
|
|
|
C4DrawGLProgram::~C4DrawGLProgram()
|
|
{
|
|
glDeleteObjectARB(Program);
|
|
}
|
|
|
|
CStdGL::CStdGL():
|
|
pMainCtx(0)
|
|
{
|
|
Default();
|
|
byByteCnt=4;
|
|
// global ptr
|
|
pGL = this;
|
|
lines_tex = 0;
|
|
}
|
|
|
|
CStdGL::~CStdGL()
|
|
{
|
|
Clear();
|
|
pGL=NULL;
|
|
}
|
|
|
|
void CStdGL::Clear()
|
|
{
|
|
NoPrimaryClipper();
|
|
//if (pTexMgr) pTexMgr->IntUnlock(); // cannot do this here or we can't preserve textures across GL reinitialization as required when changing multisampling
|
|
InvalidateDeviceObjects();
|
|
NoPrimaryClipper();
|
|
RenderTarget = NULL;
|
|
// clear context
|
|
if (pCurrCtx) pCurrCtx->Deselect();
|
|
pMainCtx=0;
|
|
C4Draw::Clear();
|
|
}
|
|
|
|
void CStdGL::FillBG(DWORD dwClr)
|
|
{
|
|
if (!pCurrCtx) return;
|
|
glClearColor((float)GetRedValue(dwClr)/255.0f, (float)GetGreenValue(dwClr)/255.0f, (float)GetBlueValue(dwClr)/255.0f, 0.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
bool CStdGL::UpdateClipper()
|
|
{
|
|
// no render target? do nothing
|
|
if (!RenderTarget || !Active) return true;
|
|
// negative/zero?
|
|
int iWdt=Min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1;
|
|
int iHgt=Min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1;
|
|
int iX=iClipX1; if (iX<0) { iWdt+=iX; iX=0; }
|
|
int iY=iClipY1; if (iY<0) { iHgt+=iY; iY=0; }
|
|
|
|
if (iWdt<=0 || iHgt<=0)
|
|
{
|
|
ClipAll=true;
|
|
return true;
|
|
}
|
|
ClipAll=false;
|
|
// set it
|
|
glViewport(iX, RenderTarget->Hgt-iY-iHgt, iWdt, iHgt);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
// Set clipping plane to -1000 and 1000 so that large meshes are not
|
|
// clipped away.
|
|
//glOrtho((GLdouble) iX, (GLdouble) (iX+iWdt), (GLdouble) (iY+iHgt), (GLdouble) iY, -1000.0f, 1000.0f);
|
|
gluOrtho2D((GLdouble) iX, (GLdouble) (iX+iWdt), (GLdouble) (iY+iHgt), (GLdouble) iY);
|
|
//gluOrtho2D((GLdouble) 0, (GLdouble) xRes, (GLdouble) yRes, (GLdouble) yRes-iHgt);
|
|
return true;
|
|
}
|
|
|
|
bool CStdGL::PrepareRendering(C4Surface * sfcToSurface)
|
|
{
|
|
// call from gfx thread only!
|
|
if (!pApp || !pApp->AssertMainThread()) return false;
|
|
// not ready?
|
|
if (!Active)
|
|
//if (!RestoreDeviceObjects())
|
|
return false;
|
|
// target?
|
|
if (!sfcToSurface) return false;
|
|
// target locked?
|
|
if (sfcToSurface->Locked) return false;
|
|
// target is already set as render target?
|
|
if (sfcToSurface != RenderTarget)
|
|
{
|
|
// target is a render-target?
|
|
if (!sfcToSurface->IsRenderTarget()) return false;
|
|
// context
|
|
if (sfcToSurface->pCtx && sfcToSurface->pCtx != pCurrCtx)
|
|
if (!sfcToSurface->pCtx->Select()) return false;
|
|
// set target
|
|
RenderTarget=sfcToSurface;
|
|
// new target has different size; needs other clipping rect
|
|
UpdateClipper();
|
|
}
|
|
// done
|
|
return true;
|
|
}
|
|
|
|
CStdGLCtx *CStdGL::CreateContext(C4Window * pWindow, C4AbstractApp *pApp)
|
|
{
|
|
DebugLog(" gl: Create Context...");
|
|
// safety
|
|
if (!pWindow) return NULL;
|
|
// create it
|
|
CStdGLCtx *pCtx = new CStdGLCtx();
|
|
if (!pMainCtx) pMainCtx = pCtx;
|
|
if (!pCtx->Init(pWindow, pApp))
|
|
{
|
|
delete pCtx; Error(" gl: Error creating secondary context!"); return NULL;
|
|
}
|
|
// creation selected the new context - switch back to previous context
|
|
RenderTarget = NULL;
|
|
pCurrCtx = NULL;
|
|
// done
|
|
return pCtx;
|
|
}
|
|
|
|
#ifdef USE_WIN32_WINDOWS
|
|
CStdGLCtx *CStdGL::CreateContext(HWND hWindow, C4AbstractApp *pApp)
|
|
{
|
|
// safety
|
|
if (!hWindow) return NULL;
|
|
// create it
|
|
CStdGLCtx *pCtx = new CStdGLCtx();
|
|
if (!pCtx->Init(NULL, pApp, hWindow))
|
|
{
|
|
delete pCtx; Error(" gl: Error creating secondary context!"); return NULL;
|
|
}
|
|
if (!pMainCtx)
|
|
{
|
|
pMainCtx = pCtx;
|
|
}
|
|
else
|
|
{
|
|
// creation selected the new context - switch back to previous context
|
|
RenderTarget = NULL;
|
|
pCurrCtx = NULL;
|
|
}
|
|
// done
|
|
return pCtx;
|
|
}
|
|
#endif
|
|
|
|
bool CStdGL::CreatePrimarySurfaces(unsigned int, unsigned int, int iColorDepth, unsigned int)
|
|
{
|
|
// store options
|
|
return RestoreDeviceObjects();
|
|
}
|
|
|
|
void CStdGL::SetupMultiBlt(const C4BltTransform* pTransform, GLuint baseTex, GLuint overlayTex, GLuint normalTex, DWORD dwOverlayModClr)
|
|
{
|
|
// Initialize multi blit shader.
|
|
int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
|
|
glBlendFunc(GL_SRC_ALPHA, iAdditive ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
// TODO: The locations could be cached
|
|
GLint fMod2Location = glGetUniformLocationARB(multi_blt_program->Program, "fMod2");
|
|
GLint fUseLightLocation = glGetUniformLocationARB(multi_blt_program->Program, "fUseLight");
|
|
GLint fUseTextureLocation = glGetUniformLocationARB(multi_blt_program->Program, "fUseTexture");
|
|
GLint fUseOverlayLocation = glGetUniformLocationARB(multi_blt_program->Program, "fUseOverlay");
|
|
GLint fUseNormalLocation = glGetUniformLocationARB(multi_blt_program->Program, "fUseNormal");
|
|
GLint clrModLocation = glGetUniformLocationARB(multi_blt_program->Program, "clrMod");
|
|
GLint overlayClrModLocation = glGetUniformLocationARB(multi_blt_program->Program, "overlayClrMod");
|
|
GLint lightLocation = glGetUniformLocationARB(multi_blt_program->Program, "Light");
|
|
GLint ambientLocation = glGetUniformLocationARB(multi_blt_program->Program, "Ambient");
|
|
GLint textureLocation = glGetUniformLocationARB(multi_blt_program->Program, "Texture");
|
|
GLint overlayLocation = glGetUniformLocationARB(multi_blt_program->Program, "Overlay");
|
|
GLint normalLocation = glGetUniformLocationARB(multi_blt_program->Program, "Normal");
|
|
|
|
const int fMod2 = (dwBlitMode & C4GFXBLIT_MOD2) != 0;
|
|
const int fUseLight = (pFoW != NULL);
|
|
const int fUseTexture = (baseTex != 0);
|
|
const int fUseOverlay = (overlayTex != 0);
|
|
const int fUseNormal = (normalTex != 0);
|
|
const DWORD dwModClr = BlitModulated ? BlitModulateClr : 0xffffffff;
|
|
const float dwMod[4] = {
|
|
((dwModClr >> 16) & 0xff) / 255.0f,
|
|
((dwModClr >> 8) & 0xff) / 255.0f,
|
|
((dwModClr ) & 0xff) / 255.0f,
|
|
((dwModClr >> 24) & 0xff) / 255.0f
|
|
};
|
|
const float dwOverlayMod[4] = {
|
|
((dwOverlayModClr >> 16) & 0xff) / 255.0f,
|
|
((dwOverlayModClr >> 8) & 0xff) / 255.0f,
|
|
((dwOverlayModClr ) & 0xff) / 255.0f,
|
|
((dwOverlayModClr >> 24) & 0xff) / 255.0f
|
|
};
|
|
|
|
glUseProgramObjectARB(multi_blt_program->Program);
|
|
glUniform1iARB(fMod2Location, fMod2);
|
|
glUniform1iARB(fUseLightLocation, fUseLight);
|
|
glUniform1iARB(fUseTextureLocation, fUseTexture);
|
|
glUniform1iARB(fUseOverlayLocation, fUseOverlay);
|
|
glUniform1iARB(fUseNormalLocation, fUseNormal);
|
|
glUniform4fvARB(clrModLocation, 1, dwMod);
|
|
|
|
if(fUseNormal)
|
|
{
|
|
glActiveTexture(GL_TEXTURE4);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, normalTex);
|
|
glUniform1iARB(normalLocation, 4);
|
|
}
|
|
|
|
if(fUseLight)
|
|
{
|
|
const C4Rect LightRect = pFoW->getRegion();
|
|
const int32_t iLightWdt = pFoW->getSurface()->Wdt;
|
|
const int32_t iLightHgt = pFoW->getSurface()->Hgt;
|
|
|
|
const int iVpWdt=Min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1;
|
|
const int iVpHgt=Min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1;
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
|
|
// Ambient texture
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, pFoW->getFoW()->Ambient.Tex);
|
|
glUniform1iARB(ambientLocation, 3);
|
|
|
|
// Setup the texture matrix
|
|
glLoadIdentity();
|
|
glScalef(1.0f/pFoW->getFoW()->Ambient.GetLandscapeWidth(), 1.0f/pFoW->getFoW()->Ambient.GetLandscapeHeight(), 1.0f);
|
|
glTranslatef(LightRect.x, LightRect.y, 0.0f); // TODO: LightRect should have floating point accuracy for this to work best
|
|
glScalef( (float)LightRect.Wdt / (float)iVpWdt, (float)LightRect.Hgt / (float)iVpHgt, 1.0f);
|
|
glTranslatef(0.0f, iVpHgt, 0.0f);
|
|
glScalef(1.0f, -1.0f, 1.0f);
|
|
|
|
// Light texture
|
|
glActiveTexture(GL_TEXTURE2);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, pFoW->getSurface()->ppTex[0]->texName);
|
|
glUniform1iARB(lightLocation, 2);
|
|
|
|
// Setup the texture matrix
|
|
glLoadIdentity();
|
|
|
|
glTranslatef(0.0f, 1.0f - (float)LightRect.Hgt/(float)iLightHgt, 0.0f);
|
|
glScalef(1.0f/iLightWdt, 1.0f/iLightHgt, 1.0f);
|
|
glScalef( (float)LightRect.Wdt / (float)iVpWdt, (float)LightRect.Hgt / (float)iVpHgt, 1.0f);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
}
|
|
|
|
if(overlayTex != 0)
|
|
{
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D, overlayTex);
|
|
glUniform1iARB(overlayLocation, 1);
|
|
glUniform4fvARB(overlayClrModLocation, 1, dwOverlayMod);
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
if(baseTex != 0)
|
|
{
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, baseTex);
|
|
glUniform1iARB(textureLocation, 0);
|
|
}
|
|
|
|
// Apply zoom and transform
|
|
glPushMatrix();
|
|
glTranslatef(ZoomX, ZoomY, 0.0f);
|
|
glScalef(Zoom, Zoom, 1.0f);
|
|
glTranslatef(-ZoomX, -ZoomY, 0.0f);
|
|
|
|
if(pTransform)
|
|
{
|
|
const GLfloat transform[16] = { pTransform->mat[0], pTransform->mat[3], 0, pTransform->mat[6], pTransform->mat[1], pTransform->mat[4], 0, pTransform->mat[7], 0, 0, 1, 0, pTransform->mat[2], pTransform->mat[5], 0, pTransform->mat[8] };
|
|
glMultMatrixf(transform);
|
|
}
|
|
}
|
|
|
|
void CStdGL::ResetMultiBlt(GLuint baseTex, GLuint overlayTex, GLuint normalTex)
|
|
{
|
|
glPopMatrix();
|
|
if(normalTex != 0) { glActiveTexture(GL_TEXTURE4); glDisable(GL_TEXTURE_2D); }
|
|
if(pFoW != NULL) { glActiveTexture(GL_TEXTURE3); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE2); glDisable(GL_TEXTURE_2D); }
|
|
if(overlayTex != 0) { glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); }
|
|
glActiveTexture(GL_TEXTURE0);
|
|
if(baseTex != 0) glDisable(GL_TEXTURE_2D);
|
|
glUseProgramObjectARB(0);
|
|
}
|
|
|
|
void CStdGL::PerformMultiPix(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices)
|
|
{
|
|
// Only direct rendering
|
|
assert(sfcTarget->IsRenderTarget());
|
|
if(!PrepareRendering(sfcTarget)) return;
|
|
|
|
// Draw on pixel center:
|
|
glPushMatrix();
|
|
glTranslatef(0.5f, 0.5f, 0.0f);
|
|
|
|
// Feed the vertices to the GL
|
|
SetupMultiBlt(NULL, 0, 0, 0, 0);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
// This is a workaround. Instead of submitting the whole vertex array to the GL, we do it
|
|
// in batches of 256 vertices. The intel graphics driver on Linux crashes with
|
|
// significantly larger arrays, such as 400. It's not clear to me why, maybe POINT drawing
|
|
// is just not very well tested.
|
|
const unsigned int BATCH_SIZE = 256;
|
|
for(unsigned int i = 0; i < n_vertices; i += BATCH_SIZE)
|
|
{
|
|
glVertexPointer(2, GL_FLOAT, sizeof(C4BltVertex), &vertices[i].ftx);
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(C4BltVertex), &vertices[i].color[0]);
|
|
glDrawArrays(GL_POINTS, 0, std::min(n_vertices - i, BATCH_SIZE));
|
|
}
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glPopMatrix();
|
|
|
|
ResetMultiBlt(0, 0, 0);
|
|
}
|
|
|
|
void CStdGL::PerformMultiLines(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, float width)
|
|
{
|
|
// Only direct rendering
|
|
assert(sfcTarget->IsRenderTarget());
|
|
if(!PrepareRendering(sfcTarget)) return;
|
|
|
|
// In a first step, we transform the lines array to a triangle array, so that we can draw
|
|
// the lines with some thickness.
|
|
// In principle, this step could be easily (and probably much more efficiently) performed
|
|
// by a geometry shader as well, however that would require OpenGL 3.2.
|
|
C4BltVertex* tri_vertices = new C4BltVertex[n_vertices * 3];
|
|
for(unsigned int i = 0; i < n_vertices; i += 2)
|
|
{
|
|
const float x1 = vertices[i].ftx;
|
|
const float y1 = vertices[i].fty;
|
|
const float x2 = vertices[i+1].ftx;
|
|
const float y2 = vertices[i+1].fty;
|
|
|
|
float offx = y1 - y2;
|
|
float offy = x2 - x1;
|
|
float l = sqrtf(offx * offx + offy * offy);
|
|
// avoid division by zero
|
|
l += 0.000000005f;
|
|
offx *= width/l;
|
|
offy *= width/l;
|
|
|
|
tri_vertices[3*i + 0].ftx = x1 + offx; tri_vertices[3*i + 0].fty = y1 + offy;
|
|
tri_vertices[3*i + 1].ftx = x1 - offx; tri_vertices[3*i + 1].fty = y1 - offy;
|
|
tri_vertices[3*i + 2].ftx = x2 - offx; tri_vertices[3*i + 2].fty = y2 - offy;
|
|
tri_vertices[3*i + 3].ftx = x2 + offx; tri_vertices[3*i + 3].fty = y2 + offy;
|
|
|
|
for(int j = 0; j < 4; ++j)
|
|
{
|
|
tri_vertices[3*i + 0].color[j] = vertices[i].color[j];
|
|
tri_vertices[3*i + 1].color[j] = vertices[i].color[j];
|
|
tri_vertices[3*i + 2].color[j] = vertices[i + 1].color[j];
|
|
tri_vertices[3*i + 3].color[j] = vertices[i + 1].color[j];
|
|
}
|
|
|
|
tri_vertices[3*i + 0].tx = 0.f; tri_vertices[3*i + 0].ty = 0.f;
|
|
tri_vertices[3*i + 1].tx = 0.f; tri_vertices[3*i + 1].ty = 2.f;
|
|
tri_vertices[3*i + 2].tx = 1.f; tri_vertices[3*i + 2].ty = 2.f;
|
|
tri_vertices[3*i + 3].tx = 1.f; tri_vertices[3*i + 3].ty = 0.f;
|
|
|
|
tri_vertices[3*i + 4] = tri_vertices[3*i + 2]; // duplicate vertex
|
|
tri_vertices[3*i + 5] = tri_vertices[3*i + 0]; // duplicate vertex
|
|
}
|
|
|
|
// Then, feed the vertices to the GL
|
|
SetupMultiBlt(NULL, lines_tex, 0, 0, 0);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
glClientActiveTexture(GL_TEXTURE0); // lines_tex was loaded in tex0 by SetupMultiBlt
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(C4BltVertex), &tri_vertices[0].tx);
|
|
glVertexPointer(2, GL_FLOAT, sizeof(C4BltVertex), &tri_vertices[0].ftx);
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(C4BltVertex), &tri_vertices[0].color[0]);
|
|
glDrawArrays(GL_TRIANGLES, 0, n_vertices * 3);
|
|
delete[] tri_vertices;
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
ResetMultiBlt(lines_tex, 0, 0);
|
|
}
|
|
|
|
void CStdGL::PerformMultiTris(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, const C4BltTransform* pTransform, C4TexRef* pTex, C4TexRef* pOverlay, C4TexRef* pNormal, DWORD dwOverlayModClr)
|
|
{
|
|
// Only direct rendering
|
|
assert(sfcTarget->IsRenderTarget());
|
|
if(!PrepareRendering(sfcTarget)) return;
|
|
|
|
// Feed the vertices to the GL
|
|
SetupMultiBlt(pTransform, pTex ? pTex->texName : 0, pOverlay ? pOverlay->texName : 0, pNormal ? pNormal->texName : 0, dwOverlayModClr);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
if(pTex)
|
|
{
|
|
// We use the texture coordinate array in texture0 for
|
|
// both the base and the overlay texture
|
|
glClientActiveTexture(GL_TEXTURE0); // pTex was loaded in tex0 by SetupMultiBlt
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(C4BltVertex), &vertices[0].tx);
|
|
}
|
|
|
|
glVertexPointer(2, GL_FLOAT, sizeof(C4BltVertex), &vertices[0].ftx);
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(C4BltVertex), &vertices[0].color[0]);
|
|
glDrawArrays(GL_TRIANGLES, 0, n_vertices);
|
|
|
|
if(pTex) glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
ResetMultiBlt(pTex ? pTex->texName : 0, pOverlay ? pOverlay->texName : 0, pNormal ? pNormal->texName : 0);
|
|
}
|
|
|
|
bool CStdGL::RestoreDeviceObjects()
|
|
{
|
|
assert(pMainCtx);
|
|
// delete any previous objects
|
|
InvalidateDeviceObjects();
|
|
|
|
// set states
|
|
Active = pMainCtx->Select();
|
|
RenderTarget = pApp->pWindow->pSurface;
|
|
|
|
// TODO: I think this should be updated. We need at least GLSL shaders now, which I think is OpenGL 2.0(?)
|
|
// BGRA Pixel Formats, Multitexturing, Texture Combine Environment Modes
|
|
// Check for GL 1.2 and two functions from 1.3 we need.
|
|
if( !GLEW_VERSION_1_2 ||
|
|
glActiveTexture == NULL ||
|
|
glClientActiveTexture == NULL
|
|
) {
|
|
return Error(" gl: OpenGL Version 1.3 or higher required. A better graphics driver will probably help.");
|
|
}
|
|
|
|
// lines texture
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glGenTextures(1, &lines_tex);
|
|
glBindTexture(GL_TEXTURE_2D, lines_tex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
const char * linedata = byByteCnt == 2 ? "\xff\xf0\xff\xff" : "\xff\xff\xff\x00\xff\xff\xff\xff";
|
|
glTexImage2D(GL_TEXTURE_2D, 0, 4, 1, 2, 0, GL_BGRA, byByteCnt == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, linedata);
|
|
|
|
MaxTexSize = 64;
|
|
GLint s = 0;
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s);
|
|
if (s>0) MaxTexSize = s;
|
|
|
|
// restore gamma if active
|
|
if (Active)
|
|
EnableGamma();
|
|
// reset blit states
|
|
dwBlitMode = 0;
|
|
|
|
// The following shaders are used for drawing primitives such as points, lines and sprites.
|
|
// They are used in PerformMultiPix, PerformMultiLines and PerformMultiTris.
|
|
// The fragment shader applies the color modulation, mod2 drawing and the color modulation map
|
|
// on top of the original fragment color.
|
|
// The vertex shader does not do anything special, but it delegates input values to the
|
|
// fragment shader.
|
|
// TODO: It might be more efficient to use separate shaders for pixels, lines and tris.
|
|
const char* vertex_shader_text =
|
|
"varying vec2 texcoord;"
|
|
"void main()"
|
|
"{"
|
|
" texcoord = gl_MultiTexCoord0.xy;"
|
|
" gl_FrontColor = gl_Color;"
|
|
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"
|
|
"}";
|
|
const char* fragment_shader_text =
|
|
"uniform int fMod2;"
|
|
"uniform int fUseLight;"
|
|
"uniform int fUseTexture;"
|
|
"uniform int fUseOverlay;"
|
|
"uniform int fUseNormal;"
|
|
"uniform vec4 clrMod;"
|
|
"uniform vec4 overlayClrMod;"
|
|
"uniform sampler2D Texture;"
|
|
"uniform sampler2D Overlay;"
|
|
"uniform sampler2D Light;"
|
|
"uniform sampler2D Ambient;"
|
|
"uniform sampler2D Normal;"
|
|
"varying vec2 texcoord;"
|
|
"void main()"
|
|
"{"
|
|
// Start with the base color
|
|
" vec4 primaryColor = gl_Color;"
|
|
// Get texture
|
|
" if(fUseTexture != 0)"
|
|
" primaryColor = primaryColor * texture2D(Texture, texcoord);"
|
|
// Get overlay, if any
|
|
" vec4 overlayColor = vec4(1.0, 1.0, 1.0, 0.0);"
|
|
" if(fUseOverlay != 0)"
|
|
" overlayColor = gl_Color * texture2D(Overlay, texcoord);"
|
|
// Mix base with overlay, and apply clrmod (separately for base and overlay)
|
|
" primaryColor.rgb = overlayColor.a * overlayClrMod.rgb * overlayColor.rgb + (1.0 - overlayColor.a) * clrMod.rgb * primaryColor.rgb;"
|
|
// Add alpha for base and overlay, and use weighted mean of clrmod alpha
|
|
" primaryColor.a = clamp(primaryColor.a + overlayColor.a, 0.0, 1.0) * (primaryColor.a * clrMod.a + overlayColor.a * overlayClrMod.a) / (primaryColor.a + overlayColor.a);"
|
|
// Add fog of war (light)
|
|
" vec3 lightClr = vec3(1.0, 1.0, 1.0);"
|
|
" if(fUseLight != 0)"
|
|
" {"
|
|
" vec4 lightPx = texture2D(Light, (gl_TextureMatrix[2] * gl_FragCoord).xy);"
|
|
" vec3 lightDir = normalize(vec3(vec2(1.0, 1.0) - lightPx.gb * 3.0, 0.3));"
|
|
" float lightIntensity = 2.0 * lightPx.r;"
|
|
" vec3 normalDir;"
|
|
" if(fUseNormal != 0)"
|
|
" {"
|
|
" vec4 normalPx = texture2D(Normal, texcoord);"
|
|
" normalDir = normalize(vec3( (normalPx.xy - vec2(0.5, 0.5))*2.0, 0.3));"
|
|
" }"
|
|
" else"
|
|
" {"
|
|
" normalDir = vec3(0.0, 0.0, 1.0);"
|
|
" }"
|
|
" float ambient = texture2D(Ambient, (gl_TextureMatrix[3] * gl_FragCoord).xy).r;"
|
|
" lightClr = ambient * lightClr + (1.0 - ambient) * vec3(1.0, 1.0, 1.0) * lightIntensity * (0.25 + 0.75 * dot(normalDir, lightDir));"
|
|
" }"
|
|
// Final output, depending on blit mode
|
|
" if(fMod2 != 0)"
|
|
" gl_FragColor = clamp(2.0 * primaryColor * vec4(lightClr, 1.0) - 0.5, 0.0, 1.0);"
|
|
" else"
|
|
" gl_FragColor = clamp(primaryColor * vec4(lightClr, 1.0), 0.0, 1.0);"
|
|
"}";
|
|
|
|
C4DrawGLShader vertex_shader(StdMeshMaterialShader::VERTEX);
|
|
vertex_shader.Load(vertex_shader_text);
|
|
|
|
C4DrawGLShader fragment_shader(StdMeshMaterialShader::FRAGMENT);
|
|
fragment_shader.Load(fragment_shader_text);
|
|
|
|
multi_blt_program.reset(new C4DrawGLProgram(&fragment_shader, &vertex_shader, NULL));
|
|
|
|
// done
|
|
return Active;
|
|
}
|
|
|
|
bool CStdGL::InvalidateDeviceObjects()
|
|
{
|
|
bool fSuccess=true;
|
|
// clear gamma
|
|
#ifndef USE_SDL_MAINLOOP
|
|
DisableGamma();
|
|
#endif
|
|
// deactivate
|
|
Active=false;
|
|
multi_blt_program.reset(NULL);
|
|
// invalidate font objects
|
|
// invalidate primary surfaces
|
|
if (lines_tex)
|
|
{
|
|
glDeleteTextures(1, &lines_tex);
|
|
lines_tex = 0;
|
|
}
|
|
return fSuccess;
|
|
}
|
|
|
|
bool CStdGL::EnsureAnyContext()
|
|
{
|
|
// Make sure some context is selected
|
|
if (pCurrCtx) return true;
|
|
if (!pMainCtx) return false;
|
|
return pMainCtx->Select();
|
|
}
|
|
|
|
bool CStdGL::Error(const char *szMsg)
|
|
{
|
|
#ifdef USE_WIN32_WINDOWS
|
|
DWORD err = GetLastError();
|
|
#endif
|
|
bool r = C4Draw::Error(szMsg);
|
|
#ifdef USE_WIN32_WINDOWS
|
|
wchar_t * lpMsgBuf;
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
err,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &lpMsgBuf,
|
|
0, NULL );
|
|
LogF(" gl: GetLastError() = %d - %s", err, StdStrBuf(lpMsgBuf).getData());
|
|
LocalFree(lpMsgBuf);
|
|
#endif
|
|
LogF(" gl: %s", glGetString(GL_VENDOR));
|
|
LogF(" gl: %s", glGetString(GL_RENDERER));
|
|
LogF(" gl: %s", glGetString(GL_VERSION));
|
|
LogF(" gl: %s", glGetString(GL_EXTENSIONS));
|
|
return r;
|
|
}
|
|
|
|
bool CStdGL::CheckGLError(const char *szAtOp)
|
|
{
|
|
GLenum err = glGetError();
|
|
if (!err) return true;
|
|
Log(szAtOp);
|
|
switch (err)
|
|
{
|
|
case GL_INVALID_ENUM: Log("GL_INVALID_ENUM"); break;
|
|
case GL_INVALID_VALUE: Log("GL_INVALID_VALUE"); break;
|
|
case GL_INVALID_OPERATION: Log("GL_INVALID_OPERATION"); break;
|
|
case GL_STACK_OVERFLOW: Log("GL_STACK_OVERFLOW"); break;
|
|
case GL_STACK_UNDERFLOW: Log("GL_STACK_UNDERFLOW"); break;
|
|
case GL_OUT_OF_MEMORY: Log("GL_OUT_OF_MEMORY"); break;
|
|
default: Log("unknown error"); break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CStdGL *pGL=NULL;
|
|
|
|
#ifdef USE_WIN32_WINDOWS
|
|
void CStdGL::TaskOut()
|
|
{
|
|
if (pCurrCtx) pCurrCtx->Deselect();
|
|
}
|
|
#endif
|
|
|
|
bool CStdGL::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)
|
|
{
|
|
// Re-create primary clipper to adapt to new size.
|
|
CreatePrimaryClipper(iXRes, iYRes);
|
|
RestoreDeviceObjects();
|
|
return true;
|
|
}
|
|
|
|
void CStdGL::Default()
|
|
{
|
|
C4Draw::Default();
|
|
pCurrCtx = NULL;
|
|
iPixelFormat=0;
|
|
sfcFmt=0;
|
|
iClrDpt=0;
|
|
}
|
|
|
|
#endif // USE_CONSOLE
|