/* Silence deprecation warnings on modern macOS/iOS */ #define GL_SILENCE_DEPRECATION #define GLES_SILENCE_DEPRECATION #include "Core.h" #if CC_GFX_BACKEND == CC_GFX_BACKEND_GL2 #include "_GraphicsBase.h" #include "Errors.h" #include "Window.h" /* OpenGL 2.0 backend (alternative modern-ish backend) */ #include "../misc/opengl/GLCommon.h" /* e.g. GLAPI void APIENTRY glFunction(int args); */ #define GL_FUNC(_retType, name) GLAPI _retType APIENTRY name #include "../misc/opengl/GL1Funcs.h" /* Functions must be dynamically linked on Windows */ #ifdef CC_BUILD_WIN /* e.g. static void (APIENTRY *_glFunction)(int args); */ #undef GL_FUNC #define GL_FUNC(_retType, name) static _retType (APIENTRY *name) #include "../misc/opengl/GL2Funcs.h" #define GLSym(sym) { DYNAMICLIB_QUOTE(sym), (void**)& ## sym } static const struct DynamicLibSym core_funcs[] = { GLSym(glBindBuffer), GLSym(glDeleteBuffers), GLSym(glGenBuffers), GLSym(glBufferData), GLSym(glBufferSubData), GLSym(glCreateShader), GLSym(glDeleteShader), GLSym(glGetShaderiv), GLSym(glGetShaderInfoLog), GLSym(glShaderSource), GLSym(glAttachShader), GLSym(glBindAttribLocation), GLSym(glCompileShader), GLSym(glDetachShader), GLSym(glLinkProgram), GLSym(glCreateProgram), GLSym(glDeleteProgram), GLSym(glGetProgramiv), GLSym(glGetProgramInfoLog), GLSym(glUseProgram), GLSym(glDisableVertexAttribArray), GLSym(glEnableVertexAttribArray), GLSym(glVertexAttribPointer), GLSym(glGetUniformLocation), GLSym(glUniform1f), GLSym(glUniform2f), GLSym(glUniform3f), GLSym(glUniformMatrix4fv), }; #else #include "../misc/opengl/GL2Funcs.h" #endif #define _glBindTexture glBindTexture #define _glDeleteTextures glDeleteTextures #define _glGenTextures glGenTextures #define _glTexImage2D glTexImage2D #define _glTexSubImage2D glTexSubImage2D #include "_GLShared.h" static GfxResourceID white_square; /*########################################################################################################################* *-------------------------------------------------------Index buffers-----------------------------------------------------* *#########################################################################################################################*/ static GLuint GL_GenAndBind(GLenum target) { GLuint id; glGenBuffers(1, &id); glBindBuffer(target, id); return id; } GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { cc_uint16 indices[GFX_MAX_INDICES]; GLuint id = GL_GenAndBind(GL_ELEMENT_ARRAY_BUFFER); cc_uint32 size = count * sizeof(cc_uint16); fillFunc(indices, count, obj); glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW); return uint_to_ptr(id); } void Gfx_BindIb(GfxResourceID ib) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ptr_to_uint(ib)); } void Gfx_DeleteIb(GfxResourceID* ib) { GLuint id = ptr_to_uint(*ib); if (!id) return; glDeleteBuffers(1, &id); *ib = 0; } /*########################################################################################################################* *------------------------------------------------------Vertex buffers-----------------------------------------------------* *#########################################################################################################################*/ static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { GLuint id = GL_GenAndBind(GL_ARRAY_BUFFER); return uint_to_ptr(id); } void Gfx_BindVb(GfxResourceID vb) { glBindBuffer(GL_ARRAY_BUFFER, ptr_to_uint(vb)); } void Gfx_DeleteVb(GfxResourceID* vb) { GLuint id = ptr_to_uint(*vb); if (id) glDeleteBuffers(1, &id); *vb = 0; } void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { return FastAllocTempMem(count * strideSizes[fmt]); } void Gfx_UnlockVb(GfxResourceID vb) { glBufferData(GL_ARRAY_BUFFER, tmpSize, tmpData, GL_STATIC_DRAW); } /*########################################################################################################################* *--------------------------------------------------Dynamic vertex buffers-------------------------------------------------* *#########################################################################################################################*/ static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { GLuint id = GL_GenAndBind(GL_ARRAY_BUFFER); cc_uint32 size = maxVertices * strideSizes[fmt]; glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW); return uint_to_ptr(id); } void Gfx_BindDynamicVb(GfxResourceID vb) { glBindBuffer(GL_ARRAY_BUFFER, ptr_to_uint(vb)); } void Gfx_DeleteDynamicVb(GfxResourceID* vb) { GLuint id = ptr_to_uint(*vb); if (id) glDeleteBuffers(1, &id); *vb = 0; } void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { return FastAllocTempMem(count * strideSizes[fmt]); } void Gfx_UnlockDynamicVb(GfxResourceID vb) { glBindBuffer(GL_ARRAY_BUFFER, ptr_to_uint(vb)); glBufferSubData(GL_ARRAY_BUFFER, 0, tmpSize, tmpData); } void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) { cc_uint32 size = vCount * gfx_stride; glBindBuffer(GL_ARRAY_BUFFER, ptr_to_uint(vb)); glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices); } /*########################################################################################################################* *------------------------------------------------------OpenGL modern------------------------------------------------------* *#########################################################################################################################*/ #define FTR_TEXTURE_UV (1 << 0) #define FTR_ALPHA_TEST (1 << 1) #define FTR_TEX_OFFSET (1 << 2) #define FTR_LINEAR_FOG (1 << 3) #define FTR_DENSIT_FOG (1 << 4) #define FTR_HASANY_FOG (FTR_LINEAR_FOG | FTR_DENSIT_FOG) #define FTR_FS_MEDIUMP (1 << 7) #define UNI_MVP_MATRIX (1 << 0) #define UNI_TEX_OFFSET (1 << 1) #define UNI_FOG_COL (1 << 2) #define UNI_FOG_END (1 << 3) #define UNI_FOG_DENS (1 << 4) #define UNI_MASK_ALL 0x1F /* cached uniforms (cached for multiple programs */ static struct Matrix _view, _proj, _mvp; static cc_bool gfx_texTransform; static float _texX, _texY; static PackedCol gfx_fogColor; static float gfx_fogEnd = -1.0f, gfx_fogDensity = -1.0f; static int gfx_fogMode = -1; /* shader programs (emulate fixed function) */ static struct GLShader { int features; /* what features are enabled for this shader */ int uniforms; /* which associated uniforms need to be resent to GPU */ GLuint program; /* OpenGL program ID (0 if not yet compiled) */ int locations[5]; /* location of uniforms (not constant) */ } shaders[6 * 3] = { /* no fog */ { 0 }, { 0 | FTR_ALPHA_TEST }, { FTR_TEXTURE_UV }, { FTR_TEXTURE_UV | FTR_ALPHA_TEST }, { FTR_TEXTURE_UV | FTR_TEX_OFFSET }, { FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST }, /* linear fog */ { FTR_LINEAR_FOG | 0 }, { FTR_LINEAR_FOG | 0 | FTR_ALPHA_TEST }, { FTR_LINEAR_FOG | FTR_TEXTURE_UV }, { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_ALPHA_TEST }, { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET }, { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST }, /* density fog */ { FTR_DENSIT_FOG | 0 }, { FTR_DENSIT_FOG | 0 | FTR_ALPHA_TEST }, { FTR_DENSIT_FOG | FTR_TEXTURE_UV }, { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_ALPHA_TEST }, { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET }, { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST }, }; static struct GLShader* gfx_activeShader; /* Generates source code for a GLSL vertex shader, based on shader's flags */ static void GenVertexShader(const struct GLShader* shader, cc_string* dst) { int uv = shader->features & FTR_TEXTURE_UV; int tm = shader->features & FTR_TEX_OFFSET; String_AppendConst(dst, "attribute vec3 in_pos;\n"); String_AppendConst(dst, "attribute vec4 in_col;\n"); if (uv) String_AppendConst(dst, "attribute vec2 in_uv;\n"); String_AppendConst(dst, "varying vec4 out_col;\n"); if (uv) String_AppendConst(dst, "varying vec2 out_uv;\n"); String_AppendConst(dst, "uniform mat4 mvp;\n"); if (tm) String_AppendConst(dst, "uniform vec2 texOffset;\n"); String_AppendConst(dst, "void main() {\n"); String_AppendConst(dst, " gl_Position = mvp * vec4(in_pos, 1.0);\n"); String_AppendConst(dst, " out_col = in_col;\n"); if (uv) String_AppendConst(dst, " out_uv = in_uv;\n"); if (tm) String_AppendConst(dst, " out_uv = out_uv + texOffset;\n"); String_AppendConst(dst, "}"); } /* Generates source code for a GLSL fragment shader, based on shader's flags */ static void GenFragmentShader(const struct GLShader* shader, cc_string* dst) { int uv = shader->features & FTR_TEXTURE_UV; int al = shader->features & FTR_ALPHA_TEST; int fl = shader->features & FTR_LINEAR_FOG; int fd = shader->features & FTR_DENSIT_FOG; int fm = shader->features & FTR_HASANY_FOG; #ifdef CC_BUILD_GLES int mp = shader->features & FTR_FS_MEDIUMP; if (mp) String_AppendConst(dst, "precision mediump float;\n"); else String_AppendConst(dst, "precision highp float;\n"); #endif String_AppendConst(dst, "varying vec4 out_col;\n"); if (uv) String_AppendConst(dst, "varying vec2 out_uv;\n"); if (uv) String_AppendConst(dst, "uniform sampler2D texImage;\n"); if (fm) String_AppendConst(dst, "uniform vec3 fogCol;\n"); if (fl) String_AppendConst(dst, "uniform float fogEnd;\n"); if (fd) String_AppendConst(dst, "uniform float fogDensity;\n"); String_AppendConst(dst, "void main() {\n"); if (uv) String_AppendConst(dst, " vec4 col = texture2D(texImage, out_uv) * out_col;\n"); else String_AppendConst(dst, " vec4 col = out_col;\n"); if (al) String_AppendConst(dst, " if (col.a < 0.5) discard;\n"); if (fm) String_AppendConst(dst, " float depth = 1.0 / gl_FragCoord.w;\n"); if (fl) String_AppendConst(dst, " float f = clamp((fogEnd - depth) / fogEnd, 0.0, 1.0);\n"); if (fd) String_AppendConst(dst, " float f = clamp(exp(fogDensity * depth), 0.0, 1.0);\n"); if (fm) String_AppendConst(dst, " col.rgb = mix(fogCol, col.rgb, f);\n"); String_AppendConst(dst, " gl_FragColor = col;\n"); String_AppendConst(dst, "}"); } /* Tries to compile GLSL shader code */ static GLint CompileShader(GLint shader, const cc_string* src) { const char* str = src->buffer; int len = src->length; GLint temp; glShaderSource(shader, 1, &str, &len); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &temp); return temp; } /* Logs information then aborts program */ static void ShaderFailed(GLint shader) { char logInfo[2048]; GLint temp; if (!shader) Logger_Abort("Failed to create shader"); temp = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &temp); if (temp > 1) { glGetShaderInfoLog(shader, 2047, NULL, logInfo); logInfo[2047] = '\0'; Window_ShowDialog("Failed to compile shader", logInfo); } Logger_Abort("Failed to compile shader"); } /* Tries to compile vertex and fragment shaders, then link into an OpenGL program */ static void CompileProgram(struct GLShader* shader) { char tmpBuffer[2048]; cc_string tmp; GLuint vs, fs, program; GLint temp; vs = glCreateShader(GL_VERTEX_SHADER); if (!vs) { Platform_LogConst("Failed to create vertex shader"); return; } String_InitArray(tmp, tmpBuffer); GenVertexShader(shader, &tmp); if (!CompileShader(vs, &tmp)) ShaderFailed(vs); fs = glCreateShader(GL_FRAGMENT_SHADER); if (!fs) { Platform_LogConst("Failed to create fragment shader"); glDeleteShader(vs); return; } tmp.length = 0; GenFragmentShader(shader, &tmp); if (!CompileShader(fs, &tmp)) { /* Sometimes fails 'highp precision is not supported in fragment shader' */ /* So try compiling shader again without highp precision */ shader->features |= FTR_FS_MEDIUMP; tmp.length = 0; GenFragmentShader(shader, &tmp); if (!CompileShader(fs, &tmp)) ShaderFailed(fs); } program = glCreateProgram(); if (!program) Logger_Abort("Failed to create program"); shader->program = program; glAttachShader(program, vs); glAttachShader(program, fs); /* Force in_pos/in_col/in_uv attributes to be bound to 0,1,2 locations */ /* Although most browsers assign the attributes in this order anyways, */ /* the specification does not require this. (e.g. Safari doesn't) */ glBindAttribLocation(program, 0, "in_pos"); glBindAttribLocation(program, 1, "in_col"); glBindAttribLocation(program, 2, "in_uv"); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &temp); if (temp) { glDetachShader(program, vs); glDetachShader(program, fs); glDeleteShader(vs); glDeleteShader(fs); shader->locations[0] = glGetUniformLocation(program, "mvp"); shader->locations[1] = glGetUniformLocation(program, "texOffset"); shader->locations[2] = glGetUniformLocation(program, "fogCol"); shader->locations[3] = glGetUniformLocation(program, "fogEnd"); shader->locations[4] = glGetUniformLocation(program, "fogDensity"); return; } temp = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &temp); if (temp > 0) { glGetProgramInfoLog(program, 2047, NULL, tmpBuffer); tmpBuffer[2047] = '\0'; Window_ShowDialog("Failed to compile program", tmpBuffer); } Logger_Abort("Failed to compile program"); } /* Marks a uniform as changed on all programs */ static void DirtyUniform(int uniform) { int i; for (i = 0; i < Array_Elems(shaders); i++) { shaders[i].uniforms |= uniform; } } /* Sends changed uniforms to the GPU for current program */ static void ReloadUniforms(void) { struct GLShader* s = gfx_activeShader; if (!s) return; /* NULL if context is lost */ if (s->uniforms & UNI_MVP_MATRIX) { glUniformMatrix4fv(s->locations[0], 1, false, (float*)&_mvp); s->uniforms &= ~UNI_MVP_MATRIX; } if ((s->uniforms & UNI_TEX_OFFSET) && (s->features & FTR_TEX_OFFSET)) { glUniform2f(s->locations[1], _texX, _texY); s->uniforms &= ~UNI_TEX_OFFSET; } if ((s->uniforms & UNI_FOG_COL) && (s->features & FTR_HASANY_FOG)) { glUniform3f(s->locations[2], PackedCol_R(gfx_fogColor) / 255.0f, PackedCol_G(gfx_fogColor) / 255.0f, PackedCol_B(gfx_fogColor) / 255.0f); s->uniforms &= ~UNI_FOG_COL; } if ((s->uniforms & UNI_FOG_END) && (s->features & FTR_LINEAR_FOG)) { glUniform1f(s->locations[3], gfx_fogEnd); s->uniforms &= ~UNI_FOG_END; } if ((s->uniforms & UNI_FOG_DENS) && (s->features & FTR_DENSIT_FOG)) { /* See https://docs.microsoft.com/en-us/previous-versions/ms537113(v%3Dvs.85) */ /* The equation for EXP mode is exp(-density * z), so just negate density here */ glUniform1f(s->locations[4], -gfx_fogDensity); s->uniforms &= ~UNI_FOG_DENS; } } /* Switches program to one that duplicates current fixed function state */ /* Compiles program and reloads uniforms if needed */ static void SwitchProgram(void) { struct GLShader* shader; int index = 0; if (gfx_fogEnabled) { index += 6; /* linear fog */ if (gfx_fogMode >= 1) index += 6; /* exp fog */ } if (gfx_format == VERTEX_FORMAT_TEXTURED) index += 2; if (gfx_texTransform) index += 2; if (gfx_alphaTest) index += 1; shader = &shaders[index]; if (shader == gfx_activeShader) { ReloadUniforms(); return; } if (!shader->program) CompileProgram(shader); gfx_activeShader = shader; glUseProgram(shader->program); ReloadUniforms(); } /*########################################################################################################################* *---------------------------------------------------------Textures--------------------------------------------------------* *#########################################################################################################################*/ void Gfx_BindTexture(GfxResourceID texId) { /* Texture 0 has different behaviour depending on backend */ /* Desktop OpenGL - pure white 1x1 texture */ /* WebGL/OpenGL ES - pure black 1x1 texture */ /* So for consistency, always use a 1x1 pure white texture */ if (!texId) texId = white_square; glBindTexture(GL_TEXTURE_2D, ptr_to_uint(texId)); } /*########################################################################################################################* *-----------------------------------------------------State management----------------------------------------------------* *#########################################################################################################################*/ void Gfx_SetFog(cc_bool enabled) { gfx_fogEnabled = enabled; SwitchProgram(); } void Gfx_SetFogCol(PackedCol color) { if (color == gfx_fogColor) return; gfx_fogColor = color; DirtyUniform(UNI_FOG_COL); ReloadUniforms(); } void Gfx_SetFogDensity(float value) { if (gfx_fogDensity == value) return; gfx_fogDensity = value; DirtyUniform(UNI_FOG_DENS); ReloadUniforms(); } void Gfx_SetFogEnd(float value) { if (gfx_fogEnd == value) return; gfx_fogEnd = value; DirtyUniform(UNI_FOG_END); ReloadUniforms(); } void Gfx_SetFogMode(FogFunc func) { if (gfx_fogMode == func) return; gfx_fogMode = func; SwitchProgram(); } static void SetAlphaTest(cc_bool enabled) { SwitchProgram(); } void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); } /*########################################################################################################################* *---------------------------------------------------------Matrices--------------------------------------------------------* *#########################################################################################################################*/ void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { if (type == MATRIX_VIEW) _view = *matrix; if (type == MATRIX_PROJECTION) _proj = *matrix; Matrix_Mul(&_mvp, &_view, &_proj); DirtyUniform(UNI_MVP_MATRIX); ReloadUniforms(); } void Gfx_LoadIdentityMatrix(MatrixType type) { Gfx_LoadMatrix(type, &Matrix_Identity); } void Gfx_EnableTextureOffset(float x, float y) { _texX = x; _texY = y; gfx_texTransform = true; DirtyUniform(UNI_TEX_OFFSET); SwitchProgram(); } void Gfx_DisableTextureOffset(void) { gfx_texTransform = false; SwitchProgram(); } /*########################################################################################################################* *-------------------------------------------------------State setup-------------------------------------------------------* *#########################################################################################################################*/ static void GLContext_GetAll(const struct DynamicLibSym* syms, int count) { int i; for (i = 0; i < count; i++) { *syms[i].symAddr = GLContext_GetAddress(syms[i].name); } } static void GLBackend_Init(void) { #ifdef CC_BUILD_WIN GLContext_GetAll(core_funcs, Array_Elems(core_funcs)); #endif #ifdef CC_BUILD_GLES // OpenGL ES 2.0 doesn't support custom mipmaps levels, but 3.2 does // Note that GL_MAJOR_VERSION and GL_MINOR_VERSION were not actually // implemented until 3.0.. but hopefully older GPU drivers out there // don't try and set a value even when it's unsupported #define _GL_MAJOR_VERSION 33307 #define _GL_MINOR_VERSION 33308 GLint major = 0, minor = 0; glGetIntegerv(_GL_MAJOR_VERSION, &major); glGetIntegerv(_GL_MINOR_VERSION, &minor); customMipmapsLevels = major >= 3 && minor >= 2; #else customMipmapsLevels = true; const GLubyte* ver = glGetString(GL_VERSION); int major = ver[0] - '0', minor = ver[2] - '0'; if (major >= 2) return; // OpenGL 1.x.. will likely either not work or perform poorly cc_string str; char strBuffer[1024]; String_InitArray_NT(str, strBuffer); String_Format2(&str,"Modern OpenGL build requires at least OpenGL 2.0\n" \ "Your system only supports OpenGL %i.%i however\n\n" \ "As such ClassiCube will likely perform poorly or not work\n" \ "It is recommended you use the Normal OpenGL build instead\n", &major, &minor); strBuffer[str.length] = '\0'; Window_ShowDialog("Compatibility warning", strBuffer); #endif } static void Gfx_FreeState(void) { int i; FreeDefaultResources(); gfx_activeShader = NULL; for (i = 0; i < Array_Elems(shaders); i++) { glDeleteProgram(shaders[i].program); shaders[i].program = 0; } Gfx_DeleteTexture(&white_square); } static void Gfx_RestoreState(void) { InitDefaultResources(); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); gfx_format = -1; DirtyUniform(UNI_MASK_ALL); GL_ClearColor(gfx_clearColor); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthFunc(GL_LEQUAL); /* 1x1 dummy white texture */ struct Bitmap bmp; BitmapCol pixels[1] = { BITMAPCOLOR_WHITE }; Bitmap_Init(bmp, 1, 1, pixels); Gfx_RecreateTexture(&white_square, &bmp, 0, false); } cc_bool Gfx_WarnIfNecessary(void) { return false; } /*########################################################################################################################* *----------------------------------------------------------Drawing--------------------------------------------------------* *#########################################################################################################################*/ typedef void (*GL_SetupVBFunc)(void); typedef void (*GL_SetupVBRangeFunc)(int startVertex); static GL_SetupVBFunc gfx_setupVBFunc; static GL_SetupVBRangeFunc gfx_setupVBRangeFunc; static void GL_SetupVbColoured(void) { glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_COLOURED, uint_to_ptr( 0)); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_COLOURED, uint_to_ptr(12)); } static void GL_SetupVbTextured(void) { glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, uint_to_ptr( 0)); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_TEXTURED, uint_to_ptr(12)); glVertexAttribPointer(2, 2, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, uint_to_ptr(16)); } static void GL_SetupVbColoured_Range(int startVertex) { cc_uint32 offset = startVertex * SIZEOF_VERTEX_COLOURED; glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_COLOURED, uint_to_ptr(offset )); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_COLOURED, uint_to_ptr(offset + 12)); } static void GL_SetupVbTextured_Range(int startVertex) { cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED; glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, uint_to_ptr(offset )); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_TEXTURED, uint_to_ptr(offset + 12)); glVertexAttribPointer(2, 2, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, uint_to_ptr(offset + 16)); } void Gfx_SetVertexFormat(VertexFormat fmt) { if (fmt == gfx_format) return; gfx_format = fmt; gfx_stride = strideSizes[fmt]; if (fmt == VERTEX_FORMAT_TEXTURED) { glEnableVertexAttribArray(2); gfx_setupVBFunc = GL_SetupVbTextured; gfx_setupVBRangeFunc = GL_SetupVbTextured_Range; } else { glDisableVertexAttribArray(2); gfx_setupVBFunc = GL_SetupVbColoured; gfx_setupVBRangeFunc = GL_SetupVbColoured_Range; } SwitchProgram(); } void Gfx_DrawVb_Lines(int verticesCount) { gfx_setupVBFunc(); glDrawArrays(GL_LINES, 0, verticesCount); } void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { gfx_setupVBRangeFunc(startVertex); glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL); } void Gfx_DrawVb_IndexedTris(int verticesCount) { gfx_setupVBFunc(); glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL); } void Gfx_BindVb_Textured(GfxResourceID vb) { Gfx_BindVb(vb); GL_SetupVbTextured(); } void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { if (startVertex + verticesCount > GFX_MAX_VERTICES) { GL_SetupVbTextured_Range(startVertex); glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL); GL_SetupVbTextured(); } else { /* ICOUNT(startVertex) * 2 = startVertex * 3 */ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, (void*)(startVertex * 3)); } } #endif