Index: code/qcommon/q_platform.h =================================================================== --- code/qcommon/q_platform.h (revision 1803) +++ code/qcommon/q_platform.h (working copy) @@ -98,7 +98,7 @@ #define DLL_EXT ".dll" -#elif __WIN32__ +#elif defined(__WIN32__) || defined(_WIN32) #undef QDECL #define QDECL __cdecl Index: code/qcommon/qfiles.h =================================================================== --- code/qcommon/qfiles.h (revision 1803) +++ code/qcommon/qfiles.h (working copy) @@ -37,6 +37,7 @@ // surface geometry should not exceed these limits #define SHADER_MAX_VERTEXES 1000 #define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES) +#define SHADER_MAX_TRIANGLES (SHADER_MAX_INDEXES / 3) // the maximum size of game relative pathnames Index: code/renderer/qgl.h =================================================================== --- code/renderer/qgl.h (revision 1803) +++ code/renderer/qgl.h (working copy) @@ -39,7 +39,109 @@ extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); extern void (APIENTRYP qglUnlockArraysEXT) (void); +// GL_EXT_multi_draw_arrays +extern void (APIENTRY * qglMultiDrawArraysEXT) (GLenum, GLint *, GLsizei *, GLsizei); +extern void (APIENTRY * qglMultiDrawElementsEXT) (GLenum, GLsizei *, GLenum, const GLvoid **, GLsizei); +// GL_ARB_vertex_program +extern void (APIENTRY * qglVertexAttrib4fARB) (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +extern void (APIENTRY * qglVertexAttrib4fvARB) (GLuint, const GLfloat *); +extern void (APIENTRY * qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); +extern void (APIENTRY * qglEnableVertexAttribArrayARB) (GLuint index); +extern void (APIENTRY * qglDisableVertexAttribArrayARB) (GLuint index); + +// GL_ARB_vertex_buffer_object +extern void (APIENTRY * qglBindBufferARB) (GLenum target, GLuint buffer); +extern void (APIENTRY * qglDeleteBuffersARB) (GLsizei n, const GLuint * buffers); +extern void (APIENTRY * qglGenBuffersARB) (GLsizei n, GLuint * buffers); +extern GLboolean(APIENTRY * qglIsBufferARB) (GLuint buffer); +extern void (APIENTRY * qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage); +extern void (APIENTRY * qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data); +extern void (APIENTRY * qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data); +extern void (APIENTRY * qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint * params); +extern void (APIENTRY * qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid * *params); + +// GL_ARB_shader_objects +extern void (APIENTRY * qglDeleteObjectARB) (GLhandleARB obj); +extern GLhandleARB(APIENTRY * qglGetHandleARB) (GLenum pname); +extern void (APIENTRY * qglDetachObjectARB) (GLhandleARB containerObj, GLhandleARB attachedObj); +extern GLhandleARB(APIENTRY * qglCreateShaderObjectARB) (GLenum shaderType); +extern void (APIENTRY * qglShaderSourceARB) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, + const GLint * length); +extern void (APIENTRY * qglCompileShaderARB) (GLhandleARB shaderObj); +extern GLhandleARB(APIENTRY * qglCreateProgramObjectARB) (void); +extern void (APIENTRY * qglAttachObjectARB) (GLhandleARB containerObj, GLhandleARB obj); +extern void (APIENTRY * qglLinkProgramARB) (GLhandleARB programObj); +extern void (APIENTRY * qglUseProgramObjectARB) (GLhandleARB programObj); +extern void (APIENTRY * qglValidateProgramARB) (GLhandleARB programObj); +extern void (APIENTRY * qglUniform1fARB) (GLint location, GLfloat v0); +extern void (APIENTRY * qglUniform2fARB) (GLint location, GLfloat v0, GLfloat v1); +extern void (APIENTRY * qglUniform3fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +extern void (APIENTRY * qglUniform4fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +extern void (APIENTRY * qglUniform1iARB) (GLint location, GLint v0); +extern void (APIENTRY * qglUniform2iARB) (GLint location, GLint v0, GLint v1); +extern void (APIENTRY * qglUniform3iARB) (GLint location, GLint v0, GLint v1, GLint v2); +extern void (APIENTRY * qglUniform4iARB) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +extern void (APIENTRY * qglUniform2fvARB) (GLint location, GLsizei count, const GLfloat * value); +extern void (APIENTRY * qglUniform3fvARB) (GLint location, GLsizei count, const GLfloat * value); +extern void (APIENTRY * qglUniform4fvARB) (GLint location, GLsizei count, const GLfloat * value); +extern void (APIENTRY * qglUniform2ivARB) (GLint location, GLsizei count, const GLint * value); +extern void (APIENTRY * qglUniform3ivARB) (GLint location, GLsizei count, const GLint * value); +extern void (APIENTRY * qglUniform4ivARB) (GLint location, GLsizei count, const GLint * value); +extern void (APIENTRY * qglUniformMatrix2fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +extern void (APIENTRY * qglUniformMatrix3fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +extern void (APIENTRY * qglUniformMatrix4fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +extern void (APIENTRY * qglGetObjectParameterfvARB) (GLhandleARB obj, GLenum pname, GLfloat * params); +extern void (APIENTRY * qglGetObjectParameterivARB) (GLhandleARB obj, GLenum pname, GLint * params); +extern void (APIENTRY * qglGetInfoLogARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); +extern void (APIENTRY * qglGetAttachedObjectsARB) (GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, + GLhandleARB * obj); +extern GLint(APIENTRY * qglGetUniformLocationARB) (GLhandleARB programObj, const GLcharARB * name); +extern void (APIENTRY * qglGetActiveUniformARB) (GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +extern void (APIENTRY * qglGetUniformfvARB) (GLhandleARB programObj, GLint location, GLfloat * params); +extern void (APIENTRY * qglGetUniformivARB) (GLhandleARB programObj, GLint location, GLint * params); +extern void (APIENTRY * qglGetShaderSourceARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); + +// GL_ARB_vertex_shader +extern void (APIENTRY * qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB * name); +extern void (APIENTRY * qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +extern GLint(APIENTRY * qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB * name); + +#if defined(WIN32) +// WGL_ARB_create_context +#ifndef WGL_ARB_create_context +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif + +extern HGLRC(APIENTRY * qwglCreateContextAttribsARB) (HDC hdC, HGLRC hShareContext, const int *attribList); +#endif + +#if 0 //defined(__linux__) +// GLX_ARB_create_context +#ifndef GLX_ARB_create_context +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#endif + +extern GLXContext (APIENTRY * qglXCreateContextAttribsARB) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +#endif + //=========================================================================== #define qglAccum glAccum Index: code/renderer/tr_backend.c =================================================================== --- code/renderer/tr_backend.c (revision 1803) +++ code/renderer/tr_backend.c (working copy) @@ -386,7 +386,30 @@ } +void GL_SetProjectionMatrix(matrix_t matrix) +{ + Matrix16Copy(matrix, glState.projection); + if (!(r_arb_vertex_buffer_object->integer && r_arb_shader_objects->integer)) + { + qglMatrixMode(GL_PROJECTION); + qglLoadMatrixf(matrix); + qglMatrixMode(GL_MODELVIEW); + } +} + + +void GL_SetModelviewMatrix(matrix_t matrix) +{ + Matrix16Copy(matrix, glState.modelview); + + if (!(r_arb_vertex_buffer_object->integer && r_arb_shader_objects->integer)) + { + qglLoadMatrixf(matrix); + } +} + + /* ================ RB_Hyperspace @@ -410,9 +433,7 @@ static void SetViewportAndScissor( void ) { - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf( backEnd.viewParms.projectionMatrix ); - qglMatrixMode(GL_MODELVIEW); + GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); // set the window clipping qglViewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, @@ -500,11 +521,18 @@ plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; - qglLoadMatrixf( s_flipMatrix ); - qglClipPlane (GL_CLIP_PLANE0, plane2); - qglEnable (GL_CLIP_PLANE0); + GL_SetModelviewMatrix( s_flipMatrix ); + + if (!(r_arb_vertex_buffer_object->integer && r_arb_shader_objects->integer)) + { + qglClipPlane (GL_CLIP_PLANE0, plane2); + qglEnable (GL_CLIP_PLANE0); + } } else { - qglDisable (GL_CLIP_PLANE0); + if (!(r_arb_vertex_buffer_object->integer && r_arb_shader_objects->integer)) + { + qglDisable (GL_CLIP_PLANE0); + } } } @@ -609,7 +637,7 @@ R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); } - qglLoadMatrixf( backEnd.or.modelMatrix ); + GL_SetModelviewMatrix( backEnd.or.modelMatrix ); // // change depthrange. Also change projection matrix so first person weapon does not look like coming @@ -626,9 +654,7 @@ if(oldDepthRange) { // was not a crosshair but now is, change back proj matrix - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf(backEnd.viewParms.projectionMatrix); - qglMatrixMode(GL_MODELVIEW); + GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); } } else @@ -637,9 +663,7 @@ R_SetupProjection(&temp, r_znear->value, qfalse); - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf(temp.projectionMatrix); - qglMatrixMode(GL_MODELVIEW); + GL_SetProjectionMatrix( temp.projectionMatrix ); } } @@ -650,9 +674,7 @@ { if(!wasCrosshair && backEnd.viewParms.stereoFrame != STEREO_CENTER) { - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf(backEnd.viewParms.projectionMatrix); - qglMatrixMode(GL_MODELVIEW); + GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); } qglDepthRange (0, 1); @@ -677,7 +699,8 @@ } // go back to the world modelview matrix - qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + + GL_SetModelviewMatrix( backEnd.viewParms.world.modelMatrix ); if ( depthRange ) { qglDepthRange (0, 1); } @@ -708,17 +731,19 @@ ================ */ void RB_SetGL2D (void) { + matrix_t matrix; + backEnd.projection2D = qtrue; // set 2D virtual screen size qglViewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); qglScissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); - qglMatrixMode(GL_PROJECTION); - qglLoadIdentity (); - qglOrtho (0, glConfig.vidWidth, glConfig.vidHeight, 0, 0, 1); - qglMatrixMode(GL_MODELVIEW); - qglLoadIdentity (); + Matrix16Ortho(0, glConfig.vidWidth, glConfig.vidHeight, 0, 0, 1, matrix); + GL_SetProjectionMatrix(matrix); + Matrix16Identity(matrix); + GL_SetModelviewMatrix(matrix); + GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); @@ -744,6 +769,8 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { int i, j; int start, end; + matrix_t matrix; + vec4_t color; if ( !tr.registered ) { return; @@ -793,18 +820,120 @@ RB_SetGL2D(); - qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); + if (r_arb_vertex_buffer_object->integer) + { + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; - qglBegin (GL_QUADS); - qglTexCoord2f ( 0.5f / cols, 0.5f / rows ); - qglVertex2f (x, y); - qglTexCoord2f ( ( cols - 0.5f ) / cols , 0.5f / rows ); - qglVertex2f (x+w, y); - qglTexCoord2f ( ( cols - 0.5f ) / cols, ( rows - 0.5f ) / rows ); - qglVertex2f (x+w, y+h); - qglTexCoord2f ( 0.5f / cols, ( rows - 0.5f ) / rows ); - qglVertex2f (x, y+h); - qglEnd (); + tess.xyz[tess.numVertexes][0] = x; + tess.xyz[tess.numVertexes][1] = y; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = 0.5f / cols; + tess.texCoords[tess.numVertexes][0][1] = 0.5f / rows; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = x + w; + tess.xyz[tess.numVertexes][1] = y; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = (cols - 0.5f) / cols; + tess.texCoords[tess.numVertexes][0][1] = 0.5f / rows; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = x + w; + tess.xyz[tess.numVertexes][1] = y + h; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = (cols - 0.5f) / cols; + tess.texCoords[tess.numVertexes][0][1] = (rows - 0.5f) / rows; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = x; + tess.xyz[tess.numVertexes][1] = y + h; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = 0.5f / cols; + tess.texCoords[tess.numVertexes][0][1] = (rows - 0.5f) / rows; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 1; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 3; + + // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); + + if (r_arb_shader_objects->integer) + { + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + + GLSL_BindProgram(&tr.genericShader); + + GLSL_SetUniform_ModelMatrix(&tr.genericShader, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, matrix); + GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, matrix); + GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + GLSL_SetUniform_TCGen0(&tr.genericShader, TCGEN_TEXTURE); + Matrix16Identity(matrix); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + GLSL_SetUniform_Texture1Env(&tr.genericShader, 0); + GLSL_SetUniform_ColorGen(&tr.genericShader, CGEN_CONST); + GLSL_SetUniform_AlphaGen(&tr.genericShader, AGEN_CONST); + + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + color[3] = 1.0f; + GLSL_SetUniform_Color(&tr.genericShader, color); + } + else + { + qglEnableClientState( GL_VERTEX_ARRAY ); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + qglVertexPointer(3, GL_FLOAT, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz)); + qglTexCoordPointer( 2, GL_FLOAT, glState.currentVBO->stride_st, BUFFER_OFFSET(glState.currentVBO->ofs_st) ); + } + + //qglDrawElements(GL_TRIANGLES, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex); + + R_BindNullVBO(); + R_BindNullIBO(); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + } + else + { + qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); + + qglBegin (GL_QUADS); // Alternate made + qglTexCoord2f ( 0.5f / cols, 0.5f / rows ); + qglVertex2f (x, y); + qglTexCoord2f ( ( cols - 0.5f ) / cols , 0.5f / rows ); + qglVertex2f (x+w, y); + qglTexCoord2f ( ( cols - 0.5f ) / cols, ( rows - 0.5f ) / rows ); + qglVertex2f (x+w, y+h); + qglTexCoord2f ( 0.5f / cols, ( rows - 0.5f ) / rows ); + qglVertex2f (x, y+h); + qglEnd (); + } } void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { @@ -986,6 +1115,7 @@ int i; image_t *image; float x, y, w, h; + vec4_t quadVerts[4]; int start, end; if ( !backEnd.projection2D ) { @@ -1012,17 +1142,31 @@ h *= image->uploadHeight / 512.0f; } - GL_Bind( image ); - qglBegin (GL_QUADS); - qglTexCoord2f( 0, 0 ); - qglVertex2f( x, y ); - qglTexCoord2f( 1, 0 ); - qglVertex2f( x + w, y ); - qglTexCoord2f( 1, 1 ); - qglVertex2f( x + w, y + h ); - qglTexCoord2f( 0, 1 ); - qglVertex2f( x, y + h ); - qglEnd(); + if (r_arb_vertex_buffer_object->integer) + { + GL_Bind(image); + + VectorSet4(quadVerts[0], x, y, 0, 1); + VectorSet4(quadVerts[1], x + w, y, 0, 1); + VectorSet4(quadVerts[2], x + w, y + h, 0, 1); + VectorSet4(quadVerts[3], x, y + h, 0, 1); + + RB_InstantQuad(quadVerts); + } + else + { + GL_Bind( image ); // Alternate made + qglBegin (GL_QUADS); + qglTexCoord2f( 0, 0 ); + qglVertex2f( x, y ); + qglTexCoord2f( 1, 0 ); + qglVertex2f( x + w, y ); + qglTexCoord2f( 1, 1 ); + qglVertex2f( x + w, y + h ); + qglTexCoord2f( 0, 1 ); + qglVertex2f( x, y + h ); + qglEnd(); + } } qglFinish(); Index: code/renderer/tr_bsp.c =================================================================== --- code/renderer/tr_bsp.c (revision 1803) +++ code/renderer/tr_bsp.c (working copy) @@ -127,6 +127,41 @@ /* =============== +R_ColorShiftLightingFloats +=============== +*/ +static void R_ColorShiftLightingFloats(const vec4_t in, vec4_t out) +{ + int shift, r, g, b; + + // shift the color data based on overbright range + shift = r_mapOverBrightBits->integer - tr.overbrightBits; + + // shift the data based on overbright range + r = ((byte)(in[0] * 255)) << shift; + g = ((byte)(in[1] * 255)) << shift; + b = ((byte)(in[2] * 255)) << shift; + + // normalize by color instead of saturating to white + if((r | g | b) > 255) + { + int max; + + max = r > g ? r : g; + max = max > b ? max : b; + r = r * 255 / max; + g = g * 255 / max; + b = b * 255 / max; + } + + out[0] = r * (1.0f / 255.0f); + out[1] = g * (1.0f / 255.0f); + out[2] = b * (1.0f / 255.0f); + out[3] = in[3]; +} + +/* +=============== R_LoadLightmaps =============== @@ -306,63 +341,81 @@ static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { int i, j; srfSurfaceFace_t *cv; - int numPoints, numIndexes; - int lightmapNum; - int sfaceSize, ofsIndexes; + srfTriangle_t *tri; + int numVerts, numTriangles; - lightmapNum = LittleLong( ds->lightmapNum ); + surf->lightmapNum = LittleLong( ds->lightmapNum ); // get fog volume surf->fogIndex = LittleLong( ds->fogNum ) + 1; // get shader value - surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); + surf->shader = ShaderForShaderNum( ds->shaderNum, surf->lightmapNum ); if ( r_singleShader->integer && !surf->shader->isSky ) { surf->shader = tr.defaultShader; } - numPoints = LittleLong( ds->numVerts ); - if (numPoints > MAX_FACE_POINTS) { - ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints); - numPoints = MAX_FACE_POINTS; + numVerts = LittleLong(ds->numVerts); + if (numVerts > MAX_FACE_POINTS) { + ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numVerts); + numVerts = MAX_FACE_POINTS; surf->shader = tr.defaultShader; } - numIndexes = LittleLong( ds->numIndexes ); + numTriangles = LittleLong(ds->numIndexes) / 3; - // create the srfSurfaceFace_t - sfaceSize = ( size_t ) &((srfSurfaceFace_t *)0)->points[numPoints]; - ofsIndexes = sfaceSize; - sfaceSize += sizeof( int ) * numIndexes; - - cv = ri.Hunk_Alloc( sfaceSize, h_low ); + cv = ri.Hunk_Alloc(sizeof(*cv), h_low); cv->surfaceType = SF_FACE; - cv->numPoints = numPoints; - cv->numIndices = numIndexes; - cv->ofsIndices = ofsIndexes; - verts += LittleLong( ds->firstVert ); - for ( i = 0 ; i < numPoints ; i++ ) { - for ( j = 0 ; j < 3 ; j++ ) { - cv->points[i][j] = LittleFloat( verts[i].xyz[j] ); + cv->numTriangles = numTriangles; + cv->triangles = ri.Hunk_Alloc(numTriangles * sizeof(cv->triangles[0]), h_low); + + cv->numVerts = numVerts; + cv->verts = ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); + + // copy vertexes + ClearBounds(cv->bounds[0], cv->bounds[1]); + verts += LittleLong(ds->firstVert); + for(i = 0; i < numVerts; i++) + { + for(j = 0; j < 3; j++) + { + cv->verts[i].xyz[j] = LittleFloat(verts[i].xyz[j]); + cv->verts[i].normal[j] = LittleFloat(verts[i].normal[j]); } - for ( j = 0 ; j < 2 ; j++ ) { - cv->points[i][3+j] = LittleFloat( verts[i].st[j] ); - cv->points[i][5+j] = LittleFloat( verts[i].lightmap[j] ); + AddPointToBounds(cv->verts[i].xyz, cv->bounds[0], cv->bounds[1]); + for(j = 0; j < 2; j++) + { + cv->verts[i].st[j] = LittleFloat(verts[i].st[j]); + cv->verts[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); } - R_ColorShiftLightingBytes( verts[i].color, (byte *)&cv->points[i][7] ); + + R_ColorShiftLightingBytes( verts[i].color, cv->verts[i].vertexColors ); } - indexes += LittleLong( ds->firstIndex ); - for ( i = 0 ; i < numIndexes ; i++ ) { - ((int *)((byte *)cv + cv->ofsIndices ))[i] = LittleLong( indexes[ i ] ); + // copy triangles + indexes += LittleLong(ds->firstIndex); + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + tri->indexes[j] = LittleLong(indexes[i * 3 + j]); + + if(tri->indexes[j] < 0 || tri->indexes[j] >= numVerts) + { + ri.Error(ERR_DROP, "Bad index in face surface"); + } + } } + //R_CalcSurfaceTriangleNeighbors(numTriangles, cv->triangles); + //R_CalcSurfaceTrianglePlanes(numTriangles, cv->triangles, cv->verts); + // take the plane information from the lightmap vector for ( i = 0 ; i < 3 ; i++ ) { cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); } - cv->plane.dist = DotProduct( cv->points[0], cv->plane.normal ); + cv->plane.dist = DotProduct( cv->verts[0].xyz, cv->plane.normal ); SetPlaneSignbits( &cv->plane ); cv->plane.type = PlaneTypeForNormal( cv->plane.normal ); @@ -379,19 +432,18 @@ srfGridMesh_t *grid; int i, j; int width, height, numPoints; - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; - int lightmapNum; + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; vec3_t bounds[2]; vec3_t tmpVec; static surfaceType_t skipData = SF_SKIP; - lightmapNum = LittleLong( ds->lightmapNum ); + surf->lightmapNum = LittleLong( ds->lightmapNum ); // get fog volume surf->fogIndex = LittleLong( ds->fogNum ) + 1; // get shader value - surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); + surf->shader = ShaderForShaderNum( ds->shaderNum, surf->lightmapNum ); if ( r_singleShader->integer && !surf->shader->isSky ) { surf->shader = tr.defaultShader; } @@ -406,18 +458,26 @@ width = LittleLong( ds->patchWidth ); height = LittleLong( ds->patchHeight ); + if(width < 0 || width > MAX_PATCH_SIZE || height < 0 || height > MAX_PATCH_SIZE) + ri.Error(ERR_DROP, "ParseMesh: bad size"); + verts += LittleLong( ds->firstVert ); numPoints = width * height; - for ( i = 0 ; i < numPoints ; i++ ) { - for ( j = 0 ; j < 3 ; j++ ) { - points[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); - points[i].normal[j] = LittleFloat( verts[i].normal[j] ); + for(i = 0; i < numPoints; i++) + { + for(j = 0; j < 3; j++) + { + points[i].xyz[j] = LittleFloat(verts[i].xyz[j]); + points[i].normal[j] = LittleFloat(verts[i].normal[j]); } - for ( j = 0 ; j < 2 ; j++ ) { - points[i].st[j] = LittleFloat( verts[i].st[j] ); - points[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); + + for(j = 0; j < 2; j++) + { + points[i].st[j] = LittleFloat(verts[i].st[j]); + points[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); } - R_ColorShiftLightingBytes( verts[i].color, points[i].color ); + + R_ColorShiftLightingBytes( verts[i].color, points[i].vertexColors ); } // pre-tesseleate @@ -443,10 +503,15 @@ =============== */ static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { - srfTriangles_t *tri; - int i, j; - int numVerts, numIndexes; + srfTriangles_t *cv; + srfTriangle_t *tri; + int i, j; + int numVerts, numTriangles; + static surfaceType_t skipData = SF_SKIP; + // get lightmap + surf->lightmapNum = -1; // FIXME LittleLong(ds->lightmapNum); + // get fog volume surf->fogIndex = LittleLong( ds->fogNum ) + 1; @@ -456,45 +521,57 @@ surf->shader = tr.defaultShader; } - numVerts = LittleLong( ds->numVerts ); - numIndexes = LittleLong( ds->numIndexes ); + numVerts = LittleLong(ds->numVerts); + numTriangles = LittleLong(ds->numIndexes) / 3; - tri = ri.Hunk_Alloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) - + numIndexes * sizeof( tri->indexes[0] ), h_low ); - tri->surfaceType = SF_TRIANGLES; - tri->numVerts = numVerts; - tri->numIndexes = numIndexes; - tri->verts = (drawVert_t *)(tri + 1); - tri->indexes = (int *)(tri->verts + tri->numVerts ); + cv = ri.Hunk_Alloc(sizeof(*cv), h_low); + cv->surfaceType = SF_TRIANGLES; - surf->data = (surfaceType_t *)tri; + cv->numTriangles = numTriangles; + cv->triangles = ri.Hunk_Alloc(numTriangles * sizeof(cv->triangles[0]), h_low); + cv->numVerts = numVerts; + cv->verts = ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); + + surf->data = (surfaceType_t *) cv; + // copy vertexes - ClearBounds( tri->bounds[0], tri->bounds[1] ); - verts += LittleLong( ds->firstVert ); - for ( i = 0 ; i < numVerts ; i++ ) { - for ( j = 0 ; j < 3 ; j++ ) { - tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); - tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] ); + ClearBounds(cv->bounds[0], cv->bounds[1]); + verts += LittleLong(ds->firstVert); + for(i = 0; i < numVerts; i++) + { + for(j = 0; j < 3; j++) + { + cv->verts[i].xyz[j] = LittleFloat(verts[i].xyz[j]); + cv->verts[i].normal[j] = LittleFloat(verts[i].normal[j]); } - AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] ); - for ( j = 0 ; j < 2 ; j++ ) { - tri->verts[i].st[j] = LittleFloat( verts[i].st[j] ); - tri->verts[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); + + AddPointToBounds( cv->verts[i].xyz, cv->bounds[0], cv->bounds[1] ); + + for(j = 0; j < 2; j++) + { + cv->verts[i].st[j] = LittleFloat(verts[i].st[j]); + cv->verts[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); } - R_ColorShiftLightingBytes( verts[i].color, tri->verts[i].color ); + R_ColorShiftLightingBytes( verts[i].color, cv->verts[i].vertexColors ); + } - // copy indexes - indexes += LittleLong( ds->firstIndex ); - for ( i = 0 ; i < numIndexes ; i++ ) { - tri->indexes[i] = LittleLong( indexes[i] ); - if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) { - ri.Error( ERR_DROP, "Bad index in triangle surface" ); + // copy triangles + indexes += LittleLong(ds->firstIndex); + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + tri->indexes[j] = LittleLong(indexes[i * 3 + j]); + + if(tri->indexes[j] < 0 || tri->indexes[j] >= numVerts) + { + ri.Error(ERR_DROP, "Bad index in face surface"); + } } - } -} + }} /* =============== @@ -505,6 +582,9 @@ srfFlare_t *flare; int i; + // get lightmap + surf->lightmapNum = -1; + // get fog volume surf->fogIndex = LittleLong( ds->fogNum ) + 1; @@ -1206,8 +1286,8 @@ if ( grid->surfaceType != SF_GRID ) continue; // - size = (grid->width * grid->height - 1) * sizeof( drawVert_t ) + sizeof( *grid ); - hunkgrid = ri.Hunk_Alloc( size, h_low ); + size = sizeof(*grid); + hunkgrid = ri.Hunk_Alloc(size, h_low); Com_Memcpy(hunkgrid, grid, size); hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low ); @@ -1216,14 +1296,955 @@ hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low ); Com_Memcpy( hunkgrid->heightLodError, grid->heightLodError, grid->height * 4 ); + hunkgrid->numTriangles = grid->numTriangles; + hunkgrid->triangles = ri.Hunk_Alloc(grid->numTriangles * sizeof(srfTriangle_t), h_low); + Com_Memcpy(hunkgrid->triangles, grid->triangles, grid->numTriangles * sizeof(srfTriangle_t)); + + hunkgrid->numVerts = grid->numVerts; + hunkgrid->verts = ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); + Com_Memcpy(hunkgrid->verts, grid->verts, grid->numVerts * sizeof(srfVert_t)); + R_FreeSurfaceGridMesh( grid ); s_worldData.surfaces[i].data = (void *) hunkgrid; } } + /* +================= +BSPSurfaceCompare +compare function for qsort() +================= +*/ +static int BSPSurfaceCompare(const void *a, const void *b) +{ + msurface_t *aa, *bb; + + aa = *(msurface_t **) a; + bb = *(msurface_t **) b; + + // shader first + if(aa->shader < bb->shader) + return -1; + + else if(aa->shader > bb->shader) + return 1; + + // by lightmap + if(aa->lightmapNum < bb->lightmapNum) + return -1; + + else if(aa->lightmapNum > bb->lightmapNum) + return 1; + + return 0; +} + + +static void CopyVert(const srfVert_t * in, srfVert_t * out) +{ +#if 0 + memcpy(out->xyz, in->xyz, sizeof(in->xyz)); + memcpy(out->normal, in->normal, sizeof(in->normal)); + memcpy(out->st, in->st, sizeof(in->st)); + memcpy(out->lightmap, in->lightmap, sizeof(in->lightmap)); + memcpy(out->vertexColors, in->vertexColors, sizeof(in->vertexColors)); +#else + int j; + + for(j = 0; j < 3; j++) + { + out->xyz[j] = in->xyz[j]; + //out->tangent[j] = in->tangent[j]; + //out->binormal[j] = in->binormal[j]; + out->normal[j] = in->normal[j]; + //out->lightDirection[j] = in->lightDirection[j]; + } + + for(j = 0; j < 2; j++) + { + out->st[j] = in->st[j]; + out->lightmap[j] = in->lightmap[j]; + } + + for(j = 0; j < 4; j++) + { + //out->paintColor[j] = in->paintColor[j]; + //out->lightColor[j] = in->lightColor[j]; + out->vertexColors[j] = in->vertexColors[j]; + } + +#if DEBUG_OPTIMIZEVERTICES + out->id = in->id; +#endif +#endif +} + + + +/* +================= +R_CreateClusters +================= +*/ +static void R_CreateClusters() +{ + int i, j; + int numClusters; + mnode_t *node, *parent; + bspCluster_t *cluster; + const byte *vis; + int c; + msurface_t *surface, **mark; + int surfaceNum; + vec3_t mins, maxs; + int currentNumMarkSurfaces; + + ri.Printf(PRINT_ALL, "...creating BSP clusters\n"); + + if(s_worldData.vis) + { + // go through the leaves and count clusters + numClusters = 0; + for(i = 0, node = s_worldData.nodes; i < s_worldData.numnodes; i++, node++) + { + if(node->cluster >= numClusters) + { + numClusters = node->cluster; + } + } + numClusters++; + + s_worldData.numClusters = numClusters; + s_worldData.clusters = ri.Hunk_Alloc((numClusters + 1) * sizeof(*s_worldData.clusters), h_low); // + supercluster + + // reset surfaces' viewCount + for(i = 0, surface = s_worldData.surfaces; i < s_worldData.numsurfaces; i++, surface++) + { + surface->viewCount = -1; + } + + for(j = 0, node = s_worldData.nodes; j < s_worldData.numnodes; j++, node++) + { + node->visCounts[0] = -1; + } + + for(i = 0; i < numClusters; i++) + { + cluster = &s_worldData.clusters[i]; + + // mark leaves in cluster + vis = s_worldData.vis + i * s_worldData.clusterBytes; + + for(j = 0, node = s_worldData.nodes; j < s_worldData.numnodes; j++, node++) + { + if(node->cluster < 0 || node->cluster >= numClusters) + { + continue; + } + + // check general pvs + if(!(vis[node->cluster >> 3] & (1 << (node->cluster & 7)))) + { + continue; + } + + parent = node; + do + { + if(parent->visCounts[0] == i) + break; + parent->visCounts[0] = i; + parent = parent->parent; + } while(parent); + } + + + // add cluster surfaces + cluster->numMarkSurfaces = 0; + currentNumMarkSurfaces = 0; + + ClearBounds(mins, maxs); + for(j = 0, node = s_worldData.nodes; j < s_worldData.numnodes; j++, node++) + { + if(node->contents == CONTENTS_NODE) + continue; + + if(node->visCounts[0] != i) + continue; + + //BoundsAdd(mins, maxs, node->mins, node->maxs); + AddPointToBounds(node->mins, mins, maxs); + AddPointToBounds(node->maxs, mins, maxs); + + mark = node->firstmarksurface; + c = node->nummarksurfaces; + while(c--) + { + // the surface may have already been added if it + // spans multiple leafs + surface = *mark; + + surfaceNum = surface - s_worldData.surfaces; + + if((surface->viewCount != i) && (surfaceNum < s_worldData.numWorldSurfaces)) + { + // SmileTheory: Added a little shortcut here + shader_t *shader = surface->shader; + + if (((/*r_mergeClusterFaces->integer && */ *surface->data == SF_FACE) || + (/* r_mergeClusterCurves->integer && */ *surface->data == SF_GRID) || + (/* r_mergeClusterTriangles->integer && */ *surface->data == SF_TRIANGLES)) && + !shader->isSky && !shader->isPortal && !ShaderRequiresCPUDeforms(shader)) + { + surface->viewCount = i; + surface->inCluster = qtrue; + + currentNumMarkSurfaces++; + } + } + + mark++; + } + } + + cluster->markSurfaces = ri.Hunk_Alloc(sizeof(*cluster->markSurfaces) * currentNumMarkSurfaces, h_low); + + // Actually link the surfaces to the cluster now + for(j = 0, surface = s_worldData.surfaces; j < s_worldData.numWorldSurfaces; j++, surface++) + { + surfaceNum = surface - s_worldData.surfaces; + + if(surface->viewCount == i) + { + if (cluster->numMarkSurfaces == currentNumMarkSurfaces) + { + ri.Error(ERR_DROP, "R_CreateClusters: Incorrect allocation for MarkSurfaces!\n", cluster->numMarkSurfaces); + return; + } + cluster->markSurfaces[cluster->numMarkSurfaces] = surface; + cluster->numMarkSurfaces++; + } + } + + cluster->origin[0] = (mins[0] + maxs[0]) / 2; + cluster->origin[1] = (mins[1] + maxs[1]) / 2; + cluster->origin[2] = (mins[2] + maxs[2]) / 2; + + //ri.Printf(PRINT_ALL, "cluster %i origin at (%i %i %i)\n", i, (int)cluster->origin[0], (int)cluster->origin[1], (int)cluster->origin[2]); + + //ri.Printf(PRINT_ALL, "cluster %i contains %i bsp surfaces\n", i, cluster->numMarkSurfaces); + } + } + else + { + numClusters = 0; + + s_worldData.numClusters = numClusters; + s_worldData.clusters = ri.Hunk_Alloc((numClusters + 1) * sizeof(*s_worldData.clusters), h_low); // + supercluster + } + + // create a super cluster that will be always used when no view cluster can be found + cluster = &s_worldData.clusters[numClusters]; + + cluster->numMarkSurfaces = 0; + currentNumMarkSurfaces = s_worldData.numWorldSurfaces; + cluster->markSurfaces = ri.Hunk_Alloc(sizeof(*cluster->markSurfaces) * currentNumMarkSurfaces, h_low); + + for(i = 0, surface = s_worldData.surfaces; i < s_worldData.numWorldSurfaces; i++, surface++) + { + if (cluster->numMarkSurfaces == currentNumMarkSurfaces) + { + ri.Error(ERR_DROP, "R_CreateClusters(): cluster->numMarkSurfaces == currentNumMarkSurfaces !? \n"); + } + cluster->markSurfaces[cluster->numMarkSurfaces] = surface; + cluster->numMarkSurfaces++; + } + + for(i = 0; i < MAX_VISCOUNTS; i++) + { + s_worldData.numClusterVBOSurfaces[i] = 0; + } + + //ri.Printf(PRINT_ALL, "noVis cluster contains %i bsp surfaces\n", cluster->numMarkSurfaces); + + ri.Printf(PRINT_ALL, "%i world clusters created\n", numClusters + 1); + + + // reset surfaces' viewCount + for(i = 0, surface = s_worldData.surfaces; i < s_worldData.numsurfaces; i++, surface++) + { + surface->viewCount = -1; + } + + for(j = 0, node = s_worldData.nodes; j < s_worldData.numnodes; j++, node++) + { + node->visCounts[0] = -1; + } +} + + +/* =============== +R_CreateWorldVBO +=============== +*/ +static void R_CreateWorldVBO() +{ + int i, j, k; + + int numVerts; + srfVert_t *verts; + +// srfVert_t *optimizedVerts; + + int numTriangles; + srfTriangle_t *triangles; + +// int numSurfaces; + msurface_t *surface; + +// trRefLight_t *light; + + int startTime, endTime; + + startTime = ri.Milliseconds(); + + numVerts = 0; + numTriangles = 0; + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numWorldSurfaces; k++, surface++) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *face = (srfSurfaceFace_t *) surface->data; + + if(face->numVerts) + numVerts += face->numVerts; + + if(face->numTriangles) + numTriangles += face->numTriangles; + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *grid = (srfGridMesh_t *) surface->data; + + if(grid->numVerts) + numVerts += grid->numVerts; + + if(grid->numTriangles) + numTriangles += grid->numTriangles; + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *tri = (srfTriangles_t *) surface->data; + + if(tri->numVerts) + numVerts += tri->numVerts; + + if(tri->numTriangles) + numTriangles += tri->numTriangles; + } + } + + if(!numVerts || !numTriangles) + return; + + ri.Printf(PRINT_ALL, "...calculating world VBO ( %i verts %i tris )\n", numVerts, numTriangles); + + // create arrays + + s_worldData.numVerts = numVerts; + s_worldData.verts = verts = ri.Hunk_Alloc(numVerts * sizeof(srfVert_t), h_low); + //optimizedVerts = ri.Hunk_AllocateTempMemory(numVerts * sizeof(srfVert_t)); + + s_worldData.numTriangles = numTriangles; + s_worldData.triangles = triangles = ri.Hunk_Alloc(numTriangles * sizeof(srfTriangle_t), h_low); + + // set up triangle indices + numVerts = 0; + numTriangles = 0; + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numWorldSurfaces; k++, surface++) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + srf->firstIndex = numTriangles * 3; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + srf->firstIndex = numTriangles * 3; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + srf->firstIndex = numTriangles * 3; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + } + + // build vertices + numVerts = 0; + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numWorldSurfaces; k++, surface++) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + srf->firstVert = numVerts; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + CopyVert(&srf->verts[i], &verts[numVerts + i]); + } + + numVerts += srf->numVerts; + } + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + srf->firstVert = numVerts; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + CopyVert(&srf->verts[i], &verts[numVerts + i]); + } + + numVerts += srf->numVerts; + } + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + srf->firstVert = numVerts; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + CopyVert(&srf->verts[i], &verts[numVerts + i]); + } + + numVerts += srf->numVerts; + } + } + } + +#if 0 + numVerts = OptimizeVertices(numVerts, verts, numTriangles, triangles, optimizedVerts, CompareWorldVert); + if(c_redundantVertexes) + { + ri.Printf(PRINT_DEVELOPER, + "...removed %i redundant vertices from staticWorldMesh %i ( %s, %i verts %i tris )\n", + c_redundantVertexes, vboSurfaces.currentElements, shader->name, numVerts, numTriangles); + } + + s_worldData.vbo = R_CreateVBO2(va("bspModelMesh_vertices %i", 0), numVerts, optimizedVerts, + ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BINORMAL | + ATTR_NORMAL | ATTR_COLOR | GLCS_LIGHTCOLOR | ATTR_LIGHTDIRECTION); +#else + s_worldData.vbo = R_CreateVBO2(va("staticBspModel0_VBO %i", 0), numVerts, verts, + ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BINORMAL | + ATTR_NORMAL | ATTR_COLOR | ATTR_PAINTCOLOR | ATTR_LIGHTDIRECTION, VBO_USAGE_STATIC); +#endif + + s_worldData.ibo = R_CreateIBO2(va("staticBspModel0_IBO %i", 0), numTriangles, triangles, VBO_USAGE_STATIC); + + endTime = ri.Milliseconds(); + ri.Printf(PRINT_ALL, "world VBO calculation time = %5.2f seconds\n", (endTime - startTime) / 1000.0); + + // point triangle surfaces to world VBO + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numWorldSurfaces; k++, surface++) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + if( /*r_arb_vertex_buffer_object->integer && */ srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + //srf->ibo = R_CreateIBO2(va("staticBspModel0_planarSurface_IBO %i", k), srf->numTriangles, triangles + srf->firstTriangle, VBO_USAGE_STATIC); + } + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + if( /*r_arb_vertex_buffer_object->integer && */ srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + //srf->ibo = R_CreateIBO2(va("staticBspModel0_curveSurface_IBO %i", k), srf->numTriangles, triangles + srf->firstTriangle, VBO_USAGE_STATIC); + } + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + if( /*r_arb_vertex_buffer_object->integer && */ srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + //srf->ibo = R_CreateIBO2(va("staticBspModel0_triangleSurface_IBO %i", k), srf->numTriangles, triangles + srf->firstTriangle, VBO_USAGE_STATIC); + } + } + } + + + startTime = ri.Milliseconds(); + + // Tr3B: FIXME move this to somewhere else? +#if CALC_REDUNDANT_SHADOWVERTS + s_worldData.redundantVertsCalculationNeeded = 0; + for(i = 0; i < s_worldData.numLights; i++) + { + light = &s_worldData.lights[i]; + + if((r_precomputedLighting->integer || r_vertexLighting->integer) && !light->noRadiosity) + continue; + + s_worldData.redundantVertsCalculationNeeded++; + } + + if(s_worldData.redundantVertsCalculationNeeded) + { + ri.Printf(PRINT_ALL, "...calculating redundant world vertices ( %i verts )\n", numVerts); + + s_worldData.redundantLightVerts = ri.Hunk_Alloc(numVerts * sizeof(int), h_low); + BuildRedundantIndices(numVerts, verts, s_worldData.redundantLightVerts, CompareLightVert); + + s_worldData.redundantShadowVerts = ri.Hunk_Alloc(numVerts * sizeof(int), h_low); + BuildRedundantIndices(numVerts, verts, s_worldData.redundantShadowVerts, CompareShadowVert); + + s_worldData.redundantShadowAlphaTestVerts = ri.Hunk_Alloc(numVerts * sizeof(int), h_low); + BuildRedundantIndices(numVerts, verts, s_worldData.redundantShadowAlphaTestVerts, CompareShadowVertAlphaTest); + } + + endTime = ri.Milliseconds(); + ri.Printf(PRINT_ALL, "redundant world vertices calculation time = %5.2f seconds\n", (endTime - startTime) / 1000.0); +#endif + + //ri.Hunk_FreeTempMemory(triangles); + //ri.Hunk_FreeTempMemory(optimizedVerts); + //ri.Hunk_FreeTempMemory(verts); +} + +/* +=============== +R_CreateSubModelVBOs +=============== +*/ + +static void R_CreateSubModelVBOs() +{ + int i, j, k, l, m; + + int numVerts; + srfVert_t *verts; + + srfVert_t *optimizedVerts; + + int numTriangles; + srfTriangle_t *triangles; + + shader_t *shader, *oldShader; + int lightmapNum, oldLightmapNum; + int fogIndex, oldFogIndex; + + int numSurfaces; + msurface_t *surface, *surface2; + msurface_t **surfacesSorted; + + bmodel_t *model; + + srfVBOMesh_t *vboSurf; + + for(m = 1, model = s_worldData.bmodels; m < s_worldData.numBModels; m++, model++) + { + // count number of static area surfaces + numSurfaces = 0; + for(k = 0; k < model->numSurfaces; k++) + { + surface = model->firstSurface + k; + shader = surface->shader; + + if(shader->isSky) + continue; + + if(shader->isPortal) + continue; + + if(ShaderRequiresCPUDeforms(shader)) + continue; + + numSurfaces++; + } + + if(!numSurfaces) + continue; + + // build interaction caches list + surfacesSorted = ri.Malloc(numSurfaces * sizeof(surfacesSorted[0])); + + numSurfaces = 0; + for(k = 0; k < model->numSurfaces; k++) + { + surface = model->firstSurface + k; + shader = surface->shader; + + if(shader->isSky) + continue; + + if(shader->isPortal) + continue; + + if(ShaderRequiresCPUDeforms(shader)) + continue; + + surfacesSorted[numSurfaces] = surface; + numSurfaces++; + } + + model->numVBOSurfaces = 0; + + // sort surfaces by shader + qsort(surfacesSorted, numSurfaces, sizeof(surfacesSorted), BSPSurfaceCompare); + + // create a VBO for each shader + shader = oldShader = NULL; + lightmapNum = oldLightmapNum = -1; + fogIndex = oldFogIndex = -1; + + for(k = 0; k < numSurfaces; k++) + { + surface = surfacesSorted[k]; + shader = surface->shader; + lightmapNum = surface->lightmapNum; + fogIndex = surface->fogIndex; + + if(shader != oldShader) // || (r_precomputedLighting->integer ? lightmapNum != oldLightmapNum : 0)) + { + oldShader = shader; + oldLightmapNum = lightmapNum; + oldFogIndex = fogIndex; + + // count vertices and indices + numVerts = 0; + numTriangles = 0; + + for(l = k; l < numSurfaces; l++) + { + surface2 = surfacesSorted[l]; + + if(surface2->shader != shader) + break; // was continue, but continue made no sense + + if(*surface2->data == SF_FACE) + { + srfSurfaceFace_t *face = (srfSurfaceFace_t *) surface2->data; + + if(face->numVerts) + numVerts += face->numVerts; + + if(face->numTriangles) + numTriangles += face->numTriangles; + } + else if(*surface2->data == SF_GRID) + { + srfGridMesh_t *grid = (srfGridMesh_t *) surface2->data; + + if(grid->numVerts) + numVerts += grid->numVerts; + + if(grid->numTriangles) + numTriangles += grid->numTriangles; + } + else if(*surface2->data == SF_TRIANGLES) + { + srfTriangles_t *tri = (srfTriangles_t *) surface2->data; + + if(tri->numVerts) + numVerts += tri->numVerts; + + if(tri->numTriangles) + numTriangles += tri->numTriangles; + } + } + + if(!numVerts || !numTriangles) + continue; + + ri.Printf(PRINT_DEVELOPER, "...calculating entity mesh VBOs ( %s, %i verts %i tris )\n", shader->name, numVerts, + numTriangles); + + // create surface + vboSurf = ri.Hunk_Alloc(sizeof(*vboSurf), h_low); + + if (model->numVBOSurfaces == MAX_MODEL_VBO_SURFACES) + { + ri.Error(ERR_DROP, "Hit MAX_MODEL_VBO_SURFACES!\n"); + } + + model->vboSurfaces[model->numVBOSurfaces] = vboSurf; + model->numVBOSurfaces++; + + vboSurf->surfaceType = SF_VBO_MESH; + vboSurf->numIndexes = numTriangles * 3; + vboSurf->numVerts = numVerts; + + vboSurf->shader = shader; + vboSurf->lightmapNum = lightmapNum; + vboSurf->fogIndex = fogIndex; + + // create arrays + verts = ri.Malloc(numVerts * sizeof(srfVert_t)); + optimizedVerts = ri.Malloc(numVerts * sizeof(srfVert_t)); + numVerts = 0; + + triangles = ri.Malloc(numTriangles * sizeof(srfTriangle_t)); + numTriangles = 0; + + ClearBounds(vboSurf->bounds[0], vboSurf->bounds[1]); + + // build triangle indices + for(l = k; l < numSurfaces; l++) + { + surface2 = surfacesSorted[l]; + + if(surface2->shader != shader) + break; // was continue, but continue made no sense + + // set up triangle indices + if(*surface2->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface2->data; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + else if(*surface2->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface2->data; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + else if(*surface2->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface2->data; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + } + + // build vertices + numVerts = 0; + for(l = k; l < numSurfaces; l++) + { + surface2 = surfacesSorted[l]; + + if(surface2->shader != shader) + break; // was continue, but continue made no sense + + if(*surface2->data == SF_FACE) + { + srfSurfaceFace_t *cv = (srfSurfaceFace_t *) surface2->data; + + if(cv->numVerts) + { + for(i = 0; i < cv->numVerts; i++) + { + CopyVert(&cv->verts[i], &verts[numVerts + i]); + + AddPointToBounds(cv->verts[i].xyz, vboSurf->bounds[0], vboSurf->bounds[1]); + } + + numVerts += cv->numVerts; + } + } + else if(*surface2->data == SF_GRID) + { + srfGridMesh_t *cv = (srfGridMesh_t *) surface2->data; + + if(cv->numVerts) + { + for(i = 0; i < cv->numVerts; i++) + { + CopyVert(&cv->verts[i], &verts[numVerts + i]); + + AddPointToBounds(cv->verts[i].xyz, vboSurf->bounds[0], vboSurf->bounds[1]); + } + + numVerts += cv->numVerts; + } + } + else if(*surface2->data == SF_TRIANGLES) + { + srfTriangles_t *cv = (srfTriangles_t *) surface2->data; + + if(cv->numVerts) + { + for(i = 0; i < cv->numVerts; i++) + { + CopyVert(&cv->verts[i], &verts[numVerts + i]); + + AddPointToBounds(cv->verts[i].xyz, vboSurf->bounds[0], vboSurf->bounds[1]); + } + + numVerts += cv->numVerts; + } + } + } + +#if 0 + numVerts = OptimizeVertices(numVerts, verts, numTriangles, triangles, optimizedVerts, CompareWorldVert); + if(c_redundantVertexes) + { + ri.Printf(PRINT_DEVELOPER, + "...removed %i redundant vertices from staticEntityMesh %i ( %s, %i verts %i tris )\n", + c_redundantVertexes, model->numVBOSurfaces, shader->name, numVerts, numTriangles); + } + + vboSurf->vbo = + R_CreateVBO2(va("staticBspModel%i_VBO %i", m, model->numVBOSurfaces), numVerts, optimizedVerts, + ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BINORMAL | ATTR_NORMAL + | ATTR_COLOR | GLCS_LIGHTCOLOR | ATTR_LIGHTDIRECTION, VBO_USAGE_STATIC); +#else + vboSurf->vbo = + R_CreateVBO2(va("staticBspModel%i_VBO %i", m, model->numVBOSurfaces), numVerts, verts, + ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BINORMAL | ATTR_NORMAL + | ATTR_COLOR | ATTR_PAINTCOLOR | ATTR_LIGHTDIRECTION, VBO_USAGE_STATIC); +#endif + + vboSurf->ibo = + R_CreateIBO2(va("staticBspModel%i_IBO %i", m, model->numVBOSurfaces), numTriangles, triangles, + VBO_USAGE_STATIC); + + ri.Free(triangles); + ri.Free(optimizedVerts); + ri.Free(verts); + } + } + + ri.Free(surfacesSorted); + + ri.Printf(PRINT_ALL, "%i VBO surfaces created for BSP submodel %i\n", model->numVBOSurfaces, m); + } +} + +/* +=============== R_LoadSurfaces =============== */ @@ -1313,6 +2334,7 @@ ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); count = l->filelen / sizeof(*in); + s_worldData.numBModels = count; s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low ); for ( i=0 ; ifirstSurface = s_worldData.surfaces + LittleLong( in->firstSurface ); out->numSurfaces = LittleLong( in->numSurfaces ); + + if(i == 0) + { + // Tr3B: add this for limiting VBO surface creation + s_worldData.numWorldSurfaces = out->numSurfaces; + } } } @@ -1861,11 +2889,20 @@ R_LoadEntities( &header->lumps[LUMP_ENTITIES] ); R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] ); + // create static VBOS from the world + R_CreateWorldVBO(); + R_CreateClusters(); + R_CreateSubModelVBOs(); + s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker; // only set tr.world now that we know the entire level has loaded properly tr.world = &s_worldData; + // make sure the VBO glState entries are save + R_BindNullVBO(); + R_BindNullIBO(); + ri.FS_FreeFile( buffer.v ); } Index: code/renderer/tr_cmds.c =================================================================== --- code/renderer/tr_cmds.c (revision 1803) +++ code/renderer/tr_cmds.c (working copy) @@ -69,6 +69,11 @@ ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n", backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders ); } + else if (r_speeds->integer == 7 ) + { + ri.Printf( PRINT_ALL, "surface IBOs:%i merged IBOs:%i reduced: %i\n", + backEnd.pc.c_surfaceIBOs, backEnd.pc.c_mergedIBOs, backEnd.pc.c_surfaceIBOs - backEnd.pc.c_mergedIBOs); + } Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); Index: code/renderer/tr_curve.c =================================================================== --- code/renderer/tr_curve.c (revision 1803) +++ code/renderer/tr_curve.c (working copy) @@ -33,7 +33,7 @@ Only a single entry point: srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { */ @@ -43,7 +43,7 @@ LerpDrawVert ============ */ -static void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) { +static void LerpDrawVert( srfVert_t *a, srfVert_t *b, srfVert_t *out ) { out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]); out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]); out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]); @@ -54,10 +54,10 @@ out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]); out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]); - out->color[0] = (a->color[0] + b->color[0]) >> 1; - out->color[1] = (a->color[1] + b->color[1]) >> 1; - out->color[2] = (a->color[2] + b->color[2]) >> 1; - out->color[3] = (a->color[3] + b->color[3]) >> 1; + out->vertexColors[0] = (a->vertexColors[0] + b->vertexColors[0]) >> 1; + out->vertexColors[1] = (a->vertexColors[1] + b->vertexColors[1]) >> 1; + out->vertexColors[2] = (a->vertexColors[2] + b->vertexColors[2]) >> 1; + out->vertexColors[3] = (a->vertexColors[3] + b->vertexColors[3]) >> 1; } /* @@ -65,9 +65,9 @@ Transpose ============ */ -static void Transpose( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { +static void Transpose( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { int i, j; - drawVert_t temp; + srfVert_t temp; if ( width > height ) { for ( i = 0 ; i < height ; i++ ) { @@ -109,7 +109,7 @@ Handles all the complicated wrapping and degenerate cases ================= */ -static void MakeMeshNormals( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { +static void MakeMeshNormals( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { int i, j, k, dist; vec3_t normal; vec3_t sum; @@ -117,7 +117,7 @@ vec3_t base; vec3_t delta; int x, y; - drawVert_t *dv; + srfVert_t *dv; vec3_t around[8], temp; qboolean good[8]; qboolean wrapWidth, wrapHeight; @@ -214,14 +214,68 @@ } +static int MakeMeshTriangles(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], + srfTriangle_t triangles[SHADER_MAX_TRIANGLES]) +{ + int i, j; + int numTriangles; + int w, h; + srfVert_t *dv; + static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE]; + + h = height - 1; + w = width - 1; + numTriangles = 0; + for(i = 0; i < h; i++) + { + for(j = 0; j < w; j++) + { + int v1, v2, v3, v4; + + // vertex order to be reckognized as tristrips + v1 = i * width + j + 1; + v2 = v1 - 1; + v3 = v2 + width; + v4 = v3 + 1; + + triangles[numTriangles].indexes[0] = v2; + triangles[numTriangles].indexes[1] = v3; + triangles[numTriangles].indexes[2] = v1; + numTriangles++; + + triangles[numTriangles].indexes[0] = v1; + triangles[numTriangles].indexes[1] = v3; + triangles[numTriangles].indexes[2] = v4; + numTriangles++; + } + } + + R_CalcSurfaceTriangleNeighbors(numTriangles, triangles); + + // FIXME: use more elegant way + for(i = 0; i < width; i++) + { + for(j = 0; j < height; j++) + { + dv = &ctrl2[j * width + i]; + *dv = ctrl[j][i]; + } + } + + R_CalcSurfaceTrianglePlanes(numTriangles, triangles, ctrl2); + + return numTriangles; +} + + /* ============ InvertCtrl ============ */ -static void InvertCtrl( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { +static void InvertCtrl( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { int i, j; - drawVert_t temp; + srfVert_t temp; for ( i = 0 ; i < height ; i++ ) { for ( j = 0 ; j < width/2 ; j++ ) { @@ -259,10 +313,10 @@ PutPointsOnCurve ================== */ -static void PutPointsOnCurve( drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], +static void PutPointsOnCurve( srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], int width, int height ) { int i, j; - drawVert_t prev, next; + srfVert_t prev, next; for ( i = 0 ; i < width ; i++ ) { for ( j = 1 ; j < height ; j += 2 ) { @@ -288,14 +342,15 @@ ================= */ srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height, - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE] ) { + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE], + int numTriangles, srfTriangle_t triangles[SHADER_MAX_TRIANGLES]) { int i, j, size; - drawVert_t *vert; + srfVert_t *vert; vec3_t tmpVec; srfGridMesh_t *grid; // copy the results out to a grid - size = (width * height - 1) * sizeof( drawVert_t ) + sizeof( *grid ); + size = (width * height - 1) * sizeof( srfVert_t ) + sizeof( *grid ); #ifdef PATCH_STITCHING grid = /*ri.Hunk_Alloc*/ ri.Malloc( size ); @@ -306,6 +361,13 @@ grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 ); Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); + + grid->numTriangles = numTriangles; + grid->triangles = ri.Malloc(grid->numTriangles * sizeof(srfTriangle_t)); + Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t)); + + grid->numVerts = (width * height); + grid->verts = ri.Malloc(grid->numVerts * sizeof(srfVert_t)); #else grid = ri.Hunk_Alloc( size ); Com_Memset(grid, 0, size); @@ -315,6 +377,13 @@ grid->heightLodError = ri.Hunk_Alloc( height * 4 ); Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); + + grid->numTriangles = numTriangles; + grid->triangles = ri.Hunk_Alloc(grid->numTriangles * sizeof(srfTriangle_t), h_low); + Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t)); + + grid->numVerts = (width * height); + grid->verts = ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); #endif grid->width = width; @@ -349,6 +418,8 @@ void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) { ri.Free(grid->widthLodError); ri.Free(grid->heightLodError); + ri.Free(grid->triangles); + ri.Free(grid->verts); ri.Free(grid); } @@ -358,16 +429,18 @@ ================= */ srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { int i, j, k, l; - drawVert_t_cleared( prev ); - drawVert_t_cleared( next ); - drawVert_t_cleared( mid ); + srfVert_t_cleared( prev ); + srfVert_t_cleared( next ); + srfVert_t_cleared( mid ); float len, maxLen; int dir; int t; - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; float errorTable[2][MAX_GRID_SIZE]; + int numTriangles; + static srfTriangle_t triangles[SHADER_MAX_TRIANGLES]; for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { @@ -511,10 +584,13 @@ } #endif + // calculate triangles + numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); + // calculate normals MakeMeshNormals( width, height, ctrl ); - return R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); + return R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); } /* @@ -525,10 +601,12 @@ srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) { int i, j; int width, height, oldwidth; - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; float errorTable[2][MAX_GRID_SIZE]; float lodRadius; vec3_t lodOrigin; + int numTriangles; + static srfTriangle_t triangles[SHADER_MAX_TRIANGLES]; oldwidth = 0; width = grid->width + 1; @@ -557,6 +635,10 @@ } // put all the aproximating points on the curve //PutPointsOnCurve( ctrl, width, height ); + + // calculate triangles + numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); + // calculate normals MakeMeshNormals( width, height, ctrl ); @@ -565,7 +647,7 @@ // free the old grid R_FreeSurfaceGridMesh(grid); // create a new grid - grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); + grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); grid->lodRadius = lodRadius; VectorCopy(lodOrigin, grid->lodOrigin); return grid; @@ -579,10 +661,12 @@ srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) { int i, j; int width, height, oldheight; - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; float errorTable[2][MAX_GRID_SIZE]; float lodRadius; vec3_t lodOrigin; + int numTriangles; + static srfTriangle_t triangles[SHADER_MAX_TRIANGLES]; oldheight = 0; width = grid->width; @@ -611,6 +695,10 @@ } // put all the aproximating points on the curve //PutPointsOnCurve( ctrl, width, height ); + + // calculate triangles + numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); + // calculate normals MakeMeshNormals( width, height, ctrl ); @@ -619,7 +707,7 @@ // free the old grid R_FreeSurfaceGridMesh(grid); // create a new grid - grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); + grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); grid->lodRadius = lodRadius; VectorCopy(lodOrigin, grid->lodOrigin); return grid; Index: code/renderer/tr_extramath.c =================================================================== --- code/renderer/tr_extramath.c (revision 0) +++ code/renderer/tr_extramath.c (revision 0) @@ -0,0 +1,110 @@ +/* +=========================================================================== +Copyright (C) 2010 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_extramath.c - extra math needed by the renderer not in qmath.c + +#include "tr_local.h" + +// Some matrix helper functions +// FIXME: do these already exist in ioq3 and I don't know about them? + +void Matrix16Zero( matrix_t out ) +{ + out[ 0] = 0.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f; + out[ 1] = 0.0f; out[ 5] = 0.0f; out[ 9] = 0.0f; out[13] = 0.0f; + out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 0.0f; out[14] = 0.0f; + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 0.0f; +} + +void Matrix16Identity( matrix_t out ) +{ + out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f; + out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = 0.0f; + out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = 0.0f; + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; +} + +void Matrix16Copy( const matrix_t in, matrix_t out ) +{ + out[ 0] = in[ 0]; out[ 4] = in[ 4]; out[ 8] = in[ 8]; out[12] = in[12]; + out[ 1] = in[ 1]; out[ 5] = in[ 5]; out[ 9] = in[ 9]; out[13] = in[13]; + out[ 2] = in[ 2]; out[ 6] = in[ 6]; out[10] = in[10]; out[14] = in[14]; + out[ 3] = in[ 3]; out[ 7] = in[ 7]; out[11] = in[11]; out[15] = in[15]; +} + +void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out ) +{ + out[ 0] = in1[ 0] * in2[ 0] + in1[ 4] * in2[ 1] + in1[ 8] * in2[ 2] + in1[12] * in2[ 3]; + out[ 1] = in1[ 1] * in2[ 0] + in1[ 5] * in2[ 1] + in1[ 9] * in2[ 2] + in1[13] * in2[ 3]; + out[ 2] = in1[ 2] * in2[ 0] + in1[ 6] * in2[ 1] + in1[10] * in2[ 2] + in1[14] * in2[ 3]; + out[ 3] = in1[ 3] * in2[ 0] + in1[ 7] * in2[ 1] + in1[11] * in2[ 2] + in1[15] * in2[ 3]; + + out[ 4] = in1[ 0] * in2[ 4] + in1[ 4] * in2[ 5] + in1[ 8] * in2[ 6] + in1[12] * in2[ 7]; + out[ 5] = in1[ 1] * in2[ 4] + in1[ 5] * in2[ 5] + in1[ 9] * in2[ 6] + in1[13] * in2[ 7]; + out[ 6] = in1[ 2] * in2[ 4] + in1[ 6] * in2[ 5] + in1[10] * in2[ 6] + in1[14] * in2[ 7]; + out[ 7] = in1[ 3] * in2[ 4] + in1[ 7] * in2[ 5] + in1[11] * in2[ 6] + in1[15] * in2[ 7]; + + out[ 8] = in1[ 0] * in2[ 8] + in1[ 4] * in2[ 9] + in1[ 8] * in2[10] + in1[12] * in2[11]; + out[ 9] = in1[ 1] * in2[ 8] + in1[ 5] * in2[ 9] + in1[ 9] * in2[10] + in1[13] * in2[11]; + out[10] = in1[ 2] * in2[ 8] + in1[ 6] * in2[ 9] + in1[10] * in2[10] + in1[14] * in2[11]; + out[11] = in1[ 3] * in2[ 8] + in1[ 7] * in2[ 9] + in1[11] * in2[10] + in1[15] * in2[11]; + + out[12] = in1[ 0] * in2[12] + in1[ 4] * in2[13] + in1[ 8] * in2[14] + in1[12] * in2[15]; + out[13] = in1[ 1] * in2[12] + in1[ 5] * in2[13] + in1[ 9] * in2[14] + in1[13] * in2[15]; + out[14] = in1[ 2] * in2[12] + in1[ 6] * in2[13] + in1[10] * in2[14] + in1[14] * in2[15]; + out[15] = in1[ 3] * in2[12] + in1[ 7] * in2[13] + in1[11] * in2[14] + in1[15] * in2[15]; +} + +qboolean Matrix16Compare( const matrix_t a, const matrix_t b ) +{ + return (a[ 0] == b[ 0] && a[ 4] == b[ 4] && a[ 8] == b[ 8] && a[12] == b[12] && + a[ 1] == b[ 1] && a[ 5] == b[ 5] && a[ 9] == b[ 9] && a[13] == b[13] && + a[ 2] == b[ 2] && a[ 6] == b[ 6] && a[10] == b[10] && a[14] == b[14] && + a[ 3] == b[ 3] && a[ 7] == b[ 7] && a[11] == b[11] && a[15] == b[15]); +} + +void Matrix16Dump( const matrix_t in ) +{ + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 0], in[ 4], in[ 8], in[12]); + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 1], in[ 5], in[ 9], in[13]); + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 2], in[ 6], in[10], in[14]); + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 3], in[ 7], in[11], in[15]); +} + +void Matrix16Translation( vec3_t vec, matrix_t out ) +{ + out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = vec[0]; + out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = vec[1]; + out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = vec[2]; + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; +} + +void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out ) +{ + Matrix16Zero(out); + out[ 0] = 2.0f / (right - left); + out[ 5] = 2.0f / (top - bottom); + out[10] = 2.0f / (zfar - znear); + out[12] = -(right + left) / (right - left); + out[13] = -(top + bottom) / (top - bottom); + out[14] = -(zfar + znear) / (zfar - znear); + out[15] = 1.0f; +} Index: code/renderer/tr_extramath.h =================================================================== --- code/renderer/tr_extramath.h (revision 0) +++ code/renderer/tr_extramath.h (revision 0) @@ -0,0 +1,50 @@ +/* +=========================================================================== +Copyright (C) 2010 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_extramath.h + +#ifndef __TR_EXTRAMATH_H__ +#define __TR_EXTRAMATH_H__ + +typedef vec_t matrix_t[16]; + +void Matrix16Zero( matrix_t out ); +void Matrix16Identity( matrix_t out ); +void Matrix16Copy( const matrix_t in, matrix_t out ); +void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out ); +qboolean Matrix16Compare(const matrix_t a, const matrix_t b); +void Matrix16Dump( const matrix_t in ); +void Matrix16Translation( vec3_t vec, matrix_t out ); +void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out ); + +#define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) +#define VectorSet4(v,x,y,z,w) ((v)[0]=(x),(v)[1]=(y),(v)[2]=(z),(v)[3]=(w)) + +static ID_INLINE int VectorCompare4(const vec4_t v1, const vec4_t v2) +{ + if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3]) + { + return 0; + } + return 1; +} + +#endif Index: code/renderer/tr_glsl.c =================================================================== --- code/renderer/tr_glsl.c (revision 0) +++ code/renderer/tr_glsl.c (revision 0) @@ -0,0 +1,859 @@ +/* +=========================================================================== +Copyright (C) 2006-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +XreaL source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_glsl.c +#include "tr_local.h" + +void GLSL_BindNullProgram(void); + +// FIXME: Do something that isn't this messy +static const char *fallbackGenericShader_vp = +"attribute vec4 attr_Position;\nattribute vec4 attr_TexCoord0;\nattribute ve" +"c4 attr_TexCoord1;\nattribute vec3 attr_Normal;\nattribute vec4 attr_Color" +";\n\nuniform mat4 u_Texture0Matrix;\nuniform mat4 u_Texture1Matrix;\nun" +"iform vec3 u_ViewOrigin;\nuniform int u_TCGen0;\nuniform int u_TCG" +"en1;\nuniform vec4 u_TCGen0Vector0;\nuniform vec4 u_TCGen0Vector1;\nuni" +"form vec4 u_TCGen1Vector0;\nuniform vec4 u_TCGen1Vector1;\nuniform int " +" u_DeformGen;\nuniform vec4 u_DeformWave; // [base amplitude phase fr" +"eq]\nuniform vec3 u_DeformBulge; // [width height speed]\nuniform float " +" u_DeformSpread;\nuniform float u_Time;\nuniform int u_ColorGen;\nunifo" +"rm int u_AlphaGen;\nuniform vec4 u_Color;\nuniform mat4 u_ModelView" +"ProjectionMatrix;\nuniform vec3 u_AmbientLight;\nuniform vec3 u_Directe" +"dLight;\nuniform vec3 u_LightDir;\nuniform int u_PortalClipping;\nunif" +"orm vec4 u_PortalPlane;\nuniform float u_PortalRange;\nuniform float u" +"_FogEyeT;\n\nvarying vec3 var_Position;\nvarying vec2 var_Tex1;\nvaryin" +"g vec2 var_Tex2;\nvarying vec4 var_Color;\nvarying float var_Discard;" +"\n\n\nfloat triangle(float x)\n{\n\treturn max(1.0 - abs(x), 0);\n}\n\nfloat " +"sawtooth(float x)\n{\n\treturn x - floor(x);\n}\n\nvec4 DeformPosition(const " +"vec4 pos, const vec3 normal, const vec2 st)\n{\n\tvec4 deformed = pos;\n\n\t/" +"*\n\t\tdefine\tWAVEVALUE( table, base, amplitude, phase, freq ) \\\n\t\t\t((b" +"ase) + table[ Q_ftol( ( ( (phase) + backEnd.refdef.floatTime * (freq) ) * FUN" +"CTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude))\n\t*/\n\n\tif (u_DeformGen" +" == DGEN_WAVE_SIN)\n\t{\n\t\tfloat off = (pos.x + pos.y + pos.z) * u_DeformSp" +"read;\n\t\tfloat scale = u_DeformWave.x + sin(off + u_DeformWave.z + (u_Time" +" * u_DeformWave.w)) * u_DeformWave.y;\n\t\tvec3 offset = normal * scale;\n\n" +"\t\tdeformed.xyz += offset;\n\t}\n\n\tif (u_DeformGen == DGEN_WAVE_SQUARE)\n" +"\t{\n\t\tfloat off = (pos.x + pos.y + pos.z) * u_DeformSpread;\n\t\tfloat sca" +"le = u_DeformWave.x + sign(sin(off + u_DeformWave.z + (u_Time * u_DeformWave" +".w))) * u_DeformWave.y;\n\t\tvec3 offset = normal * scale;\n\n\t\tdeformed.xy" +"z += offset;\n\t}\n\n\tif (u_DeformGen == DGEN_WAVE_TRIANGLE)\n\t{\n\t\tfloat" +" off = (pos.x + pos.y + pos.z) * u_DeformSpread;\n\t\tfloat scale = u_DeformW" +"ave.x + triangle(off + u_DeformWave.z + (u_Time * u_DeformWave.w)) * u_Defor" +"mWave.y;\n\t\tvec3 offset = normal * scale;\n\n\t\tdeformed.xyz += offset;\n" +"\t}\n\n\tif (u_DeformGen == DGEN_WAVE_SAWTOOTH)\n\t{\n\t\tfloat off = (pos.x " +"+ pos.y + pos.z) * u_DeformSpread;\n\t\tfloat scale = u_DeformWave.x + sawto" +"oth(off + u_DeformWave.z + (u_Time * u_DeformWave.w)) * u_DeformWave.y;\n\t\t" +"vec3 offset = normal * scale;\n\n\t\tdeformed.xyz += offset;\n\t}\n\n\tif (u_" +"DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH)\n\t{\n\t\tfloat off = (pos.x + pos.y" +" + pos.z) * u_DeformSpread;\n\t\tfloat scale = u_DeformWave.x + (1.0 - sawtoo" +"th(off + u_DeformWave.z + (u_Time * u_DeformWave.w))) * u_DeformWave.y;\n\t\t" +"vec3 offset = normal * scale;\n\n\t\tdeformed.xyz += offset;\n\t}\n\n\tif (u_" +"DeformGen == DGEN_BULGE)\n\t{\n\t\tfloat bulgeWidth = u_DeformBulge.x;\n\t\tf" +"loat bulgeHeight = u_DeformBulge.y;\n\t\tfloat bulgeSpeed = u_DeformBulge.z;" +"\n\n\t\tfloat now = u_Time * bulgeSpeed;\n\n\t\tfloat off = (M_PI * 0.25) * s" +"t.x * bulgeWidth + now;\n\t\tfloat scale = sin(off) * bulgeHeight;\n\t\tvec3 " +"offset = normal * scale;\n\n\t\tdeformed.xyz += offset;\n\t}\n\n\treturn defo" +"rmed;\n}\n\nvec2 GenTexCoords(int TCGen, vec4 position, mat4 texMatrix, vec4 " +"TCGenVector0, vec4 TCGenVector1)\n{\n\tvec4 tex;\n\n\tif (TCGen == TCGEN_LIGH" +"TMAP)\n\t{\n\t\ttex = attr_TexCoord1;\n\t}\n\telse if (TCGen == TCGEN_TEXTURE" +")\n\t{\n\t\ttex = attr_TexCoord0;\n\t}\n\telse if (TCGen == TCGEN_FOG)\n\t{\n" +"\t\t// TCGenVector0 is fogDistanceVector\n\t\t// TCGenVector1 is fogDepthVect" +"or\n\t\t// u_FogEyeT is eyeT\n\t\tfloat s = dot(position, TCGenVector0);\n\t" +"\tfloat t = dot(position, TCGenVector1);\n\n\t\t// partially clipped fogs use" +" the T axis\n\t\tif (u_FogEyeT < 0) // eyeOutside\n\t\t{\n\t\t\tif (t < 1.0)" +"\n\t\t\t{\n\t\t\t\tt = 1.0/32;\t// point is outside, so no fogging\n\t\t\t}\n" +"\t\t\telse\n\t\t\t{\n\t\t\t\tt = 1.0/32 + 30.0/32 * t / (t - u_FogEyeT);\t// " +"cut the distance at the fog plane\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif " +"(t < 0)\n\t\t\t{\n\t\t\t\tt = 1.0/32;\t// point is outside, so no fogging\n\t" +"\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tt = 31.0/32;\n\t\t\t}\n\t\t}\n\t\ttex.s =" +" s;\n\t\ttex.t = t;\n\t}\n\telse if (TCGen == TCGEN_ENVIRONMENT_MAPPED)\n\t{" +"\n\t\tvec3 viewer = normalize(u_ViewOrigin - position.xyz);\n\n\t\tfloat d = " +"dot(attr_Normal, viewer);\n\n\t\tvec3 reflected = attr_Normal * 2.0 * d - vie" +"wer;\n\n\t\ttex.s = 0.5 + reflected.y * 0.5;\n\t\ttex.t = 0.5 - reflected.z *" +" 0.5;\n\t}\n\telse if (TCGen == TCGEN_VECTOR)\n\t{\n\t\ttex.s = dot(position." +"xyz, TCGenVector0.xyz);\n\t\ttex.t = dot(position.xyz, TCGenVector1.xyz);\n\t" +"}\n\telse\n\t{\n\t\t// if (TCGen == TCGEN_IDENTITY)\n\t\ttex = vec4(0.0);\n\t" +"}\n\n\treturn (texMatrix * tex).st;\n}\n\nvoid\tmain()\n{\n\tvec4 position = " +"vec4(0.0);\n\n\t{\n\t\t// transform vertex position into homogenous clip-spac" +"e\n\t\tposition = DeformPosition(attr_Position, attr_Normal, attr_TexCoord0.s" +"t);\n\t\tif (bool(u_PortalClipping))\n\t\t{\n\t\t\tfloat dist = dot(position." +"xyz, u_PortalPlane.xyz) - u_PortalPlane.w;\n\t\t\tif (dist < 0.0)\n\t\t\t{\n" +"\t\t\t\tvar_Discard = 1.0f;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tvar_Disca" +"rd = 0.0f;\n\n\t\tgl_Position = u_ModelViewProjectionMatrix * position;\n\t}" +"\n\n\t// transform texcoords\n\n\tvar_Tex1 = GenTexCoords(u_TCGen0, position," +" u_Texture0Matrix, u_TCGen0Vector0, u_TCGen0Vector1);\n\tvar_Tex2 = GenTexCoo" +"rds(u_TCGen1, position, u_Texture1Matrix, u_TCGen1Vector0, u_TCGen1Vector1);" +"\n\t//var_Tex2 = (u_Texture1Matrix * attr_TexCoord1).st;\n\n\tif (u_ColorGen " +"== CGEN_LIGHTING_DIFFUSE)\n\t{\n\t\tfloat incoming = dot(attr_Normal, u_Light" +"Dir);\n\n\t\tif (incoming <= 0)\n\t\t{\n\t\t\tvar_Color.rgb = u_AmbientLight;" +"\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvar_Color.r = min(u_AmbientLight.r + incoming" +" * u_DirectedLight.r, 1.0);\n\t\t\tvar_Color.g = min(u_AmbientLight.g + incom" +"ing * u_DirectedLight.g, 1.0);\n\t\t\tvar_Color.b = min(u_AmbientLight.b + in" +"coming * u_DirectedLight.b, 1.0);\n\t\t}\n\t}\n\telse if (u_ColorGen == CGEN_" +"EXACT_VERTEX)\n\t{\n\t\tvar_Color.r = attr_Color.r;\n\t\tvar_Color.g = attr_C" +"olor.g;\n\t\tvar_Color.b = attr_Color.b;\n\t}\n\telse if (u_ColorGen == CGEN_" +"VERTEX)\n\t{\n\t\tvar_Color.r = attr_Color.r * u_Color.r;\n\t\tvar_Color.g = " +"attr_Color.g * u_Color.g;\n\t\tvar_Color.b = attr_Color.b * u_Color.b;\n\t}\n" +"\telse if (u_ColorGen == CGEN_ONE_MINUS_VERTEX)\n\t{\n\t\tvar_Color.r = (1.0 " +"- attr_Color.r) * u_Color.r;\n\t\tvar_Color.g = (1.0 - attr_Color.g) * u_Colo" +"r.g;\n\t\tvar_Color.b = (1.0 - attr_Color.b) * u_Color.b;\n\t}\n\telse\n\t{\n" +"\t\t// if(u_ColorGen == CGEN_IDENTITY)\n\t\t// if(u_ColorGen == CGEN_IDENTITY" +"_LIGHTING)\n\t\t// if(u_ColorGen == CGEN_ENTITY)\n\t\t// if(u_ColorGen == CGE" +"N_ONE_MINUS_ENTITY)\n\t\t// if(u_ColorGen == CGEN_CONST)\n\t\t// if(u_ColorGe" +"n == CGEN_WAVEFORM)\n\t\t// if(u_ColorGen == CGEN_FOG)\n\t\tvar_Color.r = u_C" +"olor.r;\n\t\tvar_Color.g = u_Color.g;\n\t\tvar_Color.b = u_Color.b;\n\t}\n\n" +"\tif (u_AlphaGen == AGEN_LIGHTING_SPECULAR)\n\t{\n\t\tvec3 lightDir = vec3(-9" +"60, -1980, 96) - position.xyz;\n\t\tlightDir = normalize(lightDir);\n\n\t\tfl" +"oat d = dot(attr_Normal, lightDir);\n\t\tvec3 reflected = attr_Normal * 2 * d" +" - lightDir;\n\n\t\tvec3 viewer = u_ViewOrigin - position.xyz;\n\t\tfloat ile" +"ngth = 1.0 / length(viewer);\n\n\t\tfloat l = dot(reflected, viewer);\n\t\tl " +"*= ilength;\n\n\t\tif (l < 0)\n\t\t{\n\t\t\tvar_Color.a = 0;\n\t\t}\n\t\telse" +"\n\t\t{\n\t\t\tl = l*l;\n\t\t\tl = l*l;\n\t\t\tvar_Color.a = min(l, 1.0);\n\t" +"\t}\n\t}\n\telse if (u_AlphaGen == AGEN_VERTEX)\n\t{\n\t\tvar_Color.a = attr_" +"Color.a;\n\t}\n\telse if (u_AlphaGen == AGEN_ONE_MINUS_VERTEX)\n\t{\n\t\tvar_" +"Color.a = 1.0 - attr_Color.a;\n\t}\n\telse if (u_AlphaGen == AGEN_PORTAL)\n\t" +"{\n\t\tfloat len;\n\t\tvec3 v;\n\n\t\tv = position.xyz - u_ViewOrigin;\n\t\tl" +"en = length(v);\n\n\t\tlen /= u_PortalRange;\n\n\t\tvar_Color.a = clamp(len, " +"0.0, 1.0);\n\t}\n\telse\n\t{\n\t\t// if(u_AlphaGen == AGEN_IDENTITY)\n\t\t// " +"if(u_AlphaGen == AGEN_CONST)\n\t\t// if(u_AlphaGen == AGEN_ENTITY,\n\t\t// if" +"(u_AlphaGen == AGEN_ONE_MINUS_ENTITY)\n\t\t// if(u_AlphaGen == AGEN_WAVEFORM)" +"\n\t\t// if(u_AlphaGen == AGEN_PORTAL)\n\t\t// if(u_AlphaGen == AGEN_CONST)\n" +"\t\tvar_Color.a = u_Color.a;\n\t}\n\n}\n"; + +static const char *fallbackGenericShader_fp = +"uniform sampler2D u_Texture0Map;\nuniform sampler2D u_Texture1Map;\nuni" +"form int u_Texture1Env;\n\nvarying vec3 var_Position;\nvaryi" +"ng vec2 var_Tex1;\nvarying vec2 var_Tex2;\nvarying vec4 " +" var_Color;\nvarying float var_Discard;\n\nvoid\tmain()\n{\n\tif (va" +"r_Discard > 0.0)\n\t{\n\t\tdiscard;\n\t\treturn;\n\t}\n\n\tvec4 color = text" +"ure2D(u_Texture0Map, var_Tex1);\n\tvec4 color2 = texture2D(u_Texture1Map, var" +"_Tex2);\n\n\tif (u_Texture1Env == 1) // GL_MODULATE\n\t{\n\t\tcolor *= color2" +";\n\t}\n\telse if (u_Texture1Env == 2) // GL_REPLACE\n\t{\n\t\tcolor = color2" +";\n\t}\n\telse if (u_Texture1Env == 3) // GL_DECAL\n\t{\n\t\tcolor = color * " +"vec4(1.0 - color2.a) + color2 * vec4(color2.a);\n\t}\n\telse if (u_Texture1En" +"v == 4) // GL_ADD\n\t{\n\t\tcolor += color2;\n\t}\n\n\tcolor *= var_Color;\n" +"\tgl_FragColor = color;\n}\n"; + +static void GLSL_PrintInfoLog(GLhandleARB object, qboolean developerOnly) +{ + char *msg; + static char msgPart[1024]; + int maxLength = 0; + int i; + + qglGetObjectParameterivARB(object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &maxLength); + + msg = ri.Malloc(maxLength); + + qglGetInfoLogARB(object, maxLength, &maxLength, msg); + + if(developerOnly) + { + ri.Printf(PRINT_DEVELOPER, "compile log:\n"); + } + else + { + ri.Printf(PRINT_ALL, "compile log:\n"); + } + + for(i = 0; i < maxLength; i += 1024) + { + Q_strncpyz(msgPart, msg + i, sizeof(msgPart)); + + if(developerOnly) + ri.Printf(PRINT_DEVELOPER, "%s\n", msgPart); + else + ri.Printf(PRINT_ALL, "%s\n", msgPart); + } + + ri.Free(msg); +} + +static void GLSL_PrintShaderSource(GLhandleARB object) +{ + char *msg; + static char msgPart[1024]; + int maxLength = 0; + int i; + + qglGetObjectParameterivARB(object, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &maxLength); + + msg = ri.Malloc(maxLength); + + qglGetShaderSourceARB(object, maxLength, &maxLength, msg); + + for(i = 0; i < maxLength; i += 1024) + { + Q_strncpyz(msgPart, msg + i, sizeof(msgPart)); + ri.Printf(PRINT_ALL, "%s\n", msgPart); + } + + ri.Free(msg); +} + +static int GLSL_CompileGPUShader(GLhandleARB program, const GLcharARB *buffer, int size, GLenum shaderType) +{ + GLint compiled; + GLhandleARB shader; + + shader = qglCreateShaderObjectARB(shaderType); + + { + static char bufferExtra[32000]; + int sizeExtra; + + char *bufferFinal = NULL; + int sizeFinal; + + float fbufWidthScale, fbufHeightScale; + + Com_Memset(bufferExtra, 0, sizeof(bufferExtra)); + + // HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones + if(0) //(glConfig.driverType == GLDRV_OPENGL3) + { + Q_strcat(bufferExtra, sizeof(bufferExtra), "#version 130\n"); + + if(shaderType == GL_VERTEX_SHADER_ARB) + { + Q_strcat(bufferExtra, sizeof(bufferExtra), "#define attribute in\n"); + Q_strcat(bufferExtra, sizeof(bufferExtra), "#define varying out\n"); + } + else + { + Q_strcat(bufferExtra, sizeof(bufferExtra), "#define varying in\n"); + + Q_strcat(bufferExtra, sizeof(bufferExtra), "out vec4 out_Color;\n"); + Q_strcat(bufferExtra, sizeof(bufferExtra), "#define gl_FragColor out_Color\n"); + } + } + else + { + Q_strcat(bufferExtra, sizeof(bufferExtra), "#version 120\n"); + } + + // HACK: add some macros to avoid extra uniforms and save speed and code maintenance + //Q_strcat(bufferExtra, sizeof(bufferExtra), + // va("#ifndef r_SpecularExponent\n#define r_SpecularExponent %f\n#endif\n", r_specularExponent->value)); + //Q_strcat(bufferExtra, sizeof(bufferExtra), + // va("#ifndef r_SpecularScale\n#define r_SpecularScale %f\n#endif\n", r_specularScale->value)); + //Q_strcat(bufferExtra, sizeof(bufferExtra), + // va("#ifndef r_NormalScale\n#define r_NormalScale %f\n#endif\n", r_normalScale->value)); + + + Q_strcat(bufferExtra, sizeof(bufferExtra), "#ifndef M_PI\n#define M_PI 3.14159265358979323846f\n#endif\n"); + + //Q_strcat(bufferExtra, sizeof(bufferExtra), va("#ifndef MAX_SHADOWMAPS\n#define MAX_SHADOWMAPS %i\n#endif\n", MAX_SHADOWMAPS)); + + Q_strcat(bufferExtra, sizeof(bufferExtra), + va("#ifndef deformGen_t\n" + "#define deformGen_t\n" + "#define DGEN_WAVE_SIN %i\n" + "#define DGEN_WAVE_SQUARE %i\n" + "#define DGEN_WAVE_TRIANGLE %i\n" + "#define DGEN_WAVE_SAWTOOTH %i\n" + "#define DGEN_WAVE_INVERSE_SAWTOOTH %i\n" + "#define DGEN_BULGE %i\n" + "#define DGEN_MOVE %i\n" + "#endif\n", + DGEN_WAVE_SIN, + DGEN_WAVE_SQUARE, + DGEN_WAVE_TRIANGLE, + DGEN_WAVE_SAWTOOTH, + DGEN_WAVE_INVERSE_SAWTOOTH, + DGEN_BULGE, + DGEN_MOVE)); + + Q_strcat(bufferExtra, sizeof(bufferExtra), + va("#ifndef tcGen_t\n" + "#define tcGen_t\n" + "#define TCGEN_LIGHTMAP %i\n" + "#define TCGEN_TEXTURE %i\n" + "#define TCGEN_ENVIRONMENT_MAPPED %i\n" + "#define TCGEN_FOG %i\n" + "#define TCGEN_VECTOR %i\n" + "#endif\n", + TCGEN_LIGHTMAP, + TCGEN_TEXTURE, + TCGEN_ENVIRONMENT_MAPPED, + TCGEN_FOG, + TCGEN_VECTOR)); + + Q_strcat(bufferExtra, sizeof(bufferExtra), + va("#ifndef colorGen_t\n" + "#define colorGen_t\n" + "#define CGEN_VERTEX %i\n" + "#define CGEN_ONE_MINUS_VERTEX %i\n" + "#define CGEN_EXACT_VERTEX %i\n" + "#define CGEN_LIGHTING_DIFFUSE %i\n" + "#endif\n", + CGEN_VERTEX, + CGEN_ONE_MINUS_VERTEX, + CGEN_EXACT_VERTEX, + CGEN_LIGHTING_DIFFUSE)); + + Q_strcat(bufferExtra, sizeof(bufferExtra), + va("#ifndef alphaGen_t\n" + "#define alphaGen_t\n" + "#define AGEN_VERTEX %i\n" + "#define AGEN_ONE_MINUS_VERTEX %i\n" + "#define AGEN_LIGHTING_SPECULAR %i\n" + "#define AGEN_PORTAL %i\n" + "#endif\n", + AGEN_VERTEX, + AGEN_ONE_MINUS_VERTEX, + AGEN_LIGHTING_SPECULAR, + AGEN_PORTAL)); + + Q_strcat(bufferExtra, sizeof(bufferExtra), + va("#ifndef alphaTest_t\n" + "#define alphaTest_t\n" + "#define ATEST_GT_0 %i\n" + "#define ATEST_LT_128 %i\n" + "#define ATEST_GE_128 %i\n" + "#endif\n", + ATEST_GT_0, + ATEST_LT_128, + ATEST_GE_128)); + + fbufWidthScale = 1.0f / ((float)glConfig.vidWidth); + fbufHeightScale = 1.0f / ((float)glConfig.vidHeight); + Q_strcat(bufferExtra, sizeof(bufferExtra), + va("#ifndef r_FBufScale\n#define r_FBufScale vec2(%f, %f)\n#endif\n", fbufWidthScale, fbufHeightScale)); + + // OK we added a lot of stuff but if we do something bad in the GLSL shaders then we want the proper line + // so we have to reset the line counting + Q_strcat(bufferExtra, sizeof(bufferExtra), "#line 0\n"); + + sizeExtra = strlen(bufferExtra); + sizeFinal = sizeExtra + size; + + //ri.Printf(PRINT_ALL, "GLSL extra: %s\n", bufferExtra); + + bufferFinal = ri.Hunk_AllocateTempMemory(size + sizeExtra); + + strcpy(bufferFinal, bufferExtra); + Q_strcat(bufferFinal, sizeFinal, buffer); + + qglShaderSourceARB(shader, 1, (const GLcharARB **)&bufferFinal, &sizeFinal); + + ri.Hunk_FreeTempMemory(bufferFinal); + } + + // compile shader + qglCompileShaderARB(shader); + + // check if shader compiled + qglGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); + if(!compiled) + { + GLSL_PrintShaderSource(shader); + GLSL_PrintInfoLog(shader, qfalse); + ri.Error(ERR_DROP, "Couldn't compile shader"); + return 0; + } + + GLSL_PrintInfoLog(shader, qtrue); + //ri.Printf(PRINT_ALL, "%s\n", GLSL_PrintShaderSource(shader)); + + // attach shader to program + qglAttachObjectARB(program, shader); + + // delete shader, no longer needed + qglDeleteObjectARB(shader); + + return 1; +} + + +static int GLSL_LoadGPUShader(GLhandleARB program, const char *name, GLenum shaderType) +{ + char filename[MAX_QPATH]; + GLcharARB *buffer = NULL; + int size; + int result; + + if(shaderType == GL_VERTEX_SHADER_ARB) + { + Com_sprintf(filename, sizeof(filename), "glsl/%s_vp.glsl", name); + } + else + { + Com_sprintf(filename, sizeof(filename), "glsl/%s_fp.glsl", name); + } + + ri.Printf(PRINT_ALL, "...loading '%s'\n", filename); + size = ri.FS_ReadFile(filename, (void **)&buffer); + if(!buffer) + { + //ri.Error(ERR_DROP, "Couldn't load %s", filename); + ri.Printf(PRINT_ALL, "Couldn't load %s, size %d\n", filename, size); + return 0; + } + + result = GLSL_CompileGPUShader(program, buffer, size, shaderType); + + ri.FS_FreeFile(buffer); + + return result; +} + +static void GLSL_LinkProgram(GLhandleARB program) +{ + GLint linked; + + qglLinkProgramARB(program); + + qglGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &linked); + if(!linked) + { + GLSL_PrintInfoLog(program, qfalse); + ri.Error(ERR_DROP, "%s\nshaders failed to link"); + } +} + +static void GLSL_ValidateProgram(GLhandleARB program) +{ + GLint validated; + + qglValidateProgramARB(program); + + qglGetObjectParameterivARB(program, GL_OBJECT_VALIDATE_STATUS_ARB, &validated); + if(!validated) + { + GLSL_PrintInfoLog(program, qfalse); + ri.Error(ERR_DROP, "%s\nshaders failed to validate"); + } +} + +static void GLSL_ShowProgramUniforms(GLhandleARB program) +{ + int i, count, size; + GLenum type; + char uniformName[1000]; + + // install the executables in the program object as part of current state. + qglUseProgramObjectARB(program); + + // check for GL Errors + + // query the number of active uniforms + qglGetObjectParameterivARB(program, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &count); + + // Loop over each of the active uniforms, and set their value + for(i = 0; i < count; i++) + { + qglGetActiveUniformARB(program, i, sizeof(uniformName), NULL, &size, &type, uniformName); + + ri.Printf(PRINT_DEVELOPER, "active uniform: '%s'\n", uniformName); + } + + qglUseProgramObjectARB(0); +} + +static int GLSL_InitGPUShader(shaderProgram_t * program, const char *name, int attribs, qboolean fragmentShader) +{ + ri.Printf(PRINT_DEVELOPER, "------- GPU shader -------\n"); + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "GLSL_InitGPUShader: \"%s\" is too long\n", name); + } + + Q_strncpyz(program->name, name, sizeof(program->name)); + + program->program = qglCreateProgramObjectARB(); + program->attribs = attribs; + + if (!(GLSL_LoadGPUShader(program->program, name, GL_VERTEX_SHADER_ARB))) + { + ri.Printf(PRINT_ALL, "GLSL_InitGPUShader: Unable to load \"%s\" as GL_VERTEX_SHADER_ARB\n", name); + return 0; + } + + if(fragmentShader) + GLSL_LoadGPUShader(program->program, name, GL_FRAGMENT_SHADER_ARB); + + if(attribs & ATTR_POSITION) + qglBindAttribLocationARB(program->program, ATTR_INDEX_POSITION, "attr_Position"); + + if(attribs & ATTR_TEXCOORD) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD0, "attr_TexCoord0"); + + if(attribs & ATTR_LIGHTCOORD) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD1, "attr_TexCoord1"); + +// if(attribs & ATTR_TEXCOORD2) +// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD2, "attr_TexCoord2"); + +// if(attribs & ATTR_TEXCOORD3) +// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD3, "attr_TexCoord3"); + + if(attribs & ATTR_TANGENT) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TANGENT, "attr_Tangent"); + + if(attribs & ATTR_BINORMAL) + qglBindAttribLocationARB(program->program, ATTR_INDEX_BINORMAL, "attr_Binormal"); + + if(attribs & ATTR_NORMAL) + qglBindAttribLocationARB(program->program, ATTR_INDEX_NORMAL, "attr_Normal"); + + if(attribs & ATTR_COLOR) + qglBindAttribLocationARB(program->program, ATTR_INDEX_COLOR, "attr_Color"); + + if(attribs & ATTR_PAINTCOLOR) + qglBindAttribLocationARB(program->program, ATTR_INDEX_PAINTCOLOR, "attr_PaintColor"); + + if(attribs & ATTR_LIGHTDIRECTION) + qglBindAttribLocationARB(program->program, ATTR_INDEX_LIGHTDIRECTION, "attr_LightDirection"); + + GLSL_LinkProgram(program->program); + + return 1; +} + + +static int GLSL_InitFallbackGenericShader(shaderProgram_t * program, const char *name, int attribs, qboolean fragmentShader) +{ + ri.Printf(PRINT_DEVELOPER, "------- GPU shader -------\n"); + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "GLSL_InitGPUShader: \"%s\" is too long\n", name); + } + + Q_strncpyz(program->name, name, sizeof(program->name)); + + program->program = qglCreateProgramObjectARB(); + program->attribs = attribs; + + if (!(GLSL_CompileGPUShader(program->program, fallbackGenericShader_vp, strlen(fallbackGenericShader_vp), GL_VERTEX_SHADER_ARB))) + { + ri.Printf(PRINT_ALL, "GLSL_InitFallbackGenericShader: Unable to load \"%s\" as GL_VERTEX_SHADER_ARB\n", name); + return 0; + } + + if(fragmentShader) + GLSL_CompileGPUShader(program->program, fallbackGenericShader_fp, strlen(fallbackGenericShader_fp), GL_FRAGMENT_SHADER_ARB); + + if(attribs & ATTR_POSITION) + qglBindAttribLocationARB(program->program, ATTR_INDEX_POSITION, "attr_Position"); + + if(attribs & ATTR_TEXCOORD) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD0, "attr_TexCoord0"); + + if(attribs & ATTR_LIGHTCOORD) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD1, "attr_TexCoord1"); + +// if(attribs & ATTR_TEXCOORD2) +// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD2, "attr_TexCoord2"); + +// if(attribs & ATTR_TEXCOORD3) +// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD3, "attr_TexCoord3"); + + if(attribs & ATTR_TANGENT) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TANGENT, "attr_Tangent"); + + if(attribs & ATTR_BINORMAL) + qglBindAttribLocationARB(program->program, ATTR_INDEX_BINORMAL, "attr_Binormal"); + + if(attribs & ATTR_NORMAL) + qglBindAttribLocationARB(program->program, ATTR_INDEX_NORMAL, "attr_Normal"); + + if(attribs & ATTR_COLOR) + qglBindAttribLocationARB(program->program, ATTR_INDEX_COLOR, "attr_Color"); + + if(attribs & ATTR_PAINTCOLOR) + qglBindAttribLocationARB(program->program, ATTR_INDEX_PAINTCOLOR, "attr_PaintColor"); + + if(attribs & ATTR_LIGHTDIRECTION) + qglBindAttribLocationARB(program->program, ATTR_INDEX_LIGHTDIRECTION, "attr_LightDirection"); + + GLSL_LinkProgram(program->program); + + return 1; +} + + +void GLSL_InitGPUShaders(void) +{ + int startTime, endTime; + + ri.Printf(PRINT_ALL, "------- GLSL_InitGPUShaders -------\n"); + + // make sure the render thread is stopped + R_SyncRenderThread(); + + startTime = ri.Milliseconds(); + + if (!GLSL_InitGPUShader(&tr.genericShader, "generic", ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_NORMAL | ATTR_COLOR, qtrue)) + { + // Failed to load, init the fallback one + GLSL_InitFallbackGenericShader(&tr.genericShader, "generic", ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_NORMAL | ATTR_COLOR, qtrue); + } + + tr.genericShader.u_ModelViewProjectionMatrix = + qglGetUniformLocationARB(tr.genericShader.program, "u_ModelViewProjectionMatrix"); + + tr.genericShader.u_AlphaTest = -1; //qglGetUniformLocationARB(tr.genericShader.program, "u_AlphaTest"); + tr.genericShader.u_ColorGen = qglGetUniformLocationARB(tr.genericShader.program, "u_ColorGen"); + tr.genericShader.u_AlphaGen = qglGetUniformLocationARB(tr.genericShader.program, "u_AlphaGen"); + tr.genericShader.u_TCGen0 = qglGetUniformLocationARB(tr.genericShader.program, "u_TCGen0"); + tr.genericShader.u_TCGen1 = qglGetUniformLocationARB(tr.genericShader.program, "u_TCGen1"); + tr.genericShader.u_TCGen0Vector0 = qglGetUniformLocationARB(tr.genericShader.program, "u_TCGen0Vector0"); + tr.genericShader.u_TCGen0Vector1 = qglGetUniformLocationARB(tr.genericShader.program, "u_TCGen0Vector1"); + tr.genericShader.u_TCGen1Vector0 = qglGetUniformLocationARB(tr.genericShader.program, "u_TCGen1Vector0"); + tr.genericShader.u_TCGen1Vector1 = qglGetUniformLocationARB(tr.genericShader.program, "u_TCGen1Vector1"); + tr.genericShader.u_DeformGen = qglGetUniformLocationARB(tr.genericShader.program, "u_DeformGen"); + tr.genericShader.u_DeformWave = qglGetUniformLocationARB(tr.genericShader.program, "u_DeformWave"); + tr.genericShader.u_DeformBulge = qglGetUniformLocationARB(tr.genericShader.program, "u_DeformBulge"); + tr.genericShader.u_DeformSpread = qglGetUniformLocationARB(tr.genericShader.program, "u_DeformSpread"); + tr.genericShader.u_Time = qglGetUniformLocationARB(tr.genericShader.program, "u_Time"); + tr.genericShader.u_Color = qglGetUniformLocationARB(tr.genericShader.program, "u_Color"); + tr.genericShader.u_AmbientLight = qglGetUniformLocationARB(tr.genericShader.program, "u_AmbientLight"); + tr.genericShader.u_DirectedLight = qglGetUniformLocationARB(tr.genericShader.program, "u_DirectedLight"); + tr.genericShader.u_LightDir = qglGetUniformLocationARB(tr.genericShader.program, "u_LightDir"); + tr.genericShader.u_ViewOrigin = qglGetUniformLocationARB(tr.genericShader.program, "u_ViewOrigin"); + tr.genericShader.u_Texture0Matrix = qglGetUniformLocationARB(tr.genericShader.program, "u_Texture0Matrix"); + tr.genericShader.u_Texture1Matrix = qglGetUniformLocationARB(tr.genericShader.program, "u_Texture1Matrix"); + tr.genericShader.u_Texture1Env = qglGetUniformLocationARB(tr.genericShader.program, "u_Texture1Env"); + + tr.genericShader.u_Texture0Map = qglGetUniformLocationARB(tr.genericShader.program, "u_Texture0Map"); + tr.genericShader.u_Texture1Map = qglGetUniformLocationARB(tr.genericShader.program, "u_Texture1Map"); + + tr.genericShader.u_PortalClipping = qglGetUniformLocationARB(tr.genericShader.program, "u_PortalClipping"); + tr.genericShader.u_PortalPlane = qglGetUniformLocationARB(tr.genericShader.program, "u_PortalPlane"); + tr.genericShader.u_PortalRange = qglGetUniformLocationARB(tr.genericShader.program, "u_PortalRange"); + tr.genericShader.u_FogEyeT = qglGetUniformLocationARB(tr.genericShader.program, "u_FogEyeT"); + tr.genericShader.u_ModelMatrix = -1; //qglGetUniformLocationARB(tr.genericShader.program, "u_ModelMatrix"); + + qglUseProgramObjectARB(tr.genericShader.program); + qglUniform1iARB(tr.genericShader.u_Texture0Map, 0); + qglUniform1iARB(tr.genericShader.u_Texture1Map, 1); + qglUseProgramObjectARB(0); + + GLSL_ValidateProgram(tr.genericShader.program); + GLSL_ShowProgramUniforms(tr.genericShader.program); + GL_CheckErrors(); + + endTime = ri.Milliseconds(); + + ri.Printf(PRINT_ALL, "GLSL shaders load time = %5.2f seconds\n", (endTime - startTime) / 1000.0); +} + +void GLSL_ShutdownGPUShaders(void) +{ + ri.Printf(PRINT_ALL, "------- GLSL_ShutdownGPUShaders -------\n"); + + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL); + qglDisableVertexAttribArrayARB(ATTR_INDEX_COLOR); + GLSL_BindNullProgram(); + + if(tr.genericShader.program) + { + qglDeleteObjectARB(tr.genericShader.program); + Com_Memset(&tr.genericShader, 0, sizeof(shaderProgram_t)); + } + + glState.currentProgram = 0; + qglUseProgramObjectARB(0); +} + + +void GLSL_BindNullProgram(void); + + +void GLSL_BindProgram(shaderProgram_t * program) +{ + if(!program) + { + GLSL_BindNullProgram(); + return; + } + + if(r_logFile->integer) + { + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- GL_BindProgram( %s ) ---\n", program->name)); + } + + if(glState.currentProgram != program) + { + qglUseProgramObjectARB(program->program); + glState.currentProgram = program; + } +} + +void GLSL_BindNullProgram(void) +{ + if(r_logFile->integer) + { + GLimp_LogComment("--- GL_BindNullProgram ---\n"); + } + + if(glState.currentProgram) + { + qglUseProgramObjectARB(0); + glState.currentProgram = NULL; + } +} + +void GLSL_VertexAttribPointers(uint32_t attribBits); + +void GLSL_VertexAttribsState(uint32_t stateBits) +{ + uint32_t diff; + + GLSL_VertexAttribPointers(stateBits); + + diff = stateBits ^ glState.vertexAttribsState; + if(!diff) + { + return; + } + + if(diff & ATTR_POSITION) + { + if(stateBits & ATTR_POSITION) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_POSITION )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); + } + } + + if(diff & ATTR_TEXCOORD) + { + if(stateBits & ATTR_TEXCOORD) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); + } + } + + if(diff & ATTR_LIGHTCOORD) + { + if(stateBits & ATTR_LIGHTCOORD) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHTCOORD )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_LIGHTCOORD )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); + } + } + + if(diff & ATTR_NORMAL) + { + if(stateBits & ATTR_NORMAL) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_NORMAL); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_NORMAL )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL); + } + } + + if(diff & ATTR_COLOR) + { + if(stateBits & ATTR_COLOR) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_COLOR )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_COLOR); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_COLOR )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_COLOR); + } + } + + glState.vertexAttribsState = stateBits; +} + +void GLSL_VertexAttribPointers(uint32_t attribBits) +{ + if(!glState.currentVBO) + { + ri.Error(ERR_FATAL, "GL_VertexAttribPointers: no VBO bound"); + return; + } + + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- GL_VertexAttribPointers( %s ) ---\n", glState.currentVBO->name)); + + if((attribBits & ATTR_POSITION) && !(glState.vertexAttribPointersSet & ATTR_POSITION)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_POSITION )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_POSITION, 3, GL_FLOAT, 0, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz)); + glState.vertexAttribPointersSet |= ATTR_POSITION; + } + + if((attribBits & ATTR_TEXCOORD) && !(glState.vertexAttribPointersSet & ATTR_TEXCOORD)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TEXCOORD0, 2, GL_FLOAT, 0, glState.currentVBO->stride_st, BUFFER_OFFSET(glState.currentVBO->ofs_st)); + glState.vertexAttribPointersSet |= ATTR_TEXCOORD; + } + + if((attribBits & ATTR_LIGHTCOORD) && !(glState.vertexAttribPointersSet & ATTR_LIGHTCOORD)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_LIGHTCOORD )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TEXCOORD1, 2, GL_FLOAT, 0, glState.currentVBO->stride_lightmap, BUFFER_OFFSET(glState.currentVBO->ofs_lightmap)); + glState.vertexAttribPointersSet |= ATTR_LIGHTCOORD; + } + + if((attribBits & ATTR_NORMAL) && !(glState.vertexAttribPointersSet & ATTR_NORMAL)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_NORMAL )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_NORMAL, 3, GL_FLOAT, 0, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal)); + glState.vertexAttribPointersSet |= ATTR_NORMAL; + } + + if((attribBits & ATTR_COLOR) && !(glState.vertexAttribPointersSet & ATTR_COLOR)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_COLOR )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_COLOR, 4, GL_UNSIGNED_BYTE, 1, glState.currentVBO->stride_vertexcolor, BUFFER_OFFSET(glState.currentVBO->ofs_vertexcolor)); + glState.vertexAttribPointersSet |= ATTR_COLOR; + } +} Index: code/renderer/tr_init.c =================================================================== --- code/renderer/tr_init.c (revision 1803) +++ code/renderer/tr_init.c (working copy) @@ -92,6 +92,12 @@ cvar_t *r_ext_texture_filter_anisotropic; cvar_t *r_ext_max_anisotropy; +cvar_t *r_arb_vertex_buffer_object; +cvar_t *r_arb_shader_objects; + +cvar_t *r_mergeClusterSurfaces; +cvar_t *r_mergeMultidraws; + cvar_t *r_ignoreGLErrors; cvar_t *r_logFile; @@ -219,7 +225,7 @@ GL_CheckErrors ================== */ -void GL_CheckErrors( void ) { +void GL_CheckErrs( char *file, int line ) { int err; char s[64]; @@ -254,7 +260,7 @@ break; } - ri.Error( ERR_FATAL, "GL_CheckErrors: %s", s ); + ri.Error( ERR_FATAL, "GL_CheckErrors: %s in %s at line %d", s , file, line); } @@ -768,7 +774,17 @@ // make sure our GL state vector is set correctly // glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE; + glState.vertexAttribsState = 0; + glState.vertexAttribPointersSet = 0; + glState.currentProgram = 0; + qglUseProgramObjectARB(0); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + glState.currentVBO = NULL; + glState.currentIBO = NULL; + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); qglDepthMask( GL_TRUE ); qglDisable( GL_DEPTH_TEST ); @@ -888,6 +904,8 @@ r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH); r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_arb_vertex_buffer_object = ri.Cvar_Get( "r_arb_vertex_buffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_arb_shader_objects = ri.Cvar_Get( "r_arb_shader_objects", "1", CVAR_ARCHIVE | CVAR_LATCH); r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", "0", CVAR_ARCHIVE | CVAR_LATCH ); @@ -962,6 +980,8 @@ r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE); + r_mergeClusterSurfaces = ri.Cvar_Get("r_mergeClusterSurfaces", "1", CVAR_ARCHIVE); + r_mergeMultidraws = ri.Cvar_Get("r_mergeMultidraws", "1", CVAR_ARCHIVE); // // temporary variables that can change at any time @@ -1112,8 +1132,12 @@ InitOpenGL(); + GLSL_InitGPUShaders(); + R_InitImages(); + R_InitVBOs(); + R_InitShaders(); R_InitSkins(); @@ -1154,6 +1178,8 @@ R_SyncRenderThread(); R_ShutdownCommandBuffers(); R_DeleteTextures(); + R_ShutdownVBOs(); + GLSL_ShutdownGPUShaders(); } R_DoneFreeType(); Index: code/renderer/tr_local.h =================================================================== --- code/renderer/tr_local.h (revision 1803) +++ code/renderer/tr_local.h (working copy) @@ -28,6 +28,7 @@ #include "../qcommon/qfiles.h" #include "../qcommon/qcommon.h" #include "tr_public.h" +#include "tr_extramath.h" #include "qgl.h" #define GL_INDEX_TYPE GL_UNSIGNED_INT @@ -40,6 +41,7 @@ #define myftol(x) ((int)(x)) #endif +#define BUFFER_OFFSET(i) ((char *)NULL + (i)) // everything that is needed by the backend needs // to be double buffered to allow it to run in @@ -56,6 +58,11 @@ // can't be increased without changing bit packing for drawsurfs +#define MAX_VISCOUNTS 5 +#define MAX_VBOS 65536 +#define MAX_IBOS 65536 +#define MAX_CLUSTER_VBO_SURFACES 32768 +#define MAX_MODEL_VBO_SURFACES 32768 typedef struct dlight_s { vec3_t origin; @@ -108,6 +115,41 @@ struct image_s* next; } image_t; +typedef enum +{ + VBO_USAGE_STATIC, + VBO_USAGE_DYNAMIC +} vboUsage_t; + +typedef struct VBO_s +{ + char name[MAX_QPATH]; + + uint32_t vertexesVBO; + int vertexesSize; // amount of memory data allocated for all vertices in bytes + uint32_t ofs_xyz; + uint32_t ofs_normal; + uint32_t ofs_st; + uint32_t ofs_lightmap; + uint32_t ofs_vertexcolor; + uint32_t stride_xyz; + uint32_t stride_normal; + uint32_t stride_st; + uint32_t stride_lightmap; + uint32_t stride_vertexcolor; + + int attribs; +} VBO_t; + +typedef struct IBO_s +{ + char name[MAX_QPATH]; + + uint32_t indexesVBO; + int indexesSize; // amount of memory data allocated for all triangles in bytes +// uint32_t ofsIndexes; +} IBO_t; + //=============================================================================== typedef enum { @@ -173,6 +215,25 @@ DEFORM_TEXT7 } deform_t; +// deformVertexes types that can be handled by the GPU +typedef enum +{ + // do not edit: same as genFunc_t + + DGEN_NONE, + DGEN_WAVE_SIN, + DGEN_WAVE_SQUARE, + DGEN_WAVE_TRIANGLE, + DGEN_WAVE_SAWTOOTH, + DGEN_WAVE_INVERSE_SAWTOOTH, + DGEN_WAVE_NOISE, + + // do not edit until this line + + DGEN_BULGE, + DGEN_MOVE +} deformGen_t; + typedef enum { AGEN_IDENTITY, AGEN_SKIP, @@ -201,6 +262,14 @@ CGEN_CONST // fixed color } colorGen_t; +typedef enum +{ + ATEST_NONE, + ATEST_GT_0, + ATEST_LT_128, + ATEST_GE_128 +} alphaTest_t; + typedef enum { TCGEN_BAD, TCGEN_IDENTITY, // clear to 0,0 @@ -379,6 +448,7 @@ fogParms_t fogParms; float portalRange; // distance to fog out at + qboolean isPortal; int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage) @@ -418,6 +488,46 @@ struct shader_s *next; } shader_t; +static ID_INLINE qboolean ShaderRequiresCPUDeforms(const shader_t * shader) +{ + if(shader->numDeforms) + { +#if 1 + const deformStage_t *ds = &shader->deforms[0]; + + switch (ds->deformation) + { + case DEFORM_WAVE: + case DEFORM_BULGE: + return qfalse; + + default: + return qtrue; + } +#else + int i; + for (i = 0; i < shader->numDeforms; i++) + { + const deformStage_t *ds = &shader->deforms[i]; + + switch (ds->deformation) + { + case DEFORM_WAVE: + case DEFORM_BULGE: + break; + + default: + return qtrue; + } + } +#endif + } + + + + return qfalse; +} + typedef struct shaderState_s { char shaderName[MAX_QPATH]; // name of shader this state belongs to char name[MAX_STATE_NAME]; // name of this state @@ -426,7 +536,934 @@ shader_t *shader; } shaderState_t; +enum +{ + ATTR_INDEX_POSITION = 0, + ATTR_INDEX_TEXCOORD0 = 1, + ATTR_INDEX_TEXCOORD1 = 2, + ATTR_INDEX_TANGENT = 3, + ATTR_INDEX_BINORMAL = 4, + ATTR_INDEX_NORMAL = 5, + ATTR_INDEX_COLOR = 6, + ATTR_INDEX_PAINTCOLOR = 7, + ATTR_INDEX_LIGHTDIRECTION = 8, + ATTR_INDEX_BONE_INDEXES = 9, + ATTR_INDEX_BONE_WEIGHTS = 10, +}; +enum +{ + GLS_SRCBLEND_ZERO = (1 << 0), + GLS_SRCBLEND_ONE = (1 << 1), + GLS_SRCBLEND_DST_COLOR = (1 << 2), + GLS_SRCBLEND_ONE_MINUS_DST_COLOR = (1 << 3), + GLS_SRCBLEND_SRC_ALPHA = (1 << 4), + GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA = (1 << 5), + GLS_SRCBLEND_DST_ALPHA = (1 << 6), + GLS_SRCBLEND_ONE_MINUS_DST_ALPHA = (1 << 7), + GLS_SRCBLEND_ALPHA_SATURATE = (1 << 8), + + GLS_SRCBLEND_BITS = GLS_SRCBLEND_ZERO + | GLS_SRCBLEND_ONE + | GLS_SRCBLEND_DST_COLOR + | GLS_SRCBLEND_ONE_MINUS_DST_COLOR + | GLS_SRCBLEND_SRC_ALPHA + | GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA + | GLS_SRCBLEND_DST_ALPHA + | GLS_SRCBLEND_ONE_MINUS_DST_ALPHA + | GLS_SRCBLEND_ALPHA_SATURATE, + + GLS_DSTBLEND_ZERO = (1 << 9), + GLS_DSTBLEND_ONE = (1 << 10), + GLS_DSTBLEND_SRC_COLOR = (1 << 11), + GLS_DSTBLEND_ONE_MINUS_SRC_COLOR = (1 << 12), + GLS_DSTBLEND_SRC_ALPHA = (1 << 13), + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA = (1 << 14), + GLS_DSTBLEND_DST_ALPHA = (1 << 15), + GLS_DSTBLEND_ONE_MINUS_DST_ALPHA = (1 << 16), + + GLS_DSTBLEND_BITS = GLS_DSTBLEND_ZERO + | GLS_DSTBLEND_ONE + | GLS_DSTBLEND_SRC_COLOR + | GLS_DSTBLEND_ONE_MINUS_SRC_COLOR + | GLS_DSTBLEND_SRC_ALPHA + | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA + | GLS_DSTBLEND_DST_ALPHA + | GLS_DSTBLEND_ONE_MINUS_DST_ALPHA, + + GLS_DEPTHMASK_TRUE = (1 << 17), + + GLS_POLYMODE_LINE = (1 << 18), + + GLS_DEPTHTEST_DISABLE = (1 << 19), + + GLS_DEPTHFUNC_LESS = (1 << 20), + GLS_DEPTHFUNC_EQUAL = (1 << 21), + + GLS_DEPTHFUNC_BITS = GLS_DEPTHFUNC_LESS + | GLS_DEPTHFUNC_EQUAL, + + GLS_ATEST_GT_0 = (1 << 22), + GLS_ATEST_LT_128 = (1 << 23), + GLS_ATEST_GE_128 = (1 << 24), +// GLS_ATEST_GE_CUSTOM = (1 << 25), + + GLS_ATEST_BITS = GLS_ATEST_GT_0 + | GLS_ATEST_LT_128 + | GLS_ATEST_GE_128, +// | GLS_ATEST_GT_CUSTOM, + + GLS_REDMASK_FALSE = (1 << 26), + GLS_GREENMASK_FALSE = (1 << 27), + GLS_BLUEMASK_FALSE = (1 << 28), + GLS_ALPHAMASK_FALSE = (1 << 29), + + GLS_COLORMASK_BITS = GLS_REDMASK_FALSE + | GLS_GREENMASK_FALSE + | GLS_BLUEMASK_FALSE + | GLS_ALPHAMASK_FALSE, + + GLS_STENCILTEST_ENABLE = (1 << 30), + + GLS_DEFAULT = GLS_DEPTHMASK_TRUE +}; + +enum +{ + ATTR_POSITION = 0x001, + ATTR_TEXCOORD = 0x002, + ATTR_LIGHTCOORD = 0x004, + ATTR_TANGENT = 0x008, + ATTR_BINORMAL = 0x010, + ATTR_NORMAL = 0x020, + ATTR_COLOR = 0x040, + ATTR_PAINTCOLOR = 0x080, + ATTR_LIGHTDIRECTION = 0x100, + ATTR_BONE_INDEXES = 0x200, + ATTR_BONE_WEIGHTS = 0x400, + + ATTR_DEFAULT = ATTR_POSITION, + ATTR_BITS = ATTR_POSITION | + ATTR_TEXCOORD | + ATTR_LIGHTCOORD | + ATTR_TANGENT | + ATTR_BINORMAL | + ATTR_NORMAL | + ATTR_COLOR | + ATTR_PAINTCOLOR | + ATTR_LIGHTDIRECTION | + ATTR_BONE_INDEXES | + ATTR_BONE_WEIGHTS +}; + + +// Tr3B - shaderProgram_t represents a pair of one +// GLSL vertex and one GLSL fragment shader +typedef struct shaderProgram_s +{ + char name[MAX_QPATH]; + + GLhandleARB program; + uint32_t attribs; // vertex array attributes + + // uniform parameters + GLint u_Texture0Map; + GLint u_Texture1Map; + GLint u_Texture2Map; + GLint u_Texture3Map; + + GLint u_PortalClipping; + qboolean t_PortalClipping; + + GLint u_PortalPlane; + vec4_t t_PortalPlane; + + GLint u_PortalRange; + float t_PortalRange; + + GLint u_FogEyeT; + float t_FogEyeT; + + GLint u_ModelMatrix; // model -> world + matrix_t t_ModelMatrix; + + GLint u_ModelViewProjectionMatrix; + matrix_t t_ModelViewProjectionMatrix; + + GLint u_AlphaTest; + alphaTest_t t_AlphaTest; + + GLint u_ColorGen; + colorGen_t t_ColorGen; + + GLint u_AlphaGen; + alphaGen_t t_AlphaGen; + + GLint u_TCGen0; + texCoordGen_t t_TCGen0; + + GLint u_TCGen1; + texCoordGen_t t_TCGen1; + + GLint u_TCGen0Vector0; + vec4_t t_TCGen0Vector0; + + GLint u_TCGen0Vector1; + vec4_t t_TCGen0Vector1; + + GLint u_TCGen1Vector0; + vec4_t t_TCGen1Vector0; + + GLint u_TCGen1Vector1; + vec4_t t_TCGen1Vector1; + + GLint u_DeformGen; + deformGen_t t_DeformGen; + + GLint u_DeformWave; + vec4_t t_DeformWave; + + GLint u_DeformBulge; + vec3_t t_DeformBulge; + + GLint u_DeformSpread; + float t_DeformSpread; + + GLint u_Time; + float t_Time; + + GLint u_Color; + vec4_t t_Color; + + GLint u_AmbientLight; + vec3_t t_AmbientLight; + + GLint u_DirectedLight; + vec3_t t_DirectedLight; + + GLint u_LightDir; + vec3_t t_LightDir; + + GLint u_ViewOrigin; + vec4_t t_ViewOrigin; + + GLint u_Texture0Matrix; + matrix_t t_Texture0Matrix; + + GLint u_Texture1Matrix; + matrix_t t_Texture1Matrix; + + GLint u_Texture1Env; + uint32_t t_Texture1Env; + + GLint u_Texture2Matrix; + matrix_t t_Texture2Matrix; + + GLint u_Texture3Matrix; + matrix_t t_Texture3Matrix; + + + +} shaderProgram_t; + + +// +// Tr3B: these are fire wall functions to avoid expensive redundant glUniform* calls +#define USE_UNIFORM_FIREWALL 1 +//#define LOG_GLSL_UNIFORMS 1 + +#if defined(LOG_GLSL_UNIFORMS) +extern cvar_t *r_logFile; // number of frames to emit GL logs +void GLimp_LogComment(char *comment); +#define GLimp_LogUniformComment(...) GLimp_LogComment(...) +#else +#define GLimp_LogUniformComment(...) +#endif + +// *INDENT-OFF* + +static ID_INLINE void GLSL_SetUniform_Texture0Matrix(shaderProgram_t * program, const matrix_t m) +{ + if (program->u_Texture0Matrix == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(Matrix16Compare(program->t_Texture0Matrix, m)) + return; + + Matrix16Copy(m, program->t_Texture0Matrix); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_Texture0Matrix( program = %s, " + "matrix = \n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", + program->name, + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15])); + + qglUniformMatrix4fvARB(program->u_Texture0Matrix, 1, GL_FALSE, m); +} + +static ID_INLINE void GLSL_SetUniform_Texture1Matrix(shaderProgram_t * program, const matrix_t m) +{ + if (program->u_Texture1Matrix == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(Matrix16Compare(program->t_Texture1Matrix, m)) + return; + + Matrix16Copy(m, program->t_Texture1Matrix); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_Texture1Matrix( program = %s, " + "matrix = \n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", + program->name, + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15])); + + qglUniformMatrix4fvARB(program->u_Texture1Matrix, 1, GL_FALSE, m); +} + +static ID_INLINE void GLSL_SetUniform_Texture1Env(shaderProgram_t * program, uint32_t env) +{ + uint32_t value; + + if (program->u_Texture1Env == -1) + return; + + switch ( env ) + { + case GL_MODULATE: + value = 1; + break; + case GL_REPLACE: + value = 2; + break; + case GL_DECAL: + value = 3; + break; + case GL_ADD: + value = 4; + break; + default: + value = 0; + break; + } + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_Texture1Env == value) + return; + + program->t_Texture1Env = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_Texture1Env( program = %s, value = %i ) ---\n", program->name, value)); + + qglUniform1iARB(program->u_Texture1Env, value); +} + +static ID_INLINE void GLSL_SetUniform_Texture2Matrix(shaderProgram_t * program, const matrix_t m) +{ + if (program->u_Texture2Matrix == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(Matrix16Compare(program->t_Texture2Matrix, m)) + return; + + Matrix16Copy(m, program->t_Texture2Matrix); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_Texture2Matrix( program = %s, " + "matrix = \n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", + program->name, + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15])); + + qglUniformMatrix4fvARB(program->u_Texture2Matrix, 1, GL_FALSE, m); +} + +static ID_INLINE void GLSL_SetUniform_Texture3Matrix(shaderProgram_t * program, const matrix_t m) +{ + if (program->u_Texture3Matrix == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(Matrix16Compare(program->t_Texture3Matrix, m)) + return; + + Matrix16Copy(m, program->t_Texture3Matrix); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_Texture3Matrix( program = %s, " + "matrix = \n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", + program->name, + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15])); + + qglUniformMatrix4fvARB(program->u_Texture3Matrix, 1, GL_FALSE, m); +} + + +static ID_INLINE void GLSL_SetUniform_AlphaTest(shaderProgram_t * program, uint32_t stateBits) +{ + alphaTest_t value; + + if (program->u_AlphaTest == -1) + return; + + switch (stateBits & GLS_ATEST_BITS) + { + case GLS_ATEST_GT_0: + value = ATEST_GT_0; + break; + + case GLS_ATEST_LT_128: + value = ATEST_LT_128; + break; + + case GLS_ATEST_GE_128: + value = ATEST_GE_128; + break; + + default: + value = ATEST_NONE; + break; + } + + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogUniformComment(va("--- GLSL_SetUniformAlphaTest( program = %s, value = %i ) ---\n", program->name, value)); + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_AlphaTest == value) + return; + + program->t_AlphaTest = value; +#endif + + qglUniform1iARB(program->u_AlphaTest, value); +} + +static ID_INLINE void GLSL_SetUniform_ViewOrigin(shaderProgram_t * program, const vec3_t v) +{ + if (program->u_ViewOrigin == -1) + return; + + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_ViewOrigin, v)) + return; + + VectorCopy(v, program->t_ViewOrigin); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_ViewOrigin( program = %s, viewOrigin = ( %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2])); + + qglUniform3fARB(program->u_ViewOrigin, v[0], v[1], v[2]); +} + +static ID_INLINE void GLSL_SetUniform_TCGen0(shaderProgram_t * program, texCoordGen_t value) +{ + if (program->u_TCGen0 == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_TCGen0 == value) + return; + + program->t_TCGen0 = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_TCGen( program = %s, value = %i ) ---\n", program->name, value)); + + qglUniform1iARB(program->u_TCGen0, value); +} + +static ID_INLINE void GLSL_SetUniform_TCGen1(shaderProgram_t * program, texCoordGen_t value) +{ + if (program->u_TCGen1 == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_TCGen1 == value) + return; + + program->t_TCGen1 = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_TCGen( program = %s, value = %i ) ---\n", program->name, value)); + + qglUniform1iARB(program->u_TCGen1, value); +} + +static ID_INLINE void GLSL_SetUniform_TCGen0Vector0(shaderProgram_t * program, const vec4_t v) +{ + if (program->u_TCGen0Vector0 == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_TCGen0Vector0, v)) + return; + + VectorCopy(v, program->t_TCGen0Vector0); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_TCGen0Vector0( program = %s, color = ( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2], v[3])); + + qglUniform4fARB(program->u_TCGen0Vector0, v[0], v[1], v[2], v[3]); +} + +static ID_INLINE void GLSL_SetUniform_TCGen0Vector1(shaderProgram_t * program, const vec4_t v) +{ + if (program->u_TCGen0Vector1 == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_TCGen0Vector1, v)) + return; + + VectorCopy(v, program->t_TCGen0Vector1); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_TCGen0Vector1( program = %s, color = ( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2], v[3])); + + qglUniform4fARB(program->u_TCGen0Vector1, v[0], v[1], v[2], v[3]); +} +static ID_INLINE void GLSL_SetUniform_TCGen1Vector0(shaderProgram_t * program, const vec4_t v) +{ + if (program->u_TCGen1Vector0 == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_TCGen1Vector0, v)) + return; + + VectorCopy(v, program->t_TCGen1Vector0); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_TCGen1Vector0( program = %s, color = ( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2], v[3])); + + qglUniform4fARB(program->u_TCGen1Vector0, v[0], v[1], v[2], v[3]); +} +static ID_INLINE void GLSL_SetUniform_TCGen1Vector1(shaderProgram_t * program, const vec4_t v) +{ + if (program->u_TCGen1Vector1 == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_TCGen1Vector1, v)) + return; + + VectorCopy(v, program->t_TCGen1Vector1); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_TCGen1Vector1( program = %s, color = ( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2], v[3])); + + qglUniform4fARB(program->u_TCGen1Vector1, v[0], v[1], v[2], v[3]); +} + +static ID_INLINE void GLSL_SetUniform_DeformGen(shaderProgram_t * program, deformGen_t value) +{ + if (program->u_DeformGen == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_DeformGen == value) + return; + + program->t_DeformGen = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_DeformGen( program = %s, value = %i ) ---\n", program->name, value)); + + qglUniform1iARB(program->u_DeformGen, value); +} + +static ID_INLINE void GLSL_SetUniform_DeformWave(shaderProgram_t * program, const waveForm_t * wf) +{ + vec4_t v; + + if (program->u_DeformWave == -1) + return; + + VectorSet4(v, wf->base, wf->amplitude, wf->phase, wf->frequency); + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare4(program->t_DeformWave, v)) + return; + + VectorCopy4(v, program->t_DeformWave); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_DeformWave( program = %s, wave form = ( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2], v[3])); + + qglUniform4fARB(program->u_DeformWave, v[0], v[1], v[2], v[3]); +} + +static ID_INLINE void GLSL_SetUniform_DeformBulge(shaderProgram_t * program, const deformStage_t * ds) +{ + vec3_t v; + + if (program->u_DeformBulge == -1) + return; + + VectorSet(v, ds->bulgeWidth, ds->bulgeHeight, ds->bulgeSpeed); + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_DeformBulge, v)) + return; + + VectorCopy(v, program->t_DeformBulge); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_DeformBulge( program = %s, bulge = ( %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2])); + + qglUniform3fARB(program->u_DeformBulge, v[0], v[1], v[2]); +} + +static ID_INLINE void GLSL_SetUniform_DeformSpread(shaderProgram_t * program, float value) +{ + if (program->u_DeformSpread == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_DeformSpread == value) + return; + + program->t_DeformSpread = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_DeformSpread( program = %s, value = %f ) ---\n", program->name, value)); + + qglUniform1fARB(program->u_DeformSpread, value); +} + +static ID_INLINE void GLSL_SetUniform_ColorGen(shaderProgram_t * program, colorGen_t value) +{ +#if 0 + float floatValue; + + switch (value) + { + case CGEN_VERTEX: + floatValue = 1.0f; + break; + + case CGEN_ONE_MINUS_VERTEX: + floatValue = -1.0f; + break; + + default: + floatValue = 0.0f; + break; + } + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_ColorGen == floatValue) + return; + + program->t_ColorGen = floatValue; +#endif + + qglUniform1fARB(program->u_ColorGen, floatValue); +#else + if (program->u_ColorGen == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_ColorGen == value) + return; + + program->t_ColorGen = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_ColorGen( program = %s, value = %i ) ---\n", program->name, value)); + + qglUniform1iARB(program->u_ColorGen, value); +#endif +} + +static ID_INLINE void GLSL_SetUniform_AlphaGen(shaderProgram_t * program, alphaGen_t value) +{ +#if 0 + float floatValue; + + switch (value) + { + case AGEN_VERTEX: + floatValue = 1.0f; + break; + + case AGEN_ONE_MINUS_VERTEX: + floatValue = -1.0f; + break; + + default: + floatValue = 0.0f; + break; + } + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_AlphaGen == floatValue) + return; + + program->t_AlphaGen = floatValue; +#endif + + qglUniform1fARB(program->u_AlphaGen, floatValue); + +#else +#if defined(USE_UNIFORM_FIREWALL) + if (program->u_AlphaGen == -1) + return; + + if(program->t_AlphaGen == value) + return; + + program->t_AlphaGen = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_AlphaGen( program = %s, value = %i ) ---\n", program->name, value)); + + qglUniform1iARB(program->u_AlphaGen, value); +#endif +} + +static ID_INLINE void GLSL_SetUniform_Color(shaderProgram_t * program, const vec4_t v) +{ + if (program->u_Color == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare4(program->t_Color, v)) + return; + + VectorCopy4(v, program->t_Color); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_Color( program = %s, color = ( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2], v[3])); + + qglUniform4fARB(program->u_Color, v[0], v[1], v[2], v[3]); +} + + +static ID_INLINE void GLSL_SetUniform_AmbientLight(shaderProgram_t * program, const vec3_t v) +{ + if (program->u_AmbientLight == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_AmbientLight, v)) + return; + + VectorCopy(v, program->t_AmbientLight); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_AmbientLight( program = %s, color = ( %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2])); + + qglUniform3fARB(program->u_AmbientLight, v[0], v[1], v[2]); +} + +static ID_INLINE void GLSL_SetUniform_DirectedLight(shaderProgram_t * program, const vec3_t v) +{ + if (program->u_DirectedLight == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_DirectedLight, v)) + return; + + VectorCopy(v, program->t_DirectedLight); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_DirectedLight( program = %s, color = ( %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2])); + + qglUniform3fARB(program->u_DirectedLight, v[0], v[1], v[2]); +} + +static ID_INLINE void GLSL_SetUniform_LightDir(shaderProgram_t * program, const vec3_t v) +{ + if (program->u_LightDir == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare(program->t_LightDir, v)) + return; + + VectorCopy(v, program->t_LightDir); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_LightDir( program = %s, color = ( %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2])); + + qglUniform3fARB(program->u_LightDir, v[0], v[1], v[2]); +} + +static ID_INLINE void GLSL_SetUniform_PortalClipping(shaderProgram_t * program, qboolean value) +{ + if (program->u_PortalClipping == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_PortalClipping == value) + return; + + program->t_PortalClipping = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_PortalClipping( program = %s, value = %i ) ---\n", program->name, value)); + + qglUniform1iARB(program->u_PortalClipping, value); +} + +static ID_INLINE void GLSL_SetUniform_PortalPlane(shaderProgram_t * program, const vec4_t v) +{ + if (program->u_PortalPlane == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(VectorCompare4(program->t_PortalPlane, v)) + return; + + VectorCopy(v, program->t_PortalPlane); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_PortalPlane( program = %s, plane = ( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", program->name, v[0], v[1], v[2], v[3])); + + qglUniform4fARB(program->u_PortalPlane, v[0], v[1], v[2], v[3]); +} + +static ID_INLINE void GLSL_SetUniform_PortalRange(shaderProgram_t * program, float value) +{ + if (program->u_PortalRange == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_PortalRange == value) + return; + + program->t_PortalRange = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_PortalRange( program = %s, value = %f ) ---\n", program->name, value)); + + qglUniform1fARB(program->u_PortalRange, value); +} + +static ID_INLINE void GLSL_SetUniform_FogEyeT(shaderProgram_t * program, float value) +{ + if (program->u_FogEyeT == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_FogEyeT == value) + return; + + program->t_FogEyeT = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_FogEyeT( program = %s, value = %f ) ---\n", program->name, value)); + + qglUniform1fARB(program->u_FogEyeT, value); +} + +static ID_INLINE void GLSL_SetUniform_ModelMatrix(shaderProgram_t * program, const matrix_t m) +{ + if (program->u_ModelMatrix == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(Matrix16Compare(program->t_ModelMatrix, m)) + return; + + Matrix16Copy(m, program->t_ModelMatrix); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_ModelMatrix( program = %s, " + "matrix = \n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", + program->name, + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15])); + + qglUniformMatrix4fvARB(program->u_ModelMatrix, 1, GL_FALSE, m); +} + +static ID_INLINE void GLSL_SetUniform_ModelViewProjectionMatrix(shaderProgram_t * program, const matrix_t m) +{ + if (program->u_ModelViewProjectionMatrix == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(Matrix16Compare(program->t_ModelViewProjectionMatrix, m)) + return; + + Matrix16Copy(m, program->t_ModelViewProjectionMatrix); +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_ModelViewProjectionMatrix( program = %s, " + "matrix = \n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f )\n" + "( %5.3f, %5.3f, %5.3f, %5.3f ) ) ---\n", + program->name, + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15])); + + qglUniformMatrix4fvARB(program->u_ModelViewProjectionMatrix, 1, GL_FALSE, m); +} + +static ID_INLINE void GLSL_SetUniform_Time(shaderProgram_t * program, float value) +{ + if (program->u_Time == -1) + return; + +#if defined(USE_UNIFORM_FIREWALL) + if(program->t_Time == value) + return; + + program->t_Time = value; +#endif + + GLimp_LogUniformComment(va("--- GLSL_SetUniform_Time( program = %s, value = %f ) ---\n", program->name, value)); + + qglUniform1fARB(program->u_Time, value); +} + + + // trRefdef_t holds everything that comes in refdef_t, // as well as the locally generated scene information typedef struct { @@ -519,6 +1556,7 @@ ============================================================================== */ +typedef byte color4ub_t[4]; // any changes in surfaceType must be mirrored in rb_surfaceTable[] typedef enum { @@ -536,6 +1574,7 @@ SF_FLARE, SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity SF_DISPLAY_LIST, + SF_VBO_MESH, SF_NUM_SURFACE_TYPES, SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) @@ -574,9 +1613,35 @@ vec3_t color; } srfFlare_t; -typedef struct srfGridMesh_s { - surfaceType_t surfaceType; +typedef struct +{ + vec3_t xyz; + vec2_t st; + vec2_t lightmap; + vec3_t normal; + color4ub_t vertexColors; +#if DEBUG_OPTIMIZEVERTICES + unsigned int id; +#endif +} srfVert_t; + +#define srfVert_t_cleared(x) srfVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0}, {0, 0, 0, 0}} + +typedef struct +{ + int indexes[3]; + int neighbors[3]; + vec4_t plane; + qboolean facingLight; + qboolean degenerated; +} srfTriangle_t; + + +typedef struct srfGridMesh_s +{ + surfaceType_t surfaceType; + // dynamic lighting information int dlightBits[SMP_FRAMES]; @@ -597,49 +1662,101 @@ int width, height; float *widthLodError; float *heightLodError; - drawVert_t verts[1]; // variable sized + + int numTriangles; + srfTriangle_t *triangles; + + int numVerts; + srfVert_t *verts; + + // BSP VBO offsets + int firstVert; + int firstIndex; + + // static render data + VBO_t *vbo; // points to bsp model VBO + IBO_t *ibo; } srfGridMesh_t; +typedef struct +{ + surfaceType_t surfaceType; -#define VERTEXSIZE 8 -typedef struct { - surfaceType_t surfaceType; - cplane_t plane; - // dynamic lighting information int dlightBits[SMP_FRAMES]; - // triangle definitions (no normals at points) - int numPoints; - int numIndices; - int ofsIndices; - float points[1][VERTEXSIZE]; // variable sized - // there is a variable length list of indices here also + // culling information + cplane_t plane; + vec3_t bounds[2]; + + // triangle definitions + int numTriangles; + srfTriangle_t *triangles; + + int numVerts; + srfVert_t *verts; + + // BSP VBO offsets + int firstVert; + int firstIndex; + + // static render data + VBO_t *vbo; // points to bsp model VBO + IBO_t *ibo; } srfSurfaceFace_t; -// misc_models in maps are turned into direct geometry by q3map -typedef struct { - surfaceType_t surfaceType; +// misc_models in maps are turned into direct geometry by xmap +typedef struct +{ + surfaceType_t surfaceType; // dynamic lighting information - int dlightBits[SMP_FRAMES]; + int dlightBits[SMP_FRAMES]; - // culling information (FIXME: use this!) - vec3_t bounds[2]; - vec3_t localOrigin; - float radius; + // culling information + vec3_t bounds[2]; // triangle definitions - int numIndexes; - int *indexes; + int numTriangles; + srfTriangle_t *triangles; - int numVerts; - drawVert_t *verts; + int numVerts; + srfVert_t *verts; + + // BSP VBO offsets + int firstVert; + int firstIndex; + + // static render data + VBO_t *vbo; // points to bsp model VBO + IBO_t *ibo; } srfTriangles_t; +typedef struct srfVBOMesh_s +{ + surfaceType_t surfaceType; + + struct shader_s *shader; // FIXME move this to somewhere else + int lightmapNum; // FIXME get rid of this by merging all lightmaps at level load + int fogIndex; + + // culling information + vec3_t bounds[2]; + + // backEnd stats + int numIndexes; + int numVerts; + int firstIndex; + + // static render data + VBO_t *vbo; + IBO_t *ibo; +} srfVBOMesh_t; + + extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *); /* @@ -663,8 +1780,10 @@ int viewCount; // if == tr.viewCount, already added struct shader_s *shader; int fogIndex; + int16_t lightmapNum; // -1 = no lightmap surfaceType_t *data; // any of srf*_t + qboolean inCluster; } msurface_t; @@ -673,7 +1792,7 @@ typedef struct mnode_s { // common with leaf and node int contents; // -1 for nodes, to differentiate from leafs - int visframe; // node needs to be traversed if current + int visCounts[MAX_VISCOUNTS]; // node needs to be traversed if current vec3_t mins, maxs; // for bounding box culling struct mnode_s *parent; @@ -689,10 +1808,21 @@ int nummarksurfaces; } mnode_t; +typedef struct +{ + int numMarkSurfaces; + msurface_t **markSurfaces; + + vec3_t origin; // used for cubemaps +} bspCluster_t; + typedef struct { vec3_t bounds[2]; // for culling msurface_t *firstSurface; int numSurfaces; + + uint32_t numVBOSurfaces; + srfVBOMesh_t *vboSurfaces[MAX_MODEL_VBO_SURFACES]; } bmodel_t; typedef struct { @@ -704,6 +1834,7 @@ int numShaders; dshader_t *shaders; + int numBModels; bmodel_t *bmodels; int numplanes; @@ -713,6 +1844,20 @@ int numDecisionNodes; mnode_t *nodes; + int numVerts; + srfVert_t *verts; + int redundantVertsCalculationNeeded; + int *redundantLightVerts; // util to optimize IBOs + int *redundantShadowVerts; + int *redundantShadowAlphaTestVerts; + VBO_t *vbo; + IBO_t *ibo; + + int numTriangles; + srfTriangle_t *triangles; + + int numWorldSurfaces; + int numsurfaces; msurface_t *surfaces; @@ -730,11 +1875,15 @@ int numClusters; + bspCluster_t *clusters; int clusterBytes; const byte *vis; // may be passed in by CM_LoadMap to save space byte *novis; // clusterBytes of 0xff + int numClusterVBOSurfaces[MAX_VISCOUNTS]; + srfVBOMesh_t clusterVBOSurfaces[MAX_VISCOUNTS][MAX_CLUSTER_VBO_SURFACES]; // updated every time when changing the view cluster + char *entityString; char *entityParsePoint; } world_t; @@ -839,6 +1988,13 @@ int texEnv[2]; int faceCulling; unsigned long glStateBits; + uint32_t vertexAttribsState; + uint32_t vertexAttribPointersSet; + shaderProgram_t *currentProgram; + VBO_t *currentVBO; + IBO_t *currentIBO; + float modelview[16]; + float projection[16]; } glstate_t; @@ -846,6 +2002,14 @@ int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; float c_overDraw; + int c_vboVertexBuffers; + int c_vboIndexBuffers; + int c_vboVertexes; + int c_vboIndexes; + + int c_surfaceIBOs; + int c_mergedIBOs; + int c_dlightVertexes; int c_dlightIndexes; @@ -885,7 +2049,10 @@ typedef struct { qboolean registered; // cleared at shutdown, set at beginRegistration - int visCount; // incremented every time a new vis cluster is entered + int visIndex; + int visClusters[MAX_VISCOUNTS]; + int visCounts[MAX_VISCOUNTS]; // incremented every time a new vis cluster is entered + int frameCount; // incremented every frame int sceneCount; // incremented every scene int viewCount; // incremented every view (twice a scene if portaled) @@ -924,6 +2091,15 @@ int shiftedEntityNum; // currentEntityNum << QSORT_ENTITYNUM_SHIFT model_t *currentModel; + // + // GPU shader programs + // + + shaderProgram_t genericShader; + + + // ----------------------------------------- + viewParms_t viewParms; float identityLight; // 1.0 / ( 1 << overbrightBits ) @@ -952,6 +2128,12 @@ int numImages; image_t *images[MAX_DRAWIMAGES]; + int numVBOs; + VBO_t *vbos[MAX_VBOS]; + + int numIBOs; + IBO_t *ibos[MAX_IBOS]; + // shader indexes from other modules will be looked up in tr.shaders[] // shader indexes from drawsurfs will be looked up in sortedShaders[] // lower indexed sortedShaders must be rendered first (opaque surfaces before translucent) @@ -1056,6 +2238,9 @@ extern cvar_t *r_ext_texture_filter_anisotropic; extern cvar_t *r_ext_max_anisotropy; +extern cvar_t *r_arb_vertex_buffer_object; +extern cvar_t *r_arb_shader_objects; + extern cvar_t *r_nobind; // turns off binding to appropriate textures extern cvar_t *r_singleShader; // make most world faces use default shader extern cvar_t *r_roundImagesDown; @@ -1098,6 +2283,9 @@ extern cvar_t *r_stereoEnabled; extern cvar_t *r_anaglyphMode; +extern cvar_t *r_mergeClusterSurfaces; +extern cvar_t *r_mergeMultidraws; + extern cvar_t *r_greyscale; extern cvar_t *r_ignoreGLErrors; @@ -1140,6 +2328,8 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int fogIndex, int dlightMap ); +void R_CalcSurfaceTriangleNeighbors(int numTriangles, srfTriangle_t * triangles); +void R_CalcSurfaceTrianglePlanes(int numTriangles, srfTriangle_t * triangles, srfVert_t * verts); #define CULL_IN 0 // completely unclipped #define CULL_CLIP 1 // clipped by one or more planes @@ -1160,7 +2350,8 @@ void GL_SetDefaultState (void); void GL_SelectTexture( int unit ); void GL_TextureMode( const char *string ); -void GL_CheckErrors( void ); +void GL_CheckErrs( char *file, int line ); +#define GL_CheckErrors(...) GL_CheckErrs(__FILE__, __LINE__) void GL_State( unsigned long stateVector ); void GL_TexEnv( int env ); void GL_Cull( int cullType ); @@ -1292,7 +2483,6 @@ ==================================================================== */ -typedef byte color4ub_t[4]; typedef struct stageVars { @@ -1300,6 +2490,7 @@ vec2_t texcoords[NUM_TEXTURE_BUNDLES][SHADER_MAX_VERTEXES]; } stageVars_t; +#define MAX_MULTIDRAW_PRIMITIVES 16384 typedef struct shaderCommands_s { @@ -1310,6 +2501,9 @@ color4ub_t vertexColors[SHADER_MAX_VERTEXES] ALIGN(16); int vertexDlightBits[SHADER_MAX_VERTEXES] ALIGN(16); + VBO_t *vbo; + IBO_t *ibo; + stageVars_t svars ALIGN(16); color4ub_t constantColor255[SHADER_MAX_VERTEXES] ALIGN(16); @@ -1320,9 +2514,15 @@ int dlightBits; // or together of all vertexDlightBits + int firstIndex; int numIndexes; int numVertexes; + int multiDrawPrimitives; + GLsizei multiDrawNumIndexes[MAX_MULTIDRAW_PRIMITIVES]; + GLvoid * multiDrawFirstIndex[MAX_MULTIDRAW_PRIMITIVES]; + GLvoid * multiDrawLastIndex[MAX_MULTIDRAW_PRIMITIVES]; + // info extracted from current shader int numPasses; void (*currentStageIteratorFunc)( void ); @@ -1425,7 +2625,7 @@ #define PATCH_STITCHING srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ); + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ); srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ); srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ); void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ); @@ -1445,6 +2645,31 @@ /* ============================================================ +VERTEX BUFFER OBJECTS + +============================================================ +*/ +VBO_t *R_CreateVBO(const char *name, byte * vertexes, int vertexesSize, vboUsage_t usage); +VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * vertexes, uint32_t stateBits, vboUsage_t usage); + +IBO_t *R_CreateIBO(const char *name, byte * indexes, int indexesSize, vboUsage_t usage); +IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t * triangles, vboUsage_t usage); + +void R_BindVBO(VBO_t * vbo); +void R_BindNullVBO(void); + +void R_BindIBO(IBO_t * ibo); +void R_BindNullIBO(void); + +void R_InitVBOs(void); +void R_ShutdownVBOs(void); +void R_VBOList_f(void); + + + +/* +============================================================ + SCENE GENERATION ============================================================ @@ -1527,6 +2752,13 @@ void RB_CalcScaleTexCoords( const float scale[2], float *dstTexCoords ); void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *dstTexCoords ); void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *dstTexCoords ); + +void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ); +void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ); +void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ); +void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ); +void RB_CalcStretchTexMatrix( const waveForm_t *wf, float *matrix ); + void RB_CalcModulateColorsByFog( unsigned char *dstColors ); void RB_CalcModulateAlphasByFog( unsigned char *dstColors ); void RB_CalcModulateRGBAsByFog( unsigned char *dstColors ); Index: code/renderer/tr_main.c =================================================================== --- code/renderer/tr_main.c (revision 1803) +++ code/renderer/tr_main.c (working copy) @@ -45,12 +45,104 @@ /* ================= +R_FindSurfaceTriangleWithEdge +Tr3B - recoded from Q2E +================= +*/ +static int R_FindSurfaceTriangleWithEdge(int numTriangles, srfTriangle_t * triangles, int start, int end, int ignore) +{ + srfTriangle_t *tri; + int count, match; + int i; + + count = 0; + match = -1; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + if((tri->indexes[0] == start && tri->indexes[1] == end) || + (tri->indexes[1] == start && tri->indexes[2] == end) || (tri->indexes[2] == start && tri->indexes[0] == end)) + { + if(i != ignore) + { + match = i; + } + + count++; + } + else if((tri->indexes[1] == start && tri->indexes[0] == end) || + (tri->indexes[2] == start && tri->indexes[1] == end) || (tri->indexes[0] == start && tri->indexes[2] == end)) + { + count++; + } + } + + // detect edges shared by three triangles and make them seams + if(count > 2) + { + match = -1; + } + + return match; +} + + +/* +================= +R_CalcSurfaceTriangleNeighbors +Tr3B - recoded from Q2E +================= +*/ +void R_CalcSurfaceTriangleNeighbors(int numTriangles, srfTriangle_t * triangles) +{ + int i; + srfTriangle_t *tri; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + tri->neighbors[0] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[1], tri->indexes[0], i); + tri->neighbors[1] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[2], tri->indexes[1], i); + tri->neighbors[2] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[0], tri->indexes[2], i); + } +} + +/* +================= +R_CalcSurfaceTrianglePlanes +================= +*/ +void R_CalcSurfaceTrianglePlanes(int numTriangles, srfTriangle_t * triangles, srfVert_t * verts) +{ + int i; + srfTriangle_t *tri; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + float *v1, *v2, *v3; + vec3_t d1, d2; + + v1 = verts[tri->indexes[0]].xyz; + v2 = verts[tri->indexes[1]].xyz; + v3 = verts[tri->indexes[2]].xyz; + + VectorSubtract(v2, v1, d1); + VectorSubtract(v3, v1, d2); + + CrossProduct(d2, d1, tri->plane); + tri->plane[3] = DotProduct(tri->plane, v1); + } +} + + +/* +================= R_CullLocalBox Returns CULL_IN, CULL_CLIP, or CULL_OUT ================= */ -int R_CullLocalBox (vec3_t bounds[2]) { +int R_CullLocalBox(vec3_t localBounds[2]) { +#if 0 int i, j; vec3_t transformed[8]; float dists[8]; @@ -104,6 +196,62 @@ } return CULL_CLIP; // partially clipped +#else + int i, j; + vec3_t transformed; + vec3_t v; + cplane_t *frust; + qboolean anyClip; + int r; + vec3_t worldBounds[2]; + + if(r_nocull->integer) + { + return CULL_CLIP; + } + + // transform into world space + ClearBounds(worldBounds[0], worldBounds[1]); + + for(j = 0; j < 8; j++) + { + v[0] = localBounds[j & 1][0]; + v[1] = localBounds[(j >> 1) & 1][1]; + v[2] = localBounds[(j >> 2) & 1][2]; + + R_LocalPointToWorld(v, transformed); + + AddPointToBounds(transformed, worldBounds[0], worldBounds[1]); + } + + // check against frustum planes + anyClip = qfalse; + for(i = 0; i < 4 /*FRUSTUM_PLANES*/; i++) + { + frust = &tr.viewParms.frustum[i]; + + r = BoxOnPlaneSide(worldBounds[0], worldBounds[1], frust); + + if(r == 2) + { + // completely outside frustum + return CULL_OUT; + } + if(r == 3) + { + anyClip = qtrue; + } + } + + if(!anyClip) + { + // completely inside frustum + return CULL_IN; + } + + // partially clipped + return CULL_CLIP; +#endif } /* @@ -622,7 +770,7 @@ void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) { srfTriangles_t *tri; srfPoly_t *poly; - drawVert_t *v1, *v2, *v3; + srfVert_t *v1, *v2, *v3; vec4_t plane4; if (!surfType) { @@ -636,9 +784,9 @@ return; case SF_TRIANGLES: tri = (srfTriangles_t *)surfType; - v1 = tri->verts + tri->indexes[0]; - v2 = tri->verts + tri->indexes[1]; - v3 = tri->verts + tri->indexes[2]; + v1 = tri->verts + tri->triangles[0].indexes[0]; + v2 = tri->verts + tri->triangles[0].indexes[1]; + v3 = tri->verts + tri->triangles[0].indexes[2]; PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz ); VectorCopy( plane4, plane->normal ); plane->dist = plane4[3]; @@ -1314,6 +1462,8 @@ ================ */ void R_DebugPolygon( int color, int numPoints, float *points ) { + // FIXME: implement this +#if 0 int i; GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); @@ -1337,6 +1487,7 @@ } qglEnd(); qglDepthRange( 0, 1 ); +#endif } /* @@ -1400,3 +1551,4 @@ + Index: code/renderer/tr_marks.c =================================================================== --- code/renderer/tr_marks.c (revision 1803) +++ code/renderer/tr_marks.c (working copy) @@ -266,11 +266,11 @@ int numClipPoints; float *v; srfGridMesh_t *cv; - drawVert_t *dv; + srfTriangle_t *tri; + srfVert_t *dv; vec3_t normal; vec3_t projectionDir; vec3_t v1, v2; - int *indexes; //increment view count for double check prevention tr.viewCount++; @@ -407,11 +407,12 @@ continue; } - indexes = (int *)( (byte *)surf + surf->ofsIndices ); - for ( k = 0 ; k < surf->numIndices ; k += 3 ) { - for ( j = 0 ; j < 3 ; j++ ) { - v = surf->points[0] + VERTEXSIZE * indexes[k+j];; - VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] ); + for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++) + { + for(j = 0; j < 3; j++) + { + v = surf->verts[tri->indexes[j]].xyz; + VectorMA(v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j]); } // add the fragments of this face @@ -429,12 +430,12 @@ srfTriangles_t *surf = (srfTriangles_t *) surfaces[i]; - for (k = 0; k < surf->numIndexes; k += 3) + for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++) { for(j = 0; j < 3; j++) { - v = surf->verts[surf->indexes[k + j]].xyz; - VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]); + v = surf->verts[tri->indexes[j]].xyz; + VectorMA(v, MARKER_OFFSET, surf->verts[tri->indexes[j]].normal, clipPoints[0][j]); } // add the fragments of this face @@ -452,3 +453,4 @@ return returnedFragments; } + Index: code/renderer/tr_model.c =================================================================== --- code/renderer/tr_model.c (revision 1803) +++ code/renderer/tr_model.c (working copy) @@ -915,7 +915,9 @@ R_SyncRenderThread(); - tr.viewCluster = -1; // force markleafs to regenerate + tr.visIndex = 0; + memset(tr.visClusters, -2, sizeof(tr.visClusters)); // force markleafs to regenerate + R_ClearFlares(); RE_ClearScene(); Index: code/renderer/tr_scene.c =================================================================== --- code/renderer/tr_scene.c (revision 1803) +++ code/renderer/tr_scene.c (working copy) @@ -127,8 +127,10 @@ } if ( !hShader ) { - ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); - return; + // This isn't a useful warning, and an hShader of zero isn't a null shader, it's + // the default shader. + //ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); + //return; } for ( j = 0; j < numPolys; j++ ) { Index: code/renderer/tr_shade.c =================================================================== --- code/renderer/tr_shade.c (revision 1803) +++ code/renderer/tr_shade.c (working copy) @@ -200,6 +200,27 @@ } +void R_DrawElementsVBO( int numIndexes, int firstIndex ) +{ + qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE))); +} + + +void R_DrawMultiElementsVBO( int multiDrawPrimitives, GLvoid **multiDrawFirstIndex, GLsizei *multiDrawNumIndexes) +{ +#if 0 + int i; + + for (i = 0; i < multiDrawPrimitives; i++) + { + qglDrawElements(GL_TRIANGLES, multiDrawNumIndexes[i], GL_INDEX_TYPE, BUFFER_OFFSET(multiDrawFirstIndex[i] * sizeof(GL_INDEX_TYPE))); + } +#endif + qglMultiDrawElementsEXT(GL_TRIANGLES, multiDrawNumIndexes, GL_INDEX_TYPE, multiDrawFirstIndex, multiDrawPrimitives); +} + + + /* ============================================================= @@ -253,26 +274,73 @@ */ static void DrawTris (shaderCommands_t *input) { GL_Bind( tr.whiteImage ); - qglColor3f (1,1,1); GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); qglDepthRange( 0, 0 ); - qglDisableClientState (GL_COLOR_ARRAY); - qglDisableClientState (GL_TEXTURE_COORD_ARRAY); + if (r_arb_vertex_buffer_object->integer) + { + if (r_arb_shader_objects->integer) + { + vec4_t color; + matrix_t matrix; + GLSL_VertexAttribsState(ATTR_POSITION); + GLSL_BindProgram(&tr.genericShader); + + GLSL_SetUniform_ModelMatrix(&tr.genericShader, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, matrix); + GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, matrix); + GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + //GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + GLSL_SetUniform_TCGen0(&tr.genericShader, TCGEN_IDENTITY); + Matrix16Identity(matrix); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + GLSL_SetUniform_Texture1Env(&tr.genericShader, 0); + GLSL_SetUniform_ColorGen(&tr.genericShader, CGEN_CONST); + GLSL_SetUniform_AlphaGen(&tr.genericShader, AGEN_CONST); + + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + color[3] = 1.0f; + GLSL_SetUniform_Color(&tr.genericShader, color); - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD - - if (qglLockArraysEXT) { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); + //qglDrawElements(GL_TRIANGLES, input->numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + } + else + { + // FIXME: implement this + } } + else + { + qglColor3f (1,1,1); - R_DrawElements( input->numIndexes, input->indexes ); + qglDisableClientState (GL_COLOR_ARRAY); + qglDisableClientState (GL_TEXTURE_COORD_ARRAY); - if (qglUnlockArraysEXT) { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); + qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD + + if (qglLockArraysEXT) { + qglLockArraysEXT(0, input->numVertexes); + GLimp_LogComment( "glLockArraysEXT\n" ); + } + + R_DrawElements( input->numIndexes, input->indexes ); + + if (qglUnlockArraysEXT) { + qglUnlockArraysEXT(); + GLimp_LogComment( "glUnlockArraysEXT\n" ); + } } qglDepthRange( 0, 1 ); } @@ -286,6 +354,8 @@ ================ */ static void DrawNormals (shaderCommands_t *input) { + //FIXME: implement this +#if 0 int i; vec3_t temp; @@ -303,6 +373,7 @@ qglEnd (); qglDepthRange( 0, 1 ); +#endif } /* @@ -319,7 +390,9 @@ shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader; tess.numIndexes = 0; + tess.firstIndex = 0; tess.numVertexes = 0; + tess.multiDrawPrimitives = 0; tess.shader = state; tess.fogNum = fogNum; tess.dlightBits = 0; // will be OR'd in by surface functions @@ -335,7 +408,203 @@ } + + +extern float EvalWaveForm( const waveForm_t *wf ); +extern float EvalWaveFormClamped( const waveForm_t *wf ); + + +static void GenerateGLTexCoords( shaderStage_t *pStage, int bundleNum) +{ + vec4_t vec; + + switch(pStage->bundle[bundleNum].tcGen) + { + case TCGEN_IDENTITY: + qglDisableClientState ( GL_TEXTURE_COORD_ARRAY ); + qglEnable(GL_TEXTURE_GEN_S); + qglEnable(GL_TEXTURE_GEN_T); + + vec[0] = 0.0f; + vec[1] = 0.0f; + vec[2] = 0.0f; + vec[3] = 0.0f; + + glTexGenfv(GL_S, GL_OBJECT_PLANE, vec); + glTexGenfv(GL_T, GL_OBJECT_PLANE, vec); + break; + + case TCGEN_TEXTURE: + qglTexCoordPointer( 2, GL_FLOAT, glState.currentVBO->stride_st, BUFFER_OFFSET(glState.currentVBO->ofs_st) ); + qglEnableClientState ( GL_TEXTURE_COORD_ARRAY ); + break; + + case TCGEN_LIGHTMAP: + qglTexCoordPointer( 2, GL_FLOAT, glState.currentVBO->stride_lightmap, BUFFER_OFFSET(glState.currentVBO->ofs_lightmap) ); + qglEnableClientState ( GL_TEXTURE_COORD_ARRAY ); + break; + + case TCGEN_ENVIRONMENT_MAPPED: + //FIXME: This doesn't look anything like the original + qglDisableClientState ( GL_TEXTURE_COORD_ARRAY ); + qglEnable(GL_TEXTURE_GEN_S); + qglEnable(GL_TEXTURE_GEN_T); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + break; + + case TCGEN_VECTOR: + qglDisableClientState ( GL_TEXTURE_COORD_ARRAY ); + qglEnable(GL_TEXTURE_GEN_S); + qglEnable(GL_TEXTURE_GEN_T); + + vec[0] = pStage->bundle[0].tcGenVectors[0][0]; + vec[1] = pStage->bundle[0].tcGenVectors[0][1]; + vec[2] = pStage->bundle[0].tcGenVectors[0][2]; + vec[3] = 0.0f; + + glTexGenfv(GL_S, GL_OBJECT_PLANE, vec); + + vec[0] = pStage->bundle[0].tcGenVectors[1][0]; + vec[1] = pStage->bundle[0].tcGenVectors[2][1]; + vec[2] = pStage->bundle[0].tcGenVectors[3][2]; + vec[3] = 0.0f; + + glTexGenfv(GL_T, GL_OBJECT_PLANE, vec); + break; + case TCGEN_FOG: + // FIXME: This doesn't look anything like the original + { + vec4_t vec; + + vec[0] = 0.0f; + vec[1] = 0.0f; + vec[2] = 1.0f; + vec[3] = 0.0f; + + qglDisableClientState ( GL_TEXTURE_COORD_ARRAY ); + qglEnable(GL_TEXTURE_GEN_S); + qglEnable(GL_TEXTURE_GEN_T); + glTexGenfv(GL_S, GL_EYE_PLANE, vec); + glTexGenfv(GL_T, GL_EYE_PLANE, vec); + } + break; + default: + // nothing else is supported, bail out + break; + } +} + + +static void UndoGLTexCoords( shaderStage_t *pStage, int bundleNum) +{ + switch(pStage->bundle[bundleNum].tcGen) + { + case TCGEN_IDENTITY: + case TCGEN_ENVIRONMENT_MAPPED: + case TCGEN_VECTOR: + case TCGEN_FOG: + qglEnableClientState ( GL_TEXTURE_COORD_ARRAY ); + qglDisable(GL_TEXTURE_GEN_S); + qglDisable(GL_TEXTURE_GEN_T); + break; + + case TCGEN_TEXTURE: + case TCGEN_LIGHTMAP: + default: + break; + } +} + + +static void ComputeTexMatrix( shaderStage_t *pStage, int bundleNum, float *outmatrix) +{ + int tm; + float matrix[16], currentmatrix[16]; + textureBundle_t *bundle = &pStage->bundle[bundleNum]; + /* + if (pStage->bundle[bundleNum].tcGen == TCGEN_ENVIRONMENT_MAPPED) + { + Matrix16Identity(currentmatrix); + currentmatrix[0] = 1.0f; + currentmatrix[5] = -1.0f; + //currentmatrix[10] = 1.0f; + Matrix16Copy(currentmatrix, outmatrix); + } + else*/ + { + Matrix16Identity(outmatrix); + Matrix16Identity(currentmatrix); + } + + for ( tm = 0; tm < bundle->numTexMods ; tm++ ) { + switch ( bundle->texMods[tm].type ) + { + + case TMOD_NONE: + tm = TR_MAX_TEXMODS; // break out of for loop + break; + + case TMOD_TURBULENT: + //FIXME: implement this +#if 0 + RB_CalcTurbulentTexCoords( &pStage->bundle[b].texMods[tm].wave, + ( float * ) tess.svars.texcoords[b] ); +#endif + break; + + case TMOD_ENTITY_TRANSLATE: + RB_CalcScrollTexMatrix( backEnd.currentEntity->e.shaderTexCoord, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_SCROLL: + RB_CalcScrollTexMatrix( bundle->texMods[tm].scroll, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_SCALE: + RB_CalcScaleTexMatrix( bundle->texMods[tm].scale, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_STRETCH: + RB_CalcStretchTexMatrix( &bundle->texMods[tm].wave, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_TRANSFORM: + RB_CalcTransformTexMatrix( &bundle->texMods[tm], + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_ROTATE: + RB_CalcRotateTexMatrix( bundle->texMods[tm].rotateSpeed, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + default: + ri.Error( ERR_DROP, "ERROR: unknown texmod '%d' in shader '%s'\n", bundle->texMods[tm].type, tess.shader->name ); + break; + } + } +} + + +/* =================== DrawMultitextured @@ -394,7 +663,83 @@ } +/* +=================== +DrawMultitexturedVBO +output = t0 * t1 or t0 + t1 + +t0 = most upstream according to spec +t1 = most downstream according to spec +=================== +*/ +static void DrawMultitexturedVBO( shaderCommands_t *input, int stage ) { + shaderStage_t *pStage; + float matrix[16]; + + pStage = tess.xstages[stage]; + + GL_State( pStage->stateBits ); + + // this is an ugly hack to work around a GeForce driver + // bug with multitexture and clip planes + if ( backEnd.viewParms.isPortal ) { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + + // + // base + // + GL_SelectTexture( 0 ); + GenerateGLTexCoords( pStage, 0 ); + ComputeTexMatrix( pStage, 0, matrix ); + qglMatrixMode(GL_TEXTURE); + qglLoadMatrixf(matrix); + qglMatrixMode(GL_MODELVIEW); + R_BindAnimatedImage( &pStage->bundle[0] ); + + // + // lightmap/secondary pass + // + GL_SelectTexture( 1 ); + qglEnable( GL_TEXTURE_2D ); + GenerateGLTexCoords( pStage, 1 ); + ComputeTexMatrix( pStage, 1, matrix ); + qglMatrixMode(GL_TEXTURE); + qglLoadMatrixf(matrix); + qglMatrixMode(GL_MODELVIEW); + + if ( r_lightmap->integer ) { + GL_TexEnv( GL_REPLACE ); + } else { + GL_TexEnv( tess.shader->multitextureEnv ); + } + + R_BindAnimatedImage( &pStage->bundle[1] ); + + //qglDrawElements(GL_TRIANGLES, input->numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + qglDisable( GL_TEXTURE_2D ); + UndoGLTexCoords( pStage, 1 ); + qglMatrixMode(GL_TEXTURE); + qglLoadIdentity(); + qglMatrixMode(GL_MODELVIEW); + + GL_SelectTexture( 0 ); + UndoGLTexCoords( pStage, 0 ); + qglMatrixMode(GL_TEXTURE); + qglLoadIdentity(); + qglMatrixMode(GL_MODELVIEW); +} + + /* =================== ProjectDlightTexture @@ -768,6 +1113,102 @@ } /* +=================== +RB_FogPassVBOGLSL + +Blends a fog texture on top of everything else +=================== +*/ +static void RB_FogPassVBOGLSL( void ) { + fog_t *fog; + int i; + vec4_t color; + vec3_t local; + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT; + matrix_t matrix; + + GLSL_BindProgram(&tr.genericShader); + + //GLSL_SetUniform_ModelMatrix(&tr.genericShader, glState.modelview); + //Matrix16Multiply(glState.projection, glState.modelview, matrix); + //GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, matrix); + //GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + fog = tr.world->fogs + tess.fogNum; + + //GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + GLSL_SetUniform_TCGen0(&tr.genericShader, TCGEN_FOG); + Matrix16Identity(matrix); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + GLSL_SetUniform_Texture1Env(&tr.genericShader, 0); + GLSL_SetUniform_ColorGen(&tr.genericShader, CGEN_CONST); + GLSL_SetUniform_AlphaGen(&tr.genericShader, AGEN_CONST); + + color[0] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; + color[1] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; + color[2] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; + color[3] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; + GLSL_SetUniform_Color(&tr.genericShader, color); + + // from RB_CalcFogTexCoords() + VectorSubtract( backEnd.or.origin, backEnd.viewParms.or.origin, local ); + fogDistanceVector[0] = -backEnd.or.modelMatrix[2]; + fogDistanceVector[1] = -backEnd.or.modelMatrix[6]; + fogDistanceVector[2] = -backEnd.or.modelMatrix[10]; + fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.or.axis[0] ); + + // scale the fog vectors based on the fog's thickness + fogDistanceVector[0] *= fog->tcScale; + fogDistanceVector[1] *= fog->tcScale; + fogDistanceVector[2] *= fog->tcScale; + fogDistanceVector[3] *= fog->tcScale; + + // rotate the gradient vector for this orientation + if ( fog->hasSurface ) { + fogDepthVector[0] = fog->surface[0] * backEnd.or.axis[0][0] + + fog->surface[1] * backEnd.or.axis[0][1] + fog->surface[2] * backEnd.or.axis[0][2]; + fogDepthVector[1] = fog->surface[0] * backEnd.or.axis[1][0] + + fog->surface[1] * backEnd.or.axis[1][1] + fog->surface[2] * backEnd.or.axis[1][2]; + fogDepthVector[2] = fog->surface[0] * backEnd.or.axis[2][0] + + fog->surface[1] * backEnd.or.axis[2][1] + fog->surface[2] * backEnd.or.axis[2][2]; + fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.or.origin, fog->surface ); + + eyeT = DotProduct( backEnd.or.viewOrigin, fogDepthVector ) + fogDepthVector[3]; + } else { + eyeT = 1; // non-surface fog always has eye inside + } + + fogDistanceVector[3] += 1.0/512; + + //ri.Printf(PRINT_ALL,"eyeT %f fogDistanceVector %f %f %f %f fogDepthVector %f %f %f %f\n", eyeT, fogDistanceVector[0], fogDistanceVector[1], fogDistanceVector[2], fogDistanceVector[3], fogDepthVector[0], fogDepthVector[1], fogDepthVector[2], fogDepthVector[3]); + //ri.Printf(PRINT_ALL, "fogsurface %f %f %f %f\n", fog->surface[0], fog->surface[1], fog->surface[2], fog->surface[3]); + + GLSL_SetUniform_TCGen0Vector0(&tr.genericShader, fogDistanceVector); + GLSL_SetUniform_TCGen0Vector1(&tr.genericShader, fogDepthVector); + GLSL_SetUniform_FogEyeT(&tr.genericShader, eyeT); + + GL_Bind( tr.fogImage ); + + if ( tess.shader->fogPass == FP_EQUAL ) { + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); + } else { + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); + } + + //qglDrawElements(GL_TRIANGLES, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + if (tess.multiDrawPrimitives) + { + R_DrawMultiElementsVBO(tess.multiDrawPrimitives, tess.multiDrawFirstIndex, tess.multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex); + } +} + + +/* =============== ComputeColors =============== @@ -973,8 +1414,544 @@ } } +static void ComputeHelperColor( shaderStage_t *pStage, vec4_t color) +{ + // + // rgbGen + // + switch ( pStage->rgbGen ) + { + case CGEN_IDENTITY: + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + color[3] = 1.0f; + break; + case CGEN_IDENTITY_LIGHTING: + color[0] = tr.identityLight; + color[1] = tr.identityLight; + color[2] = tr.identityLight; + color[3] = tr.identityLight; // FIXME: Code was like this in quake 3, is this a bug? + break; + case CGEN_LIGHTING_DIFFUSE: + // Done entirely in vertex program + break; + case CGEN_EXACT_VERTEX: + // Done entirely in vertex program + break; + case CGEN_CONST: + color[0] = pStage->constantColor[0] / 255.0f; + color[1] = pStage->constantColor[1] / 255.0f; + color[2] = pStage->constantColor[2] / 255.0f; + color[3] = pStage->constantColor[3] / 255.0f; + break; + case CGEN_VERTEX: + color[0] = tr.identityLight; + color[1] = tr.identityLight; + color[2] = tr.identityLight; + color[3] = 1.0f; + break; + case CGEN_ONE_MINUS_VERTEX: + color[0] = tr.identityLight; + color[1] = tr.identityLight; + color[2] = tr.identityLight; + color[3] = 1.0f; + case CGEN_FOG: + { + fog_t *fog; + + fog = tr.world->fogs + tess.fogNum; + + color[0] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; + color[1] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; + color[2] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; + color[3] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; + } + break; + case CGEN_WAVEFORM: + { + // from RB_CalcWaveColor + float glow; + waveForm_t *wf = &pStage->rgbWave; + + if ( wf->func == GF_NOISE ) { + glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; + } else { + glow = EvalWaveForm( wf ) * tr.identityLight; + } + + if ( glow < 0 ) { + glow = 0; + } + else if ( glow > 1 ) { + glow = 1; + } + + color[0] = glow; + color[1] = glow; + color[2] = glow; + color[3] = 1.0f; + } + break; + case CGEN_ENTITY: + if (backEnd.currentEntity) + { + color[0] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + color[1] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + color[2] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + color[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + else // FIXME: does original quake3 black out vertex colors like this? + { + color[0] = 0.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 0.0f; + } + break; + case CGEN_ONE_MINUS_ENTITY: + if (backEnd.currentEntity) + { + color[0] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + color[1] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + color[2] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + color[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + else // FIXME: does original quake3 black out vertex colors like this? + { + color[0] = 0.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 0.0f; + } + break; + } + + // + // alphaGen + // + switch ( pStage->alphaGen ) + { + case AGEN_SKIP: + break; + case AGEN_IDENTITY: + if ( pStage->rgbGen != CGEN_IDENTITY ) { + if ( ( pStage->rgbGen == CGEN_VERTEX && tr.identityLight != 1 ) || + pStage->rgbGen != CGEN_VERTEX ) { + color[3] = 1.0f; + } + } + break; + case AGEN_CONST: + if ( pStage->rgbGen != CGEN_CONST ) { + color[3] = pStage->constantColor[3] / 255.0f; + } + break; + case AGEN_WAVEFORM: + // From RB_CalcWaveAlpha + { + float glow; + waveForm_t *wf = &pStage->alphaWave; + glow = EvalWaveFormClamped( wf ); + color[3] = glow; + } + break; + case AGEN_LIGHTING_SPECULAR: + // FIXME: Can't do this with just a matrix + // RB_CalcSpecularAlpha( ( unsigned char * ) tess.svars.colors ); + break; + case AGEN_ENTITY: + //RB_CalcAlphaFromEntity( ( unsigned char * ) tess.svars.colors ); + if (backEnd.currentEntity) + { + color[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + break; + case AGEN_ONE_MINUS_ENTITY: + //RB_CalcAlphaFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); + if (backEnd.currentEntity) + { + color[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + break; + case AGEN_VERTEX: + // Done entirely in vertex program + break; + case AGEN_ONE_MINUS_VERTEX: + // Done entirely in vertex program + break; + case AGEN_PORTAL: + // Done entirely in vertex program + break; + } + + // FIXME: find some way to implement this stuff. +#if 0 + // + // fog adjustment for colors to fade out as fog increases + // + if ( tess.fogNum ) + { + switch ( pStage->adjustColorsForFog ) + { + case ACFF_MODULATE_RGB: + RB_CalcModulateColorsByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_MODULATE_ALPHA: + RB_CalcModulateAlphasByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_MODULATE_RGBA: + RB_CalcModulateRGBAsByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_NONE: + break; + } + } + + // if in greyscale rendering mode turn all color values into greyscale. + if(r_greyscale->integer) + { + int scale; + + for(i = 0; i < tess.numVertexes; i++) + { + scale = (tess.svars.colors[i][0] + tess.svars.colors[i][1] + tess.svars.colors[i][2]) / 3; + tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale; + } + } +#endif + +} + /* =============== +ComputeColorMatrix + +if disableVertexColors is true, outmatrix[12-15] contains a color that should be used. +=============== +*/ + +static void ComputeColorMatrix( shaderStage_t *pStage, float *outmatrix, qboolean *disableVertexColors) +{ + *disableVertexColors = qfalse; + + // + // rgbGen + // + switch ( pStage->rgbGen ) + { + case CGEN_IDENTITY: + *disableVertexColors = qtrue; + Matrix16Zero(outmatrix); + outmatrix[12] = 1.0f; + outmatrix[13] = 1.0f; + outmatrix[14] = 1.0f; + outmatrix[15] = 1.0f; + break; + default: + case CGEN_IDENTITY_LIGHTING: + *disableVertexColors = qtrue; + Matrix16Zero(outmatrix); + outmatrix[12] = tr.identityLight; + outmatrix[13] = tr.identityLight; + outmatrix[14] = tr.identityLight; + outmatrix[15] = 1.0f; // FIXME: used to just be straight tr.identityLight, is this a bug? + break; + case CGEN_LIGHTING_DIFFUSE: + // FIXME: Can't do this with just a matrix + //RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors ); + *disableVertexColors = qtrue; + Matrix16Zero(outmatrix); + outmatrix[12] = 1.0f; + outmatrix[13] = 1.0f; + outmatrix[14] = 1.0f; + outmatrix[15] = 1.0f; + break; + case CGEN_EXACT_VERTEX: + Matrix16Identity(outmatrix); + break; + case CGEN_CONST: + *disableVertexColors = qtrue; + Matrix16Zero(outmatrix); + outmatrix[12] = pStage->constantColor[0] / 255.0f; + outmatrix[13] = pStage->constantColor[1] / 255.0f; + outmatrix[14] = pStage->constantColor[2] / 255.0f; + outmatrix[15] = pStage->constantColor[3] / 255.0f; + break; + case CGEN_VERTEX: + Matrix16Identity(outmatrix); + outmatrix[ 0] = tr.identityLight; + outmatrix[ 5] = tr.identityLight; + outmatrix[10] = tr.identityLight; + outmatrix[15] = 1.0f; + break; + case CGEN_ONE_MINUS_VERTEX: + // FIXME: Not a perfect fit, if alpha is less than 1.0f or identitylight isn't 1 then this doesn't work +#if 0 + if ( tr.identityLight == 1 ) + { + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = 255 - tess.vertexColors[i][0]; + tess.svars.colors[i][1] = 255 - tess.vertexColors[i][1]; + tess.svars.colors[i][2] = 255 - tess.vertexColors[i][2]; + } + } + else + { + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = ( 255 - tess.vertexColors[i][0] ) * tr.identityLight; + tess.svars.colors[i][1] = ( 255 - tess.vertexColors[i][1] ) * tr.identityLight; + tess.svars.colors[i][2] = ( 255 - tess.vertexColors[i][2] ) * tr.identityLight; + } + } +#endif + Matrix16Zero(outmatrix); + outmatrix[0] = -1.0f; + outmatrix[5] = -1.0f; + outmatrix[10] = -1.0f; + + outmatrix[12] = 1.0f; + outmatrix[13] = 1.0f; + outmatrix[14] = 1.0f; + outmatrix[15] = 1.0f; + break; + case CGEN_FOG: + *disableVertexColors = qtrue; + { + fog_t *fog; + + fog = tr.world->fogs + tess.fogNum; + + outmatrix[12] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; + outmatrix[13] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; + outmatrix[14] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; + outmatrix[15] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; + + } + break; + case CGEN_WAVEFORM: + *disableVertexColors = qtrue; + { + // from RB_CalcWaveColor + float glow; + waveForm_t *wf = &pStage->rgbWave; + + if ( wf->func == GF_NOISE ) { + glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; + } else { + glow = EvalWaveForm( wf ) * tr.identityLight; + } + + if ( glow < 0 ) { + glow = 0; + } + else if ( glow > 1 ) { + glow = 1; + } + + outmatrix[12] = glow; + outmatrix[13] = glow; + outmatrix[14] = glow; + outmatrix[15] = 1.0f; + } + break; + case CGEN_ENTITY: + *disableVertexColors = qtrue; + if (backEnd.currentEntity) + { + outmatrix[12] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + outmatrix[13] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + outmatrix[14] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + outmatrix[15] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + else // FIXME: does original quake3 black out vertex colors like this? + { + outmatrix[12] = 0.0f; + outmatrix[13] = 0.0f; + outmatrix[14] = 0.0f; + outmatrix[15] = 0.0f; + } + break; + case CGEN_ONE_MINUS_ENTITY: + *disableVertexColors = qtrue; + if (backEnd.currentEntity) + { + outmatrix[12] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + outmatrix[13] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + outmatrix[14] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + outmatrix[15] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + else // FIXME: does original quake3 black out vertex colors like this? + { + outmatrix[12] = 0.0f; + outmatrix[13] = 0.0f; + outmatrix[14] = 0.0f; + outmatrix[15] = 0.0f; + } + break; + } + + // + // alphaGen + // + switch ( pStage->alphaGen ) + { + case AGEN_SKIP: + break; + case AGEN_IDENTITY: + if ( pStage->rgbGen != CGEN_IDENTITY ) { + if ( ( pStage->rgbGen == CGEN_VERTEX && tr.identityLight != 1 ) || + pStage->rgbGen != CGEN_VERTEX ) { + // FIXME: Not a perfect fit, if alpha is less than 1.0f and vertex colors are enabled then this doesn't work + outmatrix[15] = 1.0f; +#if 0 + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = 0xff; + } +#endif + } + } + break; + case AGEN_CONST: + if ( pStage->rgbGen != CGEN_CONST ) { + // FIXME: Not a perfect fit, if alpha is less than 1.0f and vertex colors are enabled then this doesn't work +#if 0 + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = pStage->constantColor[3]; + } +#endif + outmatrix[15] = pStage->constantColor[3] / 255.0f; + } + break; + case AGEN_WAVEFORM: + // From RB_CalcWaveAlpha + // FIXME: Not a perfect fit, if alpha is less than 1.0f and vertex colors are enabled then this doesn't work + { + float glow; + waveForm_t *wf = &pStage->alphaWave; + glow = EvalWaveFormClamped( wf ); + outmatrix[15] = glow; + } + break; + case AGEN_LIGHTING_SPECULAR: + // FIXME: Can't do this with just a matrix + // RB_CalcSpecularAlpha( ( unsigned char * ) tess.svars.colors ); + break; + case AGEN_ENTITY: + // FIXME: Doesn't work if alpha is less than 1.0f and vertex colors are enabled + //RB_CalcAlphaFromEntity( ( unsigned char * ) tess.svars.colors ); + if (backEnd.currentEntity) + { + outmatrix[15] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + break; + case AGEN_ONE_MINUS_ENTITY: + // FIXME: Doesn't work if alpha is less than 1.0f and vertex colors are enabled + //RB_CalcAlphaFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); + if (backEnd.currentEntity) + { + outmatrix[15] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + break; + case AGEN_VERTEX: + // FIXME: Doesn't work if vertex colors are disabled +#if 0 + if ( pStage->rgbGen != CGEN_VERTEX ) { + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = tess.vertexColors[i][3]; + } + } +#endif + outmatrix[15] = 1.0f; + break; + case AGEN_ONE_MINUS_VERTEX: + // FIXME: Doesn't work at all + outmatrix[15] = 1.0f; +#if 0 + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][3] = 255 - tess.vertexColors[i][3]; + } +#endif + break; + case AGEN_PORTAL: + // FIXME: This very doesn't work. +#if 0 + { + unsigned char alpha; + + for ( i = 0; i < tess.numVertexes; i++ ) + { + float len; + vec3_t v; + + VectorSubtract( tess.xyz[i], backEnd.viewParms.or.origin, v ); + len = VectorLength( v ); + + len /= tess.shader->portalRange; + + if ( len < 0 ) + { + alpha = 0; + } + else if ( len > 1 ) + { + alpha = 0xff; + } + else + { + alpha = len * 0xff; + } + + tess.svars.colors[i][3] = alpha; + } + } +#endif + break; + } + + // FIXME: find some way to implement this stuff. +#if 0 + // + // fog adjustment for colors to fade out as fog increases + // + if ( tess.fogNum ) + { + switch ( pStage->adjustColorsForFog ) + { + case ACFF_MODULATE_RGB: + RB_CalcModulateColorsByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_MODULATE_ALPHA: + RB_CalcModulateAlphasByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_MODULATE_RGBA: + RB_CalcModulateRGBAsByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_NONE: + break; + } + } + + // if in greyscale rendering mode turn all color values into greyscale. + if(r_greyscale->integer) + { + int scale; + + for(i = 0; i < tess.numVertexes; i++) + { + scale = (tess.svars.colors[i][0] + tess.svars.colors[i][1] + tess.svars.colors[i][2]) / 3; + tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale; + } + } +#endif +} + + +/* +=============== ComputeTexCoords =============== */ @@ -1140,16 +2117,481 @@ /* +** RB_IterateStagesGenericVBO +*/ +static void RB_IterateStagesGenericVBO( shaderCommands_t *input ) +{ + int stage; + + qglEnableClientState( GL_VERTEX_ARRAY ); + qglVertexPointer(3, GL_FLOAT, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz)); + qglEnableClientState( GL_NORMAL_ARRAY ); + qglNormalPointer(GL_FLOAT, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal)); + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = tess.xstages[stage]; + qboolean disableVertexColors = qfalse; + float matrix[16]; + + if ( !pStage ) + { + break; + } + + ComputeColorMatrix( pStage, matrix, &disableVertexColors); + + if (!disableVertexColors) + { + qglMatrixMode(GL_COLOR); + qglLoadMatrixf(matrix); + } + else + { + qglColor4fv(&matrix[12]); + } + + qglMatrixMode(GL_MODELVIEW); + + if (!disableVertexColors) + { + qglEnableClientState( GL_COLOR_ARRAY ); + qglColorPointer(4, GL_UNSIGNED_BYTE, glState.currentVBO->stride_vertexcolor, BUFFER_OFFSET(glState.currentVBO->ofs_vertexcolor)); + } + else + { + qglDisableClientState( GL_COLOR_ARRAY ); + } + + // + // do multitexture + // + if ( pStage->bundle[1].image[0] != 0 ) + { + DrawMultitexturedVBO( input, stage ); + } + else + { + float matrix[16]; + + GenerateGLTexCoords( pStage, 0 ); + + ComputeTexMatrix( pStage, 0, matrix ); + qglMatrixMode(GL_TEXTURE); + qglLoadMatrixf(matrix); + qglMatrixMode(GL_MODELVIEW); + + // + // set state + // + if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) + { + GL_Bind( tr.whiteImage ); + } + else + R_BindAnimatedImage( &pStage->bundle[0] ); + + GL_State( pStage->stateBits ); + + // + // draw + // + + //qglDrawElements(GL_TRIANGLES, input->numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + + UndoGLTexCoords( pStage, 0 ); + qglEnableClientState ( GL_COLOR_ARRAY ); + qglMatrixMode(GL_COLOR); + qglLoadIdentity(); + qglMatrixMode(GL_TEXTURE); + qglLoadIdentity(); + qglMatrixMode(GL_MODELVIEW); + + } + // allow skipping out to show just lightmaps during development + if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) + { + break; + } + } +} + + +static void DrawMultitexturedVBOGLSL( shaderCommands_t *input, int stage ) { + shaderStage_t *pStage; + matrix_t matrix; + vec4_t vector; + + pStage = tess.xstages[stage]; + + GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + // this is an ugly hack to work around a GeForce driver + // bug with multitexture and clip planes + //if ( backEnd.viewParms.isPortal ) { + //qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + //} + + // + // base + // + GL_SelectTexture( 0 ); + + GLSL_SetUniform_TCGen0(&tr.genericShader, pStage->bundle[0].tcGen); + if (pStage->bundle[0].tcGen == TCGEN_VECTOR) + { + vector[3] = 0.0f; + VectorCopy(pStage->bundle[0].tcGenVectors[0], vector); + GLSL_SetUniform_TCGen0Vector0(&tr.genericShader, vector); + VectorCopy(pStage->bundle[0].tcGenVectors[1], vector); + GLSL_SetUniform_TCGen0Vector1(&tr.genericShader, vector); + } + + ComputeTexMatrix( pStage, 0, matrix ); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + + R_BindAnimatedImage( &pStage->bundle[0] ); + + // + // lightmap/secondary pass + // + GL_SelectTexture( 1 ); + + GLSL_SetUniform_TCGen1(&tr.genericShader, pStage->bundle[1].tcGen); + if (pStage->bundle[1].tcGen == TCGEN_VECTOR) + { + vector[3] = 0.0f; + VectorCopy(pStage->bundle[1].tcGenVectors[0], vector); + GLSL_SetUniform_TCGen1Vector0(&tr.genericShader, vector); + VectorCopy(pStage->bundle[1].tcGenVectors[1], vector); + GLSL_SetUniform_TCGen1Vector1(&tr.genericShader, vector); + } + + ComputeTexMatrix( pStage, 1, matrix ); + GLSL_SetUniform_Texture1Matrix(&tr.genericShader, matrix); + + if ( r_lightmap->integer ) { + GLSL_SetUniform_Texture1Env(&tr.genericShader, GL_REPLACE); + } else { + GLSL_SetUniform_Texture1Env(&tr.genericShader, tess.shader->multitextureEnv); + } + + R_BindAnimatedImage( &pStage->bundle[1] ); + + //qglDrawElements(GL_TRIANGLES, input->numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + GL_SelectTexture( 0 ); +} + +static unsigned int RB_CalcShaderVertexAttribs( shaderCommands_t *input ) +{ + unsigned int vertexAttribs = ATTR_POSITION; + int stage; + + if(input->shader->numDeforms) + { + deformStage_t *ds; + + // only support the first one + ds = &input->shader->deforms[0]; + + switch (ds->deformation) + { + case DEFORM_WAVE: + case DEFORM_BULGE: + vertexAttribs |= ATTR_NORMAL | ATTR_TEXCOORD; + break; + + default: + break; + } + } + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + int i; + shaderStage_t *pStage = input->xstages[stage]; + + if ( !pStage ) + { + break; + } + + for (i = 0; i < 2; i++) + { + if (i == 1 && ( pStage->bundle[1].image[0] == 0 )) + { + break; + } + + switch(pStage->bundle[i].tcGen) + { + case TCGEN_TEXTURE: + vertexAttribs |= ATTR_TEXCOORD; + break; + case TCGEN_LIGHTMAP: + vertexAttribs |= ATTR_LIGHTCOORD; + break; + case TCGEN_ENVIRONMENT_MAPPED: + vertexAttribs |= ATTR_NORMAL; + break; + + default: + break; + } + } + + switch(pStage->rgbGen) + { + case CGEN_EXACT_VERTEX: + case CGEN_VERTEX: + case CGEN_ONE_MINUS_VERTEX: + vertexAttribs |= ATTR_COLOR; + break; + + default: + break; + } + + switch(pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + vertexAttribs |= ATTR_NORMAL; + break; + + case AGEN_VERTEX: + case AGEN_ONE_MINUS_VERTEX: + vertexAttribs |= ATTR_COLOR; + break; + + default: + break; + } + } + + return vertexAttribs; +} + + +static void RB_IterateStagesGenericVBOGLSL( shaderCommands_t *input ) +{ + int stage; + matrix_t matrix; + vec4_t vector; + + GLSL_BindProgram(&tr.genericShader); + + GLSL_SetUniform_ModelMatrix(&tr.genericShader, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, matrix); + GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, matrix); + GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + GLSL_SetUniform_PortalClipping(&tr.genericShader, backEnd.viewParms.isPortal); + if(backEnd.viewParms.isPortal) + { + float plane[4]; + + // clipping plane in world space + plane[0] = backEnd.viewParms.portalPlane.normal[0]; + plane[1] = backEnd.viewParms.portalPlane.normal[1]; + plane[2] = backEnd.viewParms.portalPlane.normal[2]; + plane[3] = backEnd.viewParms.portalPlane.dist; + + GLSL_SetUniform_PortalPlane(&tr.genericShader, plane); + } + + // u_DeformGen + if(input->shader->numDeforms) + { + deformStage_t *ds; + + // only support the first one + ds = &input->shader->deforms[0]; + + switch (ds->deformation) + { + case DEFORM_WAVE: + GLSL_SetUniform_DeformGen(&tr.genericShader, ds->deformationWave.func); + GLSL_SetUniform_DeformWave(&tr.genericShader, &ds->deformationWave); + GLSL_SetUniform_DeformSpread(&tr.genericShader, ds->deformationSpread); + GLSL_SetUniform_Time(&tr.genericShader, backEnd.refdef.floatTime); + break; + + case DEFORM_BULGE: + GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_BULGE); + GLSL_SetUniform_DeformBulge(&tr.genericShader, ds); + GLSL_SetUniform_Time(&tr.genericShader, backEnd.refdef.floatTime); + break; + + default: + GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + break; + } + } + else + { + GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + } + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = input->xstages[stage]; + qboolean setcolor = qfalse; + + if ( !pStage ) + { + break; + } + + GL_State( pStage->stateBits ); + + switch (pStage->rgbGen) + { + case CGEN_EXACT_VERTEX: + case CGEN_LIGHTING_DIFFUSE: + break; + default: + setcolor = qtrue; + } + + switch (pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + case AGEN_VERTEX: + case AGEN_ONE_MINUS_VERTEX: + case AGEN_PORTAL: + break; + default: + setcolor = qtrue; + } + + if (setcolor) + { + ComputeHelperColor (pStage, vector); + GLSL_SetUniform_Color(&tr.genericShader, vector); + } + + if (pStage->rgbGen == CGEN_LIGHTING_DIFFUSE) + { + vec3_t vec; + + VectorScale(backEnd.currentEntity->ambientLight, 1.0f / 255.0f, vec); + GLSL_SetUniform_AmbientLight(&tr.genericShader, vec); + + VectorScale(backEnd.currentEntity->directedLight, 1.0f / 255.0f, vec); + GLSL_SetUniform_DirectedLight(&tr.genericShader, vec); + + GLSL_SetUniform_LightDir(&tr.genericShader, backEnd.currentEntity->lightDir); + } + + if (pStage->alphaGen == AGEN_PORTAL) + { + GLSL_SetUniform_PortalRange(&tr.genericShader, tess.shader->portalRange); + } + + GLSL_SetUniform_ColorGen(&tr.genericShader, pStage->rgbGen); + GLSL_SetUniform_AlphaGen(&tr.genericShader, pStage->alphaGen); + + // + // do multitexture + // + if ( pStage->bundle[1].image[0] != 0 ) + { + DrawMultitexturedVBOGLSL( input, stage ); + } + else + { + GLSL_SetUniform_TCGen0(&tr.genericShader, pStage->bundle[0].tcGen); + if (pStage->bundle[0].tcGen == TCGEN_VECTOR) + { + vector[3] = 0.0f; + VectorCopy(pStage->bundle[0].tcGenVectors[0], vector); + GLSL_SetUniform_TCGen0Vector0(&tr.genericShader, vector); + VectorCopy(pStage->bundle[0].tcGenVectors[1], vector); + GLSL_SetUniform_TCGen0Vector1(&tr.genericShader, vector); + } + + ComputeTexMatrix( pStage, 0, matrix ); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + + // + // set state + // + if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) + { + GL_Bind( tr.whiteImage ); + } + else + R_BindAnimatedImage( &pStage->bundle[0] ); + + GLSL_SetUniform_Texture1Env(&tr.genericShader, 0); + + // + // draw + // + + //qglDrawElements(GL_TRIANGLES, input->numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + } + + // allow skipping out to show just lightmaps during development + if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) + { + break; + } + } +} + + +/* ** RB_StageIteratorGeneric */ void RB_StageIteratorGeneric( void ) { shaderCommands_t *input; + unsigned int vertexAttribs = 0; input = &tess; + + if (!input->numVertexes || !input->numIndexes) + { + return; + } RB_DeformTessGeometry(); + if(r_arb_vertex_buffer_object->integer) + { + vertexAttribs = RB_CalcShaderVertexAttribs( input ); + } + + if(r_arb_vertex_buffer_object->integer && (!glState.currentVBO || !glState.currentIBO || glState.currentVBO == tess.vbo || glState.currentIBO == tess.ibo)) + { + RB_UpdateVBOs(vertexAttribs); + } + // // log this call // @@ -1178,37 +2620,51 @@ // to avoid compiling those arrays since they will change // during multipass rendering // - if ( tess.numPasses > 1 || input->shader->multitextureEnv ) + if (!r_arb_vertex_buffer_object->integer) { - setArraysOnce = qfalse; - qglDisableClientState (GL_COLOR_ARRAY); - qglDisableClientState (GL_TEXTURE_COORD_ARRAY); + if ( tess.numPasses > 1 || input->shader->multitextureEnv ) + { + setArraysOnce = qfalse; + qglDisableClientState (GL_COLOR_ARRAY); + qglDisableClientState (GL_TEXTURE_COORD_ARRAY); + } + else + { + setArraysOnce = qtrue; + + qglEnableClientState( GL_COLOR_ARRAY); + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); + + qglEnableClientState( GL_TEXTURE_COORD_ARRAY); + qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); + } } else { - setArraysOnce = qtrue; + if (r_arb_shader_objects->integer) + { + GLSL_VertexAttribsState(vertexAttribs); + } + } - qglEnableClientState( GL_COLOR_ARRAY); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); - qglEnableClientState( GL_TEXTURE_COORD_ARRAY); - qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); - } - // // lock XYZ // - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD - if (qglLockArraysEXT) + if (!r_arb_vertex_buffer_object->integer) { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); + qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD + if (qglLockArraysEXT) + { + qglLockArraysEXT(0, input->numVertexes); + GLimp_LogComment( "glLockArraysEXT\n" ); + } } // // enable color and texcoord arrays after the lock if necessary // - if ( !setArraysOnce ) + if ( !setArraysOnce && !r_arb_vertex_buffer_object->integer ) { qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); qglEnableClientState( GL_COLOR_ARRAY ); @@ -1217,27 +2673,54 @@ // // call shader function // - RB_IterateStagesGeneric( input ); + if (r_arb_vertex_buffer_object->integer) + { + if (r_arb_shader_objects->integer) + { + RB_IterateStagesGenericVBOGLSL( input ); + } + else + { + RB_IterateStagesGenericVBO( input ); + } + } + else + { + RB_IterateStagesGeneric( input ); + } // // now do any dynamic lighting needed // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE - && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { - ProjectDlightTexture(); + if (!r_arb_vertex_buffer_object->integer) + { + if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { + ProjectDlightTexture(); + } } // // now do fog // if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); + if (r_arb_vertex_buffer_object->integer) + { + if (r_arb_shader_objects->integer) + { + RB_FogPassVBOGLSL(); + } + } + else + { + RB_FogPass(); + } } // // unlock arrays // - if (qglUnlockArraysEXT) + if (qglUnlockArraysEXT && !r_arb_vertex_buffer_object->integer) { qglUnlockArraysEXT(); GLimp_LogComment( "glUnlockArraysEXT\n" ); @@ -1261,6 +2744,13 @@ shaderCommands_t *input; shader_t *shader; + if(r_arb_vertex_buffer_object->integer) + { + //R_BindNullVBO(); + //R_BindNullIBO(); + return; + } + input = &tess; shader = input->shader; @@ -1337,6 +2827,13 @@ void RB_StageIteratorLightmappedMultitexture( void ) { shaderCommands_t *input; + if(r_arb_vertex_buffer_object->integer) + { + //R_BindNullVBO(); + //R_BindNullIBO(); + return; + } + input = &tess; // @@ -1444,7 +2941,7 @@ input = &tess; - if (input->numIndexes == 0) { + if (input->numIndexes == 0 || input->numVertexes == 0) { return; } @@ -1487,8 +2984,15 @@ if ( r_shownormals->integer ) { DrawNormals (input); } + + R_BindNullVBO(); + R_BindNullIBO(); + // clear shader so we can tell we don't have any unclosed surfaces tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + tess.multiDrawPrimitives = 0; GLimp_LogComment( "----------\n" ); } Index: code/renderer/tr_shade_calc.c =================================================================== --- code/renderer/tr_shade_calc.c (revision 1803) +++ code/renderer/tr_shade_calc.c (working copy) @@ -57,7 +57,7 @@ ** ** Evaluates a given waveForm_t, referencing backEnd.refdef.time directly */ -static float EvalWaveForm( const waveForm_t *wf ) +float EvalWaveForm( const waveForm_t *wf ) { float *table; @@ -66,7 +66,7 @@ return WAVEVALUE( table, wf->base, wf->amplitude, wf->phase, wf->frequency ); } -static float EvalWaveFormClamped( const waveForm_t *wf ) +float EvalWaveFormClamped( const waveForm_t *wf ) { float glow = EvalWaveForm( wf ); @@ -104,6 +104,24 @@ RB_CalcTransformTexCoords( &tmi, st ); } +void RB_CalcStretchTexMatrix( const waveForm_t *wf, float *matrix ) +{ + float p; + texModInfo_t tmi; + + p = 1.0f / EvalWaveForm( wf ); + + tmi.matrix[0][0] = p; + tmi.matrix[1][0] = 0; + tmi.translate[0] = 0.5f - 0.5f * p; + + tmi.matrix[0][1] = 0; + tmi.matrix[1][1] = p; + tmi.translate[1] = 0.5f - 0.5f * p; + + RB_CalcTransformTexMatrix( &tmi, matrix ); +} + /* ==================================================================== @@ -305,6 +323,7 @@ // clear the shader indexes tess.numIndexes = 0; tess.numVertexes = 0; + tess.firstIndex = 0; color[0] = color[1] = color[2] = color[3] = 255; @@ -368,6 +387,7 @@ oldVerts = tess.numVertexes; tess.numVertexes = 0; tess.numIndexes = 0; + tess.firstIndex = 0; if ( backEnd.currentEntity != &tr.worldEntity ) { GlobalVectorToLocal( backEnd.viewParms.or.axis[1], leftDir ); @@ -542,6 +562,18 @@ int i; deformStage_t *ds; + if(glState.currentVBO && glState.currentIBO && (glState.currentVBO != tess.vbo || glState.currentIBO != tess.ibo)) + { + // static VBOs are incompatible with deformVertexes + return; + } + + if(!ShaderRequiresCPUDeforms(tess.shader)) + { + // we don't need the following CPU deforms + return; + } + for ( i = 0 ; i < tess.shader->numDeforms ; i++ ) { ds = &tess.shader->deforms[ i ]; @@ -948,6 +980,14 @@ } } +void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ) +{ + matrix[ 0] = scale[0]; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = 0.0f; + matrix[ 1] = 0.0f; matrix[ 5] = scale[1]; matrix[ 9] = 0.0f; matrix[13] = 0.0f; + matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; + matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; +} + /* ** RB_CalcScrollTexCoords */ @@ -972,6 +1012,26 @@ } } +void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ) +{ + float timeScale = tess.shaderTime; + float adjustedScrollS, adjustedScrollT; + + adjustedScrollS = scrollSpeed[0] * timeScale; + adjustedScrollT = scrollSpeed[1] * timeScale; + + // clamp so coordinates don't continuously get larger, causing problems + // with hardware limits + adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); + adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); + + + matrix[ 0] = 1.0f; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = adjustedScrollS; + matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = 0.0f; matrix[13] = adjustedScrollT; + matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; + matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; +} + /* ** RB_CalcTransformTexCoords */ @@ -989,6 +1049,14 @@ } } +void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ) +{ + matrix[ 0] = tmi->matrix[0][0]; matrix[ 4] = tmi->matrix[1][0]; matrix[ 8] = 0.0f; matrix[12] = tmi->translate[0]; + matrix[ 1] = tmi->matrix[0][1]; matrix[ 5] = tmi->matrix[1][1]; matrix[ 9] = 0.0f; matrix[13] = tmi->translate[1]; + matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; + matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; +} + /* ** RB_CalcRotateTexCoords */ @@ -1017,11 +1085,36 @@ RB_CalcTransformTexCoords( &tmi, st ); } +void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ) +{ + float timeScale = tess.shaderTime; + float degs; + int index; + float sinValue, cosValue; + texModInfo_t tmi; + degs = -degsPerSecond * timeScale; + index = degs * ( FUNCTABLE_SIZE / 360.0f ); + sinValue = tr.sinTable[ index & FUNCTABLE_MASK ]; + cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ]; + tmi.matrix[0][0] = cosValue; + tmi.matrix[1][0] = -sinValue; + tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue; + tmi.matrix[0][1] = sinValue; + tmi.matrix[1][1] = cosValue; + tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; + RB_CalcTransformTexMatrix( &tmi, matrix ); +} + + + + + + #if id386 && !defined(__GNUC__) long myftol( float f ) { Index: code/renderer/tr_shader.c =================================================================== --- code/renderer/tr_shader.c (revision 1803) +++ code/renderer/tr_shader.c (working copy) @@ -1552,6 +1552,7 @@ else if ( !Q_stricmp(token, "portal") ) { shader.sort = SS_PORTAL; + shader.isPortal = qtrue; continue; } // skyparms Index: code/renderer/tr_sky.c =================================================================== --- code/renderer/tr_sky.c (revision 1803) +++ code/renderer/tr_sky.c (working copy) @@ -369,7 +369,7 @@ for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t < maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) { - qglBegin( GL_TRIANGLE_STRIP ); + qglBegin( GL_TRIANGLE_STRIP ); // Alternate made for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) { @@ -384,6 +384,101 @@ } } +static void DrawSkySideVBO( struct image_s *image, const int mins[2], const int maxs[2] ) +{ + int s, t, i; + //int firstVertex = tess.numVertexes; + //int firstIndex = tess.numIndexes; + matrix_t matrix; + vec4_t color; + + tess.numVertexes = 0; + tess.numIndexes = 0; + tess.firstIndex = 0; + + GL_Bind( image ); + + for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t < maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + for ( i = 0; i < 2; i++) + { + tess.xyz[tess.numVertexes][0] = s_skyPoints[t+i][s][0]; + tess.xyz[tess.numVertexes][1] = s_skyPoints[t+i][s][1]; + tess.xyz[tess.numVertexes][2] = s_skyPoints[t+i][s][2]; + tess.xyz[tess.numVertexes][3] = 1.0; + + tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t+i][s][0]; + tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t+i][s][1]; + + tess.indexes[tess.numIndexes] = tess.numVertexes; + + tess.numVertexes++; + + if(tess.numVertexes >= SHADER_MAX_VERTEXES) + { + ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()\n"); + } + + tess.numIndexes++; + + if(tess.numIndexes >= SHADER_MAX_INDEXES) + { + ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()\n"); + } + } + } + } + + // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); + + if (r_arb_shader_objects->integer) + { + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + GLSL_BindProgram(&tr.genericShader); + + GLSL_SetUniform_ModelMatrix(&tr.genericShader, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, matrix); + GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, matrix); + GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + GLSL_SetUniform_TCGen0(&tr.genericShader, TCGEN_TEXTURE); + Matrix16Identity(matrix); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + GLSL_SetUniform_Texture1Env(&tr.genericShader, 0); + GLSL_SetUniform_ColorGen(&tr.genericShader, CGEN_CONST); + GLSL_SetUniform_AlphaGen(&tr.genericShader, AGEN_CONST); + + color[0] = tr.identityLight; + color[1] = tr.identityLight; + color[2] = tr.identityLight; + color[3] = 1.0f; + GLSL_SetUniform_Color(&tr.genericShader, color); + + qglDrawElements(GL_TRIANGLE_STRIP, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex)); + + } + else + { + qglEnableClientState( GL_VERTEX_ARRAY ); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + qglVertexPointer(3, GL_FLOAT, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz)); + qglTexCoordPointer( 2, GL_FLOAT, glState.currentVBO->stride_st, BUFFER_OFFSET(glState.currentVBO->ofs_st) ); + + qglDrawElements(GL_TRIANGLE_STRIP, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex)); + } + + //R_BindNullVBO(); + //R_BindNullIBO(); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; +} + static void DrawSkyBox( shader_t *shader ) { int i; @@ -447,9 +542,19 @@ } } - DrawSkySide( shader->sky.outerbox[sky_texorder[i]], - sky_mins_subd, - sky_maxs_subd ); + if (r_arb_vertex_buffer_object->integer) + { + DrawSkySideVBO( shader->sky.outerbox[sky_texorder[i]], + sky_mins_subd, + sky_maxs_subd ); + } + else + { + DrawSkySide( shader->sky.outerbox[sky_texorder[i]], + sky_mins_subd, + sky_maxs_subd ); + } + } } @@ -617,6 +722,7 @@ // set up for drawing tess.numIndexes = 0; tess.numVertexes = 0; + tess.firstIndex = 0; if ( input->shader->sky.cloudHeight ) { @@ -708,9 +814,18 @@ if ( !r_drawSun->integer ) { return; } - qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); - qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + //qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + { + // FIXME: this could be a lot cleaner + matrix_t trans, product; + + Matrix16Translation( backEnd.viewParms.or.origin, trans ); + Matrix16Multiply( backEnd.viewParms.world.modelMatrix, trans, product ); + GL_SetModelviewMatrix( product ); + } + dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) size = dist * 0.4; @@ -816,15 +931,28 @@ // draw the outer skybox if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) { - qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); + matrix_t oldmodelview; + + if (!(r_arb_vertex_buffer_object->integer && r_arb_shader_objects->integer)) + qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); - qglPushMatrix (); GL_State( 0 ); - qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + { + // FIXME: this could be a lot cleaner + matrix_t trans, product; + + Matrix16Copy( glState.modelview, oldmodelview ); + Matrix16Translation( backEnd.viewParms.or.origin, trans ); + Matrix16Multiply( glState.modelview, trans, product ); + GL_SetModelviewMatrix( product ); + + } + DrawSkyBox( tess.shader ); - qglPopMatrix(); + GL_SetModelviewMatrix( oldmodelview ); } // generate the vertexes for all the clouds, which will be drawn Index: code/renderer/tr_surface.c =================================================================== --- code/renderer/tr_surface.c (revision 1803) +++ code/renderer/tr_surface.c (working copy) @@ -150,6 +150,170 @@ /* ============== +RB_UpdateVBOs + +Adapted from Tess_UpdateVBOs from xreal + +Tr3B: update the default VBO to replace the client side vertex arrays +============== +*/ +void RB_UpdateVBOs(unsigned int attribBits) +{ + GLimp_LogComment("--- RB_UpdateVBOs ---\n"); + + // update the default VBO + if(tess.numVertexes > 0 && tess.numVertexes <= SHADER_MAX_VERTEXES) + { + R_BindVBO(tess.vbo); + + if(attribBits & ATTR_BITS) + { + if(attribBits & ATTR_POSITION) + { + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); + } + + if(attribBits & ATTR_TEXCOORD | attribBits & ATTR_LIGHTCOORD) + { + // these are interleaved, so we update both if either need it + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); + } + + if(attribBits & ATTR_NORMAL) + { + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); + } + + if(attribBits & ATTR_COLOR) + { + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); + } + } + else + { + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); + } + + } + + // update the default IBO + if(tess.numIndexes > 0 && tess.numIndexes <= SHADER_MAX_INDEXES) + { + R_BindIBO(tess.ibo); + + qglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, tess.numIndexes * sizeof(tess.indexes[0]), tess.indexes); + } +} + + +/* +============== +RB_InstantQuad + +based on Tess_InstantQuad from xreal +============== +*/ +void RB_InstantQuad(vec4_t quadVerts[4]) +{ + matrix_t matrix; + vec4_t color; + + GLimp_LogComment("--- RB_InstantQuad ---\n"); + + tess.numVertexes = 0; + tess.numIndexes = 0; + tess.firstIndex = 0; + + VectorCopy4(quadVerts[0], tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + VectorCopy4(quadVerts[1], tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + VectorCopy4(quadVerts[2], tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + VectorCopy4(quadVerts[3], tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.texCoords[tess.numVertexes][0][2] = 0; + tess.texCoords[tess.numVertexes][0][3] = 1; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 1; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 3; + + // FIXME: A lot of this can probably be removed for speed + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); + + if (r_arb_shader_objects->integer) + { + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + GLSL_BindProgram(&tr.genericShader); + + GLSL_SetUniform_ModelMatrix(&tr.genericShader, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, matrix); + GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, matrix); + GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + GLSL_SetUniform_TCGen0(&tr.genericShader, TCGEN_TEXTURE); + Matrix16Identity(matrix); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + GLSL_SetUniform_Texture1Env(&tr.genericShader, 0); + GLSL_SetUniform_ColorGen(&tr.genericShader, CGEN_CONST); + GLSL_SetUniform_AlphaGen(&tr.genericShader, AGEN_CONST); + + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; + color[3] = 1.0f; + GLSL_SetUniform_Color(&tr.genericShader, color); + + //qglDrawElements(GL_TRIANGLES, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex); + } + else + { + qglEnableClientState( GL_VERTEX_ARRAY ); + qglVertexPointer(3, GL_FLOAT, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz)); + qglEnableClientState( GL_NORMAL_ARRAY ); + qglNormalPointer(GL_FLOAT, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal)); + + //qglDrawElements(GL_TRIANGLES, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex); + } + + //R_BindNullVBO(); + //R_BindNullIBO(); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; +} + + +/* +============== RB_SurfaceSprite ============== */ @@ -217,40 +381,33 @@ tess.numVertexes = numv; } - -/* -============= -RB_SurfaceTriangles -============= -*/ -static void RB_SurfaceTriangles( srfTriangles_t *srf ) { +static void RB_SurfaceHelper( int numVerts, srfVert_t *verts, int numTriangles, srfTriangle_t *triangles, int dlightBits) +{ int i; - drawVert_t *dv; - float *xyz, *normal, *texCoords; + srfVert_t *dv; + float *xyz, *normal, *texCoords; byte *color; - int dlightBits; qboolean needsNormal; - dlightBits = srf->dlightBits[backEnd.smpFrame]; tess.dlightBits |= dlightBits; - RB_CHECKOVERFLOW( srf->numVerts, srf->numIndexes ); + RB_CHECKOVERFLOW( numVerts, numTriangles * 3 ); - for ( i = 0 ; i < srf->numIndexes ; i += 3 ) { - tess.indexes[ tess.numIndexes + i + 0 ] = tess.numVertexes + srf->indexes[ i + 0 ]; - tess.indexes[ tess.numIndexes + i + 1 ] = tess.numVertexes + srf->indexes[ i + 1 ]; - tess.indexes[ tess.numIndexes + i + 2 ] = tess.numVertexes + srf->indexes[ i + 2 ]; + for ( i = 0 ; i < numTriangles ; i += 1 ) { + tess.indexes[ tess.numIndexes + i * 3 + 0 ] = tess.numVertexes + triangles[ i ].indexes[0]; + tess.indexes[ tess.numIndexes + i * 3 + 1 ] = tess.numVertexes + triangles[ i ].indexes[1]; + tess.indexes[ tess.numIndexes + i * 3 + 2 ] = tess.numVertexes + triangles[ i ].indexes[2]; } - tess.numIndexes += srf->numIndexes; + tess.numIndexes += numTriangles * 3; - dv = srf->verts; + dv = verts; xyz = tess.xyz[ tess.numVertexes ]; normal = tess.normal[ tess.numVertexes ]; texCoords = tess.texCoords[ tess.numVertexes ][0]; color = tess.vertexColors[ tess.numVertexes ]; needsNormal = tess.shader->needsNormal; - for ( i = 0 ; i < srf->numVerts ; i++, dv++, xyz += 4, normal += 4, texCoords += 4, color += 4 ) { + for ( i = 0 ; i < numVerts ; i++, dv++, xyz += 4, normal += 4, texCoords += 4, color += 4 ) { xyz[0] = dv->xyz[0]; xyz[1] = dv->xyz[1]; xyz[2] = dv->xyz[2]; @@ -267,19 +424,132 @@ texCoords[2] = dv->lightmap[0]; texCoords[3] = dv->lightmap[1]; - *(int *)color = *(int *)dv->color; + *(int *)color = *(int *)dv->vertexColors; } - for ( i = 0 ; i < srf->numVerts ; i++ ) { + for ( i = 0 ; i < numVerts ; i++ ) { tess.vertexDlightBits[ tess.numVertexes + i] = dlightBits; } - tess.numVertexes += srf->numVerts; + tess.numVertexes += numVerts; } +static qboolean RB_SurfaceHelperVBO(VBO_t *vbo, IBO_t *ibo, int numVerts, int numIndexes, int firstIndex, qboolean shaderCheck) +{ + if( r_arb_vertex_buffer_object->integer && vbo && ibo) + { + int i, mergeForward, mergeBack; + GLvoid *firstIndexOffset, *lastIndexOffset; + if (!(shaderCheck && !ShaderRequiresCPUDeforms(tess.shader) && !tess.shader->isSky && !tess.shader->isPortal)) + { + return qfalse; + } + if (!(vbo == glState.currentVBO && ibo == glState.currentIBO) || tess.multiDrawPrimitives >= MAX_MULTIDRAW_PRIMITIVES) + { + RB_EndSurface(); + RB_BeginSurface(tess.shader, tess.fogNum); + + R_BindVBO(vbo); + R_BindIBO(ibo); + } + + backEnd.pc.c_surfaceIBOs++; + + // merge this into any existing multidraw primitives + mergeForward = -1; + mergeBack = -1; + firstIndexOffset = BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE)); + lastIndexOffset = BUFFER_OFFSET((firstIndex + numIndexes) * sizeof(GL_INDEX_TYPE)); + + if (r_mergeMultidraws->integer) + { + i = 0; + + if (r_mergeMultidraws->integer == 1) + { + // lazy merge, only check the last primitive + + if (tess.multiDrawPrimitives) + { + i = tess.multiDrawPrimitives - 1; + } + } + + for (; i < tess.multiDrawPrimitives; i++) + { + if (tess.multiDrawLastIndex[i] == firstIndexOffset) + { + mergeBack = i; + } + + if (lastIndexOffset == tess.multiDrawFirstIndex[i]) + { + mergeForward = i; + } + } + } + + if (mergeBack != -1 && mergeForward == -1) + { + tess.multiDrawNumIndexes[mergeBack] += numIndexes; + tess.multiDrawLastIndex[mergeBack] = (char *)tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack] * sizeof(GL_INDEX_TYPE); + } + else if (mergeBack == -1 && mergeForward != -1) + { + tess.multiDrawNumIndexes[mergeForward] += numIndexes; + tess.multiDrawFirstIndex[mergeForward] = firstIndexOffset; + tess.multiDrawLastIndex[mergeForward] = (char *)tess.multiDrawFirstIndex[mergeForward] + tess.multiDrawNumIndexes[mergeForward] * sizeof(GL_INDEX_TYPE); + } + else if (mergeBack != -1 && mergeForward != -1) + { + tess.multiDrawNumIndexes[mergeBack] += numIndexes + tess.multiDrawNumIndexes[mergeForward]; + tess.multiDrawLastIndex[mergeBack] = (char *)tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack] * sizeof(GL_INDEX_TYPE); + tess.multiDrawPrimitives--; + backEnd.pc.c_mergedIBOs--; + + if (mergeForward != tess.multiDrawPrimitives) + { + tess.multiDrawNumIndexes[mergeForward] = tess.multiDrawNumIndexes[tess.multiDrawPrimitives]; + tess.multiDrawFirstIndex[mergeForward] = tess.multiDrawFirstIndex[tess.multiDrawPrimitives]; + } + } + else if (mergeBack == -1 && mergeForward == -1) + { + tess.multiDrawNumIndexes[tess.multiDrawPrimitives] = numIndexes; + tess.multiDrawFirstIndex[tess.multiDrawPrimitives] = firstIndexOffset; + tess.multiDrawLastIndex[tess.multiDrawPrimitives] = lastIndexOffset; + tess.multiDrawPrimitives++; + backEnd.pc.c_mergedIBOs++; + } + + tess.numIndexes += numIndexes; + tess.numVertexes += numVerts; + + return qtrue; + } + + return qfalse; +} + /* +============= +RB_SurfaceTriangles +============= +*/ +static void RB_SurfaceTriangles( srfTriangles_t *srf ) { + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, srf->firstIndex, qtrue ) ) + { + return; + } + + RB_SurfaceHelper(srf->numVerts, srf->verts, srf->numTriangles, srf->triangles, srf->dlightBits[backEnd.smpFrame]); +} + + + +/* ============== RB_SurfaceBeam ============== @@ -293,6 +563,8 @@ vec3_t direction, normalized_direction; vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; vec3_t oldorigin, origin; + matrix_t matrix; + vec4_t color; e = &backEnd.currentEntity->e; @@ -326,6 +598,8 @@ GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + //FIXME: Quake3 doesn't use this, but some mod might +#if 0 qglColor3f( 1, 0, 0 ); qglBegin( GL_TRIANGLE_STRIP ); @@ -334,6 +608,67 @@ qglVertex3fv( end_points[ i % NUM_BEAM_SEGS] ); } qglEnd(); +#endif +#if 0 + tess.numIndexes = 0; + tess.numVertexes = 0; + + for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { + VectorCopy( start_points[ i % NUM_BEAM_SEGS], tess.xyz[tess.numVertexes] ); + tess.xyz[tess.numVertexes][3] = 1.0f; + + tess.indexes[tess.numIndexes] = tess.numVertexes; + + tess.numVertexes++; + tess.numIndexes++; + + VectorCopy( end_points[ i % NUM_BEAM_SEGS], tess.xyz[tess.numVertexes] ); + tess.xyz[tess.numVertexes][3] = 1.0f; + + tess.indexes[tess.numIndexes] = tess.numVertexes; + + tess.numVertexes++; + tess.numIndexes++; + } + + // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); + + qglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION); + qglVertexAttribPointerARB(ATTR_INDEX_POSITION, 3, GL_FLOAT, 0, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz)); + + GLSL_BindProgram(&tr.genericShader); + + GLSL_SetUniform_ModelMatrix(&tr.genericShader, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, matrix); + GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, matrix); + GLSL_SetUniform_ViewOrigin(&tr.genericShader, backEnd.or.viewOrigin); + + GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); + GLSL_SetUniform_TCGen0(&tr.genericShader, TCGEN_IDENTITY); + Matrix16Identity(matrix); + GLSL_SetUniform_Texture0Matrix(&tr.genericShader, matrix); + GLSL_SetUniform_Texture1Env(&tr.genericShader, 0); + GLSL_SetUniform_ColorGen(&tr.genericShader, CGEN_CONST); + GLSL_SetUniform_AlphaGen(&tr.genericShader, AGEN_CONST); + + color[0] = 1.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 1.0f; + GLSL_SetUniform_Color(&tr.genericShader, color); + + qglDrawElements(GL_TRIANGLE_STRIP, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(0)); + + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); + //GLSL_BindNullProgram(); + + R_BindNullVBO(); + R_BindNullIBO(); + + tess.numIndexes = 0; + tess.numVertexes = 0; +#endif } //================================================================================ @@ -903,56 +1238,13 @@ RB_SurfaceFace ============== */ -static void RB_SurfaceFace( srfSurfaceFace_t *surf ) { - int i; - unsigned *indices, *tessIndexes; - float *v; - float *normal; - int ndx; - int Bob; - int numPoints; - int dlightBits; - - RB_CHECKOVERFLOW( surf->numPoints, surf->numIndices ); - - dlightBits = surf->dlightBits[backEnd.smpFrame]; - tess.dlightBits |= dlightBits; - - indices = ( unsigned * ) ( ( ( char * ) surf ) + surf->ofsIndices ); - - Bob = tess.numVertexes; - tessIndexes = tess.indexes + tess.numIndexes; - for ( i = surf->numIndices-1 ; i >= 0 ; i-- ) { - tessIndexes[i] = indices[i] + Bob; +static void RB_SurfaceFace( srfSurfaceFace_t *srf ) { + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, srf->firstIndex, qtrue ) ) + { + return; } - tess.numIndexes += surf->numIndices; - - v = surf->points[0]; - - ndx = tess.numVertexes; - - numPoints = surf->numPoints; - - if ( tess.shader->needsNormal ) { - normal = surf->plane.normal; - for ( i = 0, ndx = tess.numVertexes; i < numPoints; i++, ndx++ ) { - VectorCopy( normal, tess.normal[ndx] ); - } - } - - for ( i = 0, v = surf->points[0], ndx = tess.numVertexes; i < numPoints; i++, v += VERTEXSIZE, ndx++ ) { - VectorCopy( v, tess.xyz[ndx]); - tess.texCoords[ndx][0][0] = v[3]; - tess.texCoords[ndx][0][1] = v[4]; - tess.texCoords[ndx][1][0] = v[5]; - tess.texCoords[ndx][1][1] = v[6]; - * ( unsigned int * ) &tess.vertexColors[ndx] = * ( unsigned int * ) &v[7]; - tess.vertexDlightBits[ndx] = dlightBits; - } - - - tess.numVertexes += surf->numPoints; + RB_SurfaceHelper(srf->numVerts, srf->verts, srf->numTriangles, srf->triangles, srf->dlightBits[backEnd.smpFrame]); } @@ -993,13 +1285,13 @@ Just copy the grid of points and triangulate ============= */ -static void RB_SurfaceGrid( srfGridMesh_t *cv ) { +static void RB_SurfaceGrid( srfGridMesh_t *srf ) { int i, j; float *xyz; float *texCoords; float *normal; unsigned char *color; - drawVert_t *dv; + srfVert_t *dv; int rows, irows, vrows; int used; int widthTable[MAX_GRID_SIZE]; @@ -1011,34 +1303,39 @@ int *vDlightBits; qboolean needsNormal; - dlightBits = cv->dlightBits[backEnd.smpFrame]; + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, srf->firstIndex, qtrue ) ) + { + return; + } + + dlightBits = srf->dlightBits[backEnd.smpFrame]; tess.dlightBits |= dlightBits; // determine the allowable discrepance - lodError = LodErrorForVolume( cv->lodOrigin, cv->lodRadius ); + lodError = LodErrorForVolume( srf->lodOrigin, srf->lodRadius ); // determine which rows and columns of the subdivision // we are actually going to use widthTable[0] = 0; lodWidth = 1; - for ( i = 1 ; i < cv->width-1 ; i++ ) { - if ( cv->widthLodError[i] <= lodError ) { + for ( i = 1 ; i < srf->width-1 ; i++ ) { + if ( srf->widthLodError[i] <= lodError ) { widthTable[lodWidth] = i; lodWidth++; } } - widthTable[lodWidth] = cv->width-1; + widthTable[lodWidth] = srf->width-1; lodWidth++; heightTable[0] = 0; lodHeight = 1; - for ( i = 1 ; i < cv->height-1 ; i++ ) { - if ( cv->heightLodError[i] <= lodError ) { + for ( i = 1 ; i < srf->height-1 ; i++ ) { + if ( srf->heightLodError[i] <= lodError ) { heightTable[lodHeight] = i; lodHeight++; } } - heightTable[lodHeight] = cv->height-1; + heightTable[lodHeight] = srf->height-1; lodHeight++; @@ -1081,7 +1378,7 @@ for ( i = 0 ; i < rows ; i++ ) { for ( j = 0 ; j < lodWidth ; j++ ) { - dv = cv->verts + heightTable[ used + i ] * cv->width + dv = srf->verts + heightTable[ used + i ] * srf->width + widthTable[ j ]; xyz[0] = dv->xyz[0]; @@ -1096,7 +1393,7 @@ normal[1] = dv->normal[1]; normal[2] = dv->normal[2]; } - * ( unsigned int * ) color = * ( unsigned int * ) dv->color; + * ( unsigned int * ) color = * ( unsigned int * ) dv->vertexColors; *vDlightBits++ = dlightBits; xyz += 4; normal += 4; @@ -1161,6 +1458,8 @@ =================== */ static void RB_SurfaceAxis( void ) { + // FIXME: implement this +#if 0 GL_Bind( tr.whiteImage ); qglLineWidth( 3 ); qglBegin( GL_LINES ); @@ -1175,6 +1474,7 @@ qglVertex3f( 0,0,16 ); qglEnd(); qglLineWidth( 1 ); +#endif } //=========================================================================== @@ -1220,6 +1520,15 @@ RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); } +static void RB_SurfaceVBOMesh(srfVBOMesh_t * srf) +{ + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numIndexes, srf->firstIndex, qfalse ) ) + { + return; + } +} + + static void RB_SurfaceDisplayList( srfDisplayList_t *surf ) { // all apropriate state must be set in RB_BeginSurface // this isn't implemented yet... @@ -1244,5 +1553,6 @@ #endif (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY - (void(*)(void*))RB_SurfaceDisplayList // SF_DISPLAY_LIST + (void(*)(void*))RB_SurfaceDisplayList, // SF_DISPLAY_LIST + (void(*)(void*))RB_SurfaceVBOMesh // SF_VBO_MESH, }; Index: code/renderer/tr_vbo.c =================================================================== --- code/renderer/tr_vbo.c (revision 0) +++ code/renderer/tr_vbo.c (revision 0) @@ -0,0 +1,758 @@ +/* +=========================================================================== +Copyright (C) 2007-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +XreaL source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_vbo.c +#include "tr_local.h" + +/* +============ +R_CreateVBO +============ +*/ +VBO_t *R_CreateVBO(const char *name, byte * vertexes, int vertexesSize, vboUsage_t usage) +{ + VBO_t *vbo; + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateVBO: \"%s\" is too long\n", name); + } + + if ( tr.numVBOs == MAX_VBOS ) { + ri.Error( ERR_DROP, "R_CreateVBO: MAX_VBOS hit\n"); + } + + // make sure the render thread is stopped + R_SyncRenderThread(); + + vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low); + tr.numVBOs++; + + Q_strncpyz(vbo->name, name, sizeof(vbo->name)); + + vbo->ofs_xyz = 0; + vbo->ofs_normal = 0; + vbo->ofs_st = 0; + vbo->ofs_lightmap = 0; + vbo->ofs_vertexcolor = 0; + vbo->stride_xyz = 0; + vbo->stride_normal = 0; + vbo->stride_st = 0; + vbo->stride_lightmap = 0; + vbo->stride_vertexcolor = 0; + + vbo->vertexesSize = vertexesSize; + + qglGenBuffersARB(1, &vbo->vertexesVBO); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); + qglBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexesSize, vertexes, glUsage); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + + GL_CheckErrors(); + + return vbo; +} + +/* +============ +R_CreateVBO2 +============ +*/ +VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * verts, unsigned int stateBits, vboUsage_t usage) +{ + VBO_t *vbo; + int i; + + byte *data; + int dataSize; + int dataOfs; + + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(!numVertexes) + return NULL; + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateVBO2: \"%s\" is too long\n", name); + } + + if ( tr.numVBOs == MAX_VBOS ) { + ri.Error( ERR_DROP, "R_CreateVBO2: MAX_VBOS hit\n"); + } + + // make sure the render thread is stopped + R_SyncRenderThread(); + + vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low); + tr.numVBOs++; + + Q_strncpyz(vbo->name, name, sizeof(vbo->name)); + + if(usage == VBO_USAGE_STATIC) + { + // since these vertex attributes are never altered, interleave them + vbo->ofs_xyz = 0; + dataSize = sizeof(verts[0].xyz); + + if(stateBits & ATTR_NORMAL) + { + vbo->ofs_normal = dataSize; + dataSize += sizeof(verts[0].normal); + } + + if(stateBits & ATTR_TEXCOORD) + { + vbo->ofs_st = dataSize; + dataSize += sizeof(verts[0].st); + } + + if(stateBits & ATTR_LIGHTCOORD) + { + vbo->ofs_lightmap = dataSize; + dataSize += sizeof(verts[0].lightmap); + } + + if(stateBits & ATTR_COLOR) + { + vbo->ofs_vertexcolor = dataSize; + dataSize += sizeof(verts[0].vertexColors); + } + + vbo->stride_xyz = dataSize; + vbo->stride_normal = dataSize; + vbo->stride_st = dataSize; + vbo->stride_lightmap = dataSize; + vbo->stride_vertexcolor = dataSize; + + // create VBO + dataSize *= numVertexes; + data = ri.Hunk_AllocateTempMemory(dataSize); + dataOfs = 0; + + //ri.Printf(PRINT_ALL, "CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor, + //vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor); + + for (i = 0; i < numVertexes; i++) + { + // xyz + memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz)); + dataOfs += sizeof(verts[i].xyz); + + // normal + if(stateBits & ATTR_NORMAL) + { + memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal)); + dataOfs += sizeof(verts[i].normal); + } + + // vertex texcoords + if(stateBits & ATTR_TEXCOORD) + { + memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st)); + dataOfs += sizeof(verts[i].st); + } + + // feed vertex lightmap texcoords + if(stateBits & ATTR_LIGHTCOORD) + { + memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap)); + dataOfs += sizeof(verts[i].lightmap); + } + + // feed vertex colors + if(stateBits & ATTR_COLOR) + { + memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors)); + dataOfs += sizeof(verts[i].vertexColors); + } + } + } + else + { + // since these vertex attributes may be changed, put them in flat arrays + dataSize = sizeof(verts[0].xyz); + + if(stateBits & ATTR_NORMAL) + { + dataSize += sizeof(verts[0].normal); + } + + if(stateBits & ATTR_TEXCOORD) + { + dataSize += sizeof(verts[0].st); + } + + if(stateBits & ATTR_LIGHTCOORD) + { + dataSize += sizeof(verts[0].lightmap); + } + + if(stateBits & ATTR_COLOR) + { + dataSize += sizeof(verts[0].vertexColors); + } + + // create VBO + dataSize *= numVertexes; + data = ri.Hunk_AllocateTempMemory(dataSize); + dataOfs = 0; + + vbo->ofs_xyz = 0; + vbo->ofs_normal = 0; + vbo->ofs_st = 0; + vbo->ofs_lightmap = 0; + vbo->ofs_vertexcolor = 0; + vbo->stride_xyz = 0; + vbo->stride_normal = 0; + vbo->stride_st = 0; + vbo->stride_lightmap = 0; + vbo->stride_vertexcolor = 0; + + //ri.Printf(PRINT_ALL, "2CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor, + //vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor); + + // xyz + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz)); + dataOfs += sizeof(verts[i].xyz); + } + + // normal + if(stateBits & ATTR_NORMAL) + { + vbo->ofs_normal = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal)); + dataOfs += sizeof(verts[i].normal); + } + } + + // vertex texcoords + if(stateBits & ATTR_TEXCOORD) + { + vbo->ofs_st = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st)); + dataOfs += sizeof(verts[i].st); + } + } + + // feed vertex lightmap texcoords + if(stateBits & ATTR_LIGHTCOORD) + { + vbo->ofs_lightmap = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap)); + dataOfs += sizeof(verts[i].lightmap); + } + } + + // feed vertex colors + if(stateBits & ATTR_COLOR) + { + vbo->ofs_vertexcolor = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors)); + dataOfs += sizeof(verts[i].vertexColors); + } + } + } + + + vbo->vertexesSize = dataSize; + + qglGenBuffersARB(1, &vbo->vertexesVBO); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); + qglBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, data, glUsage); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + + GL_CheckErrors(); + + ri.Hunk_FreeTempMemory(data); + + return vbo; +} + + +/* +============ +R_CreateIBO +============ +*/ +IBO_t *R_CreateIBO(const char *name, byte * indexes, int indexesSize, vboUsage_t usage) +{ + IBO_t *ibo; + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateIBO: \"%s\" is too long\n", name); + } + + if ( tr.numIBOs == MAX_IBOS ) { + ri.Error( ERR_DROP, "R_CreateIBO: MAX_IBOS hit\n"); + } + + // make sure the render thread is stopped + R_SyncRenderThread(); + + ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low); + tr.numIBOs++; + + Q_strncpyz(ibo->name, name, sizeof(ibo->name)); + + ibo->indexesSize = indexesSize; + + qglGenBuffersARB(1, &ibo->indexesVBO); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + + GL_CheckErrors(); + + return ibo; +} + +/* +============ +R_CreateIBO2 +============ +*/ +IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t * triangles, vboUsage_t usage) +{ + IBO_t *ibo; + int i, j; + + byte *indexes; + int indexesSize; + int indexesOfs; + + srfTriangle_t *tri; + glIndex_t index; + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(!numTriangles) + return NULL; + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateIBO2: \"%s\" is too long\n", name); + } + + if ( tr.numIBOs == MAX_IBOS ) { + ri.Error( ERR_DROP, "R_CreateIBO2: MAX_IBOS hit\n"); + } + + // make sure the render thread is stopped + R_SyncRenderThread(); + + ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low); + tr.numIBOs++; + + Q_strncpyz(ibo->name, name, sizeof(ibo->name)); + + indexesSize = numTriangles * 3 * sizeof(int); + indexes = ri.Hunk_AllocateTempMemory(indexesSize); + indexesOfs = 0; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + index = tri->indexes[j]; + memcpy(indexes + indexesOfs, &index, sizeof(glIndex_t)); + indexesOfs += sizeof(glIndex_t); + } + } + + ibo->indexesSize = indexesSize; + + qglGenBuffersARB(1, &ibo->indexesVBO); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + + GL_CheckErrors(); + + ri.Hunk_FreeTempMemory(indexes); + + return ibo; +} + +/* +============ +R_BindVBO +============ +*/ +void R_BindVBO(VBO_t * vbo) +{ + if(!vbo) + { + //R_BindNullVBO(); + ri.Error(ERR_DROP, "R_BindNullVBO: NULL vbo"); + return; + } + + if(r_logFile->integer) + { + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- R_BindVBO( %s ) ---\n", vbo->name)); + } + + if(glState.currentVBO != vbo) + { + glState.currentVBO = vbo; + glState.vertexAttribPointersSet = 0; + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); + + backEnd.pc.c_vboVertexBuffers++; + } +} + +/* +============ +R_BindNullVBO +============ +*/ +void R_BindNullVBO(void) +{ + GLimp_LogComment("--- R_BindNullVBO ---\n"); + + if(glState.currentVBO) + { + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + glState.currentVBO = NULL; + } + + GL_CheckErrors(); +} + +/* +============ +R_BindIBO +============ +*/ +void R_BindIBO(IBO_t * ibo) +{ + if(!ibo) + { + //R_BindNullIBO(); + ri.Error(ERR_DROP, "R_BindIBO: NULL ibo"); + return; + } + + if(r_logFile->integer) + { + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- R_BindIBO( %s ) ---\n", ibo->name)); + } + + if(glState.currentIBO != ibo) + { + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); + + glState.currentIBO = ibo; + + backEnd.pc.c_vboIndexBuffers++; + } +} + +/* +============ +R_BindNullIBO +============ +*/ +void R_BindNullIBO(void) +{ + GLimp_LogComment("--- R_BindNullIBO ---\n"); + + if(glState.currentIBO) + { + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + glState.currentIBO = NULL; + glState.vertexAttribPointersSet = 0; + } +} + +/* +============ +R_InitVBOs +============ +*/ +void R_InitVBOs(void) +{ + int dataSize; + byte *data; + + ri.Printf(PRINT_ALL, "------- R_InitVBOs -------\n"); + + tr.numVBOs = 0; + tr.numIBOs = 0; + + dataSize = sizeof(tess.xyz[0]); + dataSize += sizeof(tess.normal[0]); + dataSize += sizeof(tess.vertexColors[0]); + dataSize += sizeof(tess.texCoords[0][0]) * 2; + dataSize *= SHADER_MAX_VERTEXES; + + data = ri.Malloc(dataSize); + memset(data, 0, dataSize); + + tess.vbo = R_CreateVBO("tessVertexArray_VBO", data, dataSize, VBO_USAGE_DYNAMIC); + + ri.Free(data); + + tess.vbo->ofs_xyz = 0; + tess.vbo->ofs_normal = tess.vbo->ofs_xyz + sizeof(tess.xyz[0]) * SHADER_MAX_VERTEXES; + // these next two are actually interleaved + tess.vbo->ofs_st = tess.vbo->ofs_normal + sizeof(tess.normal[0]) * SHADER_MAX_VERTEXES; + tess.vbo->ofs_lightmap = tess.vbo->ofs_st + sizeof(tess.texCoords[0][0]); + + tess.vbo->ofs_vertexcolor = tess.vbo->ofs_st + sizeof(tess.texCoords[0][0]) * 2 * SHADER_MAX_VERTEXES; + + tess.vbo->stride_xyz = sizeof(tess.xyz[0]); + tess.vbo->stride_normal = sizeof(tess.normal[0]); + tess.vbo->stride_vertexcolor = sizeof(tess.vertexColors[0]);; + tess.vbo->stride_st = sizeof(tess.texCoords[0][0]) * 2; + tess.vbo->stride_lightmap = sizeof(tess.texCoords[0][0]) * 2; + + dataSize = sizeof(tess.indexes[0]) * SHADER_MAX_INDEXES; + + data = ri.Malloc(dataSize); + memset(data, 0, dataSize); + + tess.ibo = R_CreateIBO("tessVertexArray_IBO", data, dataSize, VBO_USAGE_DYNAMIC); + + ri.Free(data); + + R_BindNullVBO(); + R_BindNullIBO(); + + GL_CheckErrors(); +} + +/* +============ +R_ShutdownVBOs +============ +*/ +void R_ShutdownVBOs(void) +{ + int i, j; + VBO_t *vbo; + IBO_t *ibo; + + ri.Printf(PRINT_ALL, "------- R_ShutdownVBOs -------\n"); + + R_BindNullVBO(); + R_BindNullIBO(); + + + for(i = 0; i < tr.numVBOs; i++) + { + vbo = tr.vbos[i]; + + if(vbo->vertexesVBO) + { + qglDeleteBuffersARB(1, &vbo->vertexesVBO); + } + + //ri.Free(vbo); + } + + for(i = 0; i < tr.numIBOs; i++) + { + ibo = tr.ibos[i]; + + if(ibo->indexesVBO) + { + qglDeleteBuffersARB(1, &ibo->indexesVBO); + } + + //ri.Free(ibo); + } + + if(tr.world) + { + for(j = 0; j < MAX_VISCOUNTS; j++) + { + // FIXME: clean up this code + for(i = 0; i < tr.world->numClusterVBOSurfaces[j]; i++) + { + srfVBOMesh_t *vboSurf; + + vboSurf = &tr.world->clusterVBOSurfaces[j][i]; + ibo = vboSurf->ibo; + + if(ibo->indexesVBO) + { + qglDeleteBuffersARB(1, &ibo->indexesVBO); + } + } + + tr.world->numClusterVBOSurfaces[j] = 0; + } + } + + tr.numVBOs = 0; + tr.numIBOs = 0; +} + +/* +============ +R_VBOList_f +============ +*/ +void R_VBOList_f(void) +{ + int i, j; + VBO_t *vbo; + IBO_t *ibo; + int vertexesSize = 0; + int indexesSize = 0; + + ri.Printf(PRINT_ALL, " size name\n"); + ri.Printf(PRINT_ALL, "----------------------------------------------------------\n"); + + for(i = 0; i < tr.numVBOs; i++) + { + vbo = tr.vbos[i]; + + ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", vbo->vertexesSize / (1024 * 1024), + (vbo->vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024), vbo->name); + + vertexesSize += vbo->vertexesSize; + } + + if(tr.world) + { + for(j = 0; j < MAX_VISCOUNTS; j++) + { + // FIXME: clean up this code + for(i = 0; i < tr.world->numClusterVBOSurfaces[j]; i++) + { + srfVBOMesh_t *vboSurf; + + vboSurf = &tr.world->clusterVBOSurfaces[j][i]; + ibo = vboSurf->ibo; + + ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", ibo->indexesSize / (1024 * 1024), + (ibo->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), ibo->name); + + indexesSize += ibo->indexesSize; + } + } + } + + for(i = 0; i < tr.numIBOs; i++) + { + ibo = tr.ibos[i]; + + ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", ibo->indexesSize / (1024 * 1024), + (ibo->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), ibo->name); + + indexesSize += ibo->indexesSize; + } + + ri.Printf(PRINT_ALL, " %i total VBOs\n", tr.numVBOs); + ri.Printf(PRINT_ALL, " %d.%02d MB total vertices memory\n", vertexesSize / (1024 * 1024), + (vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); + + ri.Printf(PRINT_ALL, " %i total IBOs\n", tr.numIBOs); + ri.Printf(PRINT_ALL, " %d.%02d MB total triangle indices memory\n", indexesSize / (1024 * 1024), + (indexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); +} Index: code/renderer/tr_world.c =================================================================== --- code/renderer/tr_world.c (revision 1803) +++ code/renderer/tr_world.c (working copy) @@ -131,6 +131,17 @@ return qfalse; } + // now it must be a SF_FACE + sface = (srfSurfaceFace_t *) surface; + + if(shader->isPortal) + { + if(R_CullLocalBox(sface->bounds) == CULL_OUT) + { + return qtrue; + } + } + if ( shader->cullType == CT_TWO_SIDED ) { return qfalse; } @@ -281,12 +292,32 @@ R_AddWorldSurface ====================== */ -static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) { +static void R_AddWorldSurface( msurface_t *surf, int dlightBits, qboolean skipClusterCheck ) { + shader_t *shader; + if ( surf->viewCount == tr.viewCount ) { return; // already in this view } surf->viewCount = tr.viewCount; + + shader = surf->shader; + +#if 0 + if( r_arb_vertex_buffer_object->integer && r_mergeClusterSurfaces->integer && + /*!r_dynamicBspOcclusionCulling->integer && */ + ((/*r_mergeClusterFaces->integer && */ *surf->data == SF_FACE) || + (/* r_mergeClusterCurves->integer && */ *surf->data == SF_GRID) || + (/* r_mergeClusterTriangles->integer && */ *surf->data == SF_TRIANGLES)) && + !shader->isSky && !shader->isPortal && !ShaderRequiresCPUDeforms(shader) && + !skipClusterCheck) + return; +#else + if( r_arb_vertex_buffer_object->integer && r_mergeClusterSurfaces->integer && + surf->inCluster && !skipClusterCheck ) + return; +#endif + // FIXME: bmodel fog? // try to cull before dlighting or adding @@ -334,7 +365,7 @@ R_DlightBmodel( bmodel ); for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights ); + R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights, qtrue ); } } @@ -359,7 +390,7 @@ int newDlights[2]; // if the node wasn't marked as potentially visible, exit - if (node->visframe != tr.visCount) { + if (node->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex]) { return; } @@ -485,7 +516,7 @@ // the surface may have already been added if it // spans multiple leafs surf = *mark; - R_AddWorldSurface( surf, dlightBits ); + R_AddWorldSurface( surf, dlightBits, qfalse); mark++; } } @@ -557,7 +588,405 @@ } /* +================= +BSPSurfaceCompare +compare function for qsort() +================= +*/ +static int BSPSurfaceCompare(const void *a, const void *b) +{ + msurface_t *aa, *bb; + + aa = *(msurface_t **) a; + bb = *(msurface_t **) b; + + // shader first + if(aa->shader < bb->shader) + return -1; + + else if(aa->shader > bb->shader) + return 1; + + // by lightmap + if(aa->lightmapNum < bb->lightmapNum) + return -1; + + else if(aa->lightmapNum > bb->lightmapNum) + return 1; + + // by fog + if(aa->fogIndex < bb->fogIndex) + return -1; + + else if(aa->fogIndex > bb->fogIndex) + return 1; + + return 0; +} + +/* =============== +R_UpdateClusterSurfaces() +=============== +*/ +static void R_UpdateClusterSurfaces() +{ + int i, k, l; + + int numVerts; + int numTriangles; + +// static glIndex_t indexes[MAX_MAP_DRAW_INDEXES]; +// static byte indexes[MAX_MAP_DRAW_INDEXES * sizeof(glIndex_t)]; + glIndex_t *indexes; + int indexesSize; + + shader_t *shader, *oldShader; + int lightmapNum, oldLightmapNum; + int fogIndex, oldFogIndex; + + int numSurfaces; + int currentNumClusterVBOSurfaces; + msurface_t *surface, *surface2; + msurface_t **surfacesSorted; + + bspCluster_t *cluster; + + srfVBOMesh_t *vboSurf; + IBO_t *ibo; + + vec3_t bounds[2]; + + //int startTime, currentTime; + //int triangleTime = 0, allocTime = 0; + //int totaltris = 0; + + if(tr.visClusters[tr.visIndex] < 0 || tr.visClusters[tr.visIndex] >= tr.world->numClusters) + { + // Tr3B: this is not a bug, the super cluster is the last one in the array + cluster = &tr.world->clusters[tr.world->numClusters]; + } + else + { + cluster = &tr.world->clusters[tr.visClusters[tr.visIndex]]; + } + + //startTime = ri.Milliseconds(); + + currentNumClusterVBOSurfaces = 0; + + // count number of static cluster surfaces + numSurfaces = 0; + for(k = 0; k < cluster->numMarkSurfaces; k++) + { + surface = cluster->markSurfaces[k]; + shader = surface->shader; + + if(shader->isSky) + continue; + + if(shader->isPortal) + continue; + + if(ShaderRequiresCPUDeforms(shader)) + continue; + + numSurfaces++; + } + + if(!numSurfaces) + return; + + // build interaction caches list + // previously used Hunk_AllocateTempMemory, but that memory isn't freed until all files are closed + surfacesSorted = ri.Malloc(numSurfaces * sizeof(surfacesSorted[0])); + + numSurfaces = 0; + for(k = 0; k < cluster->numMarkSurfaces; k++) + { + surface = cluster->markSurfaces[k]; +#if 0 + shader = surface->shader; + + if(shader->isSky) + continue; + + if(shader->isPortal) + continue; + + if(ShaderRequiresCPUDeforms(shader)) + continue; +#endif + surfacesSorted[numSurfaces] = surface; + numSurfaces++; + } + + // sort surfaces by shader + qsort(surfacesSorted, numSurfaces, sizeof(surfacesSorted), BSPSurfaceCompare); + + //currentTime = ri.Milliseconds(); + //ri.Printf(PRINT_ALL, "built & sorted cache: %d\n", currentTime - startTime); + + shader = oldShader = NULL; + lightmapNum = oldLightmapNum = -1; + fogIndex = oldFogIndex = -1; + + for(k = 0; k < numSurfaces; k++) + { + surface = surfacesSorted[k]; + shader = surface->shader; + lightmapNum = surface->lightmapNum; + fogIndex = surface->fogIndex; + + if(shader != oldShader || lightmapNum != oldLightmapNum || fogIndex != oldFogIndex) + { + //currentTime = ri.Milliseconds(); + oldShader = shader; + oldLightmapNum = lightmapNum; + oldFogIndex = fogIndex; + + // count vertices and indices + numVerts = 0; + numTriangles = 0; + + for(l = k; l < numSurfaces; l++) + { + surface2 = surfacesSorted[l]; + + if(surface2->shader != shader || surface2->lightmapNum != lightmapNum || surface2->fogIndex != fogIndex) + break; // was continue, but continue made no sense + + if(*surface2->data == SF_FACE) + { + srfSurfaceFace_t *face = (srfSurfaceFace_t *) surface2->data; + + //if(!r_mergeClusterFaces->integer) + //continue; + + if(face->numVerts) + numVerts += face->numVerts; + + if(face->numTriangles) + numTriangles += face->numTriangles; + } + else if(*surface2->data == SF_GRID) + { + srfGridMesh_t *grid = (srfGridMesh_t *) surface2->data; + + //if(!r_mergeClusterCurves->integer) + //continue; + + if(grid->numVerts) + numVerts += grid->numVerts; + + if(grid->numTriangles) + numTriangles += grid->numTriangles; + } + else if(*surface2->data == SF_TRIANGLES) + { + srfTriangles_t *tri = (srfTriangles_t *) surface2->data; + + //if(!r_mergeClusterTriangles->integer) + //continue; + + if(tri->numVerts) + numVerts += tri->numVerts; + + if(tri->numTriangles) + numTriangles += tri->numTriangles; + } + } + + //totaltris += numTriangles; + + if(!numVerts || !numTriangles) + continue; + + ClearBounds(bounds[0], bounds[1]); + + // build triangle indices + indexesSize = numTriangles * 3 * sizeof(glIndex_t); + // previously used Hunk_AllocateTempMemory, but that memory isn't freed until all files are closed + indexes = ri.Malloc(indexesSize); + + numTriangles = 0; + for(l = k; l < numSurfaces; l++) + { + surface2 = surfacesSorted[l]; + + if(surface2->shader != shader || surface2->lightmapNum != lightmapNum || surface2->fogIndex != fogIndex) + break; // was continue, but continue made no sense + + // set up triangle indices + if(*surface2->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface2->data; + + //if(!r_mergeClusterFaces->integer) + //continue; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = tr.world->triangles + srf->firstIndex / 3; i < srf->numTriangles; i++, tri++) + { + indexes[numTriangles * 3 + i * 3 + 0] = tri->indexes[0]; + indexes[numTriangles * 3 + i * 3 + 1] = tri->indexes[1]; + indexes[numTriangles * 3 + i * 3 + 2] = tri->indexes[2]; + } + + numTriangles += srf->numTriangles; + //BoundsAdd(bounds[0], bounds[1], srf->bounds[0], srf->bounds[1]); + AddPointToBounds(srf->bounds[0], bounds[0], bounds[1]); + AddPointToBounds(srf->bounds[1], bounds[0], bounds[1]); + } + } + else if(*surface2->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface2->data; + + //if(!r_mergeClusterCurves->integer) + //continue; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = tr.world->triangles + srf->firstIndex / 3; i < srf->numTriangles; i++, tri++) + { + indexes[numTriangles * 3 + i * 3 + 0] = tri->indexes[0]; + indexes[numTriangles * 3 + i * 3 + 1] = tri->indexes[1]; + indexes[numTriangles * 3 + i * 3 + 2] = tri->indexes[2]; + } + + numTriangles += srf->numTriangles; + //BoundsAdd(bounds[0], bounds[1], srf->meshBounds[0], srf->meshBounds[1]); + AddPointToBounds(srf->meshBounds[0], bounds[0], bounds[1]); + AddPointToBounds(srf->meshBounds[1], bounds[0], bounds[1]); + } + } + else if(*surface2->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface2->data; + + //if(!r_mergeClusterTriangles->integer) + //continue; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + for(i = 0, tri = tr.world->triangles + srf->firstIndex / 3; i < srf->numTriangles; i++, tri++) + { + indexes[numTriangles * 3 + i * 3 + 0] = tri->indexes[0]; + indexes[numTriangles * 3 + i * 3 + 1] = tri->indexes[1]; + indexes[numTriangles * 3 + i * 3 + 2] = tri->indexes[2]; + } + + numTriangles += srf->numTriangles; + //BoundsAdd(bounds[0], bounds[1], srf->bounds[0], srf->bounds[1]); + AddPointToBounds(srf->bounds[0], bounds[0], bounds[1]); + AddPointToBounds(srf->bounds[1], bounds[0], bounds[1]); + } + } + } + + //triangleTime += ri.Milliseconds() - currentTime; + //currentTime = ri.Milliseconds(); + + if(currentNumClusterVBOSurfaces < tr.world->numClusterVBOSurfaces[tr.visIndex]) + { + vboSurf = &tr.world->clusterVBOSurfaces[tr.visIndex][currentNumClusterVBOSurfaces]; + + ibo = vboSurf->ibo; + + /* + if(ibo->indexesVBO) + { + qglDeleteBuffersARB(1, &ibo->indexesVBO); + ibo->indexesVBO = 0; + } + */ + + //Com_Dealloc(ibo); + //Com_Dealloc(vboSurf); + } + else + { + if (tr.world->numClusterVBOSurfaces[tr.visIndex] == MAX_CLUSTER_VBO_SURFACES) + { + ri.Error( ERR_DROP, "R_UpdateClusterSurfaces: MAX_CLUSTER_VBO_SURFACES hit\n"); + } + vboSurf = &tr.world->clusterVBOSurfaces[tr.visIndex][tr.world->numClusterVBOSurfaces[tr.visIndex]]; + tr.world->numClusterVBOSurfaces[tr.visIndex]++; + + vboSurf->surfaceType = SF_VBO_MESH; + + vboSurf->vbo = tr.world->vbo; + vboSurf->ibo = ibo = ri.Hunk_Alloc(sizeof(*ibo), h_low); + + qglGenBuffersARB(1, &ibo->indexesVBO); + } + + //ri.Printf(PRINT_ALL, "creating VBO cluster surface for shader '%s'\n", shader->name); + + // update surface properties + vboSurf->numIndexes = numTriangles * 3; + vboSurf->numVerts = numVerts; + vboSurf->firstIndex = 0; + + vboSurf->shader = shader; + vboSurf->lightmapNum = lightmapNum; + vboSurf->fogIndex = fogIndex; + + VectorCopy(bounds[0], vboSurf->bounds[0]); + VectorCopy(bounds[1], vboSurf->bounds[1]); + + // make sure the render thread is stopped + R_SyncRenderThread(); + + // update IBO + Q_strncpyz(ibo->name, + va("staticWorldMesh_IBO_visIndex%i_surface%i", 0, tr.world->numClusterVBOSurfaces[tr.visIndex] - 1), + sizeof(ibo->name)); + ibo->indexesSize = indexesSize; + + R_BindIBO(ibo); + + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, GL_DYNAMIC_DRAW_ARB); + + R_BindNullIBO(); + + //GL_CheckErrors(); + + // previously used Hunk_FreeTempMemory, but that memory isn't freed until all files are closed + ri.Free(indexes); + + currentNumClusterVBOSurfaces++; + //allocTime += ri.Milliseconds() - currentTime; + } + } + + //currentTime = ri.Milliseconds(); + //ri.Printf(PRINT_ALL, "tri time: %d, total tris: %d, total time: %d\n", triangleTime, totaltris, currentTime - startTime); + + // previously used Hunk_FreeTempMemory, but that memory isn't freed until all files are closed + ri.Free(surfacesSorted); + + if(r_showcluster->modified || r_showcluster->integer) + { + r_showcluster->modified = qfalse; + if(r_showcluster->integer) + { + ri.Printf(PRINT_ALL, " surfaces:%i\n", tr.world->numClusterVBOSurfaces[tr.visIndex]); + } + } + +} + +/* +=============== R_MarkLeaves Mark the leaves and nodes that are in the PVS for the current @@ -583,12 +1012,30 @@ // if the cluster is the same and the area visibility matrix // hasn't changed, we don't need to mark everything again + for(i = 0; i < MAX_VISCOUNTS; i++) + { + if(tr.visClusters[i] == cluster) + { + //tr.visIndex = i; + break; + } + } + // if r_showcluster was just turned on, remark everything - if ( tr.viewCluster == cluster && !tr.refdef.areamaskModified - && !r_showcluster->modified ) { + if(i != MAX_VISCOUNTS && !tr.refdef.areamaskModified && !r_showcluster->modified)// && !r_dynamicBspOcclusionCulling->modified) + { + if(tr.visClusters[i] != tr.visClusters[tr.visIndex] && r_showcluster->integer) + { + ri.Printf(PRINT_ALL, "found cluster:%i area:%i index:%i\n", cluster, leaf->area, i); + } + tr.visIndex = i; return; } + tr.visIndex = (tr.visIndex + 1) % MAX_VISCOUNTS; + tr.visCounts[tr.visIndex]++; + tr.visClusters[tr.visIndex] = cluster; + if ( r_showcluster->modified || r_showcluster->integer ) { r_showcluster->modified = qfalse; if ( r_showcluster->integer ) { @@ -596,19 +1043,21 @@ } } - tr.visCount++; - tr.viewCluster = cluster; + if (r_mergeClusterSurfaces->integer && r_arb_vertex_buffer_object->integer) + { + R_UpdateClusterSurfaces(); + } - if ( r_novis->integer || tr.viewCluster == -1 ) { + if (r_novis->integer || tr.visClusters[tr.visIndex] == -1) { for (i=0 ; inumnodes ; i++) { if (tr.world->nodes[i].contents != CONTENTS_SOLID) { - tr.world->nodes[i].visframe = tr.visCount; + tr.world->nodes[i].visCounts[tr.visIndex] = tr.visCounts[tr.visIndex]; } } return; } - vis = R_ClusterPVS (tr.viewCluster); + vis = R_ClusterPVS(tr.visClusters[tr.visIndex]); for (i=0,leaf=tr.world->nodes ; inumnodes ; i++, leaf++) { cluster = leaf->cluster; @@ -628,9 +1077,9 @@ parent = leaf; do { - if (parent->visframe == tr.visCount) + if(parent->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex]) break; - parent->visframe = tr.visCount; + parent->visCounts[tr.visIndex] = tr.visCounts[tr.visIndex]; parent = parent->parent; } while (parent); } @@ -664,5 +1113,40 @@ if ( tr.refdef.num_dlights > 32 ) { tr.refdef.num_dlights = 32 ; } + R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 ); + + //if(r_mergeClusterSurfaces->integer && !r_dynamicBspOcclusionCulling->integer) + if (r_mergeClusterSurfaces->integer && r_arb_vertex_buffer_object->integer) + { + int j, i; + srfVBOMesh_t *srf; + cplane_t *frust; + int r; + + for(j = 0; j < tr.world->numClusterVBOSurfaces[tr.visIndex]; j++) + { + srf = &tr.world->clusterVBOSurfaces[tr.visIndex][j]; + + for(i = 0; i < 4 /* FRUSTUM_PLANES */; i++) + { + frust = &tr.viewParms.frustum[i]; + + r = BoxOnPlaneSide(srf->bounds[0], srf->bounds[1], frust); + + if(r == 2) + { + // completely outside frustum + continue; + } + } + + // try to cull before dlighting or adding + if ( R_CullSurface( (void *)srf, srf->shader ) ) { + continue; + } + + R_AddDrawSurf( (void *)srf, srf->shader, srf->fogIndex, 0 /*dlightBits*/ ); + } + } } Index: code/sdl/sdl_glimp.c =================================================================== --- code/sdl/sdl_glimp.c (revision 1803) +++ code/sdl/sdl_glimp.c (working copy) @@ -50,6 +50,13 @@ typedef CGLContextObj QGLContext; #define GLimp_GetCurrentContext() CGLGetCurrentContext() #define GLimp_SetCurrentContext(ctx) CGLSetCurrentContext(ctx) +#elif defined(_WIN32) +typedef struct +{ + HDC hDC; // handle to device context + HGLRC hGLRC; // handle to GL rendering context +} QGLContext_t; +typedef QGLContext_t QGLContext; #else typedef void *QGLContext; #define GLimp_GetCurrentContext() (NULL) @@ -58,6 +65,50 @@ static QGLContext opengl_context; +#ifdef _WIN32 +#include "SDL_syswm.h" +static QGLContext GLimp_GetCurrentContext(void) +{ + SDL_SysWMinfo info; + + QGLContext newcontext; + + SDL_VERSION(&info.version); + if(!SDL_GetWMInfo(&info)) + { + ri.Printf(PRINT_WARNING, "Failed to obtain HWND from SDL (InputRegistry)"); + newcontext.hDC = 0; + newcontext.hGLRC = 0; + return newcontext; + } + + newcontext.hDC = GetDC(info.window); + newcontext.hGLRC = info.hglrc; + + return newcontext; +} + +#ifdef SMP +static void GLimp_SetCurrentContext(qboolean enable) +{ + if(enable) + wglMakeCurrent(opengl_context.hDC, opengl_context.hGLRC); + else + wglMakeCurrent(opengl_context.hDC, NULL); +} +#endif +#else +static void GLimp_GetCurrentContext(void) +{ +} + +#ifdef SMP +static void GLimp_SetCurrentContext(qboolean enable) +{ +} +#endif +#endif + typedef enum { RSERR_OK, @@ -83,6 +134,92 @@ void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); void (APIENTRYP qglUnlockArraysEXT) (void); +// GL_EXT_multi_draw_arrays +void (APIENTRY * qglMultiDrawArraysEXT) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +void (APIENTRY * qglMultiDrawElementsEXT) (GLenum mode, GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount); + +// GL_ARB_vertex_shader +void (APIENTRY * qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB * name); +void (APIENTRY * qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +GLint(APIENTRY * qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB * name); + +// GL_ARB_vertex_program +void (APIENTRY * qglVertexAttrib4fARB) (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +void (APIENTRY * qglVertexAttrib4fvARB) (GLuint, const GLfloat *); +void (APIENTRY * qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); +void (APIENTRY * qglEnableVertexAttribArrayARB) (GLuint index); +void (APIENTRY * qglDisableVertexAttribArrayARB) (GLuint index); + +// GL_ARB_vertex_buffer_object +void (APIENTRY * qglBindBufferARB) (GLenum target, GLuint buffer); +void (APIENTRY * qglDeleteBuffersARB) (GLsizei n, const GLuint * buffers); +void (APIENTRY * qglGenBuffersARB) (GLsizei n, GLuint * buffers); + +GLboolean(APIENTRY * qglIsBufferARB) (GLuint buffer); +void (APIENTRY * qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage); +void (APIENTRY * qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data); +void (APIENTRY * qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data); + +void (APIENTRY * qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint * params); +void (APIENTRY * qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid * *params); + +// GL_ARB_shader_objects +void (APIENTRY * qglDeleteObjectARB) (GLhandleARB obj); + +GLhandleARB(APIENTRY * qglGetHandleARB) (GLenum pname); +void (APIENTRY * qglDetachObjectARB) (GLhandleARB containerObj, GLhandleARB attachedObj); + +GLhandleARB(APIENTRY * qglCreateShaderObjectARB) (GLenum shaderType); +void (APIENTRY * qglShaderSourceARB) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, + const GLint * length); +void (APIENTRY * qglCompileShaderARB) (GLhandleARB shaderObj); + +GLhandleARB(APIENTRY * qglCreateProgramObjectARB) (void); +void (APIENTRY * qglAttachObjectARB) (GLhandleARB containerObj, GLhandleARB obj); +void (APIENTRY * qglLinkProgramARB) (GLhandleARB programObj); +void (APIENTRY * qglUseProgramObjectARB) (GLhandleARB programObj); +void (APIENTRY * qglValidateProgramARB) (GLhandleARB programObj); +void (APIENTRY * qglUniform1fARB) (GLint location, GLfloat v0); +void (APIENTRY * qglUniform2fARB) (GLint location, GLfloat v0, GLfloat v1); +void (APIENTRY * qglUniform3fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +void (APIENTRY * qglUniform4fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +void (APIENTRY * qglUniform1iARB) (GLint location, GLint v0); +void (APIENTRY * qglUniform2iARB) (GLint location, GLint v0, GLint v1); +void (APIENTRY * qglUniform3iARB) (GLint location, GLint v0, GLint v1, GLint v2); +void (APIENTRY * qglUniform4iARB) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +void (APIENTRY * qglUniform2fvARB) (GLint location, GLsizei count, const GLfloat * value); +void (APIENTRY * qglUniform3fvARB) (GLint location, GLsizei count, const GLfloat * value); +void (APIENTRY * qglUniform4fvARB) (GLint location, GLsizei count, const GLfloat * value); +void (APIENTRY * qglUniform2ivARB) (GLint location, GLsizei count, const GLint * value); +void (APIENTRY * qglUniform3ivARB) (GLint location, GLsizei count, const GLint * value); +void (APIENTRY * qglUniform4ivARB) (GLint location, GLsizei count, const GLint * value); +void (APIENTRY * qglUniformMatrix2fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +void (APIENTRY * qglUniformMatrix3fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +void (APIENTRY * qglUniformMatrix4fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +void (APIENTRY * qglGetObjectParameterfvARB) (GLhandleARB obj, GLenum pname, GLfloat * params); +void (APIENTRY * qglGetObjectParameterivARB) (GLhandleARB obj, GLenum pname, GLint * params); +void (APIENTRY * qglGetInfoLogARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); +void (APIENTRY * qglGetAttachedObjectsARB) (GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, + GLhandleARB * obj); +GLint(APIENTRY * qglGetUniformLocationARB) (GLhandleARB programObj, const GLcharARB * name); +void (APIENTRY * qglGetActiveUniformARB) (GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +void (APIENTRY * qglGetUniformfvARB) (GLhandleARB programObj, GLint location, GLfloat * params); +void (APIENTRY * qglGetUniformivARB) (GLhandleARB programObj, GLint location, GLint * params); +void (APIENTRY * qglGetShaderSourceARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); + +#if defined(WIN32) +// WGL_ARB_create_context +HGLRC(APIENTRY * qwglCreateContextAttribsARB) (HDC hdC, HGLRC hShareContext, const int *attribList); +#endif + +#if 0 //defined(__linux__) +// GLX_ARB_create_context +GLXContext (APIENTRY * qglXCreateContextAttribsARB) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +#endif + /* =============== GLimp_Shutdown @@ -106,6 +243,7 @@ */ void GLimp_LogComment( char *comment ) { + //ri.Printf(PRINT_ALL, comment); } /* @@ -413,6 +551,74 @@ break; } + // try to initialize an OpenGL 3.2 context +#if 0 //defined(WIN32) + qwglCreateContextAttribsARB = SDL_GL_GetProcAddress("wglCreateContextAttribsARB"); + if(qwglCreateContextAttribsARB) + { + int attribs[] = + { + WGL_CONTEXT_MAJOR_VERSION_ARB, + 3, + WGL_CONTEXT_MINOR_VERSION_ARB, + 1, + WGL_CONTEXT_FLAGS_ARB, + 0, //WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0 + }; + + ri.Printf(PRINT_ALL, "Initializing OpenGL 3.1 context..."); + + opengl_context.hGLRC = qwglCreateContextAttribsARB(opengl_context.hDC, opengl_context.hGLRC, attribs); + if(wglMakeCurrent(opengl_context.hDC, opengl_context.hGLRC)) + { + ri.Printf(PRINT_ALL, " done\n"); + } + else + { + ri.Printf(PRINT_ALL, " failed\n"); + } + } +#elif 0 //defined(__linux__) + + // TODO + + /* +// GLX_ARB_create_context +#ifndef GLX_ARB_create_context +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 + +extern GLXContext (APIENTRY * qglXCreateContextAttribsARB) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +*/ + + qglXCreateContextAttribsARB = SDL_GL_GetProcAddress("glXCreateContextAttribsARB"); + if(qglXCreateContextAttribsARB) + { + int attribs[3]; + + ri.Printf(PRINT_ALL, "Initializing OpenGL 3.0 context..."); + + attribs[0] = WGL_CONTEXT_MAJOR_VERSION_ARB; + attribs[1] = 3; + attribs[2] = 0; //terminate first pair + + opengl_context->hGLRC = qglXCreateContextAttribsARB(opengl_context->, attribs); + if(wglMakeCurrent(opengl_context->hDC, opengl_context->hGLRC)) + { + ri.Printf(PRINT_ALL, " done\n"); + glConfig.driverType = GLDRV_OPENGL3; + } + else + { + ri.Printf(PRINT_ALL, " failed\n"); + } + } +#endif + GLimp_DetectAvailableModes(); if (!vidscreen) @@ -654,6 +860,193 @@ { ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); } + + // GL_EXT_multi_draw_arrays + qglMultiDrawArraysEXT = NULL; + qglMultiDrawElementsEXT = NULL; + if(Q_stristr(glConfig.extensions_string, "GL_EXT_multi_draw_arrays")) + { + qglMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawArraysEXT"); + qglMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawElementsEXT"); + ri.Printf(PRINT_ALL, "...using GL_EXT_multi_draw_arrays\n"); + } + else + { + ri.Error(ERR_FATAL, "...GL_EXT_multi_draw_arrays not found\n"); + } + + // GL_ARB_vertex_program + qglVertexAttrib4fARB = NULL; + qglVertexAttrib4fvARB = NULL; + qglVertexAttribPointerARB = NULL; + qglEnableVertexAttribArrayARB = NULL; + qglDisableVertexAttribArrayARB = NULL; + if(Q_stristr(glConfig.extensions_string, "GL_ARB_vertex_program")) + { + qglVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fARB"); + qglVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fvARB"); + qglVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) SDL_GL_GetProcAddress("glVertexAttribPointerARB"); + qglEnableVertexAttribArrayARB = + (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glEnableVertexAttribArrayARB"); + qglDisableVertexAttribArrayARB = + (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glDisableVertexAttribArrayARB"); + ri.Printf(PRINT_ALL, "...using GL_ARB_vertex_program\n"); + } + else + { + ri.Error(ERR_FATAL, "...GL_ARB_vertex_program not found\n"); + } + + // GL_ARB_vertex_buffer_object + qglBindBufferARB = NULL; + qglDeleteBuffersARB = NULL; + qglGenBuffersARB = NULL; + qglIsBufferARB = NULL; + qglBufferDataARB = NULL; + qglBufferSubDataARB = NULL; + qglGetBufferSubDataARB = NULL; + qglGetBufferParameterivARB = NULL; + qglGetBufferPointervARB = NULL; + if(Q_stristr(glConfig.extensions_string, "GL_ARB_vertex_buffer_object")) + { + qglBindBufferARB = (PFNGLBINDBUFFERARBPROC) SDL_GL_GetProcAddress("glBindBufferARB"); + qglDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB"); + qglGenBuffersARB = (PFNGLGENBUFFERSARBPROC) SDL_GL_GetProcAddress("glGenBuffersARB"); + qglIsBufferARB = (PFNGLISBUFFERARBPROC) SDL_GL_GetProcAddress("glIsBufferARB"); + qglBufferDataARB = (PFNGLBUFFERDATAARBPROC) SDL_GL_GetProcAddress("glBufferDataARB"); + qglBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glBufferSubDataARB"); + qglGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glGetBufferSubDataARB"); + qglGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetBufferParameterivARB"); + qglGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC) SDL_GL_GetProcAddress("glGetBufferPointervARB"); + ri.Printf(PRINT_ALL, "...using GL_ARB_vertex_buffer_object\n"); + } + else + { + ri.Error(ERR_FATAL, "...GL_ARB_vertex_buffer_object not found\n"); + } + + // GL_ARB_shader_objects + qglDeleteObjectARB = NULL; + qglGetHandleARB = NULL; + qglDetachObjectARB = NULL; + qglCreateShaderObjectARB = NULL; + qglShaderSourceARB = NULL; + qglCompileShaderARB = NULL; + qglCreateProgramObjectARB = NULL; + qglAttachObjectARB = NULL; + qglLinkProgramARB = NULL; + qglUseProgramObjectARB = NULL; + qglValidateProgramARB = NULL; + qglUniform1fARB = NULL; + qglUniform2fARB = NULL; + qglUniform3fARB = NULL; + qglUniform4fARB = NULL; + qglUniform1iARB = NULL; + qglUniform2iARB = NULL; + qglUniform3iARB = NULL; + qglUniform4iARB = NULL; + qglUniform2fvARB = NULL; + qglUniform3fvARB = NULL; + qglUniform4fvARB = NULL; + qglUniform2ivARB = NULL; + qglUniform3ivARB = NULL; + qglUniform4ivARB = NULL; + qglUniformMatrix2fvARB = NULL; + qglUniformMatrix3fvARB = NULL; + qglUniformMatrix4fvARB = NULL; + qglGetObjectParameterfvARB = NULL; + qglGetObjectParameterivARB = NULL; + qglGetInfoLogARB = NULL; + qglGetAttachedObjectsARB = NULL; + qglGetUniformLocationARB = NULL; + qglGetActiveUniformARB = NULL; + qglGetUniformfvARB = NULL; + qglGetUniformivARB = NULL; + qglGetShaderSourceARB = NULL; + if(Q_stristr(glConfig.extensions_string, "GL_ARB_shader_objects")) + { + qglDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB"); + qglGetHandleARB = (PFNGLGETHANDLEARBPROC) SDL_GL_GetProcAddress("glGetHandleARB"); + qglDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) SDL_GL_GetProcAddress("glDetachObjectARB"); + qglCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB"); + qglShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB"); + qglCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB"); + qglCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB"); + qglAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB"); + qglLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB"); + qglUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB"); + qglValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) SDL_GL_GetProcAddress("glValidateProgramARB"); + qglUniform1fARB = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB"); + qglUniform2fARB = (PFNGLUNIFORM2FARBPROC) SDL_GL_GetProcAddress("glUniform2fARB"); + qglUniform3fARB = (PFNGLUNIFORM3FARBPROC) SDL_GL_GetProcAddress("glUniform3fARB"); + qglUniform4fARB = (PFNGLUNIFORM4FARBPROC) SDL_GL_GetProcAddress("glUniform4fARB"); + qglUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB"); + qglUniform2iARB = (PFNGLUNIFORM2IARBPROC) SDL_GL_GetProcAddress("glUniform2iARB"); + qglUniform3iARB = (PFNGLUNIFORM3IARBPROC) SDL_GL_GetProcAddress("glUniform3iARB"); + qglUniform4iARB = (PFNGLUNIFORM4IARBPROC) SDL_GL_GetProcAddress("glUniform4iARB"); + qglUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) SDL_GL_GetProcAddress("glUniform2fvARB"); + qglUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) SDL_GL_GetProcAddress("glUniform3fvARB"); + qglUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) SDL_GL_GetProcAddress("glUniform4fvARB"); + qglUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) SDL_GL_GetProcAddress("glUniform2ivARB"); + qglUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) SDL_GL_GetProcAddress("glUniform3ivARB"); + qglUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) SDL_GL_GetProcAddress("glUniform4ivARB"); + qglUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix2fvARB"); + qglUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix3fvARB"); + qglUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix4fvARB"); + qglGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterfvARB"); + qglGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB"); + qglGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB"); + qglGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) SDL_GL_GetProcAddress("glGetAttachedObjectsARB"); + qglGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB"); + qglGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) SDL_GL_GetProcAddress("glGetActiveUniformARB"); + qglGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) SDL_GL_GetProcAddress("glGetUniformfvARB"); + qglGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) SDL_GL_GetProcAddress("glGetUniformivARB"); + qglGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glGetShaderSourceARB"); + ri.Printf(PRINT_ALL, "...using GL_ARB_shader_objects\n"); + } + else + { + ri.Error(ERR_FATAL, "...GL_ARB_shader_objects not found\n"); + } + + // GL_ARB_vertex_shader + qglBindAttribLocationARB = NULL; + qglGetActiveAttribARB = NULL; + qglGetAttribLocationARB = NULL; + if(Q_stristr(glConfig.extensions_string, "GL_ARB_vertex_shader")) + { + int reservedComponents; + + //qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &glConfig.maxVertexUniforms); + //qglGetIntegerv(GL_MAX_VARYING_FLOATS_ARB, &glConfig.maxVaryingFloats); + //qglGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &glConfig.maxVertexAttribs); + + reservedComponents = 16 * 10; // approximation how many uniforms we have besides the bone matrices + +#if 0 + if(glConfig.driverType == GLDRV_MESA) + { + // HACK + // restrict to number of vertex uniforms to 512 because of: + // xreal.x86_64: nv50_program.c:4181: nv50_program_validate_data: Assertion `p->param_nr <= 512' failed + + glConfig.maxVertexUniforms = Q_bound(0, glConfig.maxVertexUniforms, 512); + } +#endif + + //glConfig.maxVertexSkinningBones = (int) Q_bound(0.0, (Q_max(glConfig.maxVertexUniforms - reservedComponents, 0) / 16), MAX_BONES); + //glConfig.vboVertexSkinningAvailable = r_vboVertexSkinning->integer && ((glConfig.maxVertexSkinningBones >= 12) ? qtrue : qfalse); + + qglBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glBindAttribLocationARB"); + qglGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) SDL_GL_GetProcAddress("glGetActiveAttribARB"); + qglGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetAttribLocationARB"); + ri.Printf(PRINT_ALL, "...using GL_ARB_vertex_shader\n"); + } + else + { + ri.Error(ERR_FATAL, "...GL_ARB_vertex_shader not found\n"); + } + } #define R_MODE_FALLBACK 3 // 640 * 480 @@ -726,8 +1119,15 @@ if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); - Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); + { + char *string = (char *) qglGetString (GL_EXTENSIONS); + if (string) + Q_strncpyz( glConfig.extensions_string, string, sizeof( glConfig.extensions_string ) ); + else + glConfig.extensions_string[0] = '\0'; + } + // initialize extensions GLimp_InitExtensions( ); Index: misc/msvc/quake3.vcproj =================================================================== --- misc/msvc/quake3.vcproj (revision 1803) +++ misc/msvc/quake3.vcproj (working copy) @@ -2441,6 +2441,10 @@ > + + @@ -2929,6 +2933,10 @@ > + + @@ -2937,6 +2945,10 @@ > + + @@ -3017,6 +3029,10 @@ > + +