934 lines
33 KiB
C++
934 lines
33 KiB
C++
// ---------------------------------------------------------------------------
|
|
//
|
|
// @file TwOpenGLCore.cpp
|
|
// @author Philippe Decaudin
|
|
// @license This file is part of the AntTweakBar library.
|
|
// For conditions of distribution and use, see License.txt
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/*
|
|
#pragma warning GL3 //// used for development
|
|
#define GL3_PROTOTYPES 1 ////
|
|
#include <GL3/gl3.h> ////
|
|
#define ANT_OGL_HEADER_INCLUDED ////
|
|
*/
|
|
|
|
#if defined ANT_OSX
|
|
# include <OpenGL/gl3.h>
|
|
# define ANT_OGL_HEADER_INCLUDED
|
|
#endif
|
|
#include "TwPrecomp.h"
|
|
#include "LoadOGLCore.h"
|
|
#include "TwOpenGLCore.h"
|
|
#include "TwMgr.h"
|
|
|
|
using namespace std;
|
|
|
|
extern const char *g_ErrCantLoadOGL;
|
|
extern const char *g_ErrCantUnloadOGL;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#ifdef _DEBUG
|
|
static void CheckGLCoreError(const char *file, int line, const char *func)
|
|
{
|
|
int err=0;
|
|
char msg[256];
|
|
while( (err=_glGetError())!=0 )
|
|
{
|
|
sprintf(msg, "%s(%d) : [%s] GL_CORE_ERROR=0x%x\n", file, line, func, err);
|
|
#ifdef ANT_WINDOWS
|
|
OutputDebugString(msg);
|
|
#endif
|
|
fprintf(stderr, msg);
|
|
}
|
|
}
|
|
# ifdef __FUNCTION__
|
|
# define CHECK_GL_ERROR CheckGLCoreError(__FILE__, __LINE__, __FUNCTION__)
|
|
# else
|
|
# define CHECK_GL_ERROR CheckGLCoreError(__FILE__, __LINE__, "")
|
|
# endif
|
|
#else
|
|
# define CHECK_GL_ERROR ((void)(0))
|
|
#endif
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static GLuint BindFont(const CTexFont *_Font)
|
|
{
|
|
GLuint TexID = 0;
|
|
_glGenTextures(1, &TexID);
|
|
_glBindTexture(GL_TEXTURE_2D, TexID);
|
|
_glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
|
_glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
|
|
_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
|
_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
|
_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
_glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, _Font->m_TexWidth, _Font->m_TexHeight, 0, GL_RED, GL_UNSIGNED_BYTE, _Font->m_TexBytes);
|
|
_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
|
|
_glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
|
|
_glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
return TexID;
|
|
}
|
|
|
|
static void UnbindFont(GLuint _FontTexID)
|
|
{
|
|
if( _FontTexID>0 )
|
|
_glDeleteTextures(1, &_FontTexID);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static GLuint CompileShader(GLuint shader)
|
|
{
|
|
_glCompileShader(shader); CHECK_GL_ERROR;
|
|
|
|
GLint status;
|
|
_glGetShaderiv(shader, GL_COMPILE_STATUS, &status); CHECK_GL_ERROR;
|
|
if (status == GL_FALSE)
|
|
{
|
|
GLint infoLogLength;
|
|
_glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); CHECK_GL_ERROR;
|
|
|
|
GLchar strInfoLog[256];
|
|
_glGetShaderInfoLog(shader, sizeof(strInfoLog), NULL, strInfoLog); CHECK_GL_ERROR;
|
|
#ifdef ANT_WINDOWS
|
|
OutputDebugString("Compile failure: ");
|
|
OutputDebugString(strInfoLog);
|
|
OutputDebugString("\n");
|
|
#endif
|
|
fprintf(stderr, "Compile failure: %s\n", strInfoLog);
|
|
shader = 0;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
static GLuint LinkProgram(GLuint program)
|
|
{
|
|
_glLinkProgram(program); CHECK_GL_ERROR;
|
|
|
|
GLint status;
|
|
_glGetProgramiv(program, GL_LINK_STATUS, &status); CHECK_GL_ERROR;
|
|
if (status == GL_FALSE)
|
|
{
|
|
GLint infoLogLength;
|
|
_glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); CHECK_GL_ERROR;
|
|
|
|
GLchar strInfoLog[256];
|
|
_glGetProgramInfoLog(program, sizeof(strInfoLog), NULL, strInfoLog); CHECK_GL_ERROR;
|
|
#ifdef ANT_WINDOWS
|
|
OutputDebugString("Linker failure: ");
|
|
OutputDebugString(strInfoLog);
|
|
OutputDebugString("\n");
|
|
#endif
|
|
fprintf(stderr, "Linker failure: %s\n", strInfoLog);
|
|
program = 0;
|
|
}
|
|
|
|
return program;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::ResizeTriBuffers(size_t _NewSize)
|
|
{
|
|
m_TriBufferSize = _NewSize;
|
|
|
|
_glBindVertexArray(m_TriVArray);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriVertices);
|
|
_glBufferData(GL_ARRAY_BUFFER, m_TriBufferSize*sizeof(Vec2), 0, GL_DYNAMIC_DRAW);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriUVs);
|
|
_glBufferData(GL_ARRAY_BUFFER, m_TriBufferSize*sizeof(Vec2), 0, GL_DYNAMIC_DRAW);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriColors);
|
|
_glBufferData(GL_ARRAY_BUFFER, m_TriBufferSize*sizeof(color32), 0, GL_DYNAMIC_DRAW);
|
|
|
|
CHECK_GL_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
int CTwGraphOpenGLCore::Init()
|
|
{
|
|
m_Drawing = false;
|
|
m_FontTexID = 0;
|
|
m_FontTex = NULL;
|
|
|
|
if( LoadOpenGLCore()==0 )
|
|
{
|
|
g_TwMgr->SetLastError(g_ErrCantLoadOGL);
|
|
return 0;
|
|
}
|
|
|
|
// Create line/rect shaders
|
|
const GLchar *lineRectVS[] = {
|
|
"#version 150 core\n"
|
|
"in vec3 vertex;"
|
|
"in vec4 color;"
|
|
"out vec4 fcolor;"
|
|
"void main() { gl_Position = vec4(vertex, 1); fcolor = color; }"
|
|
};
|
|
m_LineRectVS = _glCreateShader(GL_VERTEX_SHADER);
|
|
_glShaderSource(m_LineRectVS, 1, lineRectVS, NULL);
|
|
CompileShader(m_LineRectVS);
|
|
|
|
const GLchar *lineRectFS[] = {
|
|
"#version 150 core\n"
|
|
"precision highp float;"
|
|
"in vec4 fcolor;"
|
|
"out vec4 outColor;"
|
|
"void main() { outColor = fcolor; }"
|
|
};
|
|
m_LineRectFS = _glCreateShader(GL_FRAGMENT_SHADER);
|
|
_glShaderSource(m_LineRectFS, 1, lineRectFS, NULL);
|
|
CompileShader(m_LineRectFS);
|
|
|
|
m_LineRectProgram = _glCreateProgram();
|
|
_glAttachShader(m_LineRectProgram, m_LineRectVS);
|
|
_glAttachShader(m_LineRectProgram, m_LineRectFS);
|
|
_glBindAttribLocation(m_LineRectProgram, 0, "vertex");
|
|
_glBindAttribLocation(m_LineRectProgram, 1, "color");
|
|
LinkProgram(m_LineRectProgram);
|
|
|
|
// Create line/rect vertex buffer
|
|
const GLfloat lineRectInitVertices[] = { 0,0,0, 0,0,0, 0,0,0, 0,0,0 };
|
|
const color32 lineRectInitColors[] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
|
|
_glGenVertexArrays(1, &m_LineRectVArray);
|
|
_glBindVertexArray(m_LineRectVArray);
|
|
_glGenBuffers(1, &m_LineRectVertices);
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_LineRectVertices);
|
|
_glBufferData(GL_ARRAY_BUFFER, sizeof(lineRectInitVertices), lineRectInitVertices, GL_DYNAMIC_DRAW);
|
|
_glGenBuffers(1, &m_LineRectColors);
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_LineRectColors);
|
|
_glBufferData(GL_ARRAY_BUFFER, sizeof(lineRectInitColors), lineRectInitColors, GL_DYNAMIC_DRAW);
|
|
|
|
// Create triangles shaders
|
|
const GLchar *triVS[] = {
|
|
"#version 150 core\n"
|
|
"uniform vec2 offset;"
|
|
"uniform vec2 wndSize;"
|
|
"in vec2 vertex;"
|
|
"in vec4 color;"
|
|
"out vec4 fcolor;"
|
|
"void main() { gl_Position = vec4(2.0*(vertex.x+offset.x-0.5)/wndSize.x - 1.0, 1.0 - 2.0*(vertex.y+offset.y-0.5)/wndSize.y, 0, 1); fcolor = color; }"
|
|
};
|
|
m_TriVS = _glCreateShader(GL_VERTEX_SHADER);
|
|
_glShaderSource(m_TriVS, 1, triVS, NULL);
|
|
CompileShader(m_TriVS);
|
|
|
|
const GLchar *triUniVS[] = {
|
|
"#version 150 core\n"
|
|
"uniform vec2 offset;"
|
|
"uniform vec2 wndSize;"
|
|
"uniform vec4 color;"
|
|
"in vec2 vertex;"
|
|
"out vec4 fcolor;"
|
|
"void main() { gl_Position = vec4(2.0*(vertex.x+offset.x-0.5)/wndSize.x - 1.0, 1.0 - 2.0*(vertex.y+offset.y-0.5)/wndSize.y, 0, 1); fcolor = color; }"
|
|
};
|
|
m_TriUniVS = _glCreateShader(GL_VERTEX_SHADER);
|
|
_glShaderSource(m_TriUniVS, 1, triUniVS, NULL);
|
|
CompileShader(m_TriUniVS);
|
|
|
|
m_TriFS = m_TriUniFS = m_LineRectFS;
|
|
|
|
m_TriProgram = _glCreateProgram();
|
|
_glAttachShader(m_TriProgram, m_TriVS);
|
|
_glAttachShader(m_TriProgram, m_TriFS);
|
|
_glBindAttribLocation(m_TriProgram, 0, "vertex");
|
|
_glBindAttribLocation(m_TriProgram, 1, "color");
|
|
LinkProgram(m_TriProgram);
|
|
m_TriLocationOffset = _glGetUniformLocation(m_TriProgram, "offset");
|
|
m_TriLocationWndSize = _glGetUniformLocation(m_TriProgram, "wndSize");
|
|
|
|
m_TriUniProgram = _glCreateProgram();
|
|
_glAttachShader(m_TriUniProgram, m_TriUniVS);
|
|
_glAttachShader(m_TriUniProgram, m_TriUniFS);
|
|
_glBindAttribLocation(m_TriUniProgram, 0, "vertex");
|
|
_glBindAttribLocation(m_TriUniProgram, 1, "color");
|
|
LinkProgram(m_TriUniProgram);
|
|
m_TriUniLocationOffset = _glGetUniformLocation(m_TriUniProgram, "offset");
|
|
m_TriUniLocationWndSize = _glGetUniformLocation(m_TriUniProgram, "wndSize");
|
|
m_TriUniLocationColor = _glGetUniformLocation(m_TriUniProgram, "color");
|
|
|
|
const GLchar *triTexFS[] = {
|
|
"#version 150 core\n"
|
|
"precision highp float;"
|
|
"uniform sampler2D tex;"
|
|
"in vec2 fuv;"
|
|
"in vec4 fcolor;"
|
|
"out vec4 outColor;"
|
|
// texture2D is deprecated and replaced by texture with GLSL 3.30 but it seems
|
|
// that on Mac Lion backward compatibility is not ensured.
|
|
#if defined(ANT_OSX) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1070)
|
|
"void main() { outColor.rgb = fcolor.bgr; outColor.a = fcolor.a * texture(tex, fuv).r; }"
|
|
#else
|
|
"void main() { outColor.rgb = fcolor.bgr; outColor.a = fcolor.a * texture2D(tex, fuv).r; }"
|
|
#endif
|
|
};
|
|
m_TriTexFS = _glCreateShader(GL_FRAGMENT_SHADER);
|
|
_glShaderSource(m_TriTexFS, 1, triTexFS, NULL);
|
|
CompileShader(m_TriTexFS);
|
|
|
|
const GLchar *triTexVS[] = {
|
|
"#version 150 core\n"
|
|
"uniform vec2 offset;"
|
|
"uniform vec2 wndSize;"
|
|
"in vec2 vertex;"
|
|
"in vec2 uv;"
|
|
"in vec4 color;"
|
|
"out vec2 fuv;"
|
|
"out vec4 fcolor;"
|
|
"void main() { gl_Position = vec4(2.0*(vertex.x+offset.x-0.5)/wndSize.x - 1.0, 1.0 - 2.0*(vertex.y+offset.y-0.5)/wndSize.y, 0, 1); fuv = uv; fcolor = color; }"
|
|
};
|
|
m_TriTexVS = _glCreateShader(GL_VERTEX_SHADER);
|
|
_glShaderSource(m_TriTexVS, 1, triTexVS, NULL);
|
|
CompileShader(m_TriTexVS);
|
|
|
|
const GLchar *triTexUniVS[] = {
|
|
"#version 150 core\n"
|
|
"uniform vec2 offset;"
|
|
"uniform vec2 wndSize;"
|
|
"uniform vec4 color;"
|
|
"in vec2 vertex;"
|
|
"in vec2 uv;"
|
|
"out vec4 fcolor;"
|
|
"out vec2 fuv;"
|
|
"void main() { gl_Position = vec4(2.0*(vertex.x+offset.x-0.5)/wndSize.x - 1.0, 1.0 - 2.0*(vertex.y+offset.y-0.5)/wndSize.y, 0, 1); fuv = uv; fcolor = color; }"
|
|
};
|
|
m_TriTexUniVS = _glCreateShader(GL_VERTEX_SHADER);
|
|
_glShaderSource(m_TriTexUniVS, 1, triTexUniVS, NULL);
|
|
CompileShader(m_TriTexUniVS);
|
|
|
|
m_TriTexUniFS = m_TriTexFS;
|
|
|
|
m_TriTexProgram = _glCreateProgram();
|
|
_glAttachShader(m_TriTexProgram, m_TriTexVS);
|
|
_glAttachShader(m_TriTexProgram, m_TriTexFS);
|
|
_glBindAttribLocation(m_TriTexProgram, 0, "vertex");
|
|
_glBindAttribLocation(m_TriTexProgram, 1, "uv");
|
|
_glBindAttribLocation(m_TriTexProgram, 2, "color");
|
|
LinkProgram(m_TriTexProgram);
|
|
m_TriTexLocationOffset = _glGetUniformLocation(m_TriTexProgram, "offset");
|
|
m_TriTexLocationWndSize = _glGetUniformLocation(m_TriTexProgram, "wndSize");
|
|
m_TriTexLocationTexture = _glGetUniformLocation(m_TriTexProgram, "tex");
|
|
|
|
m_TriTexUniProgram = _glCreateProgram();
|
|
_glAttachShader(m_TriTexUniProgram, m_TriTexUniVS);
|
|
_glAttachShader(m_TriTexUniProgram, m_TriTexUniFS);
|
|
_glBindAttribLocation(m_TriTexUniProgram, 0, "vertex");
|
|
_glBindAttribLocation(m_TriTexUniProgram, 1, "uv");
|
|
_glBindAttribLocation(m_TriTexUniProgram, 2, "color");
|
|
LinkProgram(m_TriTexUniProgram);
|
|
m_TriTexUniLocationOffset = _glGetUniformLocation(m_TriTexUniProgram, "offset");
|
|
m_TriTexUniLocationWndSize = _glGetUniformLocation(m_TriTexUniProgram, "wndSize");
|
|
m_TriTexUniLocationColor = _glGetUniformLocation(m_TriTexUniProgram, "color");
|
|
m_TriTexUniLocationTexture = _glGetUniformLocation(m_TriTexUniProgram, "tex");
|
|
|
|
// Create tri vertex buffer
|
|
_glGenVertexArrays(1, &m_TriVArray);
|
|
_glGenBuffers(1, &m_TriVertices);
|
|
_glGenBuffers(1, &m_TriUVs);
|
|
_glGenBuffers(1, &m_TriColors);
|
|
ResizeTriBuffers(16384); // set initial size
|
|
|
|
CHECK_GL_ERROR;
|
|
return 1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
int CTwGraphOpenGLCore::Shut()
|
|
{
|
|
assert(m_Drawing==false);
|
|
|
|
UnbindFont(m_FontTexID);
|
|
|
|
CHECK_GL_ERROR;
|
|
|
|
_glDeleteProgram(m_LineRectProgram); m_LineRectProgram = 0;
|
|
_glDeleteShader(m_LineRectVS); m_LineRectVS = 0;
|
|
_glDeleteShader(m_LineRectFS); m_LineRectFS = 0;
|
|
|
|
_glDeleteProgram(m_TriProgram); m_TriProgram = 0;
|
|
_glDeleteShader(m_TriVS); m_TriVS = 0;
|
|
|
|
_glDeleteProgram(m_TriUniProgram); m_TriUniProgram = 0;
|
|
_glDeleteShader(m_TriUniVS); m_TriUniVS = 0;
|
|
|
|
_glDeleteProgram(m_TriTexProgram); m_TriTexProgram = 0;
|
|
_glDeleteShader(m_TriTexVS); m_TriTexVS = 0;
|
|
_glDeleteShader(m_TriTexFS); m_TriTexFS = 0;
|
|
|
|
_glDeleteProgram(m_TriTexUniProgram); m_TriTexUniProgram = 0;
|
|
_glDeleteShader(m_TriTexUniVS); m_TriTexUniVS = 0;
|
|
|
|
_glDeleteBuffers(1, &m_LineRectVertices); m_LineRectVertices = 0;
|
|
_glDeleteBuffers(1, &m_LineRectColors); m_LineRectColors = 0;
|
|
_glDeleteVertexArrays(1, &m_LineRectVArray); m_LineRectVArray = 0;
|
|
|
|
_glDeleteBuffers(1, &m_TriVertices); m_TriVertices = 0;
|
|
_glDeleteBuffers(1, &m_TriColors); m_TriColors = 0;
|
|
_glDeleteBuffers(1, &m_TriUVs); m_TriUVs = 0;
|
|
_glDeleteVertexArrays(1, &m_TriVArray); m_TriVArray = 0;
|
|
|
|
CHECK_GL_ERROR;
|
|
|
|
int Res = 1;
|
|
if( UnloadOpenGLCore()==0 )
|
|
{
|
|
g_TwMgr->SetLastError(g_ErrCantUnloadOGL);
|
|
Res = 0;
|
|
}
|
|
|
|
return Res;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::BeginDraw(int _WndWidth, int _WndHeight)
|
|
{
|
|
CHECK_GL_ERROR;
|
|
assert(m_Drawing==false && _WndWidth>0 && _WndHeight>0);
|
|
m_Drawing = true;
|
|
m_WndWidth = _WndWidth;
|
|
m_WndHeight = _WndHeight;
|
|
m_OffsetX = 0;
|
|
m_OffsetY = 0;
|
|
|
|
_glGetIntegerv(GL_VIEWPORT, m_PrevViewport); CHECK_GL_ERROR;
|
|
if( _WndWidth>0 && _WndHeight>0 )
|
|
{
|
|
GLint Vp[4];
|
|
Vp[0] = 0;
|
|
Vp[1] = 0;
|
|
Vp[2] = _WndWidth-1;
|
|
Vp[3] = _WndHeight-1;
|
|
_glViewport(Vp[0], Vp[1], Vp[2], Vp[3]);
|
|
}
|
|
|
|
m_PrevVArray = 0;
|
|
_glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&m_PrevVArray); CHECK_GL_ERROR;
|
|
_glBindVertexArray(0); CHECK_GL_ERROR;
|
|
|
|
m_PrevLineWidth = 1;
|
|
_glGetFloatv(GL_LINE_WIDTH, &m_PrevLineWidth); CHECK_GL_ERROR;
|
|
_glLineWidth(1); CHECK_GL_ERROR;
|
|
|
|
m_PrevLineSmooth = _glIsEnabled(GL_LINE_SMOOTH);
|
|
_glDisable(GL_LINE_SMOOTH); CHECK_GL_ERROR;
|
|
|
|
m_PrevCullFace = _glIsEnabled(GL_CULL_FACE);
|
|
_glDisable(GL_CULL_FACE); CHECK_GL_ERROR;
|
|
|
|
m_PrevDepthTest = _glIsEnabled(GL_DEPTH_TEST);
|
|
_glDisable(GL_DEPTH_TEST); CHECK_GL_ERROR;
|
|
|
|
m_PrevBlend = _glIsEnabled(GL_BLEND);
|
|
_glEnable(GL_BLEND); CHECK_GL_ERROR;
|
|
|
|
m_PrevScissorTest = _glIsEnabled(GL_SCISSOR_TEST);
|
|
_glDisable(GL_SCISSOR_TEST); CHECK_GL_ERROR;
|
|
|
|
_glGetIntegerv(GL_SCISSOR_BOX, m_PrevScissorBox); CHECK_GL_ERROR;
|
|
|
|
_glGetIntegerv(GL_BLEND_SRC, &m_PrevSrcBlend); CHECK_GL_ERROR;
|
|
_glGetIntegerv(GL_BLEND_DST, &m_PrevDstBlend); CHECK_GL_ERROR;
|
|
_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); CHECK_GL_ERROR;
|
|
|
|
m_PrevTexture = 0;
|
|
_glGetIntegerv(GL_TEXTURE_BINDING_2D, &m_PrevTexture); CHECK_GL_ERROR;
|
|
_glBindTexture(GL_TEXTURE_2D, 0); CHECK_GL_ERROR;
|
|
|
|
m_PrevProgramObject = 0;
|
|
_glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&m_PrevProgramObject); CHECK_GL_ERROR;
|
|
_glBindVertexArray(0); CHECK_GL_ERROR;
|
|
_glUseProgram(0); CHECK_GL_ERROR;
|
|
|
|
m_PrevActiveTexture = 0;
|
|
_glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&m_PrevActiveTexture); CHECK_GL_ERROR;
|
|
_glActiveTexture(GL_TEXTURE0);
|
|
|
|
CHECK_GL_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::EndDraw()
|
|
{
|
|
assert(m_Drawing==true);
|
|
m_Drawing = false;
|
|
|
|
_glLineWidth(m_PrevLineWidth); CHECK_GL_ERROR;
|
|
|
|
if( m_PrevLineSmooth )
|
|
{
|
|
_glEnable(GL_LINE_SMOOTH); CHECK_GL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
_glDisable(GL_LINE_SMOOTH); CHECK_GL_ERROR;
|
|
}
|
|
|
|
if( m_PrevCullFace )
|
|
{
|
|
_glEnable(GL_CULL_FACE); CHECK_GL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
_glDisable(GL_CULL_FACE); CHECK_GL_ERROR;
|
|
}
|
|
|
|
if( m_PrevDepthTest )
|
|
{
|
|
_glEnable(GL_DEPTH_TEST); CHECK_GL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
_glDisable(GL_DEPTH_TEST); CHECK_GL_ERROR;
|
|
}
|
|
|
|
if( m_PrevBlend )
|
|
{
|
|
_glEnable(GL_BLEND); CHECK_GL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
_glDisable(GL_BLEND); CHECK_GL_ERROR;
|
|
}
|
|
|
|
if( m_PrevScissorTest )
|
|
{
|
|
_glEnable(GL_SCISSOR_TEST); CHECK_GL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
_glDisable(GL_SCISSOR_TEST); CHECK_GL_ERROR;
|
|
}
|
|
|
|
_glScissor(m_PrevScissorBox[0], m_PrevScissorBox[1], m_PrevScissorBox[2], m_PrevScissorBox[3]); CHECK_GL_ERROR;
|
|
|
|
_glBlendFunc(m_PrevSrcBlend, m_PrevDstBlend); CHECK_GL_ERROR;
|
|
|
|
_glBindTexture(GL_TEXTURE_2D, m_PrevTexture); CHECK_GL_ERROR;
|
|
|
|
_glUseProgram(m_PrevProgramObject); CHECK_GL_ERROR;
|
|
|
|
_glBindVertexArray(m_PrevVArray); CHECK_GL_ERROR;
|
|
|
|
_glViewport(m_PrevViewport[0], m_PrevViewport[1], m_PrevViewport[2], m_PrevViewport[3]); CHECK_GL_ERROR;
|
|
|
|
CHECK_GL_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
bool CTwGraphOpenGLCore::IsDrawing()
|
|
{
|
|
return m_Drawing;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::Restore()
|
|
{
|
|
UnbindFont(m_FontTexID);
|
|
m_FontTexID = 0;
|
|
m_FontTex = NULL;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static inline float ToNormScreenX(float x, int wndWidth)
|
|
{
|
|
return 2.0f*((float)x-0.5f)/wndWidth - 1.0f;
|
|
}
|
|
|
|
static inline float ToNormScreenY(float y, int wndHeight)
|
|
{
|
|
return 1.0f - 2.0f*((float)y-0.5f)/wndHeight;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::DrawLine(int _X0, int _Y0, int _X1, int _Y1, color32 _Color0, color32 _Color1, bool _AntiAliased)
|
|
{
|
|
CHECK_GL_ERROR;
|
|
assert(m_Drawing==true);
|
|
|
|
//const GLfloat dx = +0.0f;
|
|
const GLfloat dx = 0;
|
|
//GLfloat dy = -0.2f;
|
|
const GLfloat dy = -0.5f;
|
|
if( _AntiAliased )
|
|
_glEnable(GL_LINE_SMOOTH);
|
|
else
|
|
_glDisable(GL_LINE_SMOOTH);
|
|
|
|
_glBindVertexArray(m_LineRectVArray);
|
|
|
|
GLfloat x0 = ToNormScreenX(_X0+dx + m_OffsetX, m_WndWidth);
|
|
GLfloat y0 = ToNormScreenY(_Y0+dy + m_OffsetY, m_WndHeight);
|
|
GLfloat x1 = ToNormScreenX(_X1+dx + m_OffsetX, m_WndWidth);
|
|
GLfloat y1 = ToNormScreenY(_Y1+dy + m_OffsetY, m_WndHeight);
|
|
GLfloat vertices[] = { x0,y0,0, x1,y1,0 };
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_LineRectVertices);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
|
_glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(0);
|
|
|
|
color32 colors[] = { _Color0, _Color1 };
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_LineRectColors);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(colors), colors);
|
|
_glVertexAttribPointer(1, GL_BGRA, GL_UNSIGNED_BYTE, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(1);
|
|
|
|
_glUseProgram(m_LineRectProgram);
|
|
_glDrawArrays(GL_LINES, 0, 2);
|
|
|
|
if( _AntiAliased )
|
|
_glDisable(GL_LINE_SMOOTH);
|
|
|
|
CHECK_GL_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::DrawRect(int _X0, int _Y0, int _X1, int _Y1, color32 _Color00, color32 _Color10, color32 _Color01, color32 _Color11)
|
|
{
|
|
CHECK_GL_ERROR;
|
|
assert(m_Drawing==true);
|
|
|
|
// border adjustment
|
|
if(_X0<_X1)
|
|
++_X1;
|
|
else if(_X0>_X1)
|
|
++_X0;
|
|
if(_Y0<_Y1)
|
|
--_Y0;
|
|
else if(_Y0>_Y1)
|
|
--_Y1;
|
|
|
|
_glBindVertexArray(m_LineRectVArray);
|
|
|
|
GLfloat x0 = ToNormScreenX((float)_X0 + m_OffsetX, m_WndWidth);
|
|
GLfloat y0 = ToNormScreenY((float)_Y0 + m_OffsetY, m_WndHeight);
|
|
GLfloat x1 = ToNormScreenX((float)_X1 + m_OffsetX, m_WndWidth);
|
|
GLfloat y1 = ToNormScreenY((float)_Y1 + m_OffsetY, m_WndHeight);
|
|
GLfloat vertices[] = { x0,y0,0, x1,y0,0, x0,y1,0, x1,y1,0 };
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_LineRectVertices);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
|
_glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(0);
|
|
|
|
GLuint colors[] = { _Color00, _Color10, _Color01, _Color11 };
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_LineRectColors);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(colors), colors);
|
|
_glVertexAttribPointer(1, GL_BGRA, GL_UNSIGNED_BYTE, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(1);
|
|
|
|
_glUseProgram(m_LineRectProgram);
|
|
_glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
CHECK_GL_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void *CTwGraphOpenGLCore::NewTextObj()
|
|
{
|
|
return new CTextObj;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::DeleteTextObj(void *_TextObj)
|
|
{
|
|
assert(_TextObj!=NULL);
|
|
delete static_cast<CTextObj *>(_TextObj);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::BuildText(void *_TextObj, const std::string *_TextLines, color32 *_LineColors, color32 *_LineBgColors, int _NbLines, const CTexFont *_Font, int _Sep, int _BgWidth)
|
|
{
|
|
assert(m_Drawing==true);
|
|
assert(_TextObj!=NULL);
|
|
assert(_Font!=NULL);
|
|
|
|
if( _Font != m_FontTex )
|
|
{
|
|
UnbindFont(m_FontTexID);
|
|
m_FontTexID = BindFont(_Font);
|
|
m_FontTex = _Font;
|
|
}
|
|
CTextObj *TextObj = static_cast<CTextObj *>(_TextObj);
|
|
TextObj->m_TextVerts.resize(0);
|
|
TextObj->m_TextUVs.resize(0);
|
|
TextObj->m_BgVerts.resize(0);
|
|
TextObj->m_Colors.resize(0);
|
|
TextObj->m_BgColors.resize(0);
|
|
|
|
int x, x1, y, y1, i, Len;
|
|
unsigned char ch;
|
|
const unsigned char *Text;
|
|
color32 LineColor = COLOR32_RED;
|
|
for( int Line=0; Line<_NbLines; ++Line )
|
|
{
|
|
x = 0;
|
|
y = Line * (_Font->m_CharHeight+_Sep);
|
|
y1 = y+_Font->m_CharHeight;
|
|
Len = (int)_TextLines[Line].length();
|
|
Text = (const unsigned char *)(_TextLines[Line].c_str());
|
|
if( _LineColors!=NULL )
|
|
LineColor = (_LineColors[Line]&0xff00ff00) | GLubyte(_LineColors[Line]>>16) | (GLubyte(_LineColors[Line])<<16);
|
|
|
|
for( i=0; i<Len; ++i )
|
|
{
|
|
ch = Text[i];
|
|
x1 = x + _Font->m_CharWidth[ch];
|
|
|
|
TextObj->m_TextVerts.push_back(Vec2(x , y ));
|
|
TextObj->m_TextVerts.push_back(Vec2(x1, y ));
|
|
TextObj->m_TextVerts.push_back(Vec2(x , y1));
|
|
TextObj->m_TextVerts.push_back(Vec2(x1, y ));
|
|
TextObj->m_TextVerts.push_back(Vec2(x1, y1));
|
|
TextObj->m_TextVerts.push_back(Vec2(x , y1));
|
|
|
|
TextObj->m_TextUVs.push_back(Vec2(_Font->m_CharU0[ch], _Font->m_CharV0[ch]));
|
|
TextObj->m_TextUVs.push_back(Vec2(_Font->m_CharU1[ch], _Font->m_CharV0[ch]));
|
|
TextObj->m_TextUVs.push_back(Vec2(_Font->m_CharU0[ch], _Font->m_CharV1[ch]));
|
|
TextObj->m_TextUVs.push_back(Vec2(_Font->m_CharU1[ch], _Font->m_CharV0[ch]));
|
|
TextObj->m_TextUVs.push_back(Vec2(_Font->m_CharU1[ch], _Font->m_CharV1[ch]));
|
|
TextObj->m_TextUVs.push_back(Vec2(_Font->m_CharU0[ch], _Font->m_CharV1[ch]));
|
|
|
|
if( _LineColors!=NULL )
|
|
{
|
|
TextObj->m_Colors.push_back(LineColor);
|
|
TextObj->m_Colors.push_back(LineColor);
|
|
TextObj->m_Colors.push_back(LineColor);
|
|
TextObj->m_Colors.push_back(LineColor);
|
|
TextObj->m_Colors.push_back(LineColor);
|
|
TextObj->m_Colors.push_back(LineColor);
|
|
}
|
|
|
|
x = x1;
|
|
}
|
|
if( _BgWidth>0 )
|
|
{
|
|
TextObj->m_BgVerts.push_back(Vec2(-1 , y ));
|
|
TextObj->m_BgVerts.push_back(Vec2(_BgWidth+1, y ));
|
|
TextObj->m_BgVerts.push_back(Vec2(-1 , y1));
|
|
TextObj->m_BgVerts.push_back(Vec2(_BgWidth+1, y ));
|
|
TextObj->m_BgVerts.push_back(Vec2(_BgWidth+1, y1));
|
|
TextObj->m_BgVerts.push_back(Vec2(-1 , y1));
|
|
|
|
if( _LineBgColors!=NULL )
|
|
{
|
|
color32 LineBgColor = (_LineBgColors[Line]&0xff00ff00) | GLubyte(_LineBgColors[Line]>>16) | (GLubyte(_LineBgColors[Line])<<16);
|
|
TextObj->m_BgColors.push_back(LineBgColor);
|
|
TextObj->m_BgColors.push_back(LineBgColor);
|
|
TextObj->m_BgColors.push_back(LineBgColor);
|
|
TextObj->m_BgColors.push_back(LineBgColor);
|
|
TextObj->m_BgColors.push_back(LineBgColor);
|
|
TextObj->m_BgColors.push_back(LineBgColor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::DrawText(void *_TextObj, int _X, int _Y, color32 _Color, color32 _BgColor)
|
|
{
|
|
CHECK_GL_ERROR;
|
|
assert(m_Drawing==true);
|
|
assert(_TextObj!=NULL);
|
|
CTextObj *TextObj = static_cast<CTextObj *>(_TextObj);
|
|
|
|
if( TextObj->m_TextVerts.size()<4 && TextObj->m_BgVerts.size()<4 )
|
|
return; // nothing to draw
|
|
|
|
// draw character background triangles
|
|
if( (_BgColor!=0 || TextObj->m_BgColors.size()==TextObj->m_BgVerts.size()) && TextObj->m_BgVerts.size()>=4 )
|
|
{
|
|
size_t numBgVerts = TextObj->m_BgVerts.size();
|
|
if( numBgVerts > m_TriBufferSize )
|
|
ResizeTriBuffers(numBgVerts + 2048);
|
|
|
|
_glBindVertexArray(m_TriVArray);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriVertices);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, numBgVerts*sizeof(Vec2), &(TextObj->m_BgVerts[0]));
|
|
_glVertexAttribPointer(0, 2, GL_FLOAT, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(0);
|
|
_glDisableVertexAttribArray(1);
|
|
_glDisableVertexAttribArray(2);
|
|
|
|
if( TextObj->m_BgColors.size()==TextObj->m_BgVerts.size() && _BgColor==0 )
|
|
{
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriColors);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, numBgVerts*sizeof(color32), &(TextObj->m_BgColors[0]));
|
|
_glVertexAttribPointer(1, GL_BGRA, GL_UNSIGNED_BYTE, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(1);
|
|
|
|
_glUseProgram(m_TriProgram);
|
|
_glUniform2f(m_TriLocationOffset, (float)_X, (float)_Y);
|
|
_glUniform2f(m_TriLocationWndSize, (float)m_WndWidth, (float)m_WndHeight);
|
|
}
|
|
else
|
|
{
|
|
_glUseProgram(m_TriUniProgram);
|
|
_glUniform4f(m_TriUniLocationColor, GLfloat((_BgColor>>16)&0xff)/256.0f, GLfloat((_BgColor>>8)&0xff)/256.0f, GLfloat(_BgColor&0xff)/256.0f, GLfloat((_BgColor>>24)&0xff)/256.0f);
|
|
_glUniform2f(m_TriUniLocationOffset, (float)_X, (float)_Y);
|
|
_glUniform2f(m_TriUniLocationWndSize, (float)m_WndWidth, (float)m_WndHeight);
|
|
}
|
|
|
|
_glDrawArrays(GL_TRIANGLES, 0, (GLsizei)TextObj->m_BgVerts.size());
|
|
}
|
|
|
|
// draw character triangles
|
|
if( TextObj->m_TextVerts.size()>=4 )
|
|
{
|
|
_glActiveTexture(GL_TEXTURE0);
|
|
_glBindTexture(GL_TEXTURE_2D, m_FontTexID);
|
|
size_t numTextVerts = TextObj->m_TextVerts.size();
|
|
if( numTextVerts > m_TriBufferSize )
|
|
ResizeTriBuffers(numTextVerts + 2048);
|
|
|
|
_glBindVertexArray(m_TriVArray);
|
|
_glDisableVertexAttribArray(2);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriVertices);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, numTextVerts*sizeof(Vec2), &(TextObj->m_TextVerts[0]));
|
|
_glVertexAttribPointer(0, 2, GL_FLOAT, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(0);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriUVs);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, numTextVerts*sizeof(Vec2), &(TextObj->m_TextUVs[0]));
|
|
_glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
|
_glEnableVertexAttribArray(1);
|
|
|
|
if( TextObj->m_Colors.size()==TextObj->m_TextVerts.size() && _Color==0 )
|
|
{
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriColors);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, numTextVerts*sizeof(color32), &(TextObj->m_Colors[0]));
|
|
_glVertexAttribPointer(2, GL_BGRA, GL_UNSIGNED_BYTE, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(2);
|
|
|
|
_glUseProgram(m_TriTexProgram);
|
|
_glUniform2f(m_TriTexLocationOffset, (float)_X, (float)_Y);
|
|
_glUniform2f(m_TriTexLocationWndSize, (float)m_WndWidth, (float)m_WndHeight);
|
|
_glUniform1i(m_TriTexLocationTexture, 0);
|
|
}
|
|
else
|
|
{
|
|
_glUseProgram(m_TriTexUniProgram);
|
|
_glUniform4f(m_TriTexUniLocationColor, GLfloat((_Color>>16)&0xff)/256.0f, GLfloat((_Color>>8)&0xff)/256.0f, GLfloat(_Color&0xff)/256.0f, GLfloat((_Color>>24)&0xff)/256.0f);
|
|
_glUniform2f(m_TriTexUniLocationOffset, (float)_X, (float)_Y);
|
|
_glUniform2f(m_TriTexUniLocationWndSize, (float)m_WndWidth, (float)m_WndHeight);
|
|
_glUniform1i(m_TriTexUniLocationTexture, 0);
|
|
}
|
|
|
|
_glDrawArrays(GL_TRIANGLES, 0, (GLsizei)TextObj->m_TextVerts.size());
|
|
}
|
|
|
|
CHECK_GL_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::ChangeViewport(int _X0, int _Y0, int _Width, int _Height, int _OffsetX, int _OffsetY)
|
|
{
|
|
// glViewport impacts the NDC; use glScissor instead
|
|
m_OffsetX = _X0 + _OffsetX;
|
|
m_OffsetY = _Y0 + _OffsetY;
|
|
SetScissor(_X0, _Y0, _Width, _Height);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::RestoreViewport()
|
|
{
|
|
m_OffsetX = m_OffsetY = 0;
|
|
SetScissor(0, 0, 0, 0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::SetScissor(int _X0, int _Y0, int _Width, int _Height)
|
|
{
|
|
if( _Width>0 && _Height>0 )
|
|
{
|
|
_glScissor(_X0-1, m_WndHeight-_Y0-_Height, _Width-1, _Height);
|
|
_glEnable(GL_SCISSOR_TEST);
|
|
}
|
|
else
|
|
_glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void CTwGraphOpenGLCore::DrawTriangles(int _NumTriangles, int *_Vertices, color32 *_Colors, Cull _CullMode)
|
|
{
|
|
assert(m_Drawing==true);
|
|
|
|
const GLfloat dx = +0.0f;
|
|
const GLfloat dy = +0.0f;
|
|
|
|
// Backup states
|
|
GLint prevCullFaceMode, prevFrontFace;
|
|
_glGetIntegerv(GL_CULL_FACE_MODE, &prevCullFaceMode);
|
|
_glGetIntegerv(GL_FRONT_FACE, &prevFrontFace);
|
|
GLboolean prevCullEnable = _glIsEnabled(GL_CULL_FACE);
|
|
_glCullFace(GL_BACK);
|
|
_glEnable(GL_CULL_FACE);
|
|
if( _CullMode==CULL_CW )
|
|
_glFrontFace(GL_CCW);
|
|
else if( _CullMode==CULL_CCW )
|
|
_glFrontFace(GL_CW);
|
|
else
|
|
_glDisable(GL_CULL_FACE);
|
|
|
|
_glUseProgram(m_TriProgram);
|
|
_glBindVertexArray(m_TriVArray);
|
|
_glUniform2f(m_TriLocationOffset, (float)m_OffsetX+dx, (float)m_OffsetY+dy);
|
|
_glUniform2f(m_TriLocationWndSize, (float)m_WndWidth, (float)m_WndHeight);
|
|
_glDisableVertexAttribArray(2);
|
|
|
|
size_t numVerts = 3*_NumTriangles;
|
|
if( numVerts > m_TriBufferSize )
|
|
ResizeTriBuffers(numVerts + 2048);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriVertices);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, numVerts*2*sizeof(int), _Vertices);
|
|
_glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, NULL);
|
|
_glEnableVertexAttribArray(0);
|
|
|
|
_glBindBuffer(GL_ARRAY_BUFFER, m_TriColors);
|
|
_glBufferSubData(GL_ARRAY_BUFFER, 0, numVerts*sizeof(color32), _Colors);
|
|
_glVertexAttribPointer(1, GL_BGRA, GL_UNSIGNED_BYTE, GL_TRUE, 0, NULL);
|
|
_glEnableVertexAttribArray(1);
|
|
|
|
_glDrawArrays(GL_TRIANGLES, 0, (GLsizei)numVerts);
|
|
|
|
// Reset states
|
|
_glCullFace(prevCullFaceMode);
|
|
_glFrontFace(prevFrontFace);
|
|
if( prevCullEnable )
|
|
_glEnable(GL_CULL_FACE);
|
|
else
|
|
_glDisable(GL_CULL_FACE);
|
|
|
|
CHECK_GL_ERROR;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|