Index: code/qcommon/q_math.c =================================================================== --- code/qcommon/q_math.c (revision 2095) +++ code/qcommon/q_math.c (working copy) @@ -652,7 +652,7 @@ int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, struct cplane_s *p) { float dist[2]; - int sides, b, i; + int sides; //, b, i; // fast axial cases if (p->type < 3) @@ -665,6 +665,7 @@ } // general case +#if 0 dist[0] = dist[1] = 0; if (p->signbits < 8) // >= 8: default case is original code (dist[0]=dist[1]=0) { @@ -675,6 +676,47 @@ dist[!b] += p->normal[i]*emins[i]; } } +#else + // general case + switch (p->signbits) + { + case 0: + dist[0] = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + dist[1] = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + break; + case 1: + dist[0] = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + dist[1] = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + break; + case 2: + dist[0] = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + dist[1] = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + break; + case 3: + dist[0] = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + dist[1] = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + break; + case 4: + dist[0] = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + dist[1] = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + break; + case 5: + dist[0] = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + dist[1] = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + break; + case 6: + dist[0] = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + dist[1] = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + break; + case 7: + dist[0] = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + dist[1] = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + break; + default: + dist[0] = dist[1] = 0; // shut up compiler + break; + } +#endif sides = 0; if (dist[0] >= p->dist) Index: code/qcommon/qfiles.h =================================================================== --- code/qcommon/qfiles.h (revision 2095) +++ code/qcommon/qfiles.h (working copy) @@ -35,8 +35,9 @@ #endif // surface geometry should not exceed these limits -#define SHADER_MAX_VERTEXES 1000 +#define SHADER_MAX_VERTEXES 4225 #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 2095) +++ code/renderer/qgl.h (working copy) @@ -39,7 +39,143 @@ 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, const 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 * qglUniform1fvARB) (GLint location, GLsizei count, const GLfloat * value); +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); + +// GL_ARB_texture_compression +extern void (APIENTRY * qglCompressedTexImage3DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexImage2DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexImage1DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, + GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexSubImage3DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, + GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglGetCompressedTexImageARB)(GLenum target, GLint lod, + GLvoid *img); + +// GL_NVX_gpu_memory_info +#ifndef GL_NVX_gpu_memory_info +#define GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047 +#define GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048 +#define GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049 +#define GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A +#define GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B +#endif + +// GL_ATI_meminfo +#ifndef GL_ATI_meminfo +#define VBO_FREE_MEMORY_ATI 0x87FB +#define TEXTURE_FREE_MEMORY_ATI 0x87FC +#define RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif + + +#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_animation.c =================================================================== --- code/renderer/tr_animation.c (revision 2095) +++ code/renderer/tr_animation.c (working copy) @@ -51,7 +51,7 @@ surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces ); for ( i = 0 ; i < lod->numSurfaces ; i++ ) { shader = R_GetShaderByHandle( surface->shaderIndex ); - R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse ); + R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse, qfalse ); surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd ); } } @@ -420,7 +420,7 @@ && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse ); + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse, qfalse ); } // projection shadows work fine with personal models @@ -429,11 +429,11 @@ && (ent->e.renderfx & RF_SHADOW_PLANE ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse ); + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse, qfalse ); } if (!personalModel) - R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse ); + R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse, qfalse ); surface = (mdrSurface_t *)( (byte *)surface + surface->ofsEnd ); } Index: code/renderer/tr_backend.c =================================================================== --- code/renderer/tr_backend.c (revision 2095) +++ code/renderer/tr_backend.c (working copy) @@ -60,6 +60,30 @@ } /* +** GL_BindCubemap +*/ +void GL_BindCubemap( image_t *image ) { + int texnum; + + if ( !image ) { + ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" ); + texnum = tr.defaultImage->texnum; + } else { + texnum = image->texnum; + } + + if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option + texnum = tr.dlightImage->texnum; + } + + if ( glState.currenttextures[glState.currenttmu] != texnum ) { + image->frameUsed = tr.frameCount; + glState.currenttextures[glState.currenttmu] = texnum; + qglBindTexture (GL_TEXTURE_CUBE_MAP, texnum); + } +} + +/* ** GL_SelectTexture */ void GL_SelectTexture( int unit ) @@ -69,22 +93,36 @@ return; } +#if 0 if ( unit == 0 ) { qglActiveTextureARB( GL_TEXTURE0_ARB ); GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE0_ARB )\n" ); - qglClientActiveTextureARB( GL_TEXTURE0_ARB ); - GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE0_ARB )\n" ); + if (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && glRefConfig.glsl && r_arb_shader_objects->integer)) + { + qglClientActiveTextureARB( GL_TEXTURE0_ARB ); + GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE0_ARB )\n" ); + } } else if ( unit == 1 ) { qglActiveTextureARB( GL_TEXTURE1_ARB ); GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE1_ARB )\n" ); - qglClientActiveTextureARB( GL_TEXTURE1_ARB ); - GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE1_ARB )\n" ); + if (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && glRefConfig.glsl && r_arb_shader_objects->integer)) + { + qglClientActiveTextureARB( GL_TEXTURE1_ARB ); + GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE1_ARB )\n" ); + } } else { ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit ); } +#else + qglActiveTextureARB( GL_TEXTURE0_ARB + unit ); + if (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && glRefConfig.glsl && r_arb_shader_objects->integer)) + { + qglClientActiveTextureARB( GL_TEXTURE0_ARB + unit ); + } +#endif glState.currenttmu = unit; } @@ -117,7 +155,26 @@ } } +/* +** GL_BindToTMU +*/ +void GL_BindToTMU( image_t *image, int tmu ) +{ + int texnum; + int oldtmu = glState.currenttmu; + texnum = image->texnum; + + if ( glState.currenttextures[tmu] != texnum ) { + GL_SelectTexture( tmu ); + image->frameUsed = tr.frameCount; + glState.currenttextures[tmu] = texnum; + qglBindTexture( GL_TEXTURE_2D, texnum ); + GL_SelectTexture( oldtmu ); + } +} + + /* ** GL_Cull */ @@ -138,7 +195,7 @@ if ( cullType == CT_BACK_SIDED ) { - if ( backEnd.viewParms.isMirror ) + if ( backEnd.viewParms.isMirror && !backEnd.viewParms.isShadowmap) { qglCullFace( GL_FRONT ); } @@ -149,7 +206,7 @@ } else { - if ( backEnd.viewParms.isMirror ) + if ( backEnd.viewParms.isMirror && !backEnd.viewParms.isShadowmap) { qglCullFace( GL_BACK ); } @@ -386,7 +443,34 @@ } +void GL_SetProjectionMatrix(matrix_t matrix) +{ + Matrix16Copy(matrix, glState.projection); + Matrix16Multiply(glState.projection, glState.modelview, glState.modelviewProjection); + if (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && r_arb_shader_objects->integer)) + { + qglMatrixMode(GL_PROJECTION); + qglLoadMatrixf(matrix); + qglMatrixMode(GL_MODELVIEW); + } +} + + +void GL_SetModelviewMatrix(matrix_t matrix) +{ + Matrix16Copy(matrix, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, glState.modelviewProjection); + + if (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && r_arb_shader_objects->integer)) + { + qglLoadMatrixf(matrix); + } +} + + /* ================ RB_Hyperspace @@ -410,9 +494,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, @@ -468,6 +550,14 @@ qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky #endif } + + // clear to white for shadow maps + if (backEnd.viewParms.isShadowmap) + { + clearBits |= GL_COLOR_BUFFER_BIT; + qglClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); + } + qglClear( clearBits ); if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) @@ -500,17 +590,27 @@ 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 (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && r_arb_shader_objects->integer)) + { + qglClipPlane (GL_CLIP_PLANE0, plane2); + qglEnable (GL_CLIP_PLANE0); + } } else { - qglDisable (GL_CLIP_PLANE0); + if (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && r_arb_shader_objects->integer)) + { + qglDisable (GL_CLIP_PLANE0); + } } } #define MAC_EVENT_PUMP_MSEC 5 + /* ================== RB_RenderDrawSurfList @@ -521,6 +621,7 @@ int fogNum, oldFogNum; int entityNum, oldEntityNum; int dlighted, oldDlighted; + int pshadowed, oldPshadowed; qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair; int i; drawSurf_t *drawSurf; @@ -541,6 +642,7 @@ oldDepthRange = qfalse; wasCrosshair = qfalse; oldDlighted = qfalse; + oldPshadowed = qfalse; oldSort = -1; depthRange = qfalse; @@ -553,21 +655,23 @@ continue; } oldSort = drawSurf->sort; - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); // // change the tess parameters if needed // a "entityMergable" shader is a shader that can have surfaces from seperate // entities merged into a single batch, like smoke and blood puff sprites - if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted + if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted || pshadowed != oldPshadowed || ( entityNum != oldEntityNum && !shader->entityMergable ) ) { if (oldShader != NULL) { RB_EndSurface(); } RB_BeginSurface( shader, fogNum ); + backEnd.pc.c_surfBatches++; oldShader = shader; oldFogNum = fogNum; oldDlighted = dlighted; + oldPshadowed = pshadowed; } // @@ -609,7 +713,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,20 +730,16 @@ 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 { viewParms_t temp = backEnd.viewParms; - R_SetupProjection(&temp, r_znear->value, qfalse); + R_SetupProjection(&temp, r_znear->value, 0, qfalse); - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf(temp.projectionMatrix); - qglMatrixMode(GL_MODELVIEW); + GL_SetProjectionMatrix( temp.projectionMatrix ); } } @@ -650,9 +750,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 +775,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 +807,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 ); @@ -793,18 +894,107 @@ RB_SetGL2D(); - qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); + if (glRefConfig.vertexBufferObject && 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 (glRefConfig.glsl && r_arb_shader_objects->integer) + { + shaderProgram_t *sp = &tr.textureColorShader; + vec4_t color; + + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + VectorSet4(color, 1, 1, 1, 1); + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, 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_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 +1176,7 @@ int i; image_t *image; float x, y, w, h; + vec4_t quadVerts[4]; int start, end; if ( !backEnd.projection2D ) { @@ -1012,17 +1203,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 (glRefConfig.vertexBufferObject && 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(); @@ -1123,6 +1328,34 @@ } /* +============= +RB_CapShadowMap + +============= +*/ +const void *RB_CapShadowMap(const void *data) +{ + const capShadowmapCommand_t *cmd = data; + + if (cmd->map != -1) + { + GL_SelectTexture(0); + if (cmd->cubeSide != -1) + { + GL_BindCubemap(tr.shadowCubemaps[cmd->map]); + qglCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cmd->cubeSide, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + 256 ), 256, 256, 0); + } + else + { + GL_Bind(tr.pshadowMaps[cmd->map]); + qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + 256 ), 256, 256, 0); + } + } + + return (const void *)(cmd + 1); +} + +/* ==================== RB_ExecuteRenderCommands @@ -1172,6 +1405,9 @@ case RC_CLEARDEPTH: data = RB_ClearDepth(data); break; + case RC_CAPSHADOWMAP: + data = RB_CapShadowMap(data); + break; case RC_END_OF_LIST: default: // stop rendering on this thread @@ -1209,3 +1445,5 @@ } } + + Index: code/renderer/tr_bsp.c =================================================================== --- code/renderer/tr_bsp.c (revision 2095) +++ code/renderer/tr_bsp.c (working copy) @@ -125,8 +125,45 @@ out[3] = in[3]; } +#if 0 /* =============== +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]; +} +#endif + +/* +=============== R_LoadLightmaps =============== @@ -136,10 +173,16 @@ byte *buf, *buf_p; int len; byte image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4]; - int i, j; + int i, j, numLightmaps; float maxIntensity = 0; double sumIntensity = 0; + if (r_mergeLightmaps->integer) + { + tr.fatLightmapSize = 4096; + tr.fatLightmapStep = 32; + } + len = l->filelen; if ( !len ) { return; @@ -150,60 +193,249 @@ R_SyncRenderThread(); // create all the lightmaps - tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3); - if ( tr.numLightmaps == 1 ) { + numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3); + + if (tr.worldDeluxeMapping) + { + numLightmaps >>= 1; + } + + if(numLightmaps == 1) + { //FIXME: HACK: maps with only one lightmap turn up fullbright for some reason. //this avoids this, but isn't the correct solution. - tr.numLightmaps++; + numLightmaps++; } + else if (r_mergeLightmaps->integer && numLightmaps >= tr.fatLightmapStep * tr.fatLightmapStep ) + { + // FIXME: fat light maps don't support more than 1024 light maps + ri.Printf(PRINT_WARNING, "WARNING: number of lightmaps > %d\n", tr.fatLightmapStep * tr.fatLightmapStep); + numLightmaps = tr.fatLightmapStep * tr.fatLightmapStep; + } - // if we are in r_vertexLight mode, we don't need the lightmaps at all - if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { - return; + // use a fat lightmap of an appropriate size + + if (r_mergeLightmaps->integer) + { + if(numLightmaps < 65) + { + tr.fatLightmapSize = 1024; + tr.fatLightmapStep = 8; + } + else if(numLightmaps < 256) + { + tr.fatLightmapSize = 2048; + tr.fatLightmapStep = 16; + } + + // allocate one fat lightmap + tr.numLightmaps = 1; } + else + { + tr.numLightmaps = numLightmaps; + } tr.lightmaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); - for ( i = 0 ; i < tr.numLightmaps ; i++ ) { + + if (tr.worldDeluxeMapping || r_deluxeMapping->integer == 2) + { + tr.deluxemaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); + } + + if (r_mergeLightmaps->integer) + { + tr.fatLightmap = R_CreateImage(va("_fatlightmap%d", 0), NULL, tr.fatLightmapSize, tr.fatLightmapSize, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + tr.lightmaps[0] = tr.fatLightmap; + + if (tr.worldDeluxeMapping || r_deluxeMapping->integer == 2) + { + tr.fatDeluxemap = R_CreateImage(va("_fatdeluxemap%d", 0), NULL, tr.fatLightmapSize, tr.fatLightmapSize, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + tr.deluxemaps[0] = tr.fatDeluxemap; + } + } + + for(i = 0; i < numLightmaps; i++) + { + int xoff = 0, yoff = 0; // expand the 24 bit on-disk to 32 bit - buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3; - if ( r_lightmap->integer == 2 ) - { // color code by intensity as development tool (FIXME: check range) - for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) - { - float r = buf_p[j*3+0]; - float g = buf_p[j*3+1]; - float b = buf_p[j*3+2]; - float intensity; - float out[3] = {0.0, 0.0, 0.0}; + if (tr.worldDeluxeMapping) + { + buf_p = buf + (i * 2) * LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3; + } + else + { + buf_p = buf + i * LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3; + } - intensity = 0.33f * r + 0.685f * g + 0.063f * b; + if (r_mergeLightmaps->integer) + { + xoff = (i % tr.fatLightmapStep) * LIGHTMAP_SIZE; + yoff = (i / tr.fatLightmapStep) * LIGHTMAP_SIZE; + } - if ( intensity > 255 ) - intensity = 1.0f; - else - intensity /= 255.0f; + // if (tr.worldLightmapping) + { + if ( r_lightmap->integer == 2 ) + { // color code by intensity as development tool (FIXME: check range) + for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) + { + float r = buf_p[j*3+0]; + float g = buf_p[j*3+1]; + float b = buf_p[j*3+2]; + float intensity; + float out[3] = {0.0, 0.0, 0.0}; - if ( intensity > maxIntensity ) - maxIntensity = intensity; + intensity = 0.33f * r + 0.685f * g + 0.063f * b; - HSVtoRGB( intensity, 1.00, 0.50, out ); + if ( intensity > 255 ) + intensity = 1.0f; + else + intensity /= 255.0f; - image[j*4+0] = out[0] * 255; - image[j*4+1] = out[1] * 255; - image[j*4+2] = out[2] * 255; + if ( intensity > maxIntensity ) + maxIntensity = intensity; + + HSVtoRGB( intensity, 1.00, 0.50, out ); + + image[j*4+0] = out[0] * 255; + image[j*4+1] = out[1] * 255; + image[j*4+2] = out[2] * 255; + image[j*4+3] = 255; + + sumIntensity += intensity; + } + } else { + for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) { + R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); + image[j*4+3] = 255; + } + } + + if (r_mergeLightmaps->integer) + { + R_UpdateSubImage(tr.fatLightmap, image, xoff, yoff, LIGHTMAP_SIZE, LIGHTMAP_SIZE); + } + else + { + tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + } + } + + if (tr.worldDeluxeMapping) + { + buf_p = buf + (i * 2 + 1) * LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3; + + for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) { + image[j*4+0] = buf_p[j*3+0]; + image[j*4+1] = buf_p[j*3+1]; + image[j*4+2] = buf_p[j*3+2]; image[j*4+3] = 255; + } - sumIntensity += intensity; + if (r_mergeLightmaps->integer) + { + R_UpdateSubImage(tr.fatDeluxemap, image, xoff, yoff, LIGHTMAP_SIZE, LIGHTMAP_SIZE); } - } else { + else + { + tr.deluxemaps[i] = R_CreateImage( va("*deluxemap%d",i), image, LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + } + } + else if ( r_deluxeMapping->integer == 2 ) + { + // approximate a deluxe map using magic. for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) { - R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); + int x, y, dx, dy, dz, count, value; + + x = j % LIGHTMAP_SIZE; + y = j / LIGHTMAP_SIZE; + + value = buf_p[j*3+0] + buf_p[j*3+1] + buf_p[j*3+2]; + + dx = 0; + count = 0; + if (x > 0) + { + int k = j - 1; + + dx += value - buf_p[k*3+0] + buf_p[k*3+1] + buf_p[k*3+2]; + count++; + } + + if (x < LIGHTMAP_SIZE - 1) + { + int k = j + 1; + + dx += buf_p[k*3+0] + buf_p[k*3+1] + buf_p[k*3+2] - value; + count++; + } + + if (count) + { + dx /= count; + } + + dy = 0; + count = 0; + if (y > 0) + { + int k = j - LIGHTMAP_SIZE; + + dy += value - (buf_p[k*3+0] + buf_p[k*3+1] + buf_p[k*3+2]); + count++; + } + + if (y < LIGHTMAP_SIZE - 1) + { + int k = j + LIGHTMAP_SIZE; + + dy += buf_p[k*3+0] + buf_p[k*3+1] + buf_p[k*3+2] - value; + count++; + } + + if (count) + { + dy /= count; + } + + dx /= 3; + dy /= 3; + + if (dx < -255) + dx = -255; + + if (dx > 255) + dx = 255; + + if (dy < -255) + dy = -255; + + if (dy > 255) + dy = 255; + + dz = sqrt(255*255 - dx*dx - dy*dy); + + dx = dx / 2 + 127; + dy = dy / 2 + 127; + dz = dz / 2 + 127; + + image[j*4+0] = dx; + image[j*4+1] = dy; + image[j*4+2] = dz; image[j*4+3] = 255; } + + if (r_mergeLightmaps->integer) + { + R_UpdateSubImage(tr.fatDeluxemap, image, xoff, yoff, LIGHTMAP_SIZE, LIGHTMAP_SIZE); + } + else + { + tr.deluxemaps[i] = R_CreateImage( va("*deluxemap%d",i), image, LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + } } - tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, - LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); } if ( r_lightmap->integer == 2 ) { @@ -212,6 +444,47 @@ } +static float FatPackU(float input, int lightmapnum) +{ + if(tr.fatLightmapSize > 0) + { + int x = (lightmapnum >> (tr.worldDeluxeMapping ? 1 : 0)) % tr.fatLightmapStep; + + return (input / ((float)tr.fatLightmapStep)) + ((1.0 / ((float)tr.fatLightmapStep)) * (float)x); + } + + return input; +} + +static float FatPackV(float input, int lightmapnum) +{ + if(tr.fatLightmapSize > 0) + { + int y = (lightmapnum >> (tr.worldDeluxeMapping ? 1 : 0)) / tr.fatLightmapStep; + + return (input / ((float)tr.fatLightmapStep)) + ((1.0 / ((float)tr.fatLightmapStep)) * (float)y); + } + + return input; +} + + +static int FatLightmap(int lightmapnum) +{ + if (lightmapnum < 0) + return lightmapnum; + + if (tr.fatLightmapSize > 0) + { + return 0; + } + + if (tr.worldDeluxeMapping) + return lightmapnum >> 1; + + return lightmapnum; +} + /* ================= RE_SetWorldVisData @@ -304,67 +577,151 @@ 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; + int realLightmapNum; - lightmapNum = LittleLong( ds->lightmapNum ); + realLightmapNum = 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, FatLightmap(realLightmapNum) ); 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 = (void *)surf->data; 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] ); + cv->verts[i].lightmap[0] = FatPackU(LittleFloat(verts[i].lightmap[0]), realLightmapNum); + cv->verts[i].lightmap[1] = FatPackV(LittleFloat(verts[i].lightmap[1]), realLightmapNum); + + 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 ); surf->data = (surfaceType_t *)cv; + + // Tr3B - calc tangent spaces +#if 0 + { + float *v; + const float *v0, *v1, *v2; + const float *t0, *t1, *t2; + vec3_t tangent; + vec3_t bitangent; + vec3_t normal; + + for(i = 0; i < numVerts; i++) + { + VectorClear(cv->verts[i].tangent); + VectorClear(cv->verts[i].bitangent); + VectorClear(cv->verts[i].normal); + } + + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + v0 = cv->verts[tri->indexes[0]].xyz; + v1 = cv->verts[tri->indexes[1]].xyz; + v2 = cv->verts[tri->indexes[2]].xyz; + + t0 = cv->verts[tri->indexes[0]].st; + t1 = cv->verts[tri->indexes[1]].st; + t2 = cv->verts[tri->indexes[2]].st; + + R_CalcTangentSpace(tangent, bitangent, normal, v0, v1, v2, t0, t1, t2); + + for(j = 0; j < 3; j++) + { + v = cv->verts[tri->indexes[j]].tangent; + VectorAdd(v, tangent, v); + v = cv->verts[tri->indexes[j]].bitangent; + VectorAdd(v, bitangent, v); + v = cv->verts[tri->indexes[j]].normal; + VectorAdd(v, normal, v); + } + } + + for(i = 0; i < numVerts; i++) + { + VectorNormalize(cv->verts[i].tangent); + VectorNormalize(cv->verts[i].bitangent); + VectorNormalize(cv->verts[i].normal); + } + } +#else + { + srfVert_t *dv[3]; + + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + dv[0] = &cv->verts[tri->indexes[0]]; + dv[1] = &cv->verts[tri->indexes[1]]; + dv[2] = &cv->verts[tri->indexes[2]]; + + R_CalcTangentVectors(dv); + } + } +#endif } @@ -377,19 +734,19 @@ 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; + int realLightmapNum; - lightmapNum = LittleLong( ds->lightmapNum ); + realLightmapNum = 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, FatLightmap(realLightmapNum) ); if ( r_singleShader->integer && !surf->shader->isSky ) { surf->shader = tr.defaultShader; } @@ -404,18 +761,28 @@ 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 ); + points[i].lightmap[0] = FatPackU(LittleFloat(verts[i].lightmap[0]), realLightmapNum); + points[i].lightmap[1] = FatPackV(LittleFloat(verts[i].lightmap[1]), realLightmapNum); + + R_ColorShiftLightingBytes( verts[i].color, points[i].vertexColors ); } // pre-tesseleate @@ -441,9 +808,10 @@ =============== */ 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, badTriangles; // get fog volume surf->fogIndex = LittleLong( ds->fogNum ) + 1; @@ -454,44 +822,149 @@ 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 = (void *)surf->data; + 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 + badTriangles = 0; + 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"); + } } + + if ((tri->indexes[0] == tri->indexes[1]) || (tri->indexes[1] == tri->indexes[2]) || (tri->indexes[0] == tri->indexes[2])) + { + tri--; + badTriangles++; + } } + + if (badTriangles) + { + ri.Printf(PRINT_WARNING, "Surface has bad triangles, originally shader %s %d tris %d verts, now %d tris\n", surf->shader->name, numTriangles, numVerts, numTriangles - badTriangles); + cv->numTriangles -= badTriangles; + } + + // Tr3B - calc tangent spaces +#if 0 + { + float *v; + const float *v0, *v1, *v2; + const float *t0, *t1, *t2; + vec3_t tangent; + vec3_t bitangent; + vec3_t normal; + + for(i = 0; i < numVerts; i++) + { + VectorClear(cv->verts[i].tangent); + VectorClear(cv->verts[i].bitangent); + VectorClear(cv->verts[i].normal); + } + + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + v0 = cv->verts[tri->indexes[0]].xyz; + v1 = cv->verts[tri->indexes[1]].xyz; + v2 = cv->verts[tri->indexes[2]].xyz; + + t0 = cv->verts[tri->indexes[0]].st; + t1 = cv->verts[tri->indexes[1]].st; + t2 = cv->verts[tri->indexes[2]].st; + +#if 1 + R_CalcTangentSpace(tangent, bitangent, normal, v0, v1, v2, t0, t1, t2); +#else + R_CalcNormalForTriangle(normal, v0, v1, v2); + R_CalcTangentsForTriangle2(tangent, bitangent, v0, v1, v2, t0, t1, t2); +#endif + + for(j = 0; j < 3; j++) + { + v = cv->verts[tri->indexes[j]].tangent; + VectorAdd(v, tangent, v); + v = cv->verts[tri->indexes[j]].bitangent; + VectorAdd(v, bitangent, v); + v = cv->verts[tri->indexes[j]].normal; + VectorAdd(v, normal, v); + } + } + + for(i = 0; i < numVerts; i++) + { + float dot; + + //VectorNormalize(cv->verts[i].tangent); + VectorNormalize(cv->verts[i].bitangent); + VectorNormalize(cv->verts[i].normal); + + // Gram-Schmidt orthogonalize + dot = DotProduct(cv->verts[i].normal, cv->verts[i].tangent); + VectorMA(cv->verts[i].tangent, -dot, cv->verts[i].normal, cv->verts[i].tangent); + VectorNormalize(cv->verts[i].tangent); + + //dot = DotProduct(cv->verts[i].normal, cv->verts[i].tangent); + //VectorMA(cv->verts[i].tangent, -dot, cv->verts[i].normal, cv->verts[i].tangent); + //VectorNormalize(cv->verts[i].tangent); + } + } +#else + { + srfVert_t *dv[3]; + + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + dv[0] = &cv->verts[tri->indexes[0]]; + dv[1] = &cv->verts[tri->indexes[1]]; + dv[2] = &cv->verts[tri->indexes[2]]; + + R_CalcTangentVectors(dv); + } + } +#endif } /* @@ -512,7 +985,8 @@ surf->shader = tr.defaultShader; } - flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); + //flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); + flare = (void *)surf->data; flare->surfaceType = SF_FLARE; surf->data = (surfaceType_t *)flare; @@ -1204,8 +1678,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 ); @@ -1214,14 +1688,378 @@ 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->sortedIndex < bb->shader->sortedIndex) + return -1; + + else if(aa->shader->sortedIndex > bb->shader->sortedIndex) + return 1; + + // by fogIndex + if(aa->fogIndex < bb->fogIndex) + return -1; + + else if(aa->fogIndex > bb->fogIndex) + 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->bitangent[j] = in->bitangent[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_CreateWorldVBO +=============== +*/ +static void R_CreateWorldVBO(void) +{ + int i, j, k; + + int numVerts; + srfVert_t *verts; + +// srfVert_t *optimizedVerts; + + int numTriangles; + srfTriangle_t *triangles; + + int numSurfaces; + msurface_t *surface; + msurface_t **surfacesSorted; + +// trRefLight_t *light; + + int startTime, endTime; + + startTime = ri.Milliseconds(); + + numVerts = 0; + numTriangles = 0; + numSurfaces = 0; + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces /* 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; + + numSurfaces++; + } + 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; + + numSurfaces++; + } + 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; + + numSurfaces++; + } + } + + if(!numVerts || !numTriangles) + return; + + ri.Printf(PRINT_ALL, "...calculating world VBO ( %i verts %i tris )\n", numVerts, numTriangles); + + // create arrays + + verts = ri.Hunk_AllocateTempMemory(numVerts * sizeof(srfVert_t)); + //optimizedVerts = ri.Hunk_AllocateTempMemory(numVerts * sizeof(srfVert_t)); + + triangles = ri.Hunk_AllocateTempMemory(numTriangles * sizeof(srfTriangle_t)); + + // presort surfaces + surfacesSorted = ri.Malloc(numSurfaces * sizeof(*surfacesSorted)); + + j = 0; + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces /* s_worldData.numWorldSurfaces */; k++, surface++) + { + if(*surface->data == SF_FACE || *surface->data == SF_GRID || *surface->data == SF_TRIANGLES) + { + surfacesSorted[j++] = surface; + } + } + + qsort(surfacesSorted, numSurfaces, sizeof(*surfacesSorted), BSPSurfaceCompare); + + // set up triangle indices + numVerts = 0; + numTriangles = 0; + for(k = 0, surface = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) + { + 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 = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) + { + 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; + } + } + } + + s_worldData.vbo = R_CreateVBO2(va("staticBspModel0_VBO %i", 0), numVerts, verts, + ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BITANGENT | + ATTR_NORMAL | ATTR_COLOR, VBO_USAGE_STATIC); + + 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 = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + if( srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + } + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + if( srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + } + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + if( srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + } + } + } + + + startTime = ri.Milliseconds(); + + ri.Free(surfacesSorted); + + ri.Hunk_FreeTempMemory(triangles); + //ri.Hunk_FreeTempMemory(optimizedVerts); + ri.Hunk_FreeTempMemory(verts); +} + + +/* +=============== R_LoadSurfaces =============== */ @@ -1256,23 +2094,80 @@ s_worldData.surfaces = out; s_worldData.numsurfaces = count; + s_worldData.surfacesViewCount = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesViewCount), h_low ); + s_worldData.surfacesDlightBits = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesDlightBits), h_low ); + s_worldData.surfacesPshadowBits = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesPshadowBits), h_low ); + //s_worldData.numWorldSurfaces = count; + // Two passes, allocate surfaces first, then load them full of data + // This ensures surfaces are close together to reduce L2 cache misses when using VBOs, + // which don't actually use the verts and tris + in = (void *)(fileBase + surfs->fileofs); + out = s_worldData.surfaces; for ( i = 0 ; i < count ; i++, in++, out++ ) { switch ( LittleLong( in->surfaceType ) ) { + case MST_PATCH: + // FIXME: do this + break; + case MST_TRIANGLE_SOUP: + out->data = ri.Hunk_Alloc( sizeof(srfTriangles_t), h_low); + break; + case MST_PLANAR: + out->data = ri.Hunk_Alloc( sizeof(srfSurfaceFace_t), h_low); + break; + case MST_FLARE: + out->data = ri.Hunk_Alloc( sizeof(srfFlare_t), h_low); + break; + default: + break; + } + } + + in = (void *)(fileBase + surfs->fileofs); + out = s_worldData.surfaces; + for ( i = 0 ; i < count ; i++, in++, out++ ) { + switch ( LittleLong( in->surfaceType ) ) { case MST_PATCH: ParseMesh ( in, dv, out ); + { + srfGridMesh_t *surface = (srfGridMesh_t *)out->data; + + out->cullinfo.type = CULLINFO_BOX | CULLINFO_SPHERE; + VectorCopy(surface->meshBounds[0], out->cullinfo.bounds[0]); + VectorCopy(surface->meshBounds[1], out->cullinfo.bounds[1]); + VectorCopy(surface->localOrigin, out->cullinfo.localOrigin); + out->cullinfo.radius = surface->meshRadius; + } numMeshes++; break; case MST_TRIANGLE_SOUP: ParseTriSurf( in, dv, out, indexes ); + { + srfTriangles_t *surface = (srfTriangles_t *)out->data; + + out->cullinfo.type = CULLINFO_BOX; + VectorCopy(surface->bounds[0], out->cullinfo.bounds[0]); + VectorCopy(surface->bounds[1], out->cullinfo.bounds[1]); + } numTriSurfs++; break; case MST_PLANAR: ParseFace( in, dv, out, indexes ); + { + srfSurfaceFace_t *surface = (srfSurfaceFace_t *)out->data; + + out->cullinfo.type = CULLINFO_PLANE | CULLINFO_BOX; + VectorCopy(surface->bounds[0], out->cullinfo.bounds[0]); + VectorCopy(surface->bounds[1], out->cullinfo.bounds[1]); + out->cullinfo.plane = surface->plane; + } numFaces++; break; case MST_FLARE: ParseFlare( in, dv, out, indexes ); + { + out->cullinfo.type = CULLINFO_NONE; + } numFlares++; break; default: @@ -1311,6 +2206,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 ; ibounds[1][j] = LittleFloat (in->maxs[j]); } - out->firstSurface = s_worldData.surfaces + LittleLong( in->firstSurface ); + out->firstSurface = LittleLong( in->firstSurface ); out->numSurfaces = LittleLong( in->numSurfaces ); + + if(i == 0) + { + // Tr3B: add this for limiting VBO surface creation + s_worldData.numWorldSurfaces = out->numSurfaces; + } } } @@ -1422,8 +2324,7 @@ s_worldData.numClusters = out->cluster + 1; } - out->firstmarksurface = s_worldData.marksurfaces + - LittleLong(inLeaf->firstLeafSurface); + out->firstmarksurface = LittleLong(inLeaf->firstLeafSurface); out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); } @@ -1469,7 +2370,7 @@ { int i, j, count; int *in; - msurface_t **out; + int *out; in = (void *)(fileBase + l->fileofs); if (l->filelen % sizeof(*in)) @@ -1483,7 +2384,7 @@ for ( i=0 ; ilightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] ); continue; } + + // check for deluxe mapping support + if(!Q_stricmp(keyname, "deluxeMapping") && !Q_stricmp(value, "1")) + { + ri.Printf(PRINT_ALL, "map features directional light mapping\n"); + tr.worldDeluxeMapping = qtrue; + continue; + } + + // check for deluxe mapping provided by NetRadiant's q3map2 + //FIXME: xmap2? + if(!Q_stricmp(keyname, "_q3map2_cmdline")) + { + s = strstr(value, "-deluxe"); + if(s) + { + ri.Printf(PRINT_ALL, "map features directional light mapping\n"); + tr.worldDeluxeMapping = qtrue; + } + continue; + } } } @@ -1783,8 +2705,373 @@ } } + /* ================= +R_MergeLeafSurfaces + +Merges surfaces that share a common leaf +================= +*/ +void R_MergeLeafSurfaces(void) +{ + int i, j, k; + int numWorldSurfaces; + int mergedSurfIndex; + int numMergedSurfaces; + int numUnmergedSurfaces; + IBO_t *ibo; + + msurface_t *mergedSurf; + + glIndex_t *iboIndexes, *outIboIndexes; + int numIboIndexes; + + int startTime, endTime; + + startTime = ri.Milliseconds(); + + numWorldSurfaces = s_worldData.numWorldSurfaces; + + // use viewcount to keep track of mergers + for (i = 0; i < numWorldSurfaces; i++) + { + s_worldData.surfacesViewCount[i] = -1; + } + + // create ibo + ibo = tr.ibos[tr.numIBOs++] = ri.Hunk_Alloc(sizeof(*ibo), h_low); + memset(ibo, 0, sizeof(*ibo)); + Q_strncpyz(ibo->name, "staticWorldMesh_IBO_mergedSurfs", sizeof(ibo->name)); + + // allocate more than we need + iboIndexes = outIboIndexes = ri.Malloc(s_worldData.ibo->indexesSize); + + // mark matching surfaces + for (i = 0; i < s_worldData.numnodes - s_worldData.numDecisionNodes; i++) + { + mnode_t *leaf = s_worldData.nodes + s_worldData.numDecisionNodes + i; + + for (j = 0; j < leaf->nummarksurfaces; j++) + { + msurface_t *surf1; + shader_t *shader1; + int fogIndex1; + int surfNum1; + + surfNum1 = *(s_worldData.marksurfaces + leaf->firstmarksurface + j); + + if (s_worldData.surfacesViewCount[surfNum1] != -1) + continue; + + surf1 = s_worldData.surfaces + surfNum1; + + if ((*surf1->data != SF_GRID) && (*surf1->data != SF_TRIANGLES) && (*surf1->data != SF_FACE)) + continue; + + shader1 = surf1->shader; + + if(shader1->isSky) + continue; + + if(shader1->isPortal) + continue; + + if(ShaderRequiresCPUDeforms(shader1)) + continue; + + fogIndex1 = surf1->fogIndex; + + s_worldData.surfacesViewCount[surfNum1] = surfNum1; + + for (k = j + 1; k < leaf->nummarksurfaces; k++) + { + msurface_t *surf2; + shader_t *shader2; + int fogIndex2; + int surfNum2; + + surfNum2 = *(s_worldData.marksurfaces + leaf->firstmarksurface + k); + + if (s_worldData.surfacesViewCount[surfNum2] != -1) + continue; + + surf2 = s_worldData.surfaces + surfNum2; + + if ((*surf2->data != SF_GRID) && (*surf2->data != SF_TRIANGLES) && (*surf2->data != SF_FACE)) + continue; + + shader2 = surf2->shader; + + if (shader1 != shader2) + continue; + + fogIndex2 = surf2->fogIndex; + + if (fogIndex1 != fogIndex2) + continue; + + s_worldData.surfacesViewCount[surfNum2] = surfNum1; + } + } + } + + // count merged/unmerged surfaces + numMergedSurfaces = 0; + numUnmergedSurfaces = 0; + for (i = 0; i < numWorldSurfaces; i++) + { + if (s_worldData.surfacesViewCount[i] == i) + { + numMergedSurfaces++; + } + else if (s_worldData.surfacesViewCount[i] == -1) + { + numUnmergedSurfaces++; + } + } + + // Allocate merged surfaces + s_worldData.mergedSurfaces = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfaces) * numMergedSurfaces, h_low); + s_worldData.mergedSurfacesViewCount = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesViewCount) * numMergedSurfaces, h_low); + s_worldData.mergedSurfacesDlightBits = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesDlightBits) * numMergedSurfaces, h_low); + s_worldData.numMergedSurfaces = numMergedSurfaces; + + // view surfaces are like mark surfaces, except negative ones represent merged surfaces + // -1 represents 0, -2 represents 1, and so on + s_worldData.viewSurfaces = ri.Hunk_Alloc(sizeof(*s_worldData.viewSurfaces) * s_worldData.nummarksurfaces, h_low); + + // copy view surfaces into mark surfaces + for (i = 0; i < s_worldData.nummarksurfaces; i++) + { + s_worldData.viewSurfaces[i] = s_worldData.marksurfaces[i]; + } + + // actually merge surfaces + numIboIndexes = 0; + mergedSurfIndex = 0; + mergedSurf = s_worldData.mergedSurfaces; + for (i = 0; i < numWorldSurfaces; i++) + { + msurface_t *surf1; + + vec3_t bounds[2]; + + int numSurfsToMerge; + int numTriangles; + int numVerts; + int firstIndex; + + srfVBOMesh_t *vboSurf; + + if (s_worldData.surfacesViewCount[i] != i) + continue; + + surf1 = s_worldData.surfaces + i; + + // count verts, indexes, and surfaces + numSurfsToMerge = 0; + numTriangles = 0; + numVerts = 0; + for (j = i; j < numWorldSurfaces; j++) + { + msurface_t *surf2; + + if (s_worldData.surfacesViewCount[j] != i) + continue; + + surf2 = s_worldData.surfaces + j; + + switch(*surf2->data) + { + case SF_FACE: + { + srfSurfaceFace_t *face; + + face = (srfSurfaceFace_t *) surf2->data; + numTriangles += face->numTriangles; + numVerts += face->numVerts; + } + break; + + case SF_GRID: + { + srfGridMesh_t *grid; + + grid = (srfGridMesh_t *) surf2->data; + numTriangles += grid->numTriangles; + numVerts += grid->numVerts; + } + break; + + case SF_TRIANGLES: + { + srfTriangles_t *tris; + + tris = (srfTriangles_t *) surf2->data; + numTriangles += tris->numTriangles; + numVerts += tris->numVerts; + } + break; + + default: + break; + } + + numSurfsToMerge++; + } + + if (numVerts == 0 || numTriangles == 0 || numSurfsToMerge < 2) + { + continue; + } + + // Merge surfaces (indexes) and calculate bounds + ClearBounds(bounds[0], bounds[1]); + firstIndex = numIboIndexes; + for (j = i; j < numWorldSurfaces; j++) + { + msurface_t *surf2; + + if (s_worldData.surfacesViewCount[j] != i) + continue; + + surf2 = s_worldData.surfaces + j; + + AddPointToBounds(surf2->cullinfo.bounds[0], bounds[0], bounds[1]); + AddPointToBounds(surf2->cullinfo.bounds[1], bounds[0], bounds[1]); + + switch(*surf2->data) + { + case SF_FACE: + { + srfSurfaceFace_t *face; + + face = (srfSurfaceFace_t *) surf2->data; + + for (k = 0; k < face->numTriangles; k++) + { + *outIboIndexes++ = face->triangles[k].indexes[0] + face->firstVert; + *outIboIndexes++ = face->triangles[k].indexes[1] + face->firstVert; + *outIboIndexes++ = face->triangles[k].indexes[2] + face->firstVert; + numIboIndexes += 3; + } + } + break; + + case SF_GRID: + { + srfGridMesh_t *grid; + + grid = (srfGridMesh_t *) surf2->data; + + for (k = 0; k < grid->numTriangles; k++) + { + *outIboIndexes++ = grid->triangles[k].indexes[0] + grid->firstVert; + *outIboIndexes++ = grid->triangles[k].indexes[1] + grid->firstVert; + *outIboIndexes++ = grid->triangles[k].indexes[2] + grid->firstVert; + numIboIndexes += 3; + } + } + break; + + case SF_TRIANGLES: + { + srfTriangles_t *tris; + + tris = (srfTriangles_t *) surf2->data; + + for (k = 0; k < tris->numTriangles; k++) + { + *outIboIndexes++ = tris->triangles[k].indexes[0] + tris->firstVert; + *outIboIndexes++ = tris->triangles[k].indexes[1] + tris->firstVert; + *outIboIndexes++ = tris->triangles[k].indexes[2] + tris->firstVert; + numIboIndexes += 3; + } + } + break; + + // never happens, but silences a compile warning + default: + break; + } + } + + vboSurf = ri.Hunk_Alloc(sizeof(*vboSurf), h_low); + memset(vboSurf, 0, sizeof(*vboSurf)); + vboSurf->surfaceType = SF_VBO_MESH; + + vboSurf->vbo = s_worldData.vbo; + vboSurf->ibo = ibo; + + vboSurf->numIndexes = numTriangles * 3; + vboSurf->numVerts = numVerts; + vboSurf->firstIndex = firstIndex; + + vboSurf->shader = surf1->shader; + vboSurf->fogIndex = surf1->fogIndex; + + VectorCopy(bounds[0], vboSurf->bounds[0]); + VectorCopy(bounds[1], vboSurf->bounds[1]); + + VectorCopy(bounds[0], mergedSurf->cullinfo.bounds[0]); + VectorCopy(bounds[1], mergedSurf->cullinfo.bounds[1]); + + mergedSurf->cullinfo.type = CULLINFO_BOX; + mergedSurf->data = (surfaceType_t *)vboSurf; + mergedSurf->fogIndex = surf1->fogIndex; + mergedSurf->shader = surf1->shader; + + // redirect view surfaces to this surf + for (j = i; j < numWorldSurfaces; j++) + { + if (s_worldData.surfacesViewCount[j] != i) + continue; + + for (k = 0; k < s_worldData.nummarksurfaces; k++) + { + int *mark = s_worldData.marksurfaces + k; + int *view = s_worldData.viewSurfaces + k; + + if (*mark == j) + *view = -(mergedSurfIndex + 1); + } + } + + mergedSurfIndex++; + mergedSurf++; + } + + // finish up the ibo + R_SyncRenderThread(); + + qglGenBuffersARB(1, &ibo->indexesVBO); + + R_BindIBO(ibo); + + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, numIboIndexes * sizeof(*iboIndexes), iboIndexes, GL_STATIC_DRAW_ARB); + + R_BindNullIBO(); + + GL_CheckErrors(); + + ri.Free(iboIndexes); + + endTime = ri.Milliseconds(); + + ri.Printf(PRINT_ALL, "Processed %d surfaces into %d merged, %d unmerged in %5.2f seconds\n", + numWorldSurfaces, numMergedSurfaces, numUnmergedSurfaces, (endTime - startTime) / 1000.0f); + + // reset viewcounts + for (i = 0; i < numWorldSurfaces; i++) + { + s_worldData.surfacesViewCount[i] = -1; + } +} + + +/* +================= RE_LoadWorldMap Called directly from cgame @@ -1800,7 +3087,7 @@ byte *startMarker; if ( tr.worldMapLoaded ) { - ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map" ); + ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map\n" ); } // set default sun direction to be used if it isn't @@ -1847,6 +3134,7 @@ } // load into heap + R_LoadEntities( &header->lumps[LUMP_ENTITIES] ); R_LoadShaders( &header->lumps[LUMP_SHADERS] ); R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] ); R_LoadPlanes (&header->lumps[LUMP_PLANES]); @@ -1856,14 +3144,32 @@ R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]); R_LoadSubmodels (&header->lumps[LUMP_MODELS]); R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); - R_LoadEntities( &header->lumps[LUMP_ENTITIES] ); R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] ); + // create static VBOS from the world + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + R_CreateWorldVBO(); + if (r_mergeLeafSurfaces->integer) + { + R_MergeLeafSurfaces(); + } + } + 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 + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + R_BindNullVBO(); + R_BindNullIBO(); + } + ri.FS_FreeFile( buffer.v ); } + + Index: code/renderer/tr_cmds.c =================================================================== --- code/renderer/tr_cmds.c (revision 2095) +++ code/renderer/tr_cmds.c (working copy) @@ -40,8 +40,8 @@ } if (r_speeds->integer == 1) { - ri.Printf (PRINT_ALL, "%i/%i shaders/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n", - backEnd.pc.c_shaders, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes, + ri.Printf (PRINT_ALL, "%i/%i/%i shaders/batches/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n", + backEnd.pc.c_shaders, backEnd.pc.c_surfBatches, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes, backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3, R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) ); } else if (r_speeds->integer == 2) { @@ -69,6 +69,13 @@ 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, "VBO draws: static %i dynamic %i\nMultidraws: %i merged %i\n", + backEnd.pc.c_staticVboDraws, backEnd.pc.c_dynamicVboDraws, backEnd.pc.c_multidraws, backEnd.pc.c_multidrawsMerged ); + ri.Printf( PRINT_ALL, "GLSL binds: %i draws: gen %i light %i fog %i dlight %i\n", + backEnd.pc.c_glslShaderBinds, backEnd.pc.c_genericDraws, backEnd.pc.c_lightallDraws, backEnd.pc.c_fogDraws, backEnd.pc.c_dlightDraws); + } Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); @@ -237,6 +244,26 @@ /* ============= +R_AddCapShadowmapCmd + +============= +*/ +void R_AddCapShadowmapCmd( int map, int cubeSide ) { + capShadowmapCommand_t *cmd; + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_CAPSHADOWMAP; + + cmd->map = map; + cmd->cubeSide = cubeSide; +} + + +/* +============= RE_SetColor Passing NULL will set the color to white Index: code/renderer/tr_curve.c =================================================================== --- code/renderer/tr_curve.c (revision 2095) +++ 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,147 @@ } +static void MakeMeshTangentVectors(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], int numTriangles, + srfTriangle_t triangles[SHADER_MAX_TRIANGLES]) +{ + int i, j; + srfVert_t *dv[3]; + static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE]; + srfTriangle_t *tri; + + // FIXME: use more elegant way + for(i = 0; i < width; i++) + { + for(j = 0; j < height; j++) + { + dv[0] = &ctrl2[j * width + i]; + *dv[0] = ctrl[j][i]; + } + } + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + dv[0] = &ctrl2[tri->indexes[0]]; + dv[1] = &ctrl2[tri->indexes[1]]; + dv[2] = &ctrl2[tri->indexes[2]]; + + R_CalcTangentVectors(dv); + } + +#if 0 + for(i = 0; i < (width * height); i++) + { + dv0 = &ctrl2[i]; + + VectorNormalize(dv0->normal); +#if 0 + VectorNormalize(dv0->tangent); + VectorNormalize(dv0->bitangent); +#else + d = DotProduct(dv0->tangent, dv0->normal); + VectorMA(dv0->tangent, -d, dv0->normal, dv0->tangent); + VectorNormalize(dv0->tangent); + + d = DotProduct(dv0->bitangent, dv0->normal); + VectorMA(dv0->bitangent, -d, dv0->normal, dv0->bitangent); + VectorNormalize(dv0->bitangent); +#endif + } +#endif + + +#if 0 + // do another extra smoothing for normals to avoid flat shading + for(i = 0; i < (width * height); i++) + { + for(j = 0; j < (width * height); j++) + { + if(R_CompareVert(&ctrl2[i], &ctrl2[j], qfalse)) + { + VectorAdd(ctrl2[i].normal, ctrl2[j].normal, ctrl2[i].normal); + } + } + + VectorNormalize(ctrl2[i].normal); + } +#endif + + for(i = 0; i < width; i++) + { + for(j = 0; j < height; j++) + { + dv[0] = &ctrl2[j * width + i]; + dv[1] = &ctrl[j][i]; + + VectorCopy(dv[0]->tangent, dv[1]->tangent); + VectorCopy(dv[0]->bitangent, dv[1]->bitangent); + } + } +} + + +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 +392,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 +421,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 +440,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 +456,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 +497,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 +508,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 +663,14 @@ } #endif + // calculate triangles + numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); + // calculate normals MakeMeshNormals( width, height, ctrl ); + MakeMeshTangentVectors(width, height, ctrl, numTriangles, triangles); - return R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); + return R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); } /* @@ -525,10 +681,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 +715,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 +727,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 +741,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 +775,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 +787,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,151 @@ +/* +=========================================================================== +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]; +} + +void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_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]; +} + +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; +} + +void VectorLerp( vec3_t a, vec3_t b, float lerp, vec3_t c) +{ + c[0] = a[0] * (1.0f - lerp) + b[0] * lerp; + c[1] = a[1] * (1.0f - lerp) + b[1] * lerp; + c[2] = a[2] * (1.0f - lerp) + b[2] * lerp; +} + +qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2) +{ + float radiusSum = radius1 + radius2; + vec3_t diff; + + VectorSubtract(origin1, origin2, diff); + + if (DotProduct(diff, diff) <= radiusSum * radiusSum) + { + return qtrue; + } + + return qfalse; +} + +void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3) +{ + vec3_t diff; + + VectorScale(origin1, 0.5f, origin3); + VectorMA(origin3, 0.5f, origin2, origin3); + + VectorSubtract(origin1, origin2, diff); + *radius3 = VectorLength(diff) * 0.5 + MAX(radius1, radius2); +} Index: code/renderer/tr_extramath.h =================================================================== --- code/renderer/tr_extramath.h (revision 0) +++ code/renderer/tr_extramath.h (revision 0) @@ -0,0 +1,74 @@ +/* +=========================================================================== +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 ); +void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_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)) +#define DotProduct4(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2] + (a)[3]*(b)[3]) +#define VectorScale4(a,b,c) ((c)[0]=(a)[0]*(b),(c)[1]=(a)[1]*(b),(c)[2]=(a)[2]*(b),(c)[3]=(a)[3]*(b)) + +#define VectorCopy5(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3],(b)[4]=(a)[4]) + +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; +} + +static ID_INLINE int VectorCompare5(const vec5_t v1, const vec5_t v2) +{ + if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3] || v1[4] != v2[4]) + { + return 0; + } + return 1; +} + +void VectorLerp( vec3_t a, vec3_t b, float lerp, vec3_t c); + + +qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2); +void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3); + +#ifndef SGN +#define SGN(x) (((x) >= 0) ? !!(x) : -1) +#endif + +#endif Index: code/renderer/tr_flares.c =================================================================== --- code/renderer/tr_flares.c (revision 2095) +++ code/renderer/tr_flares.c (working copy) @@ -444,6 +444,7 @@ flare_t *f; flare_t **prev; qboolean draw; + matrix_t oldmodelview, oldprojection, matrix; if ( !r_flares->integer ) { return; @@ -505,14 +506,14 @@ qglDisable (GL_CLIP_PLANE0); } - qglPushMatrix(); - qglLoadIdentity(); - qglMatrixMode( GL_PROJECTION ); - qglPushMatrix(); - qglLoadIdentity(); - qglOrtho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth, - backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight, - -99999, 99999 ); + Matrix16Copy(glState.projection, oldprojection); + Matrix16Copy(glState.modelview, oldmodelview); + Matrix16Identity(matrix); + GL_SetModelviewMatrix(matrix); + Matrix16Ortho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth, + backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight, + -99999, 99999, matrix ); + GL_SetProjectionMatrix(matrix); for ( f = r_activeFlares ; f ; f = f->next ) { if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum @@ -522,8 +523,9 @@ } } - qglPopMatrix(); - qglMatrixMode( GL_MODELVIEW ); - qglPopMatrix(); + GL_SetProjectionMatrix(oldprojection); + GL_SetModelviewMatrix(oldmodelview); } + + Index: code/renderer/tr_glsl.c =================================================================== --- code/renderer/tr_glsl.c (revision 0) +++ code/renderer/tr_glsl.c (revision 0) @@ -0,0 +1,2079 @@ +/* +=========================================================================== +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;\r\nattribute vec4 attr_TexCoord0;\r\nattribut" +"e vec4 attr_TexCoord1;\r\nattribute vec3 attr_Normal;\r\nattribute vec4 att" +"r_Color;\r\n\r\n#if defined(USE_VERTEX_ANIMATION)\r\nattribute vec4 attr_Po" +"sition2;\r\nattribute vec3 attr_Normal2;\r\n#endif\r\n\r\nuniform mat4 u_" +"DiffuseTexMatrix;\r\nuniform vec3 u_ViewOrigin;\r\n\r\n#if defined(USE_TC" +"GEN)\r\nuniform int u_TCGen0;\r\nuniform vec3 u_TCGen0Vector0;\r\nunif" +"orm vec3 u_TCGen0Vector1;\r\n#endif\r\n\r\n#if defined(USE_FOG)\r\nunifor" +"m vec4 u_FogDistance;\r\nuniform vec4 u_FogDepth;\r\nuniform float u_F" +"ogEyeT;\r\nuniform vec4 u_FogColorMask;\r\n#endif\r\n\r\n#if defined(USE_" +"DEFORM_VERTEXES)\r\nuniform int u_DeformGen;\r\nuniform float u_DeformP" +"arams[5];\r\n#endif\r\n\r\nuniform float u_Time;\r\n\r\nuniform mat4 u_M" +"odelViewProjectionMatrix;\r\nuniform vec4 u_BaseColor;\r\nuniform vec4 " +"u_VertColor;\r\n\r\n#if defined(USE_RGBAGEN)\r\nuniform int u_ColorGen;" +"\r\nuniform int u_AlphaGen;\r\nuniform vec3 u_AmbientLight;\r\nuniform" +" vec3 u_DirectedLight;\r\nuniform vec4 u_LightOrigin;\r\nuniform float " +" u_PortalRange;\r\n#endif\r\n\r\n#if defined(USE_VERTEX_ANIMATION)\r\nunifo" +"rm float u_VertexLerp;\r\n#endif\r\n\r\nvarying vec2 var_DiffuseTex;\r\n" +"#if defined(USE_LIGHTMAP)\r\nvarying vec2 var_LightTex;\r\n#endif\r\nvary" +"ing vec4 var_Color;\r\n\r\nvec2 DoTexMatrix(vec2 st, vec3 position, mat4 " +"texMatrix)\r\n{\r\n\tfloat amplitude = texMatrix[3][0];\r\n\tfloat phase = " +"texMatrix[3][1];\r\n\tvec2 st2 = (texMatrix * vec4(st, 1.0, 0.0)).st;\r\n\r" +"\n\tvec3 offsetPos = position.xyz / 1024.0;\r\n\toffsetPos.x += offsetPos.z" +";\r\n\r\n\tvec2 texOffset = sin((offsetPos.xy + vec2(phase)) * 2.0 * M_PI);" +"\r\n\t\r\n\treturn st2 + texOffset * amplitude;\r\n}\r\n\r\n#if defined(USE" +"_DEFORM_VERTEXES)\r\nfloat triangle(float x)\r\n{\r\n\treturn max(1.0 - abs" +"(x), 0);\r\n}\r\n\r\nfloat sawtooth(float x)\r\n{\r\n\treturn x - floor(x);" +"\r\n}\r\n\r\nvec4 DeformPosition(const vec4 pos, const vec3 normal, const v" +"ec2 st)\r\n{\r\n\tfloat base = u_DeformParams[0];\r\n\tfloat amplitude" +" = u_DeformParams[1];\r\n\tfloat phase = u_DeformParams[2];\r\n\tfloat " +"frequency = u_DeformParams[3];\r\n\tfloat spread = u_DeformParams[4];\r" +"\n\t\r\n\tif (u_DeformGen == DGEN_BULGE)\r\n\t{\r\n\t\tphase *= M_PI * 0.25" +" * st.x;\r\n\t}\r\n\telse // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH)" +"\r\n\t{\r\n\t\tphase += (pos.x + pos.y + pos.z) * spread;\r\n\t}\r\n\r\n\tf" +"loat value = phase + (u_Time * frequency);\r\n\tfloat func;\r\n\r\n\tif (u_" +"DeformGen == DGEN_WAVE_SIN)\r\n\t{\r\n\t\tfunc = sin(value * 2.0 * M_PI);\r" +"\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE_SQUARE)\r\n\t{\r\n\t\tfunc = s" +"ign(sin(value * 2.0 * M_PI));\r\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE" +"_TRIANGLE)\r\n\t{\r\n\t\tfunc = triangle(value);\r\n\t}\r\n\telse if (u_Def" +"ormGen == DGEN_WAVE_SAWTOOTH)\r\n\t{\r\n\t\tfunc = sawtooth(value);\r\n\t}" +"\r\n\telse if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH)\r\n\t{\r\n\t\tfun" +"c = (1.0 - sawtooth(value));\r\n\t}\r\n\telse if (u_DeformGen == DGEN_BULGE" +")\r\n\t{\r\n\t\tfunc = sin(value);\r\n\t}\r\n\t\r\n\tvec4 deformed = pos;\r" +"\n\tdeformed.xyz += normal * (base + func * amplitude);\r\n\r\n\treturn def" +"ormed;\r\n}\r\n#endif\r\n\r\n#if defined(USE_TCGEN)\r\nvec2 GenTexCoords(in" +"t TCGen, vec4 position, vec3 normal, vec3 TCGenVector0, vec3 TCGenVector1)" +"\r\n{\r\n\tvec2 tex = attr_TexCoord0.st;\r\n\r\n\tif (TCGen == TCGEN_LIGHTM" +"AP)\r\n\t{\r\n\t\ttex = attr_TexCoord1.st;\r\n\t}\r\n\telse if (TCGen == TC" +"GEN_ENVIRONMENT_MAPPED)\r\n\t{\r\n\t\tvec3 viewer = normalize(u_ViewOrigin " +"- position.xyz);\r\n\t\tvec3 reflected = normal * 2.0 * dot(normal, viewer)" +" - viewer;\r\n\r\n\t\ttex.s = 0.5 + reflected.y * 0.5;\r\n\t\ttex.t = 0.5 -" +" reflected.z * 0.5;\r\n\t}\r\n\telse if (TCGen == TCGEN_VECTOR)\r\n\t{\r\n" +"\t\ttex.s = dot(position.xyz, TCGenVector0);\r\n\t\ttex.t = dot(position.xy" +"z, TCGenVector1);\r\n\t}\r\n\t\r\n\treturn tex;\r\n}\r\n#endif\r\n\r\nvoid " +"main()\r\n{\r\n#if defined(USE_VERTEX_ANIMATION)\r\n\tvec4 position = mix(a" +"ttr_Position, attr_Position2, u_VertexLerp);\r\n\tvec3 normal = normalize(m" +"ix(attr_Normal, attr_Normal2, u_VertexLerp));\r\n#else\r\n\tvec4 position =" +" attr_Position;\r\n\tvec3 normal = attr_Normal;\r\n#endif\r\n\r\n#if define" +"d(USE_DEFORM_VERTEXES)\r\n\tposition = DeformPosition(position, normal, att" +"r_TexCoord0.st);\r\n#endif\r\n\r\n\tgl_Position = u_ModelViewProjectionMatr" +"ix * position;\r\n\r\n#if defined(USE_TCGEN)\r\n\tvec2 tex = GenTexCoords(u" +"_TCGen0, position, normal, u_TCGen0Vector0, u_TCGen0Vector1);\r\n#else\r\n" +"\tvec2 tex = attr_TexCoord0.st;\r\n#endif\r\n\tvar_DiffuseTex = DoTexMatrix" +"(tex, position.xyz, u_DiffuseTexMatrix);\r\n\r\n#if defined(USE_LIGHTMAP)\r" +"\n\tvar_LightTex = attr_TexCoord1.st;\r\n#endif\r\n\r\n\tvar_Color = u_Base" +"Color + u_VertColor * attr_Color;\r\n\r\n#if defined(USE_RGBAGEN)\r\n\tif (" +"u_ColorGen == CGEN_LIGHTING_DIFFUSE)\r\n\t{\r\n\t\tfloat incoming = max(dot" +"(normal, u_LightOrigin.xyz), 0.0);\r\n\r\n\t\tvar_Color.rgb = min(u_Directe" +"dLight * incoming + u_AmbientLight, 1.0);\r\n\t}\r\n\t\r\n\tvec3 toView = u" +"_ViewOrigin - position.xyz;\r\n\r\n\tif (u_AlphaGen == AGEN_LIGHTING_SPECUL" +"AR)\r\n\t{\r\n\t\tvec3 lightDir = normalize(vec3(-960.0, -1980.0, 96.0) - p" +"osition.xyz);\r\n\t\tvec3 viewer = normalize(toView);\r\n\t\tvec3 halfangle" +" = normalize(lightDir + viewer);\r\n\t\t\r\n\t\tvar_Color.a = pow(max(dot(n" +"ormal, halfangle), 0.0), 8.0);\r\n\t}\r\n\telse if (u_AlphaGen == AGEN_PORT" +"AL)\r\n\t{\r\n\t\tfloat alpha = length(toView) / u_PortalRange;\r\n\r\n\t\t" +"var_Color.a = min(alpha, 1.0);\r\n\t}\r\n\telse if (u_AlphaGen == AGEN_FRES" +"NEL)\r\n\t{\r\n\t\tvec3 viewer = normalize(toView);\r\n\t\t\r\n\t\tvar_Colo" +"r.a = 0.10 + 0.90 * pow(1.0 - dot(normal, viewer), 5);\r\n\t}\r\n#endif\r\n" +"\r\n#if defined (USE_FOG)\r\n\tfloat s = dot(position.xyz, u_FogDistance.xy" +"z) + u_FogDistance.w;\r\n\tfloat t = dot(position.xyz, u_FogDepth.xyz) + u_" +"FogDepth.w;\r\n\t\r\n\tif (t >= 1.0)\r\n\t{\r\n\t\ts *= t / (t - min(u_FogE" +"yeT, 0.0));\r\n\t}\r\n\telse\r\n\t{\r\n\t\ts *= max(t + sign(u_FogEyeT), 0." +"0);\r\n\t}\r\n\t\r\n\ts = 1.0 - sqrt(clamp(s * 8.0, 0.0, 1.0));\r\n\t\r\n\t" +"var_Color *= u_FogColorMask * s - (u_FogColorMask - vec4(1.0));\r\n#endif\r" +"\n}\r\n"; + +static const char *fallbackGenericShader_fp = +"uniform sampler2D u_DiffuseMap;\r\n\r\n#if defined(USE_LIGHTMAP)\r\nuniform" +" sampler2D u_LightMap;\r\n#endif\r\n\r\nuniform int u_Texture1Env;\r" +"\n\r\nvarying vec2 var_DiffuseTex;\r\n\r\n#if defined(USE_LIGHTMAP)\r" +"\nvarying vec2 var_LightTex;\r\n#endif\r\n\r\nvarying vec4 var_Co" +"lor;\r\n\r\n\r\nvoid main()\r\n{\r\n\tvec4 color = texture2D(u_DiffuseMap," +" var_DiffuseTex);\r\n\r\n#if defined(USE_LIGHTMAP)\r\n\tvec4 color2 = textu" +"re2D(u_LightMap, var_LightTex);\r\n\r\n\tif (u_Texture1Env == TEXENV_MODU" +"LATE)\r\n\t{\r\n\t\tcolor *= color2;\r\n\t}\r\n\telse if (u_Texture1Env == " +"TEXENV_ADD)\r\n\t{\r\n\t\tcolor += color2;\r\n\t}\r\n\telse if (u_Texture1E" +"nv == TEXENV_REPLACE)\r\n\t{\r\n\t\tcolor = color2;\r\n\t}\r\n#endif\r\n\r" +"\n\tgl_FragColor = color * var_Color;\r\n}\r\n"; + +static const char *fallbackTextureColorShader_vp = +"#version 120\r\n\r\nattribute vec4 attr_Position;\r\nattribute vec4 attr_Te" +"xCoord0;\r\n\r\nuniform mat4 u_ModelViewProjectionMatrix;\r\n\r\nvarying " +"vec2 var_Tex1;\r\n\r\n\r\nvoid main()\r\n{\r\n\tgl_Position = u_ModelView" +"ProjectionMatrix * attr_Position;\r\n\tvar_Tex1 = attr_TexCoord0.st;\r\n}\r" +"\n"; + +static const char *fallbackTextureColorShader_fp = +"#version 120\r\n\r\nuniform sampler2D u_DiffuseMap;\r\nuniform vec4 u_" +"Color;\r\n\r\nvarying vec2 var_Tex1;\r\n\r\n\r\nvoid main()\r\n{\r" +"\n\tgl_FragColor = texture2D(u_DiffuseMap, var_Tex1) * u_Color;\r\n}\r\n"; + +static const char *fallbackFogPassShader_vp = +"attribute vec4 attr_Position;\r\nattribute vec3 attr_Normal;\r\nattribute" +" vec4 attr_TexCoord0;\r\n\r\n//#if defined(USE_VERTEX_ANIMATION)\r\nattrib" +"ute vec4 attr_Position2;\r\nattribute vec3 attr_Normal2;\r\n//#endif\r\n" +"\r\nuniform vec4 u_FogDistance;\r\nuniform vec4 u_FogDepth;\r\nunifor" +"m float u_FogEyeT;\r\n\r\n//#if defined(USE_DEFORM_VERTEXES)\r\nuniform i" +"nt u_DeformGen;\r\nuniform float u_DeformParams[5];\r\n//#endif\r\n\r" +"\nuniform float u_Time;\r\nuniform mat4 u_ModelViewProjectionMatrix;\r" +"\n\r\n//#if defined(USE_VERTEX_ANIMATION)\r\nuniform float u_VertexLerp;" +"\r\n//#endif\r\n\r\nvarying float var_Scale;\r\n\r\n\r\nfloat triangle(fl" +"oat x)\r\n{\r\n\treturn max(1.0 - abs(x), 0);\r\n}\r\n\r\nfloat sawtooth(fl" +"oat x)\r\n{\r\n\treturn x - floor(x);\r\n}\r\n\r\nvec4 DeformPosition(const" +" vec4 pos, const vec3 normal, const vec2 st)\r\n{\r\n\tif (u_DeformGen == 0" +")\r\n\t{\r\n\t\treturn pos;\r\n\t}\r\n\r\n\tfloat base = u_DeformParam" +"s[0];\r\n\tfloat amplitude = u_DeformParams[1];\r\n\tfloat phase = u_De" +"formParams[2];\r\n\tfloat frequency = u_DeformParams[3];\r\n\tfloat spread " +"= u_DeformParams[4];\r\n\t\t\r\n\tif (u_DeformGen <= DGEN_WAVE_INVERSE_S" +"AWTOOTH)\r\n\t{\r\n\t\tphase += (pos.x + pos.y + pos.z) * spread;\r\n\t}\r" +"\n\telse if (u_DeformGen == DGEN_BULGE)\r\n\t{\r\n\t\tphase *= M_PI * 0.25 " +"* st.x;\r\n\t}\r\n\r\n\tfloat value = phase + (u_Time * frequency);\r\n\tfl" +"oat func;\r\n\r\n\tif (u_DeformGen == DGEN_WAVE_SIN)\r\n\t{\r\n\t\tfunc = s" +"in(value * 2.0 * M_PI);\r\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE_SQUAR" +"E)\r\n\t{\r\n\t\tfunc = sign(sin(value * 2.0 * M_PI));\r\n\t}\r\n\telse if " +"(u_DeformGen == DGEN_WAVE_TRIANGLE)\r\n\t{\r\n\t\tfunc = triangle(value);\r" +"\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE_SAWTOOTH)\r\n\t{\r\n\t\tfunc =" +" sawtooth(value);\r\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE_INVERSE_SAW" +"TOOTH)\r\n\t{\r\n\t\tfunc = (1.0 - sawtooth(value));\r\n\t}\r\n\telse if (u" +"_DeformGen == DGEN_BULGE)\r\n\t{\r\n\t\tfunc = sin(value);\r\n\t}\r\n\r\n\t" +"vec4 deformed = pos;\r\n\tdeformed.xyz += normal * (base + func * amplitude" +");\r\n\r\n\treturn deformed;\r\n\r\n}\r\n\r\nvoid main()\r\n{\r\n\tvec4 pos" +"ition = mix(attr_Position, attr_Position2, u_VertexLerp);\r\n\tvec3 normal " +"= normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp));\r\n\r\n\tpositio" +"n = DeformPosition(position, normal, attr_TexCoord0.st);\r\n\r\n\tgl_Positi" +"on = u_ModelViewProjectionMatrix * position;\r\n\r\n\tfloat s = dot(positio" +"n.xyz, u_FogDistance.xyz) + u_FogDistance.w;\r\n\tfloat t = dot(position.xy" +"z, u_FogDepth.xyz) + u_FogDepth.w;\r\n\r\n\tif (t >= 1.0)\r\n\t{\r\n\t\ts *" +"= t / (t - min(u_FogEyeT, 0.0));\r\n\t}\r\n\telse\r\n\t{\r\n\t\ts *= max(t " +"+ sign(u_FogEyeT), 0.0);\r\n\t}\r\n\r\n\tvar_Scale = clamp(s * 8.0, 0.0, 1." +"0);\r\n}\r\n"; + +static const char *fallbackFogPassShader_fp = +"uniform vec4 u_Color;\r\n\r\nvarying float var_Scale;\r\n\r\nvoid main()\r" +"\n{\r\n\tgl_FragColor = u_Color;\r\n\tgl_FragColor.a *= sqrt(var_Scale);\r" +"\n}\r\n"; + +static const char *fallbackDlightShader_vp = +"attribute vec4 attr_Position;\r\nattribute vec4 attr_TexCoord0;\r\nattribut" +"e vec3 attr_Normal;\r\n\r\nuniform vec4 u_DlightInfo;\r\n\r\nuniform int " +" u_DeformGen;\r\nuniform float u_DeformParams[5];\r\n\r\nuniform float " +"u_Time;\r\nuniform vec4 u_Color;\r\nuniform mat4 u_ModelViewProjectionM" +"atrix;\r\n\r\nvarying vec2 var_Tex1;\r\nvarying vec4 var_Color;\r\n\r\n" +"float triangle(float x)\r\n{\r\n\treturn max(1.0 - abs(x), 0);\r\n}\r\n\r\n" +"float sawtooth(float x)\r\n{\r\n\treturn x - floor(x);\r\n}\r\n\r\nvec4 Def" +"ormPosition(const vec4 pos, const vec3 normal, const vec2 st)\r\n{\r\n\tif " +"(u_DeformGen == 0)\r\n\t{\r\n\t\treturn pos;\r\n\t}\r\n\r\n\tfloat base = " +" u_DeformParams[0];\r\n\tfloat amplitude = u_DeformParams[1];\r\n\tfloat" +" phase = u_DeformParams[2];\r\n\tfloat frequency = u_DeformParams[3];\r" +"\n\tfloat spread = u_DeformParams[4];\r\n\t\t\r\n\tif (u_DeformGen <= DG" +"EN_WAVE_INVERSE_SAWTOOTH)\r\n\t{\r\n\t\tphase += (pos.x + pos.y + pos.z) * " +"spread;\r\n\t}\r\n\telse if (u_DeformGen == DGEN_BULGE)\r\n\t{\r\n\t\tphase" +" *= M_PI * 0.25 * st.x;\r\n\t}\r\n\r\n\tfloat value = phase + (u_Time * fre" +"quency);\r\n\tfloat func;\r\n\r\n\tif (u_DeformGen == DGEN_WAVE_SIN)\r\n\t{" +"\r\n\t\tfunc = sin(value * 2.0 * M_PI);\r\n\t}\r\n\telse if (u_DeformGen ==" +" DGEN_WAVE_SQUARE)\r\n\t{\r\n\t\tfunc = sign(sin(value * 2.0 * M_PI));\r\n" +"\t}\r\n\telse if (u_DeformGen == DGEN_WAVE_TRIANGLE)\r\n\t{\r\n\t\tfunc = t" +"riangle(value);\r\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE_SAWTOOTH)\r\n" +"\t{\r\n\t\tfunc = sawtooth(value);\r\n\t}\r\n\telse if (u_DeformGen == DGEN" +"_WAVE_INVERSE_SAWTOOTH)\r\n\t{\r\n\t\tfunc = (1.0 - sawtooth(value));\r\n\t" +"}\r\n\telse if (u_DeformGen == DGEN_BULGE)\r\n\t{\r\n\t\tfunc = sin(value);" +"\r\n\t}\r\n\r\n\tvec4 deformed = pos;\r\n\tdeformed.xyz += normal * (base +" +" func * amplitude);\r\n\r\n\treturn deformed;\r\n\r\n}\r\n\r\nvoid main()\r" +"\n{\r\n\tvec4 position = attr_Position;\r\n\tvec3 normal = attr_Normal;\r\n" +"\r\n\tposition = DeformPosition(position, normal, attr_TexCoord0.st);\r\n\r" +"\n\tgl_Position = u_ModelViewProjectionMatrix * position;\r\n\t\t\r\n\tvec3" +" dist = u_DlightInfo.xyz - position.xyz;\t\r\n\r\n\tfloat diffz = abs(dist." +"z);\r\n\tfloat radius = 1.0 / u_DlightInfo.a;\r\n\r\n\tvec2 tex = vec2(0.5)" +" + dist.xy * u_DlightInfo.a;\r\n\tfloat dlightmod = max(sign(dot(dist, norm" +"al)), 0.0);\r\n\tdlightmod *= clamp(2.0 * (radius - diffz) * u_DlightInfo.a" +", 0.0, 1.0);\r\n\r\n\tvar_Tex1 = tex;\r\n\tvar_Color = u_Color;\r\n\tvar_Co" +"lor.rgb *= dlightmod;\r\n}\r\n"; + +static const char *fallbackDlightShader_fp = +"uniform sampler2D u_DiffuseMap;\r\n\r\nvarying vec2 var_Tex1;\r\nvaryi" +"ng vec4 var_Color;\r\n\r\n\r\nvoid main()\r\n{\r\n\tvec4 color = textu" +"re2D(u_DiffuseMap, var_Tex1);\r\n\r\n\tgl_FragColor = color * var_Color;\r" +"\n}\r\n"; + +static const char *fallbackLightallShader_vp = +"attribute vec4 attr_TexCoord0;\r\n#if defined(USE_LIGHTMAP)\r\nattribute ve" +"c4 attr_TexCoord1;\r\n#endif\r\nattribute vec4 attr_Color;\r\n\r\nattribute" +" vec4 attr_Position;\r\nattribute vec3 attr_Normal;\r\n\r\n#if defined(USE_" +"NORMALMAP)\r\nattribute vec3 attr_Tangent;\r\nattribute vec3 attr_Bitangent" +";\r\n#endif\r\n\r\n#if defined(USE_VERTEX_ANIMATION)\r\nattribute vec4 attr" +"_Position2;\r\nattribute vec3 attr_Normal2;\r\n #if defined(USE_NORMALMAP)" +"\r\nattribute vec3 attr_Tangent2;\r\nattribute vec3 attr_Bitangent2;\r\n #" +"endif\r\n#endif\r\n\r\nuniform mat4 u_DiffuseTexMatrix;\r\nuniform vec3 " +" u_ViewOrigin;\r\nuniform mat4 u_ModelViewProjectionMatrix;\r\nuniform ve" +"c4 u_BaseColor;\r\nuniform vec4 u_VertColor;\r\n\r\n#if defined(USE_MOD" +"ELMATRIX)\r\nuniform mat4 u_ModelMatrix;\r\n#endif\r\n\r\n#if defined(USE" +"_VERTEX_ANIMATION)\r\nuniform float u_VertexLerp;\r\n#endif\r\n\r\nvarying" +" vec2 var_DiffuseTex;\r\n\r\n#if defined(USE_LIGHTMAP)\r\nvarying vec2 " +"var_LightTex;\r\n#endif\r\n\r\nvarying vec4 var_Color;\r\n\r\nvarying vec" +"3 var_Position;\r\nvarying vec3 var_Normal;\r\n\r\n#if defined(USE_NORM" +"ALMAP)\r\nvarying vec3 var_Tangent;\r\nvarying vec3 var_Bitangent;\r\n#" +"endif\r\n\r\nvec2 DoTexMatrix(vec2 st, vec3 position, mat4 texMatrix)\r\n{" +"\r\n\tvec2 st2 = (texMatrix * vec4(st, 1, 0)).st;\r\n\r\n\tvec3 offsetPos =" +" position.xyz / 1024.0;\r\n\toffsetPos.x += offsetPos.z;\r\n\r\n\tvec2 texO" +"ffset = sin((offsetPos.xy + vec2(texMatrix[3][1])) * 2.0 * M_PI);\r\n\t\r\n" +"\treturn st2 + texOffset * texMatrix[3][0];\r\n}\r\n\r\nvoid main()\r\n{\r" +"\n#if defined(USE_VERTEX_ANIMATION)\r\n\tvec4 position = mix(attr_Position" +", attr_Position2, u_VertexLerp);\r\n\tvec3 normal = normalize(mix(attr_N" +"ormal, attr_Normal2, u_VertexLerp));\r\n #if defined(USE_NORMALMAP)" +"\r\n\tvec3 tangent = normalize(mix(attr_Tangent, attr_Tangent2, u_Ver" +"texLerp));\r\n\tvec3 bitangent = normalize(mix(attr_Bitangent, attr_Bitange" +"nt2, u_VertexLerp));\r\n #endif\r\n#else\r\n\tvec4 position = attr_Positi" +"on;\r\n\tvec3 normal = attr_Normal;\r\n #if defined(USE_NORMALMAP)\r\n " +" vec3 tangent = attr_Tangent;\r\n vec3 bitangent = attr_Bita" +"ngent;\r\n #endif\r\n#endif\r\n\r\n\tgl_Position = u_ModelViewProjectionMa" +"trix * position;\r\n\r\n#if defined(TCGEN_ENVIRONMENT)\r\n\tvec2 tex;\r\n\t" +"vec3 viewer = normalize(u_ViewOrigin - position.xyz);\r\n\tvec3 reflected =" +" normal * 2.0 * dot(normal, viewer) - viewer;\r\n\r\n\ttex.s = 0.5 + reflec" +"ted.y * 0.5;\r\n\ttex.t = 0.5 - reflected.z * 0.5;\r\n#else\r\n\tvec2 tex =" +" attr_TexCoord0.st;\r\n#endif\r\n\r\n\tvar_DiffuseTex = DoTexMatrix(tex, po" +"sition.xyz, u_DiffuseTexMatrix);\r\n\r\n#if defined(USE_LIGHTMAP)\r\n\tvar_" +"LightTex = attr_TexCoord1.st;\r\n#endif\r\n\t\r\n\tvar_Color = u_BaseColor " +"+ u_VertColor * attr_Color;\r\n\r\n#if defined(USE_MODELMATRIX)\r\n\tvar_Po" +"sition = (u_ModelMatrix * position).xyz;\r\n\tvar_Normal = (u_ModelMatr" +"ix * vec4(normal, 0.0)).xyz;\r\n\r\n #if defined(USE_NORMALMAP)\r\n\tvar_T" +"angent = (u_ModelMatrix * vec4(tangent, 0.0)).xyz;\r\n\tvar_Bitangent = (" +"u_ModelMatrix * vec4(bitangent, 0.0)).xyz;\r\n #endif\r\n#else\r\n\tvar_Po" +"sition = position.xyz;\r\n\tvar_Normal = normal;\r\n\r\n #if defined(U" +"SE_NORMALMAP)\r\n\tvar_Tangent = tangent;\r\n\tvar_Bitangent = bitangent;" +"\r\n #endif\r\n#endif\r\n}\r\n"; + +static const char *fallbackLightallShader_fp = +"uniform sampler2D u_DiffuseMap;\r\n\r\n#if defined(USE_LIGHTMAP)\r\nuniform" +" sampler2D u_LightMap;\r\n#endif\r\n\r\n#if defined(USE_NORMALMAP)\r\nunifo" +"rm sampler2D u_NormalMap;\r\n#endif\r\n\r\n#if defined(USE_DELUXEMAP)\r\nun" +"iform sampler2D u_DeluxeMap;\r\n#endif\r\n\r\n#if defined(USE_SPECULARMAP)" +"\r\nuniform sampler2D u_SpecularMap;\r\n#endif\r\n\r\n#if defined(USE_SHADO" +"WMAP)\r\nuniform samplerCube u_ShadowMap;\r\n#endif\r\n\r\nuniform vec3 " +" u_ViewOrigin;\r\n\r\n#if defined(USE_LIGHT_VECTOR)\r\nuniform vec3 u" +"_DirectedLight;\r\nuniform vec3 u_AmbientLight;\r\nuniform vec4 u" +"_LightOrigin;\r\nuniform float u_LightRadius;\r\n#endif\r\n\r\n#if defi" +"ned(USE_SPECULARMAP)\r\nuniform float u_SpecularReflectance;\r\n#endif" +"\r\n\r\nvarying vec2 var_DiffuseTex;\r\n#if defined(USE_LIGHTMAP)\r\nv" +"arying vec2 var_LightTex;\r\n#endif\r\nvarying vec4 var_Color;\r" +"\n\r\nvarying vec3 var_Position;\r\n\r\n#if defined(USE_NORMALMAP)\r\n" +"varying vec3 var_Tangent;\r\nvarying vec3 var_Bitangent;\r\n#endi" +"f\r\n\r\nvarying vec3 var_Normal;\r\n\r\n\r\nfloat RayIntersectDisplac" +"eMap(vec2 dp, vec2 ds, sampler2D normalMap)\r\n{\r\n\tconst int linearSearc" +"hSteps = 5;\r\n\tconst int binarySearchSteps = 5;\r\n\r\n\tfloat depthStep " +"= 1.0 / float(linearSearchSteps);\r\n\r\n\t// current size of search window" +"\r\n\tfloat size = depthStep;\r\n\r\n\t// current depth position\r\n\tfloat" +" depth = 0.0;\r\n\r\n\t// best match found (starts with last position 1.0)" +"\r\n\tfloat bestDepth = 1.0;\r\n\r\n\t// search front to back for first poi" +"nt inside object\r\n\tfor(int i = 0; i < linearSearchSteps - 1; ++i)\r\n\t{" +"\r\n\t\tdepth += size;\r\n\t\t\r\n\t\tvec4 t = texture2D(normalMap, dp + ds" +" * depth);\r\n\r\n\t\tif(bestDepth > 0.996)\t\t// if no depth found yet\r\n" +"\t\t\tif(depth >= t.w)\r\n\t\t\t\tbestDepth = depth;\t// store best depth\r" +"\n\t}\r\n\r\n\tdepth = bestDepth;\r\n\t\r\n\t// recurse around first point " +"(depth) for closest match\r\n\tfor(int i = 0; i < binarySearchSteps; ++i)\r" +"\n\t{\r\n\t\tsize *= 0.5;\r\n\r\n\t\tvec4 t = texture2D(normalMap, dp + ds " +"* depth);\r\n\t\t\r\n\t\tif(depth >= t.w)\r\n\t\t{\r\n\t\t\tbestDepth = dep" +"th;\r\n\t\t\tdepth -= 2.0 * size;\r\n\t\t}\r\n\r\n\t\tdepth += size;\r\n\t}" +"\r\n\r\n\treturn bestDepth;\r\n}\r\n\r\nvoid main()\r\n{\r\n#if defined(USE" +"_LIGHTMAP)\r\n\tvec3 directedLight = texture2D(u_LightMap, var_LightTex).rg" +"b;\r\n #if defined(USE_DELUXEMAP)\r\n\tvec3 worldLight = 2.0 * texture2D(u" +"_DeluxeMap, var_LightTex).xyz - vec3(1.0);\r\n #endif\r\n#endif\r\n\r\n#if" +" defined(USE_LIGHT_VECTOR)\r\n\tvec3 worldLight = u_LightOrigin.xyz - (var_" +"Position * u_LightOrigin.w);\t\r\n #if defined(USE_INVSQRLIGHT)\r\n\tfloat" +" intensity = 1.0 / dot(worldLight, worldLight);\r\n #else\r\n\tfloat inten" +"sity = clamp((1.0 - dot(worldLight, worldLight) / (u_LightRadius * u_LightR" +"adius)) * 1.07, 0.0, 1.0);\r\n #endif\r\n #if defined(USE_SHADOWMAP)\r\n " +" \tvec3 dist3 = textureCube(u_ShadowMap, worldLight).rgb;\r\n\tfloat dist =" +" dot(dist3, vec3(1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0)) * u_LightRadius;" +"\r\n\r\n\tintensity *= max(sign(dist - length(worldLight)), 0.0);\r\n #end" +"if\r\n\tvec3 directedLight = u_DirectedLight * intensity;\r\n\tvec3 ambient" +"Light = u_AmbientLight;\r\n#endif\r\n\r\n#if !(defined(USE_LIGHTMAP) && de" +"fined(USE_DELUXEMAP)) && !defined(USE_LIGHT_VECTOR)\r\n\tvec3 worldLight = " +"var_Normal;\r\n#endif\r\n\r\n\tvec3 SampleToView = normalize(u_ViewOrigin -" +" var_Position);\r\n\tvec2 texOffset = vec2(0.0);\r\n\t\r\n\tfloat ambientDi" +"ff = 1.0;\r\n\r\n#if defined(USE_NORMALMAP)\r\n\tmat3 tangentToWorld = mat3" +"(var_Tangent.xyz, var_Bitangent.xyz, var_Normal.xyz);\r\n\r\n #if defined(" +"USE_PARALLAXMAP)\r\n\tvec3 offsetDir = normalize(SampleToView * tangentToWo" +"rld);\r\n #if 0\r\n\tfloat dist = 0.02 * texture2D(u_NormalMap, var_Diff" +"useTex).w - (0.02 / 2.0);\r\n #else\r\n\toffsetDir.xy *= 0.02 / offsetDi" +"r.z;\r\n\tfloat dist = RayIntersectDisplaceMap(var_DiffuseTex, offsetDir.xy" +", u_NormalMap);\r\n #endif\t\r\n\ttexOffset = offsetDir.xy * dist;\r\n " +"#endif\r\n \r\n\tvec3 normal = texture2D(u_NormalMap, var_DiffuseTex + tex" +"Offset).xyz;\r\n\tvec3 worldNormal = tangentToWorld * (2.0 * normal.xyz - v" +"ec3(1.0));\r\n #if defined(r_normalAmbient)\r\n\tambientDiff = 0.248965 * " +"normal.z + 0.069673;\r\n #endif\r\n#else\r\n\tvec3 worldNormal = var_Norma" +"l;\r\n#endif\r\n\r\n\tvec4 diffuse = texture2D(u_DiffuseMap, var_DiffuseTex" +" + texOffset);\r\n\t\r\n\tworldNormal = normalize(worldNormal);\r\n\tworldL" +"ight = normalize(worldLight);\r\n\r\n#if defined(USE_LIGHTMAP)\r\n #if def" +"ined(USE_DELUXEMAP)\r\n\tdirectedLight /= max(dot(normalize(var_Normal), wo" +"rldLight), 0.004);\r\n #endif\r\n \r\n #if defined(r_normalAmbient)\r\n" +"\tvec3 ambientLight = directedLight * r_normalAmbient;\r\n\tdirectedLight -" +"= ambientLight;\r\n #else\r\n\tvec3 ambientLight = vec3(0);\r\n #endif\r" +"\n#endif\r\n\r\n#if defined(r_normalAmbient) && (defined(USE_LIGHTMAP) || d" +"efined(USE_LIGHT_VECTOR))\r\n\tambientLight *= 3.138357;\r\n#endif\r\n\r\n#" +"if (defined(USE_LIGHTMAP) && defined(USE_DELUXEMAP)) || defined(USE_LIGHT_V" +"ECTOR)\r\n\tfloat NL = max(dot(worldNormal, worldLight), 0.0);\r\n#else" +"\r\n\tfloat NL = 1.0;\r\n#endif\r\n\r\n#if defined(USE_SPECULARMAP) && (def" +"ined(USE_LIGHTMAP) || defined(USE_LIGHT_VECTOR))\r\n\tvec4 specular = textu" +"re2D(u_SpecularMap, var_DiffuseTex + texOffset);\r\n\tfloat shininess = spe" +"cular.a * 255 + 1.0;\r\n\tfloat fzero = u_SpecularReflectance;\r\n\r\n\tvec" +"3 halfAngle = normalize(worldLight + SampleToView);\r\n\r\n\tfloat EH = max" +"(dot(SampleToView, halfAngle), 0.0);\r\n\tfloat NE = max(dot(worldNormal" +", SampleToView), 0.0001);\r\n\tfloat NH = max(dot(worldNormal, halfAngle)" +", 0.0);\r\n\r\n\tfloat factor = 0.1248582 * shininess + 0.2691817;\r\n\t" +"float fspec = fzero + (1.0 - fzero) * pow(1.0 - EH, 5);\r\n\r\n\tdiffuse.rg" +"b *= min((directedLight * NL + ambientLight) * (1.0 - fzero) , 1.0);\r\n\t" +"\r\n\tfloat directedSpec = factor * fspec * pow(NH, shininess) / max(NL, NE" +");\r\n\r\n #if defined(r_normalAmbient)\r\n #if defined(USE_NORMALMAP)" +"\r\n\tfloat ambientSpec = pow(normal.z, shininess);\r\n #else\r\n\tfloat" +" ambientSpec = 1.0f;\r\n #endif\r\n\r\n\tambientSpec *= factor * fzero *" +" 8.05438 / (shininess + 19.0);\r\n\tambientSpec += 0.04389 / (shininess * s" +"hininess);\r\n\t\r\n\tspecular.rgb *= min(directedSpec * directedLight + am" +"bientSpec * ambientLight, 1.0);\r\n #else\r\n\tspecular.rgb *= min(directe" +"dSpec * directedLight, 1.0);\r\n #endif\r\n#else\r\n #if (defined(USE_LIG" +"HTMAP) || defined(USE_LIGHT_VECTOR))\r\n\tdiffuse.rgb *= min(directedLight " +"* NL + ambientDiff * ambientLight, 1.0);\r\n #endif\r\n#endif\r\n\r\n\tgl_" +"FragColor = diffuse;\r\n\r\n#if defined(USE_SPECULARMAP) && (defined(USE_LI" +"GHTMAP) || defined(USE_LIGHT_VECTOR))\r\n\tgl_FragColor.rgb += specular.rgb" +";\r\n#endif\r\n\r\n\tgl_FragColor *= var_Color;\r\n}\r\n"; + +static const char *fallbackShadowfillShader_vp = +"attribute vec4 attr_Position;\r\nattribute vec3 attr_Normal;\r\nattribute" +" vec4 attr_TexCoord0;\r\n\r\n//#if defined(USE_VERTEX_ANIMATION)\r\nattrib" +"ute vec4 attr_Position2;\r\nattribute vec3 attr_Normal2;\r\n//#endif\r\n" +"\r\n//#if defined(USE_DEFORM_VERTEXES)\r\nuniform int u_DeformGen;\r\nu" +"niform float u_DeformParams[5];\r\n//#endif\r\n\r\nuniform float u_Tim" +"e;\r\nuniform mat4 u_ModelViewProjectionMatrix;\r\n\r\nuniform mat4 u_" +"ModelMatrix;\r\n\r\n//#if defined(USE_VERTEX_ANIMATION)\r\nuniform float " +"u_VertexLerp;\r\n//#endif\r\n\r\nvarying vec3 var_Position;\r\n\r\nfloat" +" triangle(float x)\r\n{\r\n\treturn max(1.0 - abs(x), 0);\r\n}\r\n\r\nfloat" +" sawtooth(float x)\r\n{\r\n\treturn x - floor(x);\r\n}\r\n\r\nvec4 DeformPo" +"sition(const vec4 pos, const vec3 normal, const vec2 st)\r\n{\r\n\tif (u_De" +"formGen == 0)\r\n\t{\r\n\t\treturn pos;\r\n\t}\r\n\r\n\tfloat base = u" +"_DeformParams[0];\r\n\tfloat amplitude = u_DeformParams[1];\r\n\tfloat phas" +"e = u_DeformParams[2];\r\n\tfloat frequency = u_DeformParams[3];\r\n\tf" +"loat spread = u_DeformParams[4];\r\n\t\t\r\n\tif (u_DeformGen <= DGEN_WA" +"VE_INVERSE_SAWTOOTH)\r\n\t{\r\n\t\tphase += (pos.x + pos.y + pos.z) * sprea" +"d;\r\n\t}\r\n\telse if (u_DeformGen == DGEN_BULGE)\r\n\t{\r\n\t\tphase *= M" +"_PI * 0.25 * st.x;\r\n\t}\r\n\r\n\tfloat value = phase + (u_Time * frequenc" +"y);\r\n\tfloat func;\r\n\r\n\tif (u_DeformGen == DGEN_WAVE_SIN)\r\n\t{\r\n" +"\t\tfunc = sin(value * 2.0 * M_PI);\r\n\t}\r\n\telse if (u_DeformGen == DGE" +"N_WAVE_SQUARE)\r\n\t{\r\n\t\tfunc = sign(sin(value * 2.0 * M_PI));\r\n\t}\r" +"\n\telse if (u_DeformGen == DGEN_WAVE_TRIANGLE)\r\n\t{\r\n\t\tfunc = triang" +"le(value);\r\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE_SAWTOOTH)\r\n\t{\r" +"\n\t\tfunc = sawtooth(value);\r\n\t}\r\n\telse if (u_DeformGen == DGEN_WAVE" +"_INVERSE_SAWTOOTH)\r\n\t{\r\n\t\tfunc = (1.0 - sawtooth(value));\r\n\t}\r\n" +"\telse if (u_DeformGen == DGEN_BULGE)\r\n\t{\r\n\t\tfunc = sin(value);\r\n" +"\t}\r\n\r\n\tvec4 deformed = pos;\r\n\tdeformed.xyz += normal * (base + fun" +"c * amplitude);\r\n\r\n\treturn deformed;\r\n\r\n}\r\n\r\n\r\nvoid main()\r" +"\n{\r\n\tvec4 position = mix(attr_Position, attr_Position2, u_VertexLerp);" +"\r\n\tvec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp))" +";\r\n\r\n\tposition = DeformPosition(position, normal, attr_TexCoord0.st);" +"\r\n\r\n\tgl_Position = u_ModelViewProjectionMatrix * position;\r\n\t\r\n\t" +"var_Position = (u_ModelMatrix * position).xyz;\r\n}\r\n"; + +static const char *fallbackShadowfillShader_fp = +"uniform vec4 u_LightOrigin;\r\nuniform float u_LightRadius;\r\n\r\nvarying" +" vec3 var_Position;\r\n\r\nvoid main()\r\n{\r\n\tfloat depth = length(u_Li" +"ghtOrigin.xyz - var_Position) / u_LightRadius;\r\n#if 0\r\n\t// 32 bit prec" +"ision\r\n\tconst vec4 bitSh = vec4( 256 * 256 * 256, 256 * 256, 2" +"56, 1);\r\n\tconst vec4 bitMsk = vec4( 0, 1.0 / 256." +"0, 1.0 / 256.0, 1.0 / 256.0);\r\n\t\r\n\tvec4 comp;\r\n\tcomp = depth * bit" +"Sh;\r\n\tcomp.xyz = fract(comp.xyz);\r\n\tcomp -= comp.xxyz * bitMsk;\r\n\t" +"gl_FragColor = comp;\r\n#endif\r\n\r\n#if 1\r\n\t// 24 bit precision\r\n\tc" +"onst vec3 bitSh = vec3( 256 * 256, 256, 1);\r\n\tconst ve" +"c3 bitMsk = vec3( 0, 1.0 / 256.0, 1.0 / 256.0);\r\n\t\r\n\tvec3 comp" +";\r\n\tcomp = depth * bitSh;\r\n\tcomp.xy = fract(comp.xy);\r\n\tcomp -= co" +"mp.xxy * bitMsk;\r\n\tgl_FragColor = vec4(comp, 1.0);\r\n#endif\r\n\r\n#if " +"0\r\n\t// 8 bit precision\r\n\tgl_FragColor = vec4(depth, depth, depth, 1);" +"\r\n#endif\r\n}\r\n"; + +static const char *fallbackPshadowShader_vp = +"attribute vec4 attr_Position;\r\nattribute vec3 attr_Normal;\r\n\r\nuniform" +" mat4 u_ModelViewProjectionMatrix;\r\nvarying vec3 var_Position;\r\nvar" +"ying vec3 var_Normal;\r\n\r\n\r\nvoid main()\r\n{\r\n\tvec4 position = a" +"ttr_Position;\r\n\r\n\tgl_Position = u_ModelViewProjectionMatrix * position" +";\r\n\r\n\tvar_Position = position.xyz;\r\n\tvar_Normal = attr_Normal;" +"\r\n}\r\n"; + +static const char *fallbackPshadowShader_fp = +"uniform sampler2D u_ShadowMap;\r\n\r\nuniform vec3 u_LightForward;\r\n" +"uniform vec3 u_LightUp;\r\nuniform vec3 u_LightRight;\r\nuniform " +"vec4 u_LightOrigin;\r\nuniform float u_LightRadius;\r\nvarying vec" +"3 var_Position;\r\nvarying vec3 var_Normal;\r\n\r\nfloat sampleDi" +"stMap(sampler2D texMap, vec2 uv, float scale)\r\n{\r\n\tvec3 distv = textur" +"e2D(texMap, uv).xyz;\r\n\treturn dot(distv, vec3(1.0 / (256.0 * 256.0), 1.0" +" / 256.0, 1.0)) * scale;\r\n}\r\n\r\nvoid main()\r\n{\r\n\tvec2 st;\r\n\t\r" +"\n\tvec3 lightToPos = var_Position - u_LightOrigin.xyz;\r\n\t\r\n\tst.s = -" +"dot(u_LightRight, lightToPos);\r\n\tst.t = dot(u_LightUp, lightToPos);\r\n" +"\t\r\n\tst = st * 0.5 + vec2(0.5);\r\n\r\n#if defined(USE_SOLID_PSHADOWS)\r" +"\n\tfloat intensity = max(sign(u_LightRadius - length(lightToPos)), 0.0);\r" +"\n#else\r\n\tfloat intensity = clamp((1.0 - dot(lightToPos, lightToPos) / (" +"u_LightRadius * u_LightRadius)) * 2.0, 0.0, 1.0);\r\n#endif\r\n\t\r\n\tfloa" +"t lightDist = length(lightToPos);\r\n\tfloat dist;\r\n\r\n#if defined(USE_D" +"ISCARD)\r\n\tif (dot(u_LightForward, lightToPos) <= 0.0)\r\n\t{\r\n\t\tdisc" +"ard;\r\n\t}\r\n\r\n\tif (dot(var_Normal, lightToPos) > 0.0)\r\n\t{\r\n\t\td" +"iscard;\r\n\t}\r\n#else\r\n\tintensity *= max(sign(dot(u_LightForward, ligh" +"tToPos)), 0.0);\r\n\tintensity *= max(sign(-dot(var_Normal, lightToPos)), 0" +".0);\r\n#endif\r\n\t\r\n#if defined(USE_PCF)\r\n\tfloat part;\r\n\t\r\n\tdi" +"st = sampleDistMap(u_ShadowMap, st + vec2(-1.0/256.0, -1.0/256.0), u_LightR" +"adius);\r\n\tpart = max(sign(lightDist - dist), 0.0);\r\n\r\n\tdist = samp" +"leDistMap(u_ShadowMap, st + vec2( 1.0/256.0, -1.0/256.0), u_LightRadius);\r" +"\n\tpart += max(sign(lightDist - dist), 0.0);\r\n\r\n\tdist = sampleDistMap" +"(u_ShadowMap, st + vec2(-1.0/256.0, 1.0/256.0), u_LightRadius);\r\n\tpart " +"+= max(sign(lightDist - dist), 0.0);\r\n\r\n\tdist = sampleDistMap(u_Shadow" +"Map, st + vec2( 1.0/256.0, 1.0/256.0), u_LightRadius);\r\n\tpart += max(si" +"gn(lightDist - dist), 0.0);\r\n\r\n #if defined(USE_DISCARD)\r\n\tif (part " +"<= 0.0)\r\n\t{\r\n\t\tdiscard;\r\n\t}\r\n #endif\r\n\r\n\tintensity *= part" +" * 0.25;\r\n#else\r\n\tdist = sampleDistMap(u_ShadowMap, st, u_LightRadius)" +";\r\n\r\n #if defined(USE_DISCARD)\r\n\tif (lightDist - dist <= 0.0)\r\n\t{" +"\r\n\t\tdiscard;\r\n\t}\r\n #endif\r\n\t\t\t\r\n\t//intensity *= max(sign(2" +"54.0 / 255.0 - dist / u_LightRadius), 0.0);\r\n\tintensity *= max(sign(ligh" +"tDist - dist), 0.0);\r\n#endif\r\n\t\t\r\n\tgl_FragColor.rgb = vec3(0);\r\n" +"\tgl_FragColor.a = clamp(intensity, 0.0, 0.75);\r\n}\r\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 void GLSL_GetShaderHeader( GLenum shaderType, const GLcharARB *extra, char *dest, int size ) +{ + float fbufWidthScale, fbufHeightScale; + + dest[0] = '\0'; + + // HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones + if(0) //(glConfig.driverType == GLDRV_OPENGL3) + { + Q_strcat(dest, size, "#version 130\n"); + + if(shaderType == GL_VERTEX_SHADER_ARB) + { + Q_strcat(dest, size, "#define attribute in\n"); + Q_strcat(dest, size, "#define varying out\n"); + } + else + { + Q_strcat(dest, size, "#define varying in\n"); + + Q_strcat(dest, size, "out vec4 out_Color;\n"); + Q_strcat(dest, size, "#define gl_FragColor out_Color\n"); + } + } + else + { + Q_strcat(dest, size, "#version 120\n"); + } + + // HACK: add some macros to avoid extra uniforms and save speed and code maintenance + //Q_strcat(dest, size, + // va("#ifndef r_SpecularExponent\n#define r_SpecularExponent %f\n#endif\n", r_specularExponent->value)); + //Q_strcat(dest, size, + // va("#ifndef r_SpecularScale\n#define r_SpecularScale %f\n#endif\n", r_specularScale->value)); + //Q_strcat(dest, size, + // va("#ifndef r_NormalScale\n#define r_NormalScale %f\n#endif\n", r_normalScale->value)); + + + Q_strcat(dest, size, "#ifndef M_PI\n#define M_PI 3.14159265358979323846f\n#endif\n"); + + //Q_strcat(dest, size, va("#ifndef MAX_SHADOWMAPS\n#define MAX_SHADOWMAPS %i\n#endif\n", MAX_SHADOWMAPS)); + + Q_strcat(dest, size, + 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(dest, size, + 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(dest, size, + va("#ifndef colorGen_t\n" + "#define colorGen_t\n" + "#define CGEN_LIGHTING_DIFFUSE %i\n" + "#endif\n", + CGEN_LIGHTING_DIFFUSE)); + + Q_strcat(dest, size, + va("#ifndef alphaGen_t\n" + "#define alphaGen_t\n" + "#define AGEN_LIGHTING_SPECULAR %i\n" + "#define AGEN_PORTAL %i\n" + "#define AGEN_FRESNEL %i\n" + "#endif\n", + AGEN_LIGHTING_SPECULAR, + AGEN_PORTAL, + AGEN_FRESNEL)); + + Q_strcat(dest, size, + 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)); + + Q_strcat(dest, size, + va("#ifndef texenv_t\n" + "#define texenv_t\n" + "#define TEXENV_MODULATE %i\n" + "#define TEXENV_ADD %i\n" + "#define TEXENV_REPLACE %i\n" + "#endif\n", + GL_MODULATE, + GL_ADD, + GL_REPLACE)); + + fbufWidthScale = 1.0f / ((float)glConfig.vidWidth); + fbufHeightScale = 1.0f / ((float)glConfig.vidHeight); + Q_strcat(dest, size, + va("#ifndef r_FBufScale\n#define r_FBufScale vec2(%f, %f)\n#endif\n", fbufWidthScale, fbufHeightScale)); + + if (extra) + { + Q_strcat(dest, size, extra); + } + + // 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(dest, size, "#line 0\n"); +} + +static int GLSL_CompileGPUShader(GLhandleARB program, GLhandleARB *prevShader, const GLcharARB *buffer, int size, GLenum shaderType) +{ + GLint compiled; + GLhandleARB shader; + + shader = qglCreateShaderObjectARB(shaderType); + + qglShaderSourceARB(shader, 1, (const GLcharARB **)&buffer, &size); + + // 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)); + + if (*prevShader) + { + qglDetachObjectARB(program, *prevShader); + qglDeleteObjectARB(*prevShader); + } + + // attach shader to program + qglAttachObjectARB(program, shader); + + *prevShader = shader; + + return 1; +} + + +static void GLSL_DumpText(const char *shaderText, int size, const char *name, GLenum shaderType) +{ + int i, l, inc; + ri.Printf(PRINT_ALL, "static const char *fallback%sShader_%s =\n\"", name, shaderType == GL_VERTEX_SHADER_ARB ? "vp" : "fp"); + l = 0; + + for (i = 0; i < size; i++) + { + switch (shaderText[i]) + { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + case '"': + case '\\': + inc = 2; + break; + default: + inc = 1; + break; + } + + l += inc; + + if (l >= 76) + { + ri.Printf(PRINT_ALL, "\"\n\""); + l = inc; + } + + switch (shaderText[i]) + { + case '\a': + ri.Printf(PRINT_ALL, "\\a"); + break; + case '\b': + ri.Printf(PRINT_ALL, "\\b"); + break; + case '\f': + ri.Printf(PRINT_ALL, "\\f"); + break; + case '\n': + ri.Printf(PRINT_ALL, "\\n"); + break; + case '\r': + ri.Printf(PRINT_ALL, "\\r"); + break; + case '\t': + ri.Printf(PRINT_ALL, "\\t"); + break; + case '\v': + ri.Printf(PRINT_ALL, "\\v"); + break; + case '"': + ri.Printf(PRINT_ALL, "\\\""); + break; + case '\\': + ri.Printf(PRINT_ALL, "\\\\"); + break; + default: + ri.Printf(PRINT_ALL, "%c", shaderText[i]); + break; + } + } + ri.Printf(PRINT_ALL, "\";\n\n"); +} + +static int GLSL_LoadGPUShaderText(const char *name, const char *fallback, + GLenum shaderType, char *dest, int destSize, qboolean dump) +{ + char filename[MAX_QPATH]; + GLcharARB *buffer = NULL; + const GLcharARB *shaderText = 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_DEVELOPER, "...loading '%s'\n", filename); + size = ri.FS_ReadFile(filename, (void **)&buffer); + if(!buffer) + { + if (fallback) + { + ri.Printf(PRINT_DEVELOPER, "couldn't load, using fallback\n"); + shaderText = fallback; + size = strlen(shaderText); + } + else + { + ri.Printf(PRINT_DEVELOPER, "couldn't load!\n"); + return 0; + } + } + else + { + shaderText = buffer; + } + + if (dump) + GLSL_DumpText(shaderText, size, name, shaderType); + + if (size > destSize) + { + result = 0; + } + else + { + Q_strncpyz(dest, shaderText, size + 1); + result = 1; + } + + if (buffer) + { + 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, "\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, "\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_InitGPUShader2(shaderProgram_t * program, const char *name, int attribs, const char *vpCode, const char *fpCode, int numUniforms) +{ + ri.Printf(PRINT_DEVELOPER, "------- GPU shader -------\n"); + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "GLSL_InitGPUShader2: \"%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, &program->vertexShader, vpCode, strlen(vpCode), GL_VERTEX_SHADER_ARB))) + { + ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_VERTEX_SHADER_ARB\n", name); + qglDeleteObjectARB(program->program); + return 0; + } + + if(fpCode) + { + if(!(GLSL_CompileGPUShader(program->program, &program->fragmentShader, fpCode, strlen(fpCode), GL_FRAGMENT_SHADER_ARB))) + { + ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_FRAGMENT_SHADER_ARB\n", name); + qglDeleteObjectARB(program->program); + return 0; + } + } + + 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_BITANGENT) + qglBindAttribLocationARB(program->program, ATTR_INDEX_BITANGENT, "attr_Bitangent"); + + 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"); + + if(attribs & ATTR_POSITION2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_POSITION2, "attr_Position2"); + + if(attribs & ATTR_NORMAL2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_NORMAL2, "attr_Normal2"); + + if(attribs & ATTR_TANGENT2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TANGENT2, "attr_Tangent2"); + + if(attribs & ATTR_BITANGENT2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_BITANGENT2, "attr_Bitangent2"); + + GLSL_LinkProgram(program->program); + + program->numUniforms = numUniforms; + + { + int i, size; + + size = sizeof(*program->uniforms) * numUniforms; + program->uniforms = ri.Malloc(size); + for (i = 0; i < numUniforms; i++) + { + program->uniforms[i] = -1; + } + + size = sizeof(*program->uniformTypes) * numUniforms; + program->uniformTypes = ri.Malloc(size); + memset(program->uniformTypes, 0, size); + + size = sizeof(*program->uniformBufferOffsets) * numUniforms; + program->uniformBufferOffsets = ri.Malloc(size); + memset(program->uniformBufferOffsets, 0, size); + } + + return 1; +} + +static int GLSL_InitGPUShader(shaderProgram_t * program, const char *name, + int attribs, qboolean fragmentShader, const GLcharARB *extra, qboolean addHeader, + const char *fallback_vp, const char *fallback_fp, int numUniforms) +{ + char vpCode[32000]; + char fpCode[32000]; + char *postHeader; + int size; + int result; + + size = sizeof(vpCode); + if (addHeader) + { + GLSL_GetShaderHeader(GL_VERTEX_SHADER_ARB, extra, vpCode, size); + postHeader = &vpCode[strlen(vpCode)]; + size -= strlen(vpCode); + } + else + { + postHeader = &vpCode[0]; + } + + if (!GLSL_LoadGPUShaderText(name, fallback_vp, GL_VERTEX_SHADER_ARB, postHeader, size, qfalse)) + { + return 0; + } + + if (fragmentShader) + { + size = sizeof(fpCode); + if (addHeader) + { + GLSL_GetShaderHeader(GL_FRAGMENT_SHADER_ARB, extra, fpCode, size); + postHeader = &fpCode[strlen(fpCode)]; + size -= strlen(fpCode); + } + else + { + postHeader = &fpCode[0]; + } + + if (!GLSL_LoadGPUShaderText(name, fallback_fp, GL_FRAGMENT_SHADER_ARB, postHeader, size, qfalse)) + { + return 0; + } + } + + result = GLSL_InitGPUShader2(program, name, attribs, vpCode, fragmentShader ? fpCode : NULL, numUniforms); + + return result; +} + +// intentionally deceiving the user here, not actually setting the names but getting their indexes. +void GLSL_AddUniform(shaderProgram_t *program, int uniformNum, const char *name, int type) +{ + GLint *uniforms = program->uniforms; + + uniforms[uniformNum] = qglGetUniformLocationARB(program->program, name); + program->uniformTypes[uniformNum] = type; +} + +void GLSL_FinishGPUShader(shaderProgram_t *program) +{ + if (program->numUniforms) + { + int i, size; + + size = 0; + for (i = 0; i < program->numUniforms; i++) + { + if (program->uniforms[i] != -1) + { + program->uniformBufferOffsets[i] = size; + + switch(program->uniformTypes[i]) + { + case GLSL_INT: + size += sizeof(GLint); + break; + case GLSL_FLOAT: + size += sizeof(GLfloat); + break; + case GLSL_FLOAT5: + size += sizeof(vec_t) * 5; + break; + case GLSL_VEC2: + size += sizeof(vec_t) * 2; + break; + case GLSL_VEC3: + size += sizeof(vec_t) * 3; + break; + case GLSL_VEC4: + size += sizeof(vec_t) * 4; + break; + case GLSL_MAT16: + size += sizeof(vec_t) * 16; + break; + default: + break; + } + } + } + + program->uniformBuffer = ri.Malloc(size); + + } + + GLSL_ValidateProgram(program->program); + GLSL_ShowProgramUniforms(program->program); + GL_CheckErrors(); +} + +void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value) +{ + GLint *uniforms = program->uniforms; + GLint *compare = (GLint *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_INT) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformInt: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (value == *compare) + { + return; + } + + *compare = value; + + qglUniform1iARB(uniforms[uniformNum], value); +} + +void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value) +{ + GLint *uniforms = program->uniforms; + GLfloat *compare = (GLfloat *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_FLOAT) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (value == *compare) + { + return; + } + + *compare = value; + + qglUniform1fARB(uniforms[uniformNum], value); +} + +void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_VEC2) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec2: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (v[0] == compare[0] && v[1] == compare[1]) + { + return; + } + + compare[0] = v[0]; + compare[1] = v[1]; + + qglUniform2fARB(uniforms[uniformNum], v[0], v[1]); +} + +void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_VEC3) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec3: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (VectorCompare(v, compare)) + { + return; + } + + VectorCopy(v, compare); + + qglUniform3fARB(uniforms[uniformNum], v[0], v[1], v[2]); +} + +void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_VEC4) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec4: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (VectorCompare4(v, compare)) + { + return; + } + + VectorCopy4(v, compare); + + qglUniform4fARB(uniforms[uniformNum], v[0], v[1], v[2], v[3]); +} + +void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + { + ri.Printf( PRINT_ALL, "well shit.\n"); + return; + } + + if (program->uniformTypes[uniformNum] != GLSL_FLOAT5) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat5: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (VectorCompare5(v, compare)) + { + return; + } + + VectorCopy5(v, compare); + + qglUniform1fvARB(uniforms[uniformNum], 5, v); +} + +void GLSL_SetUniformMatrix16(shaderProgram_t *program, int uniformNum, const matrix_t matrix) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_MAT16) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformMatrix16: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (Matrix16Compare(matrix, compare)) + { + return; + } + + Matrix16Copy(matrix, compare); + + qglUniformMatrix4fvARB(uniforms[uniformNum], 1, GL_FALSE, matrix); +} + +void GLSL_DeleteGPUShader(shaderProgram_t *program) +{ + if(program->program) + { + if (program->vertexShader) + { + qglDetachObjectARB(program->program, program->vertexShader); + qglDeleteObjectARB(program->vertexShader); + } + + if (program->fragmentShader) + { + qglDetachObjectARB(program->program, program->fragmentShader); + qglDeleteObjectARB(program->fragmentShader); + } + + qglDeleteObjectARB(program->program); + + if (program->uniforms) + { + ri.Free(program->uniforms); + } + + if (program->uniformTypes) + { + ri.Free(program->uniformTypes); + } + + if (program->uniformBuffer) + { + ri.Free(program->uniformBuffer); + } + + if (program->uniformBufferOffsets) + { + ri.Free(program->uniformBufferOffsets); + } + + Com_Memset(program, 0, sizeof(*program)); + } +} + +void GLSL_InitGPUShaders(void) +{ + int startTime, endTime; + int i; + char extradefines[1024]; + int attribs; + int numGenShaders = 0, numLightShaders = 0, numEtcShaders = 0; + + ri.Printf(PRINT_ALL, "------- GLSL_InitGPUShaders -------\n"); + + // make sure the render thread is stopped + R_SyncRenderThread(); + + startTime = ri.Milliseconds(); + + for (i = 0; i < GENERICDEF_COUNT; i++) + { + attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_NORMAL | ATTR_COLOR; + extradefines[0] = '\0'; + + if (i & GENERICDEF_USE_DEFORM_VERTEXES) + Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); + + if (i & GENERICDEF_USE_TCGEN) + Q_strcat(extradefines, 1024, "#define USE_TCGEN\n"); + + if (i & GENERICDEF_USE_VERTEX_ANIMATION) + { + Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); + attribs |= ATTR_POSITION2 | ATTR_NORMAL2; + } + + if (i & GENERICDEF_USE_FOG) + Q_strcat(extradefines, 1024, "#define USE_FOG\n"); + + if (i & GENERICDEF_USE_RGBAGEN) + Q_strcat(extradefines, 1024, "#define USE_RGBAGEN\n"); + + if (i & GENERICDEF_USE_LIGHTMAP) + Q_strcat(extradefines, 1024, "#define USE_LIGHTMAP\n"); + + + if (!GLSL_InitGPUShader(&tr.genericShader[i], "generic", attribs, qtrue, extradefines, qtrue, fallbackGenericShader_vp, fallbackGenericShader_fp, GENERIC_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load generic shader!\n"); + } + + // There's actually no need to filter these out, since they'll + // redirect to -1 if nonexistent, but it's more understandable this way. + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_BASECOLOR, "u_BaseColor", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VERTCOLOR, "u_VertColor", GLSL_VEC4); + + if (i & GENERICDEF_USE_RGBAGEN) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_COLORGEN, "u_ColorGen", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_ALPHAGEN, "u_AlphaGen", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_AMBIENTLIGHT, "u_AmbientLight", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIRECTEDLIGHT, "u_DirectedLight", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_PORTALRANGE, "u_PortalRange", GLSL_FLOAT); + } + + if (i & GENERICDEF_USE_TCGEN) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0, "u_TCGen0", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0VECTOR0, "u_TCGen0Vector0", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0VECTOR1, "u_TCGen0Vector1", GLSL_VEC3); + } + + if (i & GENERICDEF_USE_FOG) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGCOLORMASK, "u_FogColorMask", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGDISTANCE, "u_FogDistance", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGDEPTH, "u_FogDepth", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGEYET, "u_FogEyeT", GLSL_FLOAT); + } + + if (i & GENERICDEF_USE_DEFORM_VERTEXES) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + } + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSETEXMATRIX, "u_DiffuseTexMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TEXTURE1ENV, "u_Texture1Env", GLSL_INT); + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSEMAP, "u_DiffuseMap", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTMAP, "u_LightMap", GLSL_INT); + + + if (i & GENERICDEF_USE_VERTEX_ANIMATION) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + } + + GLSL_FinishGPUShader(&tr.genericShader[i]); + + qglUseProgramObjectARB(tr.genericShader[i].program); + GLSL_SetUniformInt(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); + GLSL_SetUniformInt(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTMAP, TB_LIGHTMAP); + qglUseProgramObjectARB(0); + + numGenShaders++; + } + + + attribs = ATTR_POSITION | ATTR_TEXCOORD; + + if (!GLSL_InitGPUShader(&tr.textureColorShader, "texturecolor", attribs, qtrue, NULL, qfalse, fallbackTextureColorShader_vp, fallbackTextureColorShader_fp, TEXTURECOLOR_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load texturecolor shader!\n"); + } + + GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_DIFFUSEMAP, "u_DiffuseMap", GLSL_INT); + + GLSL_FinishGPUShader(&tr.textureColorShader); + + qglUseProgramObjectARB(tr.textureColorShader.program); + GLSL_SetUniformInt(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + numEtcShaders++; + + + attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; + + if (!GLSL_InitGPUShader(&tr.fogShader, "fogpass", attribs, qtrue, NULL, qtrue, fallbackFogPassShader_vp, fallbackFogPassShader_fp, FOGPASS_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load fogpass shader!\n"); + } + + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_FOGDISTANCE, "u_FogDistance", GLSL_VEC4); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_FOGDEPTH, "u_FogDepth", GLSL_VEC4); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_FOGEYET, "u_FogEyeT", GLSL_FLOAT); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.fogShader, FOGPASS_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + + GLSL_FinishGPUShader(&tr.fogShader); + + numEtcShaders++; + + + attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD; + + if (!GLSL_InitGPUShader(&tr.dlightallShader, "dlight", attribs, qtrue, NULL, qtrue, fallbackDlightShader_vp, fallbackDlightShader_fp, DLIGHT_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load dlight shader!\n"); + } + + GLSL_AddUniform(&tr.dlightallShader, DLIGHT_UNIFORM_DLIGHTINFO, "u_DlightInfo", GLSL_VEC4); + GLSL_AddUniform(&tr.dlightallShader, DLIGHT_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.dlightallShader, DLIGHT_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + GLSL_AddUniform(&tr.dlightallShader, DLIGHT_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.dlightallShader, DLIGHT_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + GLSL_AddUniform(&tr.dlightallShader, DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + + GLSL_FinishGPUShader(&tr.dlightallShader); + + qglUseProgramObjectARB(tr.dlightallShader.program); + GLSL_SetUniformInt(&tr.dlightallShader, DLIGHT_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + numEtcShaders++; + + + for (i = 0; i < LIGHTDEF_COUNT; i++) + { + // skip impossible combos + if (!(i & LIGHTDEF_USE_LIGHTMAP) && (i & LIGHTDEF_USE_DELUXEMAP)) + continue; + + if (!(i & LIGHTDEF_USE_NORMALMAP) && (i & LIGHTDEF_USE_PARALLAXMAP)) + continue; +#if 0 + if ((i & LIGHTDEF_USE_LIGHTMAP) && (i & LIGHTDEF_ENTITY)) + continue; + + if ((i & LIGHTDEF_USE_DELUXEMAP) && (i & LIGHTDEF_ENTITY)) + continue; +#endif + if ((i & LIGHTDEF_USE_LIGHTMAP) && (i & LIGHTDEF_USE_LIGHT_VECTOR)) + continue; + + if ((i & LIGHTDEF_USE_DELUXEMAP) && (i & LIGHTDEF_USE_LIGHT_VECTOR)) + continue; + + attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR | ATTR_NORMAL; + extradefines[0] = '\0'; + + if (r_normalAmbient->value > 0.003f) + Q_strcat(extradefines, 1024, va("#define r_normalAmbient %f\n", r_normalAmbient->value)); + + if (r_dlightMode->integer >= 2) + Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n"); + + if (i & LIGHTDEF_USE_LIGHTMAP) + { + Q_strcat(extradefines, 1024, "#define USE_LIGHTMAP\n"); + attribs |= ATTR_LIGHTCOORD; + } + + if (i & LIGHTDEF_USE_LIGHT_VECTOR) + { + Q_strcat(extradefines, 1024, "#define USE_LIGHT_VECTOR\n"); + } + + if (i & LIGHTDEF_USE_NORMALMAP && r_normalMapping->integer) + { + Q_strcat(extradefines, 1024, "#define USE_NORMALMAP\n"); + attribs |= ATTR_TANGENT | ATTR_BITANGENT; + } + + if (i & LIGHTDEF_USE_SPECULARMAP && r_specularMapping->integer) + Q_strcat(extradefines, 1024, "#define USE_SPECULARMAP\n"); + + if (i & LIGHTDEF_USE_DELUXEMAP && r_deluxeMapping->integer) + { + Q_strcat(extradefines, 1024, "#define USE_DELUXEMAP\n"); + + if (r_deluxeMapping->integer == 2 && i & LIGHTDEF_USE_NORMALMAP) + { + Q_strcat(extradefines, 1024, "#define DELUXEMAP_IS_LOCAL\n"); + } + } + + if (i & LIGHTDEF_USE_PARALLAXMAP && !(i & LIGHTDEF_ENTITY) && r_parallaxMapping->integer) + Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP\n"); + + if (i & LIGHTDEF_TCGEN_ENVIRONMENT) + Q_strcat(extradefines, 1024, "#define TCGEN_ENVIRONMENT\n"); + + if (i & LIGHTDEF_ENTITY) + { + Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n#define USE_MODELMATRIX\n"); + attribs |= ATTR_POSITION2 | ATTR_NORMAL2; + + if (i & LIGHTDEF_USE_NORMALMAP && r_normalMapping->integer) + { + attribs |= ATTR_TANGENT2 | ATTR_BITANGENT2; + } + } + + if (!GLSL_InitGPUShader(&tr.lightallShader[i], "lightall", attribs, qtrue, extradefines, qtrue, fallbackLightallShader_vp, fallbackLightallShader_fp, GENERIC_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load lightall shader!\n"); + } + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MODELMATRIX, "u_ModelMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSETEXMATRIX, "u_DiffuseTexMatrix", GLSL_MAT16); + //GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALTEXMATRIX, "u_NormalTexMatrix", GLSL_MAT16); + //GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARTEXMATRIX, "u_SpecularTexMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSEMAP, "u_DiffuseMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTMAP, "u_LightMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALMAP, "u_NormalMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DELUXEMAP, "u_DeluxeMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARMAP, "u_SpecularMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SHADOWMAP, "u_ShadowMap", GLSL_INT); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_AMBIENTLIGHT, "u_AmbientLight", GLSL_VEC3); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIRECTEDLIGHT, "u_DirectedLight", GLSL_VEC3); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARREFLECTANCE, "u_SpecularReflectance", GLSL_FLOAT); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_BASECOLOR, "u_BaseColor", GLSL_VEC4); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VERTCOLOR, "u_VertColor", GLSL_VEC4); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + + GLSL_FinishGPUShader(&tr.lightallShader[i]); + + qglUseProgramObjectARB(tr.lightallShader[i].program); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTMAP, TB_LIGHTMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALMAP, TB_NORMALMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_DELUXEMAP, TB_DELUXEMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARMAP, TB_SPECULARMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_SHADOWMAP, TB_SHADOWMAP); + qglUseProgramObjectARB(0); + + numLightShaders++; + } + + + attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; + + if (!GLSL_InitGPUShader(&tr.shadowmapShader, "shadowfill", attribs, qtrue, NULL, qtrue, fallbackShadowfillShader_vp, fallbackShadowfillShader_fp, GENERIC_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load depth shader!\n"); + } + + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_MODELMATRIX, "u_ModelMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); + + GLSL_FinishGPUShader(&tr.shadowmapShader); + + numEtcShaders++; + + + attribs = ATTR_POSITION | ATTR_NORMAL; + extradefines[0] = '\0'; + + Q_strcat(extradefines, 1024, "#define USE_PCF\n#define USE_DISCARD"); + + if (!GLSL_InitGPUShader(&tr.pshadowShader, "pshadow", attribs, qtrue, extradefines, qtrue, fallbackPshadowShader_vp, fallbackPshadowShader_fp, PSHADOW_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load pshadow shader!\n"); + } + + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTFORWARD, "u_LightForward", GLSL_VEC3); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTUP, "u_LightUp", GLSL_VEC3); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTRIGHT, "u_LightRight", GLSL_VEC3); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); + + GLSL_FinishGPUShader(&tr.pshadowShader); + + qglUseProgramObjectARB(tr.pshadowShader.program); + GLSL_SetUniformInt(&tr.pshadowShader, PSHADOW_UNIFORM_SHADOWMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + numEtcShaders++; + + endTime = ri.Milliseconds(); + + ri.Printf(PRINT_ALL, "loaded %i GLSL shaders (%i gen %i light %i etc) in %5.2f seconds\n", + numGenShaders + numLightShaders + numEtcShaders, numGenShaders, numLightShaders, + numEtcShaders, (endTime - startTime) / 1000.0); + + if (0) + { + GLSL_LoadGPUShaderText("Generic", NULL, GL_VERTEX_SHADER_ARB, NULL, 0, qtrue); + GLSL_LoadGPUShaderText("Generic", NULL, GL_FRAGMENT_SHADER_ARB, NULL, 0, qtrue); + + GLSL_LoadGPUShaderText("TextureColor", NULL, GL_VERTEX_SHADER_ARB, NULL, 0, qtrue); + GLSL_LoadGPUShaderText("TextureColor", NULL, GL_FRAGMENT_SHADER_ARB, NULL, 0, qtrue); + + GLSL_LoadGPUShaderText("FogPass", NULL, GL_VERTEX_SHADER_ARB, NULL, 0, qtrue); + GLSL_LoadGPUShaderText("FogPass", NULL, GL_FRAGMENT_SHADER_ARB, NULL, 0, qtrue); + + GLSL_LoadGPUShaderText("Dlight", NULL, GL_VERTEX_SHADER_ARB, NULL, 0, qtrue); + GLSL_LoadGPUShaderText("Dlight", NULL, GL_FRAGMENT_SHADER_ARB, NULL, 0, qtrue); + + GLSL_LoadGPUShaderText("Lightall", NULL, GL_VERTEX_SHADER_ARB, NULL, 0, qtrue); + GLSL_LoadGPUShaderText("Lightall", NULL, GL_FRAGMENT_SHADER_ARB, NULL, 0, qtrue); + + GLSL_LoadGPUShaderText("Shadowfill", NULL, GL_VERTEX_SHADER_ARB, NULL, 0, qtrue); + GLSL_LoadGPUShaderText("Shadowfill", NULL, GL_FRAGMENT_SHADER_ARB, NULL, 0, qtrue); + + GLSL_LoadGPUShaderText("Pshadow", NULL, GL_VERTEX_SHADER_ARB, NULL, 0, qtrue); + GLSL_LoadGPUShaderText("Pshadow", NULL, GL_FRAGMENT_SHADER_ARB, NULL, 0, qtrue); + } + +} + +void GLSL_ShutdownGPUShaders(void) +{ + int i; + + ri.Printf(PRINT_ALL, "------- GLSL_ShutdownGPUShaders -------\n"); + + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION2); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); + qglDisableVertexAttribArrayARB(ATTR_INDEX_COLOR); + GLSL_BindNullProgram(); + + for ( i = 0; i < GENERICDEF_COUNT; i++) + { + GLSL_DeleteGPUShader(&tr.genericShader[i]); + } + + GLSL_DeleteGPUShader(&tr.textureColorShader); + GLSL_DeleteGPUShader(&tr.fogShader); + GLSL_DeleteGPUShader(&tr.dlightallShader); + + for ( i = 0; i < LIGHTDEF_COUNT; i++) + { + GLSL_DeleteGPUShader(&tr.lightallShader[i]); + } + + GLSL_DeleteGPUShader(&tr.shadowmapShader); + GLSL_DeleteGPUShader(&tr.pshadowShader); + + glState.currentProgram = 0; + qglUseProgramObjectARB(0); +} + + +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; + backEnd.pc.c_glslShaderBinds++; + } +} + + +void GLSL_BindNullProgram(void) +{ + if(r_logFile->integer) + { + GLimp_LogComment("--- GL_BindNullProgram ---\n"); + } + + if(glState.currentProgram) + { + qglUseProgramObjectARB(0); + glState.currentProgram = NULL; + } +} + + +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_TANGENT) + { + if(stateBits & ATTR_TANGENT) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TANGENT); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TANGENT )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT); + } + } + + if(diff & ATTR_BITANGENT) + { + if(stateBits & ATTR_BITANGENT) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_BITANGENT )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_BITANGENT )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); + } + } + + 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); + } + } + + if(diff & ATTR_POSITION2) + { + if(stateBits & ATTR_POSITION2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_POSITION2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION2); + } + } + + if(diff & ATTR_NORMAL2) + { + if(stateBits & ATTR_NORMAL2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_NORMAL2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); + } + } + + if(diff & ATTR_TANGENT2) + { + if(stateBits & ATTR_TANGENT2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TANGENT2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); + } + } + + if(diff & ATTR_BITANGENT2) + { + if(stateBits & ATTR_BITANGENT2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_BITANGENT2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_BITANGENT2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); + } + } + + 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.vertexAttribsNewFrame * glState.currentVBO->size_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.vertexAttribsNewFrame * glState.currentVBO->size_normal)); + glState.vertexAttribPointersSet |= ATTR_NORMAL; + } + + if((attribBits & ATTR_TANGENT) && !(glState.vertexAttribPointersSet & ATTR_TANGENT)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TANGENT )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TANGENT, 3, GL_FLOAT, 0, glState.currentVBO->stride_tangent, BUFFER_OFFSET(glState.currentVBO->ofs_tangent + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_TANGENT; + } + + if((attribBits & ATTR_BITANGENT) && !(glState.vertexAttribPointersSet & ATTR_BITANGENT)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_BITANGENT )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_BITANGENT, 3, GL_FLOAT, 0, glState.currentVBO->stride_bitangent, BUFFER_OFFSET(glState.currentVBO->ofs_bitangent + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_BITANGENT; + } + + 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; + } + + if((attribBits & ATTR_POSITION2) && !(glState.vertexAttribPointersSet & ATTR_POSITION2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_POSITION2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_POSITION2, 3, GL_FLOAT, 0, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz + glState.vertexAttribsOldFrame * glState.currentVBO->size_xyz)); + glState.vertexAttribPointersSet |= ATTR_POSITION2; + } + + if((attribBits & ATTR_NORMAL2) && !(glState.vertexAttribPointersSet & ATTR_NORMAL2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_NORMAL2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_NORMAL2, 3, GL_FLOAT, 0, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); + glState.vertexAttribPointersSet |= ATTR_NORMAL2; + } + + if((attribBits & ATTR_TANGENT2) && !(glState.vertexAttribPointersSet & ATTR_TANGENT2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TANGENT2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TANGENT2, 3, GL_FLOAT, 0, glState.currentVBO->stride_tangent, BUFFER_OFFSET(glState.currentVBO->ofs_tangent + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_TANGENT2; + } + + if((attribBits & ATTR_BITANGENT2) && !(glState.vertexAttribPointersSet & ATTR_BITANGENT2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_BITANGENT2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_BITANGENT2, 3, GL_FLOAT, 0, glState.currentVBO->stride_bitangent, BUFFER_OFFSET(glState.currentVBO->ofs_bitangent + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_BITANGENT2; + } +} + +shaderProgram_t *GLSL_GetGenericShaderProgram(int stage) +{ + shaderStage_t *pStage = tess.xstages[stage]; + int shaderAttribs = 0; + + if (tess.fogNum && pStage->adjustColorsForFog) + { + shaderAttribs |= GENERICDEF_USE_FOG; + } + + if (pStage->bundle[1].image[0] && tess.shader->multitextureEnv) + { + shaderAttribs |= GENERICDEF_USE_LIGHTMAP; + } + + switch (pStage->rgbGen) + { + case CGEN_LIGHTING_DIFFUSE: + shaderAttribs |= GENERICDEF_USE_RGBAGEN; + break; + default: + break; + } + + switch (pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + case AGEN_PORTAL: + case AGEN_FRESNEL: + shaderAttribs |= GENERICDEF_USE_RGBAGEN; + break; + default: + break; + } + + if (pStage->bundle[0].tcGen != TCGEN_TEXTURE) + { + shaderAttribs |= GENERICDEF_USE_TCGEN; + } + + if (tess.shader->numDeforms && !ShaderRequiresCPUDeforms(tess.shader)) + { + shaderAttribs |= GENERICDEF_USE_DEFORM_VERTEXES; + } + + if (glState.vertexAttribsInterpolation > 0.0f && backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; + } + + return &tr.genericShader[shaderAttribs]; +} + Index: code/renderer/tr_image.c =================================================================== --- code/renderer/tr_image.c (revision 2095) +++ code/renderer/tr_image.c (working copy) @@ -659,6 +659,10 @@ } else { + if ( glConfig.textureCompression == TC_S3TC_ARB ) + { + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + } if ( r_texturebits->integer == 16 ) { internalFormat = GL_RGBA4; @@ -741,16 +745,16 @@ if (mipmap) { - if ( textureFilterAnisotropic ) + if ( glRefConfig.textureFilterAnisotropic ) qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); + (GLint)Com_Clamp( 1, glRefConfig.maxAnisotropy, r_ext_max_anisotropy->integer ) ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { - if ( textureFilterAnisotropic ) + if ( glRefConfig.textureFilterAnisotropic ) qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); @@ -766,6 +770,154 @@ } +static void EmptyTexture( int width, int height, qboolean mipmap, + qboolean picmip, qboolean lightMap, int *format, int *pUploadWidth, + int *pUploadHeight ) +{ + int samples; + int scaled_width, scaled_height; + GLenum internalFormat = GL_RGB; + + // + // convert to exact power of 2 sizes + // + for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + ; + if ( r_roundImagesDown->integer && scaled_width > width ) + scaled_width >>= 1; + if ( r_roundImagesDown->integer && scaled_height > height ) + scaled_height >>= 1; + + if ( scaled_width != width || scaled_height != height ) { + width = scaled_width; + height = scaled_height; + } + + // + // perform optional picmip operation + // + if ( picmip ) { + scaled_width >>= r_picmip->integer; + scaled_height >>= r_picmip->integer; + } + + // + // clamp to minimum size + // + if (scaled_width < 1) { + scaled_width = 1; + } + if (scaled_height < 1) { + scaled_height = 1; + } + + // + // clamp to the current upper OpenGL limit + // scale both axis down equally so we don't have to + // deal with a half mip resampling + // + while ( scaled_width > glConfig.maxTextureSize + || scaled_height > glConfig.maxTextureSize ) { + scaled_width >>= 1; + scaled_height >>= 1; + } + + // + // scan the texture for each channel's max values + // and verify if the alpha channel is being used or not + // + samples = 3; + + if(lightMap) + { + if(r_greyscale->integer) + internalFormat = GL_LUMINANCE; + else + internalFormat = GL_RGB; + } + else + { + samples = 4; + + if(r_greyscale->integer) + { + if(r_texturebits->integer == 16) + internalFormat = GL_LUMINANCE8_ALPHA8; + else if(r_texturebits->integer == 32) + internalFormat = GL_LUMINANCE16_ALPHA16; + else + internalFormat = GL_LUMINANCE_ALPHA; + } + else + { + if ( glConfig.textureCompression == TC_S3TC_ARB ) + { + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + } + if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGBA4; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGBA8; + } + else + { + internalFormat = GL_RGBA; + } + } + } + + *pUploadWidth = scaled_width; + *pUploadHeight = scaled_height; + *format = internalFormat; + + qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + + qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + } + } + + if (mipmap) + { + if ( glRefConfig.textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (GLint)Com_Clamp( 1, glRefConfig.maxAnisotropy, r_ext_max_anisotropy->integer ) ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + if ( glRefConfig.textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + GL_CheckErrors(); +} + + /* ================ R_CreateImage @@ -773,8 +925,8 @@ This is the only way any image_t are created ================ */ -image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, - qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { +image_t *R_CreateImage2( const char *name, const byte *pic, int width, int height, + qboolean mipmap, qboolean allowPicmip, int glWrapClampMode, qboolean cube ) { image_t *image; qboolean isLightmap = qfalse; long hash; @@ -814,25 +966,49 @@ GL_SelectTexture( image->TMU ); } - GL_Bind(image); + if (cube) + { + GL_BindCubemap(image); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - Upload32( (unsigned *)pic, image->width, image->height, - image->mipmap, - allowPicmip, - isLightmap, - &image->internalFormat, - &image->uploadWidth, - &image->uploadHeight ); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); + image->internalFormat = GL_RGBA8; + image->uploadWidth = width; + image->uploadHeight = height; + } + else + { + GL_Bind(image); - qglBindTexture( GL_TEXTURE_2D, 0 ); + if (pic) + { + Upload32( (unsigned *)pic, image->width, image->height, image->mipmap, + allowPicmip, isLightmap, &image->internalFormat, &image->uploadWidth, + &image->uploadHeight ); + } + else + { + EmptyTexture(image->width, image->height, image->mipmap, allowPicmip, + isLightmap, &image->internalFormat, &image->uploadWidth, + &image->uploadHeight ); + } - if ( image->TMU == 1 ) { - GL_SelectTexture( 0 ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); } + GL_SelectTexture( 0 ); + hash = generateHashValue(name); image->next = hashTable[hash]; hashTable[hash] = image; @@ -840,6 +1016,161 @@ return image; } +image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, + qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) +{ + return R_CreateImage2( name, pic, width, height, mipmap, allowPicmip, glWrapClampMode, qfalse); +} + +image_t *R_CreateCubeImage( const char *name, const byte *pic, int width, int height, + qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) +{ + return R_CreateImage2( name, pic, width, height, mipmap, allowPicmip, glWrapClampMode, qtrue); +} + +void R_UpdateSubImage( image_t *image, const byte *pic, int x, int y, int width, int height) +{ + unsigned *scaledBuffer = NULL; + unsigned *resampledBuffer = NULL; + int scaled_width, scaled_height, scaled_x, scaled_y; + unsigned *data = (unsigned *)pic; + + // + // convert to exact power of 2 sizes + // + for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + ; + if ( r_roundImagesDown->integer && scaled_width > width ) + scaled_width >>= 1; + if ( r_roundImagesDown->integer && scaled_height > height ) + scaled_height >>= 1; + + if ( scaled_width != width || scaled_height != height ) { + resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); + ResampleTexture ( data, width, height, resampledBuffer, scaled_width, scaled_height); + data = resampledBuffer; + x = x * scaled_width / width; + y = y * scaled_height / height; + width = scaled_width; + height = scaled_height; + } + +// + // perform optional picmip operation + // + if ( image->allowPicmip ) { + scaled_width >>= r_picmip->integer; + scaled_height >>= r_picmip->integer; + } + + // + // clamp to minimum size + // + if (scaled_width < 1) { + scaled_width = 1; + } + if (scaled_height < 1) { + scaled_height = 1; + } + + // + // clamp to the current upper OpenGL limit + // scale both axis down equally so we don't have to + // deal with a half mip resampling + // + while ( scaled_width > glConfig.maxTextureSize + || scaled_height > glConfig.maxTextureSize ) { + scaled_width >>= 1; + scaled_height >>= 1; + } + + scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + + if ( qglActiveTextureARB ) { + GL_SelectTexture( image->TMU ); + } + + GL_Bind(image); + + // copy or resample data as appropriate for first MIP level + if ( ( scaled_width == width ) && + ( scaled_height == height ) ) { + if (!image->mipmap) + { + scaled_x = x * scaled_width / width; + scaled_y = y * scaled_height / height; + qglTexSubImage2D( GL_TEXTURE_2D, 0, scaled_x, scaled_y, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, data ); + + GL_CheckErrors(); + goto done; + } + Com_Memcpy (scaledBuffer, data, width*height*4); + } + else + { + // use the normal mip-mapping function to go down from here + while ( width > scaled_width || height > scaled_height ) { + R_MipMap( (byte *)data, width, height ); + width >>= 1; + height >>= 1; + x >>= 1; + y >>= 1; + if ( width < 1 ) { + width = 1; + } + if ( height < 1 ) { + height = 1; + } + } + Com_Memcpy( scaledBuffer, data, width * height * 4 ); + } + + R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !image->mipmap ); + + scaled_x = x * scaled_width / width; + scaled_y = y * scaled_height / height; + qglTexSubImage2D( GL_TEXTURE_2D, 0, scaled_x, scaled_y, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); + + if (image->mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + R_MipMap( (byte *)scaledBuffer, scaled_width, scaled_height ); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + + if ( r_colorMipLevels->integer ) { + R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] ); + } + + scaled_x = x * scaled_width / width; + scaled_y = y * scaled_height / height; + qglTexSubImage2D( GL_TEXTURE_2D, 0, scaled_x, scaled_y, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); + } + } + +done: + + GL_SelectTexture( 0 ); + + GL_CheckErrors(); + + if ( scaledBuffer != 0 ) + ri.Hunk_FreeTempMemory( scaledBuffer ); + if ( resampledBuffer != 0 ) + ri.Hunk_FreeTempMemory( resampledBuffer ); +} + //=================================================================== typedef struct @@ -1182,6 +1513,35 @@ Com_Memset( data, 255, sizeof( data ) ); tr.whiteImage = R_CreateImage("*white", (byte *)data, 8, 8, qfalse, qfalse, GL_REPEAT ); + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer + && glRefConfig.glsl && r_arb_shader_objects->integer) + { + if (r_dlightMode->integer >= 2) + { + for( x = 0; x < MAX_DLIGHTS; x++) + { + tr.shadowCubemaps[x] = R_CreateCubeImage(va("*shadowcubemap%i", x), (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + } + } + + for( x = 0; x < MAX_DRAWN_PSHADOWS; x++) + { + tr.pshadowMaps[x] = R_CreateImage(va("*shadowmap%i", x), (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + } + } + + // black image + for (x=0 ; x 1008) + { + char buffer[1024]; + char *p; + int size = strlen(glConfig.extensions_string); + ri.Printf( PRINT_ALL, "GL_EXTENSIONS: "); + + p = glConfig.extensions_string; + while(size > 0) + { + Q_strncpyz(buffer, p, 1024); + ri.Printf( PRINT_ALL, "%s", buffer ); + p += 1023; + size -= 1023; + } + ri.Printf( PRINT_ALL, "\n" ); + } + else + { + ri.Printf( PRINT_ALL, "GL_EXTENSIONS: %s\n", glConfig.extensions_string ); + } ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); ri.Printf( PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); @@ -975,6 +1031,58 @@ } /* +================ +GfxMemInfo_f +================ +*/ +void GfxMemInfo_f( void ) +{ + switch (glRefConfig.memInfo) + { + case MI_NONE: + { + ri.Printf(PRINT_ALL, "No extension found for GPU memory info.\n"); + } + break; + case MI_NVX: + { + int value; + + qglGetIntegerv(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: %ikb\n", value); + + qglGetIntegerv(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: %ikb\n", value); + + qglGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: %ikb\n", value); + + qglGetIntegerv(GPU_MEMORY_INFO_EVICTION_COUNT_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTION_COUNT_NVX: %i\n", value); + + qglGetIntegerv(GPU_MEMORY_INFO_EVICTED_MEMORY_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTED_MEMORY_NVX: %ikb\n", value); + } + break; + case MI_ATI: + { + // GL_ATI_meminfo + int value[4]; + + qglGetIntegerv(VBO_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "VBO_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + + qglGetIntegerv(TEXTURE_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "TEXTURE_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + + qglGetIntegerv(RENDERBUFFER_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "RENDERBUFFER_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + } + break; + } +} + +/* =============== R_Register =============== @@ -989,6 +1097,9 @@ 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_multi_draw_arrays = ri.Cvar_Get( "r_ext_multi_draw_arrays", "1", CVAR_ARCHIVE | CVAR_LATCH); r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", "0", CVAR_ARCHIVE | CVAR_LATCH ); @@ -1023,6 +1134,16 @@ r_greyscale = ri.Cvar_Get("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH); ri.Cvar_CheckRange(r_greyscale, 0, 1, qfalse); + r_normalMapping = ri.Cvar_Get( "r_normalMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_specularMapping = ri.Cvar_Get( "r_specularMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_deluxeMapping = ri.Cvar_Get( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_parallaxMapping = ri.Cvar_Get( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_normalAmbient = ri.Cvar_Get( "r_normalAmbient", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_dlightMode = ri.Cvar_Get( "r_dlightMode", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_pshadowDist = ri.Cvar_Get( "r_pshadowDist", "128", CVAR_ARCHIVE ); + r_recalcMD3Normals = ri.Cvar_Get( "r_recalcMD3Normals", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_mergeLightmaps = ri.Cvar_Get( "r_mergeLightmaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); + // // temporary latched variables that can only change over a restart // @@ -1064,6 +1185,8 @@ r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE); + r_mergeMultidraws = ri.Cvar_Get("r_mergeMultidraws", "1", CVAR_ARCHIVE); + r_mergeLeafSurfaces = ri.Cvar_Get("r_mergeLeafSurfaces", "1", CVAR_ARCHIVE); // // temporary variables that can change at any time @@ -1129,6 +1252,7 @@ ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f ); ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f ); ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f ); + ri.Cmd_AddCommand( "gfxmeminfo", GfxMemInfo_f ); } /* @@ -1215,8 +1339,19 @@ InitOpenGL(); + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer + && glRefConfig.glsl && r_arb_shader_objects->integer) + { + GLSL_InitGPUShaders(); + } + R_InitImages(); + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + R_InitVBOs(); + } + R_InitShaders(); R_InitSkins(); @@ -1257,6 +1392,14 @@ R_SyncRenderThread(); R_ShutdownCommandBuffers(); R_DeleteTextures(); + if (glRefConfig.vertexBufferObject) + { + R_ShutdownVBOs(); + } + if (glRefConfig.glsl) + { + GLSL_ShutdownGPUShaders(); + } } R_DoneFreeType(); Index: code/renderer/tr_light.c =================================================================== --- code/renderer/tr_light.c (revision 2095) +++ code/renderer/tr_light.c (working copy) @@ -92,7 +92,7 @@ // set the dlight bits in all the surfaces for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - surf = bmodel->firstSurface + i; + surf = tr.world->surfaces + bmodel->firstSurface + i; if ( *surf->data == SF_FACE ) { ((srfSurfaceFace_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask; @@ -175,13 +175,19 @@ byte *data; int lat, lng; vec3_t normal; + qboolean ignore; #if idppc float d0, d1, d2, d3, d4, d5; #endif factor = 1.0; data = gridData; + ignore = qfalse; for ( j = 0 ; j < 3 ; j++ ) { if ( i & (1<= tr.world->lightGridBounds[j] - 1) + { + ignore = qtrue; // ignore values outside lightgrid + } factor *= frac[j]; data += gridStep[j]; } else { @@ -189,7 +195,7 @@ } } - if ( !(data[0]+data[1]+data[2]) ) { + if ( ignore || !(data[0]+data[1]+data[2]+data[3]+data[4]+data[5]) ) { continue; // ignore samples in walls } totalFactor += factor; @@ -365,10 +371,19 @@ ((byte *)&ent->ambientLightInt)[3] = 0xff; // transform the direction to local space + // no need to do this if using lightentity glsl shader VectorNormalize( lightDir ); - ent->lightDir[0] = DotProduct( lightDir, ent->e.axis[0] ); - ent->lightDir[1] = DotProduct( lightDir, ent->e.axis[1] ); - ent->lightDir[2] = DotProduct( lightDir, ent->e.axis[2] ); + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && r_arb_shader_objects->integer) + { + VectorCopy(lightDir, ent->lightDir); + } + else + { + ent->lightDir[0] = DotProduct( lightDir, ent->e.axis[0] ); + ent->lightDir[1] = DotProduct( lightDir, ent->e.axis[1] ); + ent->lightDir[2] = DotProduct( lightDir, ent->e.axis[2] ); + } } /* Index: code/renderer/tr_local.h =================================================================== --- code/renderer/tr_local.h (revision 2095) +++ code/renderer/tr_local.h (working copy) @@ -28,12 +28,15 @@ #include "../qcommon/qfiles.h" #include "../qcommon/qcommon.h" #include "tr_public.h" +#include "tr_extramath.h" #include "qgl.h" #include "iqm.h" #define GL_INDEX_TYPE GL_UNSIGNED_INT typedef unsigned int glIndex_t; +#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 // parallel on a dual cpu machine @@ -50,6 +53,9 @@ #define MAX_STATE_NAME 32 +#define MAX_VISCOUNTS 5 +#define MAX_VBOS 4096 +#define MAX_IBOS 4096 typedef struct dlight_s { vec3_t origin; @@ -82,6 +88,7 @@ vec3_t axis[3]; // orientation in world vec3_t viewOrigin; // viewParms->or.origin in local coordinates float modelMatrix[16]; + float transformMatrix[16]; } orientationr_t; typedef struct image_s { @@ -102,6 +109,47 @@ 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 ofs_tangent; + uint32_t ofs_bitangent; + uint32_t stride_xyz; + uint32_t stride_normal; + uint32_t stride_st; + uint32_t stride_lightmap; + uint32_t stride_vertexcolor; + uint32_t stride_tangent; + uint32_t stride_bitangent; + uint32_t size_xyz; + uint32_t size_normal; + + 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 { @@ -167,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, @@ -177,7 +244,8 @@ AGEN_LIGHTING_SPECULAR, AGEN_WAVEFORM, AGEN_PORTAL, - AGEN_CONST + AGEN_CONST, + AGEN_FRESNEL } alphaGen_t; typedef enum { @@ -195,6 +263,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 @@ -292,9 +368,32 @@ qboolean isVideoMap; } textureBundle_t; -#define NUM_TEXTURE_BUNDLES 2 +enum +{ + TB_COLORMAP = 0, + TB_DIFFUSEMAP = 0, + TB_LIGHTMAP, + TB_NORMALMAP, + TB_DELUXEMAP, + TB_SPECULARMAP, + TB_SHADOWMAP, + NUM_TEXTURE_BUNDLES = 6 +}; +typedef enum +{ + // material shader stage types + ST_COLORMAP = 0, // vanilla Q3A style shader treatening + ST_DIFFUSEMAP = 0, // treat color and diffusemap the same + ST_NORMALMAP, + ST_NORMALPARALLAXMAP, + ST_SPECULARMAP, + ST_GLSL +} stageType_t; + typedef struct { + stageType_t type; + qboolean active; textureBundle_t bundle[NUM_TEXTURE_BUNDLES]; @@ -312,6 +411,10 @@ acff_t adjustColorsForFog; qboolean isDetail; + + struct shaderProgram_s *glslShaderGroup; + int glslShaderIndex; + float specularReflectance; } shaderStage_t; struct shaderCommands_s; @@ -373,6 +476,7 @@ fogParms_t fogParms; float portalRange; // distance to fog out at + qboolean isPortal; int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage) @@ -383,10 +487,7 @@ fogPass_t fogPass; // draw a blended pass, possibly with depth test equals - qboolean needsNormal; // not all shaders will need all data to be gathered - qboolean needsST1; - qboolean needsST2; - qboolean needsColor; + int vertexAttribs; // not all shaders will need all data to be gathered int numDeforms; deformStage_t deforms[MAX_SHADER_DEFORMS]; @@ -412,6 +513,29 @@ struct shader_s *next; } shader_t; +static ID_INLINE qboolean ShaderRequiresCPUDeforms(const shader_t * shader) +{ + if(shader->numDeforms) + { + const deformStage_t *ds = &shader->deforms[0]; + + if (shader->numDeforms > 1) + return qtrue; + + switch (ds->deformation) + { + case DEFORM_WAVE: + case DEFORM_BULGE: + return qfalse; + + default: + return qtrue; + } + } + + 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 @@ -420,7 +544,295 @@ shader_t *shader; } shaderState_t; +enum +{ + ATTR_INDEX_POSITION = 0, + ATTR_INDEX_TEXCOORD0 = 1, + ATTR_INDEX_TEXCOORD1 = 2, + ATTR_INDEX_TANGENT = 3, + ATTR_INDEX_BITANGENT = 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, + // GPU vertex animations + ATTR_INDEX_POSITION2 = 11, + ATTR_INDEX_TANGENT2 = 12, + ATTR_INDEX_BITANGENT2 = 13, + ATTR_INDEX_NORMAL2 = 14 +}; + +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 = 0x0001, + ATTR_TEXCOORD = 0x0002, + ATTR_LIGHTCOORD = 0x0004, + ATTR_TANGENT = 0x0008, + ATTR_BITANGENT = 0x0010, + ATTR_NORMAL = 0x0020, + ATTR_COLOR = 0x0040, + ATTR_PAINTCOLOR = 0x0080, + ATTR_LIGHTDIRECTION = 0x0100, + ATTR_BONE_INDEXES = 0x0200, + ATTR_BONE_WEIGHTS = 0x0400, + + // for .md3 interpolation + ATTR_POSITION2 = 0x0800, + ATTR_TANGENT2 = 0x1000, + ATTR_BITANGENT2 = 0x2000, + ATTR_NORMAL2 = 0x4000, + + ATTR_DEFAULT = ATTR_POSITION, + ATTR_BITS = ATTR_POSITION | + ATTR_TEXCOORD | + ATTR_LIGHTCOORD | + ATTR_TANGENT | + ATTR_BITANGENT | + ATTR_NORMAL | + ATTR_COLOR | + ATTR_PAINTCOLOR | + ATTR_LIGHTDIRECTION | + ATTR_BONE_INDEXES | + ATTR_BONE_WEIGHTS | + ATTR_POSITION2 | + ATTR_TANGENT2 | + ATTR_BITANGENT2 | + ATTR_NORMAL2 +}; + +enum +{ + GENERICDEF_USE_DEFORM_VERTEXES = 0x0001, + GENERICDEF_USE_TCGEN = 0x0002, + GENERICDEF_USE_VERTEX_ANIMATION = 0x0004, + GENERICDEF_USE_FOG = 0x0008, + GENERICDEF_USE_RGBAGEN = 0x0010, + GENERICDEF_USE_LIGHTMAP = 0x0020, + GENERICDEF_ALL = 0x003F, + GENERICDEF_COUNT = 0x0040, +}; + +enum +{ + LIGHTDEF_USE_LIGHTMAP = 0x0001, + LIGHTDEF_USE_LIGHT_VECTOR = 0x0002, + LIGHTDEF_USE_NORMALMAP = 0x0004, + LIGHTDEF_USE_SPECULARMAP = 0x0008, + LIGHTDEF_USE_DELUXEMAP = 0x0010, + LIGHTDEF_USE_PARALLAXMAP = 0x0020, + LIGHTDEF_TCGEN_ENVIRONMENT = 0x0040, + LIGHTDEF_ENTITY = 0x0080, + LIGHTDEF_ALL = 0x00FF, + LIGHTDEF_COUNT = 0x0100 +}; + +enum +{ + GLSL_INT, + GLSL_FLOAT, + GLSL_FLOAT5, + GLSL_VEC2, + GLSL_VEC3, + GLSL_VEC4, + GLSL_MAT16 +}; + +// 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; + GLhandleARB vertexShader; + GLhandleARB fragmentShader; + uint32_t attribs; // vertex array attributes + + // uniform parameters + int numUniforms; + GLint *uniforms; + GLint *uniformTypes; + int *uniformBufferOffsets; + char *uniformBuffer; +} shaderProgram_t; + + +enum +{ + TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX = 0, + TEXTURECOLOR_UNIFORM_DIFFUSEMAP, + TEXTURECOLOR_UNIFORM_COLOR, + TEXTURECOLOR_UNIFORM_COUNT +}; + + +enum +{ + FOGPASS_UNIFORM_FOGDISTANCE = 0, + FOGPASS_UNIFORM_FOGDEPTH, + FOGPASS_UNIFORM_FOGEYET, + FOGPASS_UNIFORM_DEFORMGEN, + FOGPASS_UNIFORM_DEFORMPARAMS, + FOGPASS_UNIFORM_TIME, + FOGPASS_UNIFORM_COLOR, + FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, + FOGPASS_UNIFORM_VERTEXLERP, + FOGPASS_UNIFORM_COUNT +}; + + +enum +{ + DLIGHT_UNIFORM_DIFFUSEMAP = 0, + DLIGHT_UNIFORM_DLIGHTINFO, + DLIGHT_UNIFORM_DEFORMGEN, + DLIGHT_UNIFORM_DEFORMPARAMS, + DLIGHT_UNIFORM_TIME, + DLIGHT_UNIFORM_COLOR, + DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, + DLIGHT_UNIFORM_VERTEXLERP, + DLIGHT_UNIFORM_COUNT +}; + + +enum +{ + PSHADOW_UNIFORM_SHADOWMAP = 0, + PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, + PSHADOW_UNIFORM_LIGHTFORWARD, + PSHADOW_UNIFORM_LIGHTUP, + PSHADOW_UNIFORM_LIGHTRIGHT, + PSHADOW_UNIFORM_LIGHTORIGIN, + PSHADOW_UNIFORM_LIGHTRADIUS, + PSHADOW_UNIFORM_COUNT +}; + + +enum +{ + GENERIC_UNIFORM_DIFFUSEMAP = 0, + GENERIC_UNIFORM_LIGHTMAP, + GENERIC_UNIFORM_NORMALMAP, + GENERIC_UNIFORM_DELUXEMAP, + GENERIC_UNIFORM_SPECULARMAP, + GENERIC_UNIFORM_SHADOWMAP, + GENERIC_UNIFORM_DIFFUSETEXMATRIX, + //GENERIC_UNIFORM_NORMALTEXMATRIX, + //GENERIC_UNIFORM_SPECULARTEXMATRIX, + GENERIC_UNIFORM_TEXTURE1ENV, + GENERIC_UNIFORM_VIEWORIGIN, + GENERIC_UNIFORM_TCGEN0, + GENERIC_UNIFORM_TCGEN0VECTOR0, + GENERIC_UNIFORM_TCGEN0VECTOR1, + GENERIC_UNIFORM_DEFORMGEN, + GENERIC_UNIFORM_DEFORMPARAMS, + GENERIC_UNIFORM_COLORGEN, + GENERIC_UNIFORM_ALPHAGEN, + GENERIC_UNIFORM_BASECOLOR, + GENERIC_UNIFORM_VERTCOLOR, + GENERIC_UNIFORM_AMBIENTLIGHT, + GENERIC_UNIFORM_DIRECTEDLIGHT, + GENERIC_UNIFORM_LIGHTORIGIN, + GENERIC_UNIFORM_LIGHTRADIUS, + GENERIC_UNIFORM_PORTALRANGE, + GENERIC_UNIFORM_FOGDISTANCE, + GENERIC_UNIFORM_FOGDEPTH, + GENERIC_UNIFORM_FOGEYET, + GENERIC_UNIFORM_FOGCOLORMASK, + GENERIC_UNIFORM_MODELMATRIX, + GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, + GENERIC_UNIFORM_TIME, + GENERIC_UNIFORM_VERTEXLERP, + GENERIC_UNIFORM_SPECULARREFLECTANCE, + GENERIC_UNIFORM_COUNT +}; + +// +// Tr3B: these are fire wall functions to avoid expensive redundant glUniform* calls +#define USE_UNIFORM_FIREWALL 1 +//#define LOG_GLSL_UNIFORMS 1 + + // trRefdef_t holds everything that comes in refdef_t, // as well as the locally generated scene information typedef struct { @@ -448,6 +860,7 @@ int num_dlights; struct dlight_s *dlights; + unsigned int dlightMask; int numPolys; struct srfPoly_s *polys; @@ -455,7 +868,10 @@ int numDrawSurfs; struct drawSurf_s *drawSurfs; + int num_pshadows; + struct pshadow_s *pshadows; + } trRefdef_t; @@ -493,13 +909,14 @@ vec3_t pvsOrigin; // may be different than or.origin for portals qboolean isPortal; // true if this view is through a portal qboolean isMirror; // the portal is a mirror, invert the face culling + qboolean isShadowmap; int frameSceneNum; // copied from tr.frameSceneNum int frameCount; // copied from tr.frameCount cplane_t portalPlane; // clip anything behind this if mirroring int viewportX, viewportY, viewportWidth, viewportHeight; float fovX, fovY; float projectionMatrix[16]; - cplane_t frustum[4]; + cplane_t frustum[5]; vec3_t visBounds[2]; float zFar; stereoFrame_t stereoFrame; @@ -513,6 +930,7 @@ ============================================================================== */ +typedef byte color4ub_t[4]; // any changes in surfaceType must be mirrored in rb_surfaceTable[] typedef enum { @@ -522,7 +940,7 @@ SF_GRID, SF_TRIANGLES, SF_POLY, - SF_MD3, + SF_MDV, SF_MD4, #ifdef RAVENMD4 SF_MDR, @@ -531,6 +949,8 @@ SF_FLARE, SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity SF_DISPLAY_LIST, + SF_VBO_MESH, + SF_VBO_MDVMESH, SF_NUM_SURFACE_TYPES, SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) @@ -569,11 +989,40 @@ 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; + vec3_t tangent; + vec3_t bitangent; + 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, 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]; + int pshadowBits[SMP_FRAMES]; // culling information vec3_t meshBounds[2]; @@ -592,46 +1041,78 @@ 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]; + int pshadowBits[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]; + int pshadowBits[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; // inter-quake-model @@ -669,11 +1150,79 @@ } srfIQModel_t; +typedef struct srfVBOMesh_s +{ + surfaceType_t surfaceType; + + struct shader_s *shader; // FIXME move this to somewhere else + int fogIndex; + + // dynamic lighting information + int dlightBits[SMP_FRAMES]; + int pshadowBits[SMP_FRAMES]; + + // 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; + +typedef struct srfVBOMDVMesh_s +{ + surfaceType_t surfaceType; + + struct mdvModel_s *mdvModel; + struct mdvSurface_s *mdvSurface; + + // backEnd stats + int numIndexes; + int numVerts; + + // static render data + VBO_t *vbo; + IBO_t *ibo; +} srfVBOMDVMesh_t; + extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *); /* ============================================================================== +SHADOWS + +============================================================================== +*/ + + +typedef struct pshadow_s +{ + float sort; + + int numEntities; + int entityNums[8]; + vec3_t entityOrigins[8]; + float entityRadiuses[8]; + + float viewRadius; + vec3_t viewOrigin; + + vec3_t lightViewAxis[3]; + vec3_t lightOrigin; + float lightRadius; + cplane_t cullPlane; +} pshadow_t; + + +/* +============================================================================== + BRUSH MODELS ============================================================================== @@ -688,21 +1237,34 @@ #define SIDE_BACK 1 #define SIDE_ON 2 +#define CULLINFO_NONE 0 +#define CULLINFO_BOX 1 +#define CULLINFO_SPHERE 2 +#define CULLINFO_PLANE 4 + +typedef struct cullinfo_s { + int type; + vec3_t bounds[2]; + vec3_t localOrigin; + float radius; + cplane_t plane; +} cullinfo_t; + typedef struct msurface_s { - int viewCount; // if == tr.viewCount, already added + //int viewCount; // if == tr.viewCount, already added struct shader_s *shader; int fogIndex; + cullinfo_t cullinfo; surfaceType_t *data; // any of srf*_t } msurface_t; - #define CONTENTS_NODE -1 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; @@ -714,13 +1276,13 @@ int cluster; int area; - msurface_t **firstmarksurface; + int firstmarksurface; int nummarksurfaces; } mnode_t; typedef struct { vec3_t bounds[2]; // for culling - msurface_t *firstSurface; + int firstSurface; int numSurfaces; } bmodel_t; @@ -733,6 +1295,7 @@ int numShaders; dshader_t *shaders; + int numBModels; bmodel_t *bmodels; int numplanes; @@ -742,11 +1305,26 @@ int numDecisionNodes; mnode_t *nodes; + VBO_t *vbo; + IBO_t *ibo; + + int numWorldSurfaces; + int numsurfaces; msurface_t *surfaces; + int *surfacesViewCount; + int *surfacesDlightBits; + int *surfacesPshadowBits; + int numMergedSurfaces; + msurface_t *mergedSurfaces; + int *mergedSurfacesViewCount; + int *mergedSurfacesDlightBits; + int *mergedSurfacesPshadowBits; + int nummarksurfaces; - msurface_t **marksurfaces; + int *marksurfaces; + int *viewSurfaces; int numfogs; fog_t *fogs; @@ -768,6 +1346,81 @@ char *entityParsePoint; } world_t; + +/* +============================================================================== +MDV MODELS - meta format for vertex animation models like .md2, .md3, .mdc +============================================================================== +*/ +typedef struct +{ + float bounds[2][3]; + float localOrigin[3]; + float radius; +} mdvFrame_t; + +typedef struct +{ + float origin[3]; + float axis[3][3]; +} mdvTag_t; + +typedef struct +{ + char name[MAX_QPATH]; // tag name +} mdvTagName_t; + +typedef struct +{ + vec3_t xyz; + vec3_t normal; + vec3_t tangent; + vec3_t bitangent; +} mdvVertex_t; + +typedef struct +{ + float st[2]; +} mdvSt_t; + +typedef struct mdvSurface_s +{ + surfaceType_t surfaceType; + + char name[MAX_QPATH]; // polyset name + + int numShaderIndexes; + int *shaderIndexes; + + int numVerts; + mdvVertex_t *verts; + mdvSt_t *st; + + int numTriangles; + srfTriangle_t *triangles; + + struct mdvModel_s *model; +} mdvSurface_t; + +typedef struct mdvModel_s +{ + int numFrames; + mdvFrame_t *frames; + + int numTags; + mdvTag_t *tags; + mdvTagName_t *tagNames; + + int numSurfaces; + mdvSurface_t *surfaces; + + int numVBOSurfaces; + srfVBOMDVMesh_t *vboSurfaces; + + int numSkins; +} mdvModel_t; + + //====================================================================== typedef enum { @@ -788,7 +1441,7 @@ int dataSize; // just for listing purposes bmodel_t *bmodel; // only if type == MOD_BRUSH - md3Header_t *md3[MD3_MAX_LODS]; // only if type == MOD_MESH + mdvModel_t *mdv[MD3_MAX_LODS]; // only if type == MOD_MESH void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM) int numLods; @@ -833,6 +1486,13 @@ 2-6 : fog index 7-16 : entity index 17-30 : sorted shader index + + SmileTheory - for pshadows +17-31 : sorted shader index +7-16 : entity index +2-6 : fog index +1 : pshadow flag +0 : dlight flag */ #define QSORT_FOGNUM_SHIFT 2 #define QSORT_ENTITYNUM_SHIFT 7 @@ -840,6 +1500,7 @@ #if (QSORT_SHADERNUM_SHIFT+SHADERNUM_BITS) > 32 #error "Need to update sorting, too many bits." #endif +#define QSORT_PSHADOW_SHIFT 1 extern int gl_filter_min, gl_filter_max; @@ -871,13 +1532,60 @@ int texEnv[2]; int faceCulling; unsigned long glStateBits; + uint32_t vertexAttribsState; + uint32_t vertexAttribPointersSet; + uint32_t vertexAttribsNewFrame; + uint32_t vertexAttribsOldFrame; + float vertexAttribsInterpolation; + shaderProgram_t *currentProgram; + VBO_t *currentVBO; + IBO_t *currentIBO; + matrix_t modelview; + matrix_t projection; + matrix_t modelviewProjection; } glstate_t; +typedef enum { + MI_NONE, + MI_NVX, + MI_ATI +} memInfo_t; +// We can't change glConfig_t without breaking DLL/vms compatibility, so +// store extensions we have here. typedef struct { + qboolean textureFilterAnisotropic; + int maxAnisotropy; + qboolean multiDrawArrays; + qboolean vertexBufferObject; + + // These next three are all required for one chunk of code, so glsl is + // set if they are all true. + qboolean vertexProgram; + qboolean shaderObjects; + qboolean vertexShader; + qboolean glsl; + + memInfo_t memInfo; +} glRefConfig_t; + + +typedef struct { int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; + int c_surfBatches; float c_overDraw; + int c_vboVertexBuffers; + int c_vboIndexBuffers; + int c_vboVertexes; + int c_vboIndexes; + + int c_staticVboDraws; + int c_dynamicVboDraws; + + int c_multidraws; + int c_multidrawsMerged; + int c_dlightVertexes; int c_dlightIndexes; @@ -885,6 +1593,12 @@ int c_flareTests; int c_flareRenders; + int c_glslShaderBinds; + int c_genericDraws; + int c_lightallDraws; + int c_fogDraws; + int c_dlightDraws; + int msec; // total msec for backend run } backEndCounters_t; @@ -917,7 +1631,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) @@ -928,6 +1645,7 @@ int frameSceneNum; // zeroed at RE_BeginFrame qboolean worldMapLoaded; + qboolean worldDeluxeMapping; world_t *world; const byte *externalVisData; // from RE_SetWorldVisData, shared with CM_Load @@ -938,7 +1656,10 @@ image_t *dlightImage; // inverse-quare highlight for projective adding image_t *flareImage; image_t *whiteImage; // full of 0xff + image_t *blackImage; // full of 0x000000ff image_t *identityLightImage; // full of tr.identityLightByte + image_t *shadowCubemaps[MAX_DLIGHTS]; + image_t *pshadowMaps[MAX_DRAWN_PSHADOWS]; shader_t *defaultShader; shader_t *shadowShader; @@ -949,13 +1670,34 @@ int numLightmaps; image_t **lightmaps; + image_t **deluxemaps; + image_t *fatLightmap; + image_t *fatDeluxemap; + int fatLightmapSize; + int fatLightmapStep; + trRefEntity_t *currentEntity; trRefEntity_t worldEntity; // point currentEntity at this when rendering world int currentEntityNum; int shiftedEntityNum; // currentEntityNum << QSORT_ENTITYNUM_SHIFT model_t *currentModel; + // + // GPU shader programs + // + + shaderProgram_t genericShader[GENERICDEF_COUNT]; + shaderProgram_t textureColorShader; + shaderProgram_t fogShader; + shaderProgram_t dlightallShader; + shaderProgram_t lightallShader[LIGHTDEF_COUNT]; + shaderProgram_t shadowmapShader; + shaderProgram_t pshadowShader; + + + // ----------------------------------------- + viewParms_t viewParms; float identityLight; // 1.0 / ( 1 << overbrightBits ) @@ -984,6 +1726,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) @@ -1010,8 +1758,7 @@ // These two variables should live inside glConfig but can't because of compatibility issues to the original ID vms. // If you release a stand-alone game and your mod uses tr_types.h from this build you can safely move them to // the glconfig_t struct. -extern qboolean textureFilterAnisotropic; -extern int maxAnisotropy; +extern glRefConfig_t glRefConfig; extern float displayAspect; @@ -1088,6 +1835,10 @@ 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_ext_multi_draw_arrays; + 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; @@ -1129,6 +1880,19 @@ extern cvar_t *r_stereoEnabled; extern cvar_t *r_anaglyphMode; +extern cvar_t *r_mergeMultidraws; +extern cvar_t *r_mergeLeafSurfaces; + +extern cvar_t *r_normalMapping; +extern cvar_t *r_specularMapping; +extern cvar_t *r_deluxeMapping; +extern cvar_t *r_parallaxMapping; +extern cvar_t *r_normalAmbient; +extern cvar_t *r_dlightMode; +extern cvar_t *r_pshadowDist; +extern cvar_t *r_recalcMD3Normals; +extern cvar_t *r_mergeLightmaps; + extern cvar_t *r_greyscale; extern cvar_t *r_ignoreGLErrors; @@ -1155,6 +1919,8 @@ void R_SwapBuffers( int ); void R_RenderView( viewParms_t *parms ); +void R_RenderDlightCubemaps(const refdef_t *fd); +void R_RenderPshadowMaps(const refdef_t *fd); void R_AddMD3Surfaces( trRefEntity_t *e ); void R_AddNullModelSurfaces( trRefEntity_t *e ); @@ -1165,32 +1931,44 @@ void R_AddPolygonSurfaces( void ); void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap ); + int *fogNum, int *dlightMap, int *pshadowMap ); -void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int fogIndex, int dlightMap ); +void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, + int fogIndex, int dlightMap, int pshadowMap ); +void R_CalcTangentSpace(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2); +qboolean R_CalcTangentVectors(srfVert_t * dv[3]); +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 #define CULL_OUT 2 // completely outside the clipping planes void R_LocalNormalToWorld (vec3_t local, vec3_t world); void R_LocalPointToWorld (vec3_t local, vec3_t world); +int R_CullBox (vec3_t bounds[2]); int R_CullLocalBox (vec3_t bounds[2]); int R_CullPointAndRadius( vec3_t origin, float radius ); int R_CullLocalPointAndRadius( vec3_t origin, float radius ); -void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum); +void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean computeFrustum); void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *or ); /* ** GL wrapper/helper functions */ void GL_Bind( image_t *image ); +void GL_BindCubemap( image_t *image ); +void GL_BindToTMU( image_t *image, int tmu ); 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_SetProjectionMatrix(matrix_t matrix); +void GL_SetModelviewMatrix(matrix_t matrix); void GL_TexEnv( int env ); void GL_Cull( int cullType ); @@ -1249,6 +2027,7 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, qboolean mipmap , qboolean allowPicmip, int wrapClampMode ); +void R_UpdateSubImage( image_t *image, const byte *pic, int x, int y, int width, int height); qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode ); void R_SetColorMappings( void ); @@ -1321,7 +2100,6 @@ ==================================================================== */ -typedef byte color4ub_t[4]; typedef struct stageVars { @@ -1329,16 +2107,23 @@ vec2_t texcoords[NUM_TEXTURE_BUNDLES][SHADER_MAX_VERTEXES]; } stageVars_t; +#define MAX_MULTIDRAW_PRIMITIVES 16384 typedef struct shaderCommands_s { glIndex_t indexes[SHADER_MAX_INDEXES] QALIGN(16); vec4_t xyz[SHADER_MAX_VERTEXES] QALIGN(16); vec4_t normal[SHADER_MAX_VERTEXES] QALIGN(16); + vec4_t tangent[SHADER_MAX_VERTEXES] QALIGN(16); + vec4_t bitangent[SHADER_MAX_VERTEXES] QALIGN(16); vec2_t texCoords[SHADER_MAX_VERTEXES][2] QALIGN(16); color4ub_t vertexColors[SHADER_MAX_VERTEXES] QALIGN(16); - int vertexDlightBits[SHADER_MAX_VERTEXES] QALIGN(16); + //int vertexDlightBits[SHADER_MAX_VERTEXES] QALIGN(16); + VBO_t *vbo; + IBO_t *ibo; + qboolean useInternalVBO; + stageVars_t svars QALIGN(16); color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); @@ -1348,10 +2133,17 @@ int fogNum; int dlightBits; // or together of all vertexDlightBits + int pshadowBits; + 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 ); @@ -1366,12 +2158,14 @@ #define RB_CHECKOVERFLOW(v,i) if (tess.numVertexes + (v) >= SHADER_MAX_VERTEXES || tess.numIndexes + (i) >= SHADER_MAX_INDEXES ) {RB_CheckOverflow(v,i);} void RB_StageIteratorGeneric( void ); +void RB_StageIteratorGenericVBO( void ); void RB_StageIteratorSky( void ); void RB_StageIteratorVertexLitTexture( void ); void RB_StageIteratorLightmappedMultitexture( void ); void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, byte *color ); void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, float s1, float t1, float s2, float t2 ); +void RB_InstantQuad( vec4_t quadVerts[4] ); void RB_ShowImages( void ); @@ -1454,7 +2248,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 ); @@ -1474,6 +2268,59 @@ /* ============================================================ +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); + +void RB_UpdateVBOs(unsigned int attribBits); + + +/* +============================================================ + +GLSL + +============================================================ +*/ + +void GLSL_InitGPUShaders(void); +void GLSL_ShutdownGPUShaders(void); +void GLSL_VertexAttribsState(uint32_t stateBits); +void GLSL_VertexAttribPointers(uint32_t attribBits); +void GLSL_BindProgram(shaderProgram_t * program); +void GLSL_BindNullProgram(void); + +void GLSL_SetNumUniforms(shaderProgram_t *program, int numUniforms); +void GLSL_SetUniformName(shaderProgram_t *program, int uniformNum, const char *name); +void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value); +void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value); +void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v); +void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v); +void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v); +void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v); +void GLSL_SetUniformMatrix16(shaderProgram_t *program, int uniformNum, const matrix_t matrix); + +shaderProgram_t *GLSL_GetGenericShaderProgram(int stage); + +/* +============================================================ + SCENE GENERATION ============================================================ @@ -1562,6 +2409,14 @@ 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_CalcTurbulentTexMatrix( const waveForm_t *wf, matrix_t 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 ); @@ -1676,6 +2531,12 @@ int commandId; } clearDepthCommand_t; +typedef struct { + int commandId; + int map; + int cubeSide; +} capShadowmapCommand_t; + typedef enum { RC_END_OF_LIST, RC_SET_COLOR, @@ -1686,7 +2547,8 @@ RC_SCREENSHOT, RC_VIDEOFRAME, RC_COLORMASK, - RC_CLEARDEPTH + RC_CLEARDEPTH, + RC_CAPSHADOWMAP } renderCommand_t; @@ -1706,6 +2568,7 @@ trRefEntity_t entities[MAX_ENTITIES]; srfPoly_t *polys;//[MAX_POLYS]; polyVert_t *polyVerts;//[MAX_POLYVERTS]; + pshadow_t pshadows[MAX_CALC_PSHADOWS]; renderCommandList_t commands; } backEndData_t; @@ -1728,6 +2591,7 @@ void R_SyncRenderThread( void ); void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ); +void R_AddCapShadowmapCmd( int dlight, int cubeSide ); void RE_SetColor( const float *rgba ); void RE_StretchPic ( float x, float y, float w, float h, Index: code/renderer/tr_main.c =================================================================== --- code/renderer/tr_main.c (revision 2095) +++ code/renderer/tr_main.c (working copy) @@ -44,13 +44,552 @@ surfaceType_t entitySurface = SF_ENTITY; /* +================ +R_CompareVert +================ +*/ +qboolean R_CompareVert(srfVert_t * v1, srfVert_t * v2, qboolean checkST) +{ + int i; + + for(i = 0; i < 3; i++) + { + if(floor(v1->xyz[i] + 0.1) != floor(v2->xyz[i] + 0.1)) + { + return qfalse; + } + + if(checkST && ((v1->st[0] != v2->st[0]) || (v1->st[1] != v2->st[1]))) + { + return qfalse; + } + } + + return qtrue; +} + +/* +============= +R_CalcNormalForTriangle +============= +*/ +void R_CalcNormalForTriangle(vec3_t normal, const vec3_t v0, const vec3_t v1, const vec3_t v2) +{ + vec3_t udir, vdir; + + // compute the face normal based on vertex points + VectorSubtract(v2, v0, udir); + VectorSubtract(v1, v0, vdir); + CrossProduct(udir, vdir, normal); + + VectorNormalize(normal); +} + +/* +============= +R_CalcTangentsForTriangle +http://members.rogers.com/deseric/tangentspace.htm +============= +*/ +void R_CalcTangentsForTriangle(vec3_t tangent, vec3_t bitangent, + const vec3_t v0, const vec3_t v1, const vec3_t v2, + const vec2_t t0, const vec2_t t1, const vec2_t t2) +{ + int i; + vec3_t planes[3]; + vec3_t u, v; + + for(i = 0; i < 3; i++) + { + VectorSet(u, v1[i] - v0[i], t1[0] - t0[0], t1[1] - t0[1]); + VectorSet(v, v2[i] - v0[i], t2[0] - t0[0], t2[1] - t0[1]); + + VectorNormalize(u); + VectorNormalize(v); + + CrossProduct(u, v, planes[i]); + } + + //So your tangent space will be defined by this : + //Normal = Normal of the triangle or Tangent X Bitangent (careful with the cross product, + // you have to make sure the normal points in the right direction) + //Tangent = ( dp(Fx(s,t)) / ds, dp(Fy(s,t)) / ds, dp(Fz(s,t)) / ds ) or ( -Bx/Ax, -By/Ay, - Bz/Az ) + //Bitangent = ( dp(Fx(s,t)) / dt, dp(Fy(s,t)) / dt, dp(Fz(s,t)) / dt ) or ( -Cx/Ax, -Cy/Ay, -Cz/Az ) + + // tangent... + tangent[0] = -planes[0][1] / planes[0][0]; + tangent[1] = -planes[1][1] / planes[1][0]; + tangent[2] = -planes[2][1] / planes[2][0]; + VectorNormalize(tangent); + + // bitangent... + bitangent[0] = -planes[0][2] / planes[0][0]; + bitangent[1] = -planes[1][2] / planes[1][0]; + bitangent[2] = -planes[2][2] / planes[2][0]; + VectorNormalize(bitangent); +} + + + + +/* +============= +R_CalcTangentSpace +============= +*/ +void R_CalcTangentSpace(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2) +{ + vec3_t cp, u, v; + vec3_t faceNormal; + + VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]); + VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]); + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[0] = -cp[1] / cp[0]; + bitangent[0] = -cp[2] / cp[0]; + } + + u[0] = v1[1] - v0[1]; + v[0] = v2[1] - v0[1]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[1] = -cp[1] / cp[0]; + bitangent[1] = -cp[2] / cp[0]; + } + + u[0] = v1[2] - v0[2]; + v[0] = v2[2] - v0[2]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[2] = -cp[1] / cp[0]; + bitangent[2] = -cp[2] / cp[0]; + } + + VectorNormalize(tangent); + VectorNormalize(bitangent); + + // compute the face normal based on vertex points + if ( normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f ) + { + VectorSubtract(v2, v0, u); + VectorSubtract(v1, v0, v); + CrossProduct(u, v, faceNormal); + } + else + { + VectorCopy(normal, faceNormal); + } + + VectorNormalize(faceNormal); + +#if 1 + // Gram-Schmidt orthogonalize + //tangent[a] = (t - n * Dot(n, t)).Normalize(); + VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent); + VectorNormalize(tangent); + + // compute the cross product B=NxT + //CrossProduct(normal, tangent, bitangent); +#else + // normal, compute the cross product N=TxB + CrossProduct(tangent, bitangent, normal); + VectorNormalize(normal); + + if(DotProduct(normal, faceNormal) < 0) + { + //VectorInverse(normal); + //VectorInverse(tangent); + //VectorInverse(bitangent); + + // compute the cross product T=BxN + CrossProduct(bitangent, faceNormal, tangent); + + // compute the cross product B=NxT + //CrossProduct(normal, tangent, bitangent); + } +#endif + + VectorCopy(faceNormal, normal); +} + +void R_CalcTangentSpaceFast(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2) +{ + vec3_t cp, u, v; + vec3_t faceNormal; + + VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]); + VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]); + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[0] = -cp[1] / cp[0]; + bitangent[0] = -cp[2] / cp[0]; + } + + u[0] = v1[1] - v0[1]; + v[0] = v2[1] - v0[1]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[1] = -cp[1] / cp[0]; + bitangent[1] = -cp[2] / cp[0]; + } + + u[0] = v1[2] - v0[2]; + v[0] = v2[2] - v0[2]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[2] = -cp[1] / cp[0]; + bitangent[2] = -cp[2] / cp[0]; + } + + VectorNormalizeFast(tangent); + VectorNormalizeFast(bitangent); + + // compute the face normal based on vertex points + VectorSubtract(v2, v0, u); + VectorSubtract(v1, v0, v); + CrossProduct(u, v, faceNormal); + + VectorNormalizeFast(faceNormal); + +#if 0 + // normal, compute the cross product N=TxB + CrossProduct(tangent, bitangent, normal); + VectorNormalizeFast(normal); + + if(DotProduct(normal, faceNormal) < 0) + { + VectorInverse(normal); + //VectorInverse(tangent); + //VectorInverse(bitangent); + + CrossProduct(normal, tangent, bitangent); + } + + VectorCopy(faceNormal, normal); +#else + // Gram-Schmidt orthogonalize + //tangent[a] = (t - n * Dot(n, t)).Normalize(); + VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent); + VectorNormalizeFast(tangent); +#endif + + VectorCopy(faceNormal, normal); +} + +/* +http://www.terathon.com/code/tangent.html +*/ +void R_CalcTBN(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t w1, const vec2_t w2, const vec2_t w3) +{ + vec3_t u, v; + float x1, x2, y1, y2, z1, z2; + float s1, s2, t1, t2; + float r, dot; + + x1 = v2[0] - v1[0]; + x2 = v3[0] - v1[0]; + y1 = v2[1] - v1[1]; + y2 = v3[1] - v1[1]; + z1 = v2[2] - v1[2]; + z2 = v3[2] - v1[2]; + + s1 = w2[0] - w1[0]; + s2 = w3[0] - w1[0]; + t1 = w2[1] - w1[1]; + t2 = w3[1] - w1[1]; + + r = 1.0f / (s1 * t2 - s2 * t1); + + VectorSet(tangent, (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); + VectorSet(bitangent, (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); + + // compute the face normal based on vertex points + VectorSubtract(v3, v1, u); + VectorSubtract(v2, v1, v); + CrossProduct(u, v, normal); + + VectorNormalize(normal); + + // Gram-Schmidt orthogonalize + //tangent[a] = (t - n * Dot(n, t)).Normalize(); + dot = DotProduct(normal, tangent); + VectorMA(tangent, -dot, normal, tangent); + VectorNormalize(tangent); + + // B=NxT + //CrossProduct(normal, tangent, bitangent); +} + +void R_CalcTBN2(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t t1, const vec2_t t2, const vec2_t t3) +{ + vec3_t v2v1; + vec3_t v3v1; + + float c2c1_T; + float c2c1_B; + + float c3c1_T; + float c3c1_B; + + float denominator; + float scale1, scale2; + + vec3_t T, B, N, C; + + + // Calculate the tangent basis for each vertex of the triangle + // UPDATE: In the 3rd edition of the accompanying article, the for-loop located here has + // been removed as it was redundant (the entire TBN matrix was calculated three times + // instead of just one). + // + // Please note, that this function relies on the fact that the input geometry are triangles + // and the tangent basis for each vertex thus is identical! + // + + // Calculate the vectors from the current vertex to the two other vertices in the triangle + VectorSubtract(v2, v1, v2v1); + VectorSubtract(v3, v1, v3v1); + + // The equation presented in the article states that: + // c2c1_T = V2.texcoord.x – V1.texcoord.x + // c2c1_B = V2.texcoord.y – V1.texcoord.y + // c3c1_T = V3.texcoord.x – V1.texcoord.x + // c3c1_B = V3.texcoord.y – V1.texcoord.y + + // Calculate c2c1_T and c2c1_B + c2c1_T = t2[0] - t1[0]; + c2c1_B = t2[1] - t2[1]; + + // Calculate c3c1_T and c3c1_B + c3c1_T = t3[0] - t1[0]; + c3c1_B = t3[1] - t1[1]; + + denominator = c2c1_T * c3c1_B - c3c1_T * c2c1_B; + //if(ROUNDOFF(fDenominator) == 0.0f) + if(denominator == 0.0f) + { + // We won't risk a divide by zero, so set the tangent matrix to the identity matrix + VectorSet(tangent, 1, 0, 0); + VectorSet(bitangent, 0, 1, 0); + VectorSet(normal, 0, 0, 1); + } + else + { + // Calculate the reciprocal value once and for all (to achieve speed) + scale1 = 1.0f / denominator; + + // T and B are calculated just as the equation in the article states + VectorSet(T, (c3c1_B * v2v1[0] - c2c1_B * v3v1[0]) * scale1, + (c3c1_B * v2v1[1] - c2c1_B * v3v1[1]) * scale1, + (c3c1_B * v2v1[2] - c2c1_B * v3v1[2]) * scale1); + + VectorSet(B, (-c3c1_T * v2v1[0] + c2c1_T * v3v1[0]) * scale1, + (-c3c1_T * v2v1[1] + c2c1_T * v3v1[1]) * scale1, + (-c3c1_T * v2v1[2] + c2c1_T * v3v1[2]) * scale1); + + // The normal N is calculated as the cross product between T and B + CrossProduct(T, B, N); + +#if 0 + VectorCopy(T, tangent); + VectorCopy(B, bitangent); + VectorCopy(N, normal); +#else + // Calculate the reciprocal value once and for all (to achieve speed) + scale2 = 1.0f / ((T[0] * B[1] * N[2] - T[2] * B[1] * N[0]) + + (B[0] * N[1] * T[2] - B[2] * N[1] * T[0]) + + (N[0] * T[1] * B[2] - N[2] * T[1] * B[0])); + + // Calculate the inverse if the TBN matrix using the formula described in the article. + // We store the basis vectors directly in the provided TBN matrix: pvTBNMatrix + CrossProduct(B, N, C); tangent[0] = C[0] * scale2; + CrossProduct(N, T, C); tangent[1] = -C[0] * scale2; + CrossProduct(T, B, C); tangent[2] = C[0] * scale2; + VectorNormalize(tangent); + + CrossProduct(B, N, C); bitangent[0] = -C[1] * scale2; + CrossProduct(N, T, C); bitangent[1] = C[1] * scale2; + CrossProduct(T, B, C); bitangent[2] = -C[1] * scale2; + VectorNormalize(bitangent); + + CrossProduct(B, N, C); normal[0] = C[2] * scale2; + CrossProduct(N, T, C); normal[1] = -C[2] * scale2; + CrossProduct(T, B, C); normal[2] = C[2] * scale2; + VectorNormalize(normal); +#endif + } +} + + +qboolean R_CalcTangentVectors(srfVert_t * dv[3]) +{ + int i; + float bb, s, t; + vec3_t bary; + + + /* calculate barycentric basis for the triangle */ + bb = (dv[1]->st[0] - dv[0]->st[0]) * (dv[2]->st[1] - dv[0]->st[1]) - (dv[2]->st[0] - dv[0]->st[0]) * (dv[1]->st[1] - dv[0]->st[1]); + if(fabs(bb) < 0.00000001f) + return qfalse; + + /* do each vertex */ + for(i = 0; i < 3; i++) + { + // calculate s tangent vector + s = dv[i]->st[0] + 10.0f; + t = dv[i]->st[1]; + bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb; + bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb; + bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb; + + dv[i]->tangent[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0]; + dv[i]->tangent[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1]; + dv[i]->tangent[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2]; + + VectorSubtract(dv[i]->tangent, dv[i]->xyz, dv[i]->tangent); + VectorNormalize(dv[i]->tangent); + + // calculate t tangent vector + s = dv[i]->st[0]; + t = dv[i]->st[1] + 10.0f; + bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb; + bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb; + bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb; + + dv[i]->bitangent[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0]; + dv[i]->bitangent[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1]; + dv[i]->bitangent[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2]; + + VectorSubtract(dv[i]->bitangent, dv[i]->xyz, dv[i]->bitangent); + VectorNormalize(dv[i]->bitangent); + + // debug code + //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i, + //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] ); + } + + return qtrue; +} + + +/* ================= +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,9 +643,78 @@ } return CULL_CLIP; // partially clipped +#else + int j; + vec3_t transformed; + vec3_t v; + 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]); + } + + return R_CullBox(worldBounds); +#endif } /* +================= +R_CullBox + +Returns CULL_IN, CULL_CLIP, or CULL_OUT +================= +*/ +int R_CullBox(vec3_t worldBounds[2]) { + int i; + cplane_t *frust; + qboolean anyClip; + int r; + + // 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; +} + +/* ** R_CullLocalPointAndRadius */ int R_CullLocalPointAndRadius( vec3_t pt, float radius ) @@ -306,6 +914,7 @@ glMatrix[11] = 0; glMatrix[15] = 1; + Matrix16Copy(glMatrix, or->transformMatrix); myGlMultMatrix( glMatrix, viewParms->world.modelMatrix, or->modelMatrix ); // calculate the viewer origin in the model's space @@ -450,7 +1059,7 @@ the projection matrix. ================= */ -void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float stereoSep) +void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float zFar, float stereoSep) { vec3_t ofsorigin; float oppleg, adjleg, length; @@ -503,6 +1112,18 @@ dest->frustum[i].dist = DotProduct (ofsorigin, dest->frustum[i].normal); SetPlaneSignbits( &dest->frustum[i] ); } + + if (zFar != 0.0f) + { + vec3_t farpoint; + + VectorMA(ofsorigin, zFar, dest->or.axis[0], farpoint); + VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); + + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].dist = DotProduct (farpoint, dest->frustum[4].normal); + SetPlaneSignbits( &dest->frustum[4] ); + } } /* @@ -510,7 +1131,7 @@ R_SetupProjection =============== */ -void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum) +void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean computeFrustum) { float xmin, xmax, ymin, ymax; float width, height, stereoSep = r_stereoSeparation->value; @@ -556,7 +1177,7 @@ // Now that we have all the data for the projection matrix we can also setup the view frustum. if(computeFrustum) - R_SetupFrustum(dest, xmin, xmax, ymax, zProj, stereoSep); + R_SetupFrustum(dest, xmin, xmax, ymax, zProj, zFar, stereoSep); } /* @@ -570,14 +1191,49 @@ { float zNear, zFar, depth; - zNear = r_znear->value; - zFar = dest->zFar; + zNear = r_znear->value; + zFar = dest->zFar; + depth = zFar - zNear; dest->projectionMatrix[2] = 0; dest->projectionMatrix[6] = 0; dest->projectionMatrix[10] = -( zFar + zNear ) / depth; dest->projectionMatrix[14] = -2 * zFar * zNear / depth; + + if (dest->isPortal) + { + float plane[4]; + float plane2[4]; + vec4_t q, c; + + // transform portal plane into camera space + plane[0] = dest->portalPlane.normal[0]; + plane[1] = dest->portalPlane.normal[1]; + plane[2] = dest->portalPlane.normal[2]; + plane[3] = dest->portalPlane.dist; + + plane2[0] = -DotProduct (dest->or.axis[1], plane); + plane2[1] = DotProduct (dest->or.axis[2], plane); + plane2[2] = -DotProduct (dest->or.axis[0], plane); + plane2[3] = DotProduct (plane, dest->or.origin) - plane[3]; + + // Lengyel, Eric. “Modifying the Projection Matrix to Perform Oblique Near-plane Clipping”. + // Terathon Software 3D Graphics Library, 2004. http://www.terathon.com/code/oblique.html + q[0] = (SGN(plane2[0]) + dest->projectionMatrix[8]) / dest->projectionMatrix[0]; + q[1] = (SGN(plane2[1]) + dest->projectionMatrix[9]) / dest->projectionMatrix[5]; + q[2] = -1.0f; + q[3] = (1.0f + dest->projectionMatrix[10]) / dest->projectionMatrix[14]; + + VectorScale4(plane2, 2.0f / DotProduct4(plane2, q), c); + + dest->projectionMatrix[2] = c[0]; + dest->projectionMatrix[6] = c[1]; + dest->projectionMatrix[10] = c[2] + 1.0f; + dest->projectionMatrix[14] = c[3]; + + } + } /* @@ -622,7 +1278,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 +1292,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]; @@ -857,6 +1513,7 @@ shader_t *shader; int fogNum; int dlighted; + int pshadowed; vec4_t clip, eye; int i; unsigned int pointOr = 0; @@ -868,7 +1525,7 @@ R_RotateForViewer(); - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); RB_BeginSurface( shader, fogNum ); rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); @@ -1105,7 +1762,7 @@ ================= */ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, - int fogIndex, int dlightMap ) { + int fogIndex, int dlightMap, int pshadowMap ) { int index; // instead of checking for overflow, we just mask the index @@ -1114,7 +1771,8 @@ // the sort data is packed into a single 32 bit value so it can be // compared quickly during the qsorting process tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT) - | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; + | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) + | ((int)pshadowMap << QSORT_PSHADOW_SHIFT) | (int)dlightMap; tr.refdef.drawSurfs[index].surface = surface; tr.refdef.numDrawSurfs++; } @@ -1125,11 +1783,12 @@ ================= */ void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap ) { + int *fogNum, int *dlightMap, int *pshadowMap ) { *fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31; *shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ]; *entityNum = ( sort >> QSORT_ENTITYNUM_SHIFT ) & (MAX_GENTITIES-1); - *dlightMap = sort & 3; + *pshadowMap = (sort & 2) >> 1; + *dlightMap = sort & 1; } /* @@ -1142,6 +1801,7 @@ int fogNum; int entityNum; int dlighted; + int pshadowed; int i; // it is possible for some views to not have any surfaces @@ -1163,126 +1823,134 @@ // check for any pass through drawing, which // may cause another view to be rendered first - for ( i = 0 ; i < numDrawSurfs ; i++ ) { - R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted ); + if (!tr.viewParms.isShadowmap) + { + for ( i = 0 ; i < numDrawSurfs ; i++ ) { + R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); - if ( shader->sort > SS_PORTAL ) { - break; - } + if ( shader->sort > SS_PORTAL ) { + break; + } - // no shader should ever have this sort type - if ( shader->sort == SS_BAD ) { - ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name ); - } + // no shader should ever have this sort type + if ( shader->sort == SS_BAD ) { + ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name ); + } - // if the mirror was completely clipped away, we may need to check another surface - if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) { - // this is a debug option to see exactly what is being mirrored - if ( r_portalOnly->integer ) { - return; + // if the mirror was completely clipped away, we may need to check another surface + if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) { + // this is a debug option to see exactly what is being mirrored + if ( r_portalOnly->integer ) { + return; + } + break; // only one mirror view at a time } - break; // only one mirror view at a time } } R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); } -/* -============= -R_AddEntitySurfaces -============= -*/ -void R_AddEntitySurfaces (void) { +static void R_AddEntitySurface (int entityNum) +{ trRefEntity_t *ent; shader_t *shader; - if ( !r_drawentities->integer ) { - return; - } + tr.currentEntityNum = entityNum; - for ( tr.currentEntityNum = 0; - tr.currentEntityNum < tr.refdef.num_entities; - tr.currentEntityNum++ ) { - ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum]; + ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum]; - ent->needDlights = qfalse; + ent->needDlights = qfalse; - // preshift the value we are going to OR into the drawsurf sort - tr.shiftedEntityNum = tr.currentEntityNum << QSORT_ENTITYNUM_SHIFT; + // preshift the value we are going to OR into the drawsurf sort + tr.shiftedEntityNum = tr.currentEntityNum << QSORT_ENTITYNUM_SHIFT; - // - // the weapon model must be handled special -- - // we don't want the hacked weapon position showing in - // mirrors, because the true body position will already be drawn - // - if ( (ent->e.renderfx & RF_FIRST_PERSON) && tr.viewParms.isPortal) { - continue; + // + // the weapon model must be handled special -- + // we don't want the hacked weapon position showing in + // mirrors, because the true body position will already be drawn + // + if ( (ent->e.renderfx & RF_FIRST_PERSON) && (tr.viewParms.isPortal || tr.viewParms.isShadowmap)) { + return; + } + + // simple generated models, like sprites and beams, are not culled + switch ( ent->e.reType ) { + case RT_PORTALSURFACE: + break; // don't draw anything + case RT_SPRITE: + case RT_BEAM: + case RT_LIGHTNING: + case RT_RAIL_CORE: + case RT_RAIL_RINGS: + // self blood sprites, talk balloons, etc should not be drawn in the primary + // view. We can't just do this check for all entities, because md3 + // entities may still want to cast shadows from them + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { + return; } + shader = R_GetShaderByHandle( ent->e.customShader ); + R_AddDrawSurf( &entitySurface, shader, R_SpriteFogNum( ent ), 0, 0 ); + break; - // simple generated models, like sprites and beams, are not culled - switch ( ent->e.reType ) { - case RT_PORTALSURFACE: - break; // don't draw anything - case RT_SPRITE: - case RT_BEAM: - case RT_LIGHTNING: - case RT_RAIL_CORE: - case RT_RAIL_RINGS: - // self blood sprites, talk balloons, etc should not be drawn in the primary - // view. We can't just do this check for all entities, because md3 - // entities may still want to cast shadows from them - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { - continue; - } - shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, shader, R_SpriteFogNum( ent ), 0 ); - break; + case RT_MODEL: + // we must set up parts of tr.or for model culling + R_RotateForEntity( ent, &tr.viewParms, &tr.or ); - case RT_MODEL: - // we must set up parts of tr.or for model culling - R_RotateForEntity( ent, &tr.viewParms, &tr.or ); - - tr.currentModel = R_GetModelByHandle( ent->e.hModel ); - if (!tr.currentModel) { - R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 ); - } else { - switch ( tr.currentModel->type ) { - case MOD_MESH: - R_AddMD3Surfaces( ent ); - break; - case MOD_MD4: - R_AddAnimSurfaces( ent ); - break; + tr.currentModel = R_GetModelByHandle( ent->e.hModel ); + if (!tr.currentModel) { + R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0, 0 ); + } else { + switch ( tr.currentModel->type ) { + case MOD_MESH: + R_AddMD3Surfaces( ent ); + break; + case MOD_MD4: + R_AddAnimSurfaces( ent ); + break; #ifdef RAVENMD4 - case MOD_MDR: - R_MDRAddAnimSurfaces( ent ); - break; + case MOD_MDR: + R_MDRAddAnimSurfaces( ent ); + break; #endif - case MOD_IQM: - R_AddIQMSurfaces( ent ); + case MOD_IQM: + R_AddIQMSurfaces( ent ); + break; + case MOD_BRUSH: + R_AddBrushModelSurfaces( ent ); + break; + case MOD_BAD: // null model axis + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { break; - case MOD_BRUSH: - R_AddBrushModelSurfaces( ent ); - break; - case MOD_BAD: // null model axis - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { - break; - } - shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 ); - break; - default: - ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" ); - break; } + shader = R_GetShaderByHandle( ent->e.customShader ); + R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0, 0 ); + break; + default: + ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" ); + break; } - break; - default: - ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" ); } + break; + default: + ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" ); } +} +/* +============= +R_AddEntitySurfaces +============= +*/ +void R_AddEntitySurfaces (void) { + int i; + + if ( !r_drawentities->integer ) { + return; + } + + for ( i = 0; i < tr.refdef.num_entities; i++) + R_AddEntitySurface(i); } @@ -1303,7 +1971,8 @@ // matrix for lod calculation // dynamically compute far clip plane distance - R_SetFarClip(); + if (!tr.viewParms.isShadowmap) + R_SetFarClip(); // we know the size of the clipping volume. Now set the rest of the projection matrix. R_SetupProjectionZ (&tr.viewParms); @@ -1317,6 +1986,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 ); @@ -1340,6 +2011,7 @@ } qglEnd(); qglDepthRange( 0, 1 ); +#endif } /* @@ -1391,7 +2063,7 @@ // set viewParms.world R_RotateForViewer (); - R_SetupProjection(&tr.viewParms, r_zproj->value, qtrue); + R_SetupProjection(&tr.viewParms, r_zproj->value, tr.viewParms.zFar, qtrue); R_GenerateDrawSurfs(); @@ -1402,4 +2074,418 @@ } +void R_RenderDlightCubemaps(const refdef_t *fd) +{ + int i; + for (i = 0; i < tr.refdef.num_dlights; i++) + { + viewParms_t shadowParms; + int j; + + // use previous frame to determine visible dlights + if ((1 << i) & tr.refdef.dlightMask) + continue; + + Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); + + shadowParms.viewportX = tr.refdef.x; + shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + 256 ); + shadowParms.viewportWidth = 256; + shadowParms.viewportHeight = 256; + shadowParms.isPortal = qfalse; + shadowParms.isMirror = qtrue; // because it is + + shadowParms.fovX = 90; + shadowParms.fovY = 90; + + shadowParms.isShadowmap = qtrue; + shadowParms.zFar = tr.refdef.dlights[i].radius; + + VectorCopy( tr.refdef.dlights[i].origin, shadowParms.or.origin ); + + for (j = 0; j < 6; j++) + { + switch(j) + { + case 0: + // -X + VectorSet( shadowParms.or.axis[0], -1, 0, 0); + VectorSet( shadowParms.or.axis[1], 0, 0, -1); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + case 1: + // +X + VectorSet( shadowParms.or.axis[0], 1, 0, 0); + VectorSet( shadowParms.or.axis[1], 0, 0, 1); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + case 2: + // -Y + VectorSet( shadowParms.or.axis[0], 0, -1, 0); + VectorSet( shadowParms.or.axis[1], 1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 0, -1); + break; + case 3: + // +Y + VectorSet( shadowParms.or.axis[0], 0, 1, 0); + VectorSet( shadowParms.or.axis[1], 1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 0, 1); + break; + case 4: + // -Z + VectorSet( shadowParms.or.axis[0], 0, 0, -1); + VectorSet( shadowParms.or.axis[1], 1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + case 5: + // +Z + VectorSet( shadowParms.or.axis[0], 0, 0, 1); + VectorSet( shadowParms.or.axis[1], -1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + } + + R_RenderView(&shadowParms); + R_AddCapShadowmapCmd( i, j ); + } + } +} + + +void R_RenderPshadowMaps(const refdef_t *fd) +{ + viewParms_t shadowParms; + int i; + + // first, make a list of shadows + for ( i = 0; i < tr.refdef.num_entities; i++) + { + trRefEntity_t *ent = &tr.refdef.entities[i]; + + if((ent->e.renderfx & (RF_FIRST_PERSON | RF_NOSHADOW))) + continue; + + //if((ent->e.renderfx & RF_THIRD_PERSON)) + //continue; + + if (ent->e.reType == RT_MODEL) + { + model_t *model = R_GetModelByHandle( ent->e.hModel ); + pshadow_t shadow; + float radius = 0.0f; + float scale = 1.0f; + vec3_t diff; + int j; + + if (!model) + continue; + + if (ent->e.nonNormalizedAxes) + { + scale = VectorLength( ent->e.axis[0] ); + } + + switch (model->type) + { + case MOD_MESH: + { + mdvFrame_t *frame = &model->mdv[0]->frames[ent->e.frame]; + + radius = frame->radius * scale; + } + break; + + case MOD_MD4: + { + // FIXME: actually calculate the radius and bounds, this is a horrible hack + radius = r_pshadowDist->value / 2.0f; + } + break; +#ifdef RAVENMD4 + case MOD_MDR: + { + // FIXME: never actually tested this + mdrHeader_t *header = model->modelData; + int frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + mdrFrame_t *frame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); + + radius = frame->radius; + } + break; +#endif + case MOD_IQM: + { + // FIXME: never actually tested this + iqmData_t *data = model->modelData; + vec3_t diag; + float *framebounds; + + framebounds = data->bounds + 6*ent->e.frame; + VectorSubtract( framebounds+3, framebounds, diag ); + radius = 0.5f * VectorLength( diag ); + } + break; + + default: + break; + } + + if (!radius) + continue; + + // Cull entities that are behind the viewer by more than lightRadius + VectorSubtract(ent->e.origin, fd->vieworg, diff); + if (DotProduct(diff, fd->viewaxis[0]) < -r_pshadowDist->value) + continue; + + memset(&shadow, 0, sizeof(shadow)); + + shadow.numEntities = 1; + shadow.entityNums[0] = i; + shadow.viewRadius = radius; + shadow.lightRadius = r_pshadowDist->value; + VectorCopy(ent->e.origin, shadow.viewOrigin); + shadow.sort = DotProduct(diff, diff) / (radius * radius); + VectorCopy(ent->e.origin, shadow.entityOrigins[0]); + shadow.entityRadiuses[0] = radius; + + for (j = 0; j < MAX_CALC_PSHADOWS; j++) + { + pshadow_t swap; + + if (j + 1 > tr.refdef.num_pshadows) + { + tr.refdef.num_pshadows = j + 1; + tr.refdef.pshadows[j] = shadow; + break; + } + + // sort shadows by distance from camera divided by radius + // FIXME: sort better + if (tr.refdef.pshadows[j].sort <= shadow.sort) + continue; + + swap = tr.refdef.pshadows[j]; + tr.refdef.pshadows[j] = shadow; + shadow = swap; + } + } + } + + // next, merge touching pshadows + for ( i = 0; i < tr.refdef.num_pshadows; i++) + { + pshadow_t *ps1 = &tr.refdef.pshadows[i]; + int j; + + for (j = i + 1; j < tr.refdef.num_pshadows; j++) + { + pshadow_t *ps2 = &tr.refdef.pshadows[j]; + int k; + qboolean touch; + + if (ps1->numEntities == 8) + break; + + touch = qfalse; + if (SpheresIntersect(ps1->viewOrigin, ps1->viewRadius, ps2->viewOrigin, ps2->viewRadius)) + { + for (k = 0; k < ps1->numEntities; k++) + { + if (SpheresIntersect(ps1->entityOrigins[k], ps1->entityRadiuses[k], ps2->viewOrigin, ps2->viewRadius)) + { + touch = qtrue; + break; + } + } + } + + if (touch) + { + vec3_t newOrigin; + float newRadius; + + BoundingSphereOfSpheres(ps1->viewOrigin, ps1->viewRadius, ps2->viewOrigin, ps2->viewRadius, newOrigin, &newRadius); + VectorCopy(newOrigin, ps1->viewOrigin); + ps1->viewRadius = newRadius; + + ps1->entityNums[ps1->numEntities] = ps2->entityNums[0]; + VectorCopy(ps2->viewOrigin, ps1->entityOrigins[ps1->numEntities]); + ps1->entityRadiuses[ps1->numEntities] = ps2->viewRadius; + + ps1->numEntities++; + + for (k = j; k < tr.refdef.num_pshadows - 1; k++) + { + tr.refdef.pshadows[k] = tr.refdef.pshadows[k + 1]; + } + + j--; + tr.refdef.num_pshadows--; + } + } + } + + // cap number of drawn pshadows + if (tr.refdef.num_pshadows > MAX_DRAWN_PSHADOWS) + { + tr.refdef.num_pshadows = MAX_DRAWN_PSHADOWS; + } + + // next, fill up the rest of the shadow info + for ( i = 0; i < tr.refdef.num_pshadows; i++) + { + trRefEntity_t fakeEnt; + pshadow_t *shadow = &tr.refdef.pshadows[i]; + vec3_t up; + vec3_t lightDir; + +#if 0 + VectorSet(lightDir, 0.57735, 0.57735, 0.57735); +#else + // light a fake entity to determine light direction + memset(&fakeEnt, 0, sizeof(fakeEnt)); + VectorCopy(shadow->viewOrigin, fakeEnt.e.origin); + R_SetupEntityLighting( &tr.refdef, &fakeEnt); + VectorCopy(fakeEnt.lightDir, lightDir); +#endif + + if (shadow->viewRadius * 3.0f > shadow->lightRadius) + { + shadow->lightRadius = shadow->viewRadius * 3.0f; + } + + VectorMA(shadow->viewOrigin, shadow->viewRadius, lightDir, shadow->lightOrigin); + + // make up a projection, up doesn't matter + VectorScale(lightDir, -1.0f, shadow->lightViewAxis[0]); + VectorSet(up, 0, 0, -1); + + if ( abs(DotProduct(up, shadow->lightViewAxis[0])) > 0.9f ) + { + VectorSet(up, -1, 0, 0); + } + + CrossProduct(shadow->lightViewAxis[0], up, shadow->lightViewAxis[1]); + VectorNormalize(shadow->lightViewAxis[1]); + CrossProduct(shadow->lightViewAxis[0], shadow->lightViewAxis[1], shadow->lightViewAxis[2]); + + VectorCopy(shadow->lightViewAxis[0], shadow->cullPlane.normal); + shadow->cullPlane.dist = DotProduct(shadow->cullPlane.normal, shadow->lightOrigin); + shadow->cullPlane.type = PLANE_NON_AXIAL; + SetPlaneSignbits(&shadow->cullPlane); + } + + // next, render shadowmaps + for ( i = 0; i < tr.refdef.num_pshadows; i++) + { + int firstDrawSurf; + pshadow_t *shadow = &tr.refdef.pshadows[i]; + int j; + + Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); + + shadowParms.viewportX = tr.refdef.x; + shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + 256 ); + shadowParms.viewportWidth = 256; + shadowParms.viewportHeight = 256; + shadowParms.isPortal = qfalse; + shadowParms.isMirror = qfalse; + + shadowParms.fovX = 90; + shadowParms.fovY = 90; + + shadowParms.isShadowmap = qtrue; + + shadowParms.zFar = shadow->lightRadius; + + VectorCopy(shadow->lightOrigin, shadowParms.or.origin); + + VectorCopy(shadow->lightViewAxis[0], shadowParms.or.axis[0]); + VectorCopy(shadow->lightViewAxis[1], shadowParms.or.axis[1]); + VectorCopy(shadow->lightViewAxis[2], shadowParms.or.axis[2]); + + { + tr.viewCount++; + + tr.viewParms = shadowParms; + tr.viewParms.frameSceneNum = tr.frameSceneNum; + tr.viewParms.frameCount = tr.frameCount; + + firstDrawSurf = tr.refdef.numDrawSurfs; + + tr.viewCount++; + + // set viewParms.world + R_RotateForViewer (); + + { + float xmin, xmax, ymin, ymax, znear, zfar; + viewParms_t *dest = &tr.viewParms; + vec3_t pop; + + xmin = ymin = -shadow->viewRadius; + xmax = ymax = shadow->viewRadius; + znear = 0; + zfar = shadow->lightRadius; + + dest->projectionMatrix[0] = 2 / (xmax - xmin); + dest->projectionMatrix[4] = 0; + dest->projectionMatrix[8] = (xmax + xmin) / (xmax - xmin); + dest->projectionMatrix[12] =0; + + dest->projectionMatrix[1] = 0; + dest->projectionMatrix[5] = 2 / (ymax - ymin); + dest->projectionMatrix[9] = ( ymax + ymin ) / (ymax - ymin); // normally 0 + dest->projectionMatrix[13] = 0; + + dest->projectionMatrix[2] = 0; + dest->projectionMatrix[6] = 0; + dest->projectionMatrix[10] = 2 / (zfar - znear); + dest->projectionMatrix[14] = 0; + + dest->projectionMatrix[3] = 0; + dest->projectionMatrix[7] = 0; + dest->projectionMatrix[11] = 0; + dest->projectionMatrix[15] = 1; + + VectorScale(dest->or.axis[1], 1.0f, dest->frustum[0].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[0].normal, pop); + dest->frustum[0].dist = DotProduct(pop, dest->frustum[0].normal); + + VectorScale(dest->or.axis[1], -1.0f, dest->frustum[1].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[1].normal, pop); + dest->frustum[1].dist = DotProduct(pop, dest->frustum[1].normal); + + VectorScale(dest->or.axis[2], 1.0f, dest->frustum[2].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[2].normal, pop); + dest->frustum[2].dist = DotProduct(pop, dest->frustum[2].normal); + + VectorScale(dest->or.axis[2], -1.0f, dest->frustum[3].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[3].normal, pop); + dest->frustum[3].dist = DotProduct(pop, dest->frustum[3].normal); + + VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); + VectorMA(dest->or.origin, -shadow->lightRadius, dest->frustum[4].normal, pop); + dest->frustum[4].dist = DotProduct(pop, dest->frustum[4].normal); + + for (j = 0; j < 5; j++) + { + dest->frustum[j].type = PLANE_NON_AXIAL; + SetPlaneSignbits (&dest->frustum[j]); + } + } + + for (j = 0; j < shadow->numEntities; j++) + { + R_AddEntitySurface(shadow->entityNums[j]); + } + + R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); + R_AddCapShadowmapCmd( i, -1 ); + } + } +} + + Index: code/renderer/tr_marks.c =================================================================== --- code/renderer/tr_marks.c (revision 2095) +++ code/renderer/tr_marks.c (working copy) @@ -134,7 +134,8 @@ void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir) { int s, c; - msurface_t *surf, **mark; + msurface_t *surf; + int *mark; // do the tail recursion in a loop while ( node->contents == -1 ) { @@ -150,37 +151,39 @@ } // add the individual surfaces - mark = node->firstmarksurface; + mark = tr.world->marksurfaces + node->firstmarksurface; c = node->nummarksurfaces; while (c--) { + int *surfViewCount; // if (*listlength >= listsize) break; // - surf = *mark; + surfViewCount = &tr.world->surfacesViewCount[*mark]; + surf = tr.world->surfaces + *mark; // check if the surface has NOIMPACT or NOMARKS set if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) ) || ( surf->shader->contentFlags & CONTENTS_FOG ) ) { - surf->viewCount = tr.viewCount; + *surfViewCount = tr.viewCount; } // extra check for surfaces to avoid list overflows else if (*(surf->data) == SF_FACE) { // the face plane should go through the box - s = BoxOnPlaneSide( mins, maxs, &(( srfSurfaceFace_t * ) surf->data)->plane ); + s = BoxOnPlaneSide( mins, maxs, &surf->cullinfo.plane ); if (s == 1 || s == 2) { - surf->viewCount = tr.viewCount; - } else if (DotProduct((( srfSurfaceFace_t * ) surf->data)->plane.normal, dir) > -0.5) { + *surfViewCount = tr.viewCount; + } else if (DotProduct(surf->cullinfo.plane.normal, dir) > -0.5) { // don't add faces that make sharp angles with the projection direction - surf->viewCount = tr.viewCount; + *surfViewCount = tr.viewCount; } } - else if (*(surfaceType_t *) (surf->data) != SF_GRID && - *(surfaceType_t *) (surf->data) != SF_TRIANGLES) - surf->viewCount = tr.viewCount; + else if (*(surf->data) != SF_GRID && + *(surf->data) != SF_TRIANGLES) + *surfViewCount = tr.viewCount; // check the viewCount because the surface may have // already been added if it spans multiple leafs - if (surf->viewCount != tr.viewCount) { - surf->viewCount = tr.viewCount; - list[*listlength] = (surfaceType_t *) surf->data; + if (*surfViewCount != tr.viewCount) { + *surfViewCount = tr.viewCount; + list[*listlength] = surf->data; (*listlength)++; } mark++; @@ -266,11 +269,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 +410,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 +433,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 +456,5 @@ return returnedFragments; } + + Index: code/renderer/tr_mesh.c =================================================================== --- code/renderer/tr_mesh.c (revision 2095) +++ code/renderer/tr_mesh.c (working copy) @@ -75,14 +75,14 @@ R_CullModel ============= */ -static int R_CullModel( md3Header_t *header, trRefEntity_t *ent ) { +static int R_CullModel( mdvModel_t *model, trRefEntity_t *ent ) { vec3_t bounds[2]; - md3Frame_t *oldFrame, *newFrame; + mdvFrame_t *oldFrame, *newFrame; int i; // compute frame pointers - newFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame; - oldFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.oldframe; + newFrame = model->frames + ent->e.frame; + oldFrame = model->frames + ent->e.oldframe; // cull bounding sphere ONLY if this is not an upscaled entity if ( !ent->e.nonNormalizedAxes ) @@ -167,7 +167,7 @@ float radius; float flod, lodscale; float projectedRadius; - md3Frame_t *frame; + mdvFrame_t *frame; #ifdef RAVENMD4 mdrHeader_t *mdr; mdrFrame_t *mdrframe; @@ -198,7 +198,8 @@ else #endif { - frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); + //frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); + frame = tr.currentModel->mdv[0]->frames; frame += ent->e.frame; @@ -246,10 +247,10 @@ ================= */ -int R_ComputeFogNum( md3Header_t *header, trRefEntity_t *ent ) { +int R_ComputeFogNum( mdvModel_t *model, trRefEntity_t *ent ) { int i, j; fog_t *fog; - md3Frame_t *md3Frame; + mdvFrame_t *mdvFrame; vec3_t localOrigin; if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { @@ -257,15 +258,15 @@ } // FIXME: non-normalized axis issues - md3Frame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame; - VectorAdd( ent->e.origin, md3Frame->localOrigin, localOrigin ); + mdvFrame = model->frames + ent->e.frame; + VectorAdd( ent->e.origin, mdvFrame->localOrigin, localOrigin ); for ( i = 1 ; i < tr.world->numfogs ; i++ ) { fog = &tr.world->fogs[i]; for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - md3Frame->radius >= fog->bounds[1][j] ) { + if ( localOrigin[j] - mdvFrame->radius >= fog->bounds[1][j] ) { break; } - if ( localOrigin[j] + md3Frame->radius <= fog->bounds[0][j] ) { + if ( localOrigin[j] + mdvFrame->radius <= fog->bounds[0][j] ) { break; } } @@ -285,9 +286,8 @@ */ void R_AddMD3Surfaces( trRefEntity_t *ent ) { int i; - md3Header_t *header = NULL; - md3Surface_t *surface = NULL; - md3Shader_t *md3Shader = NULL; + mdvModel_t *model = NULL; + mdvSurface_t *surface = NULL; shader_t *shader = NULL; int cull; int lod; @@ -295,11 +295,11 @@ qboolean personalModel; // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !(tr.viewParms.isPortal || tr.viewParms.isShadowmap); if ( ent->e.renderfx & RF_WRAP_FRAMES ) { - ent->e.frame %= tr.currentModel->md3[0]->numFrames; - ent->e.oldframe %= tr.currentModel->md3[0]->numFrames; + ent->e.frame %= tr.currentModel->mdv[0]->numFrames; + ent->e.oldframe %= tr.currentModel->mdv[0]->numFrames; } // @@ -308,9 +308,9 @@ // when the surfaces are rendered, they don't need to be // range checked again. // - if ( (ent->e.frame >= tr.currentModel->md3[0]->numFrames) + if ( (ent->e.frame >= tr.currentModel->mdv[0]->numFrames) || (ent->e.frame < 0) - || (ent->e.oldframe >= tr.currentModel->md3[0]->numFrames) + || (ent->e.oldframe >= tr.currentModel->mdv[0]->numFrames) || (ent->e.oldframe < 0) ) { ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n", ent->e.oldframe, ent->e.frame, @@ -324,13 +324,13 @@ // lod = R_ComputeLOD( ent ); - header = tr.currentModel->md3[lod]; + model = tr.currentModel->mdv[lod]; // // cull the entire model if merged bounding box of both frames // is outside the view frustum. // - cull = R_CullModel ( header, ent ); + cull = R_CullModel ( model, ent ); if ( cull == CULL_OUT ) { return; } @@ -345,13 +345,13 @@ // // see if we are in a fog volume // - fogNum = R_ComputeFogNum( header, ent ); + fogNum = R_ComputeFogNum( model, ent ); // // draw all surfaces // - surface = (md3Surface_t *)( (byte *)header + header->ofsSurfaces ); - for ( i = 0 ; i < header->numSurfaces ; i++ ) { + surface = model->surfaces; + for ( i = 0 ; i < model->numSurfaces ; i++ ) { if ( ent->e.customShader ) { shader = R_GetShaderByHandle( ent->e.customShader ); @@ -376,41 +376,58 @@ else if (shader->defaultShader) { ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); } - } else if ( surface->numShaders <= 0 ) { - shader = tr.defaultShader; + //} else if ( surface->numShaders <= 0 ) { + //shader = tr.defaultShader; } else { - md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders ); - md3Shader += ent->e.skinNum % surface->numShaders; - shader = tr.shaders[ md3Shader->shaderIndex ]; + //md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders ); + //md3Shader += ent->e.skinNum % surface->numShaders; + //shader = tr.shaders[ md3Shader->shaderIndex ]; + shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ]; } - // we will add shadows even if the main object isn't visible in the view + if (model->numVBOSurfaces && glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && r_arb_shader_objects->integer) + { + srfVBOMDVMesh_t *vboSurface = &model->vboSurfaces[i]; - // stencil shadows can't do personal models unless I polyhedron clip - if ( !personalModel - && r_shadows->integer == 2 - && fogNum == 0 - && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse ); + // don't add third_person objects if not viewing through a portal + if(!personalModel) + { + R_AddDrawSurf((void *)vboSurface, shader, fogNum, qfalse, qfalse ); + } } + else + { + // we will add shadows even if the main object isn't visible in the view - // projection shadows work fine with personal models - if ( r_shadows->integer == 3 - && fogNum == 0 - && (ent->e.renderfx & RF_SHADOW_PLANE ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse ); - } + // stencil shadows can't do personal models unless I polyhedron clip + if ( !personalModel + && r_shadows->integer == 2 + && fogNum == 0 + && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse, qfalse ); + } - // don't add third_person objects if not viewing through a portal - if ( !personalModel ) { - R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse ); + // projection shadows work fine with personal models + if ( r_shadows->integer == 3 + && fogNum == 0 + && (ent->e.renderfx & RF_SHADOW_PLANE ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse, qfalse ); + } + + // don't add third_person objects if not viewing through a portal + if ( !personalModel ) { + R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse, qfalse ); + } } - surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd ); + surface++; } } + + Index: code/renderer/tr_model.c =================================================================== --- code/renderer/tr_model.c (revision 2095) +++ code/renderer/tr_model.c (working copy) @@ -25,7 +25,7 @@ #define LL(x) x=LittleLong(x) -static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, const char *name ); +static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, int bufferSize, const char *modName); static qboolean R_LoadMD4(model_t *mod, void *buffer, const char *name ); #ifdef RAVENMD4 static qboolean R_LoadMDR(model_t *mod, void *buffer, int filesize, const char *name ); @@ -42,6 +42,7 @@ unsigned *u; void *v; } buf; + int size; int lod; int ident; qboolean loaded = qfalse; @@ -69,7 +70,7 @@ else Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext); - ri.FS_ReadFile( namebuf, &buf.v ); + size = ri.FS_ReadFile( namebuf, &buf.v ); if(!buf.u) continue; @@ -79,7 +80,7 @@ else { if (ident == MD3_IDENT) - loaded = R_LoadMD3(mod, lod, buf.u, name); + loaded = R_LoadMD3(mod, lod, buf.u, size, name); else ri.Printf(PRINT_WARNING,"R_RegisterMD3: unknown fileid for %s\n", name); } @@ -102,7 +103,7 @@ for(lod--; lod >= 0; lod--) { mod->numLods++; - mod->md3[lod] = mod->md3[lod + 1]; + mod->mdv[lod] = mod->mdv[lod + 1]; } return mod->index; @@ -383,156 +384,392 @@ R_LoadMD3 ================= */ -static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name ) { - int i, j; - md3Header_t *pinmodel; - md3Frame_t *frame; - md3Surface_t *surf; - md3Shader_t *shader; - md3Triangle_t *tri; - md3St_t *st; - md3XyzNormal_t *xyz; - md3Tag_t *tag; - int version; - int size; +static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, int bufferSize, const char *modName) +{ + int f, i, j, k; - pinmodel = (md3Header_t *)buffer; + md3Header_t *md3Model; + md3Frame_t *md3Frame; + md3Surface_t *md3Surf; + md3Shader_t *md3Shader; + md3Triangle_t *md3Tri; + md3St_t *md3st; + md3XyzNormal_t *md3xyz; + md3Tag_t *md3Tag; - version = LittleLong (pinmodel->version); - if (version != MD3_VERSION) { - ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", - mod_name, version, MD3_VERSION); + mdvModel_t *mdvModel; + mdvFrame_t *frame; + mdvSurface_t *surf;//, *surface; + int *shaderIndex; + srfTriangle_t *tri; + mdvVertex_t *v; + mdvSt_t *st; + mdvTag_t *tag; + mdvTagName_t *tagName; + + int version; + int size; + + md3Model = (md3Header_t *) buffer; + + version = LittleLong(md3Model->version); + if(version != MD3_VERSION) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MD3_VERSION); return qfalse; } mod->type = MOD_MESH; - size = LittleLong(pinmodel->ofsEnd); + size = LittleLong(md3Model->ofsEnd); mod->dataSize += size; - mod->md3[lod] = ri.Hunk_Alloc( size, h_low ); + mdvModel = mod->mdv[lod] = ri.Hunk_Alloc(sizeof(mdvModel_t), h_low); - Com_Memcpy (mod->md3[lod], buffer, LittleLong(pinmodel->ofsEnd) ); +// Com_Memcpy(mod->md3[lod], buffer, LittleLong(md3Model->ofsEnd)); - LL(mod->md3[lod]->ident); - LL(mod->md3[lod]->version); - LL(mod->md3[lod]->numFrames); - LL(mod->md3[lod]->numTags); - LL(mod->md3[lod]->numSurfaces); - LL(mod->md3[lod]->ofsFrames); - LL(mod->md3[lod]->ofsTags); - LL(mod->md3[lod]->ofsSurfaces); - LL(mod->md3[lod]->ofsEnd); + LL(md3Model->ident); + LL(md3Model->version); + LL(md3Model->numFrames); + LL(md3Model->numTags); + LL(md3Model->numSurfaces); + LL(md3Model->ofsFrames); + LL(md3Model->ofsTags); + LL(md3Model->ofsSurfaces); + LL(md3Model->ofsEnd); - if ( mod->md3[lod]->numFrames < 1 ) { - ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); + if(md3Model->numFrames < 1) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has no frames\n", modName); return qfalse; } - + // swap all the frames - frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames ); - for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) { - frame->radius = LittleFloat( frame->radius ); - for ( j = 0 ; j < 3 ; j++ ) { - frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); - frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); - frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); - } + mdvModel->numFrames = md3Model->numFrames; + mdvModel->frames = frame = ri.Hunk_Alloc(sizeof(*frame) * md3Model->numFrames, h_low); + + md3Frame = (md3Frame_t *) ((byte *) md3Model + md3Model->ofsFrames); + for(i = 0; i < md3Model->numFrames; i++, frame++, md3Frame++) + { + frame->radius = LittleFloat(md3Frame->radius); + for(j = 0; j < 3; j++) + { + frame->bounds[0][j] = LittleFloat(md3Frame->bounds[0][j]); + frame->bounds[1][j] = LittleFloat(md3Frame->bounds[1][j]); + frame->localOrigin[j] = LittleFloat(md3Frame->localOrigin[j]); + } } // swap all the tags - tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags ); - for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) { - for ( j = 0 ; j < 3 ; j++ ) { - tag->origin[j] = LittleFloat( tag->origin[j] ); - tag->axis[0][j] = LittleFloat( tag->axis[0][j] ); - tag->axis[1][j] = LittleFloat( tag->axis[1][j] ); - tag->axis[2][j] = LittleFloat( tag->axis[2][j] ); - } + mdvModel->numTags = md3Model->numTags; + mdvModel->tags = tag = ri.Hunk_Alloc(sizeof(*tag) * (md3Model->numTags * md3Model->numFrames), h_low); + + md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); + for(i = 0; i < md3Model->numTags * md3Model->numFrames; i++, tag++, md3Tag++) + { + for(j = 0; j < 3; j++) + { + tag->origin[j] = LittleFloat(md3Tag->origin[j]); + tag->axis[0][j] = LittleFloat(md3Tag->axis[0][j]); + tag->axis[1][j] = LittleFloat(md3Tag->axis[1][j]); + tag->axis[2][j] = LittleFloat(md3Tag->axis[2][j]); + } } + + mdvModel->tagNames = tagName = ri.Hunk_Alloc(sizeof(*tagName) * (md3Model->numTags), h_low); + + md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); + for(i = 0; i < md3Model->numTags; i++, tagName++, md3Tag++) + { + Q_strncpyz(tagName->name, md3Tag->name, sizeof(tagName->name)); + } + // swap all the surfaces - surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); - for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { + mdvModel->numSurfaces = md3Model->numSurfaces; + mdvModel->surfaces = surf = ri.Hunk_Alloc(sizeof(*surf) * md3Model->numSurfaces, h_low); - LL(surf->ident); - LL(surf->flags); - LL(surf->numFrames); - LL(surf->numShaders); - LL(surf->numTriangles); - LL(surf->ofsTriangles); - LL(surf->numVerts); - LL(surf->ofsShaders); - LL(surf->ofsSt); - LL(surf->ofsXyzNormals); - LL(surf->ofsEnd); - - if ( surf->numVerts > SHADER_MAX_VERTEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); + md3Surf = (md3Surface_t *) ((byte *) md3Model + md3Model->ofsSurfaces); + for(i = 0; i < md3Model->numSurfaces; i++) + { + LL(md3Surf->ident); + LL(md3Surf->flags); + LL(md3Surf->numFrames); + LL(md3Surf->numShaders); + LL(md3Surf->numTriangles); + LL(md3Surf->ofsTriangles); + LL(md3Surf->numVerts); + LL(md3Surf->ofsShaders); + LL(md3Surf->ofsSt); + LL(md3Surf->ofsXyzNormals); + LL(md3Surf->ofsEnd); + + if(md3Surf->numVerts > SHADER_MAX_VERTEXES) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i verts on a surface (%i)", + modName, SHADER_MAX_VERTEXES, md3Surf->numVerts); return qfalse; } - if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); + if(md3Surf->numTriangles * 3 > SHADER_MAX_INDEXES) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", + modName, SHADER_MAX_INDEXES / 3, md3Surf->numTriangles); return qfalse; } - + // change to surface identifier - surf->ident = SF_MD3; + surf->surfaceType = SF_MDV; + // give pointer to model for Tess_SurfaceMDX + surf->model = mdvModel; + + // copy surface name + Q_strncpyz(surf->name, md3Surf->name, sizeof(surf->name)); + // lowercase the surface name so skin compares are faster - Q_strlwr( surf->name ); + Q_strlwr(surf->name); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess - j = strlen( surf->name ); - if ( j > 2 && surf->name[j-2] == '_' ) { - surf->name[j-2] = 0; + j = strlen(surf->name); + if(j > 2 && surf->name[j - 2] == '_') + { + surf->name[j - 2] = 0; } - // register the shaders - shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); - for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { - shader_t *sh; + // register the shaders + surf->numShaderIndexes = md3Surf->numShaders; + surf->shaderIndexes = shaderIndex = ri.Hunk_Alloc(sizeof(*shaderIndex) * md3Surf->numShaders, h_low); - sh = R_FindShader( shader->name, LIGHTMAP_NONE, qtrue ); - if ( sh->defaultShader ) { - shader->shaderIndex = 0; - } else { - shader->shaderIndex = sh->index; + md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); + for(j = 0; j < md3Surf->numShaders; j++, shaderIndex++, md3Shader++) + { + shader_t *sh; + + sh = R_FindShader(md3Shader->name, LIGHTMAP_NONE, qtrue); + if(sh->defaultShader) + { + *shaderIndex = 0; } - } + else + { + *shaderIndex = sh->index; + } + } // swap all the triangles - tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); - for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { - LL(tri->indexes[0]); - LL(tri->indexes[1]); - LL(tri->indexes[2]); + surf->numTriangles = md3Surf->numTriangles; + surf->triangles = tri = ri.Hunk_Alloc(sizeof(*tri) * md3Surf->numTriangles, h_low); + + md3Tri = (md3Triangle_t *) ((byte *) md3Surf + md3Surf->ofsTriangles); + for(j = 0; j < md3Surf->numTriangles; j++, tri++, md3Tri++) + { + tri->indexes[0] = LittleLong(md3Tri->indexes[0]); + tri->indexes[1] = LittleLong(md3Tri->indexes[1]); + tri->indexes[2] = LittleLong(md3Tri->indexes[2]); } - // swap all the ST - st = (md3St_t *) ( (byte *)surf + surf->ofsSt ); - for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { - st->st[0] = LittleFloat( st->st[0] ); - st->st[1] = LittleFloat( st->st[1] ); - } + R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles); // swap all the XyzNormals - xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals ); - for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) + surf->numVerts = md3Surf->numVerts; + surf->verts = v = ri.Hunk_Alloc(sizeof(*v) * (md3Surf->numVerts * md3Surf->numFrames), h_low); + + md3xyz = (md3XyzNormal_t *) ((byte *) md3Surf + md3Surf->ofsXyzNormals); + for(j = 0; j < md3Surf->numVerts * md3Surf->numFrames; j++, md3xyz++, v++) { - xyz->xyz[0] = LittleShort( xyz->xyz[0] ); - xyz->xyz[1] = LittleShort( xyz->xyz[1] ); - xyz->xyz[2] = LittleShort( xyz->xyz[2] ); + unsigned lat, lng; + unsigned short normal; - xyz->normal = LittleShort( xyz->normal ); - } + v->xyz[0] = LittleShort(md3xyz->xyz[0]) * MD3_XYZ_SCALE; + v->xyz[1] = LittleShort(md3xyz->xyz[1]) * MD3_XYZ_SCALE; + v->xyz[2] = LittleShort(md3xyz->xyz[2]) * MD3_XYZ_SCALE; + normal = LittleShort(md3xyz->normal); + lat = ( normal >> 8 ) & 0xff; + lng = ( normal & 0xff ); + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + v->normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + v->normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + v->normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + } + + // swap all the ST + surf->st = st = ri.Hunk_Alloc(sizeof(*st) * md3Surf->numVerts, h_low); + + md3st = (md3St_t *) ((byte *) md3Surf + md3Surf->ofsSt); + for(j = 0; j < md3Surf->numVerts; j++, md3st++, st++) + { + st->st[0] = LittleFloat(md3st->st[0]); + st->st[1] = LittleFloat(md3st->st[1]); + } + + // calc tangent spaces + { + const float *v0, *v1, *v2; + const float *t0, *t1, *t2; + vec3_t tangent; + vec3_t bitangent; + vec3_t normal; + + for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) + { + VectorClear(v->tangent); + VectorClear(v->bitangent); + if (r_recalcMD3Normals->integer) + VectorClear(v->normal); + } + + for(f = 0; f < mdvModel->numFrames; f++) + { + for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) + { + v0 = surf->verts[surf->numVerts * f + tri->indexes[0]].xyz; + v1 = surf->verts[surf->numVerts * f + tri->indexes[1]].xyz; + v2 = surf->verts[surf->numVerts * f + tri->indexes[2]].xyz; + + t0 = surf->st[tri->indexes[0]].st; + t1 = surf->st[tri->indexes[1]].st; + t2 = surf->st[tri->indexes[2]].st; + + if (!r_recalcMD3Normals->integer) + VectorCopy(v->normal, normal); + + #if 1 + R_CalcTangentSpace(tangent, bitangent, normal, v0, v1, v2, t0, t1, t2); + #else + R_CalcNormalForTriangle(normal, v0, v1, v2); + R_CalcTangentsForTriangle(tangent, bitangent, v0, v1, v2, t0, t1, t2); + #endif + + for(k = 0; k < 3; k++) + { + float *v; + + v = surf->verts[surf->numVerts * f + tri->indexes[k]].tangent; + VectorAdd(v, tangent, v); + + v = surf->verts[surf->numVerts * f + tri->indexes[k]].bitangent; + VectorAdd(v, bitangent, v); + + if (r_recalcMD3Normals->integer) + { + v = surf->verts[surf->numVerts * f + tri->indexes[k]].normal; + VectorAdd(v, normal, v); + } + } + } + } + + for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) + { + VectorNormalize(v->tangent); + VectorNormalize(v->bitangent); + VectorNormalize(v->normal); + } + } + // find the next surface - surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); + md3Surf = (md3Surface_t *) ((byte *) md3Surf + md3Surf->ofsEnd); + surf++; } - + + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && r_arb_shader_objects->integer) + { + srfVBOMDVMesh_t *vboSurf; + + mdvModel->numVBOSurfaces = mdvModel->numSurfaces; + mdvModel->vboSurfaces = ri.Hunk_Alloc(sizeof(*mdvModel->vboSurfaces) * mdvModel->numSurfaces, h_low); + + vboSurf = mdvModel->vboSurfaces; + surf = mdvModel->surfaces; + for (i = 0; i < mdvModel->numSurfaces; i++, vboSurf++, surf++) + { + vec3_t *verts; + vec3_t *normals; + vec3_t *tangents; + vec3_t *bitangents; + vec2_t *texcoords; + + byte *data; + int dataSize; + + int ofs_xyz, ofs_normal, ofs_st, ofs_tangent, ofs_bitangent; + + dataSize = 0; + + ofs_xyz = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*verts); + + ofs_normal = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*normals); + + ofs_tangent = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*tangents); + + ofs_bitangent = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*bitangents); + + ofs_st = dataSize; + dataSize += surf->numVerts * sizeof(*texcoords); + + data = ri.Malloc(dataSize); + + verts = (void *)(data + ofs_xyz); + normals = (void *)(data + ofs_normal); + tangents = (void *)(data + ofs_tangent); + bitangents = (void *)(data + ofs_bitangent); + texcoords = (void *)(data + ofs_st); + + v = surf->verts; + for ( j = 0; j < surf->numVerts * mdvModel->numFrames ; j++, v++ ) + { + VectorCopy(v->xyz, verts[j]); + VectorCopy(v->normal, normals[j]); + VectorCopy(v->tangent, tangents[j]); + VectorCopy(v->bitangent, bitangents[j]); + } + + st = surf->st; + for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { + texcoords[j][0] = st->st[0]; + texcoords[j][1] = st->st[1]; + } + + vboSurf->surfaceType = SF_VBO_MDVMESH; + vboSurf->mdvModel = mdvModel; + vboSurf->mdvSurface = surf; + vboSurf->numIndexes = surf->numTriangles * 3; + vboSurf->numVerts = surf->numVerts; + vboSurf->vbo = R_CreateVBO(va("staticMD3Mesh_VBO '%s'", surf->name), data, dataSize, VBO_USAGE_STATIC); + + vboSurf->vbo->ofs_xyz = ofs_xyz; + vboSurf->vbo->ofs_normal = ofs_normal; + vboSurf->vbo->ofs_tangent = ofs_tangent; + vboSurf->vbo->ofs_bitangent = ofs_bitangent; + vboSurf->vbo->ofs_st = ofs_st; + + vboSurf->vbo->stride_xyz = sizeof(*verts); + vboSurf->vbo->stride_normal = sizeof(*normals); + vboSurf->vbo->stride_tangent = sizeof(*tangents); + vboSurf->vbo->stride_bitangent = sizeof(*bitangents); + vboSurf->vbo->stride_st = sizeof(*st); + + vboSurf->vbo->size_xyz = sizeof(*verts) * surf->numVerts; + vboSurf->vbo->size_normal = sizeof(*normals) * surf->numVerts; + + ri.Free(data); + + vboSurf->ibo = R_CreateIBO2(va("staticMD3Mesh_IBO %s", surf->name), surf->numTriangles, surf->triangles, VBO_USAGE_STATIC); + } + } + return qtrue; } @@ -1047,7 +1284,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(); @@ -1093,7 +1332,7 @@ mod = tr.models[i]; lods = 1; for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) { - if ( mod->md3[j] && mod->md3[j] != mod->md3[j-1] ) { + if ( mod->mdv[j] && mod->mdv[j] != mod->mdv[j-1] ) { lods++; } } @@ -1118,19 +1357,23 @@ R_GetTag ================ */ -static md3Tag_t *R_GetTag( md3Header_t *mod, int frame, const char *tagName ) { - md3Tag_t *tag; - int i; +static mdvTag_t *R_GetTag( mdvModel_t *mod, int frame, const char *_tagName ) { + int i; + mdvTag_t *tag; + mdvTagName_t *tagName; if ( frame >= mod->numFrames ) { // it is possible to have a bad frame while changing models, so don't error frame = mod->numFrames - 1; } - tag = (md3Tag_t *)((byte *)mod + mod->ofsTags) + frame * mod->numTags; - for ( i = 0 ; i < mod->numTags ; i++, tag++ ) { - if ( !strcmp( tag->name, tagName ) ) { - return tag; // found it + tag = mod->tags + frame * mod->numTags; + tagName = mod->tagNames; + for(i = 0; i < mod->numTags; i++, tag++, tagName++) + { + if(!strcmp(tagName->name, _tagName)) + { + return tag; } } @@ -1190,7 +1433,7 @@ */ int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, float frac, const char *tagName ) { - md3Tag_t *start, *end; + mdvTag_t *start, *end; #ifdef RAVENMD4 md3Tag_t start_space, end_space; #endif @@ -1199,7 +1442,7 @@ model_t *model; model = R_GetModelByHandle( handle ); - if ( !model->md3[0] ) + if ( !model->mdv[0] ) { #ifdef RAVENMD4 if(model->type == MOD_MDR) @@ -1225,8 +1468,8 @@ } else { - start = R_GetTag( model->md3[0], startFrame, tagName ); - end = R_GetTag( model->md3[0], endFrame, tagName ); + start = R_GetTag( model->mdv[0], startFrame, tagName ); + end = R_GetTag( model->mdv[0], endFrame, tagName ); if ( !start || !end ) { AxisClear( tag->axis ); VectorClear( tag->origin ); @@ -1266,11 +1509,11 @@ return; } else if (model->type == MOD_MESH) { - md3Header_t *header; - md3Frame_t *frame; + mdvModel_t *header; + mdvFrame_t *frame; - header = model->md3[0]; - frame = (md3Frame_t *) ((byte *)header + header->ofsFrames); + header = model->mdv[0]; + frame = header->frames; VectorCopy( frame->bounds[0], mins ); VectorCopy( frame->bounds[1], maxs ); Index: code/renderer/tr_model_iqm.c =================================================================== --- code/renderer/tr_model_iqm.c (revision 2095) +++ code/renderer/tr_model_iqm.c (working copy) @@ -859,7 +859,7 @@ && fogNum == 0 && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0 ); + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0, 0 ); } // projection shadows work fine with personal models @@ -867,11 +867,11 @@ && fogNum == 0 && (ent->e.renderfx & RF_SHADOW_PLANE ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0 ); + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0, 0 ); } if( !personalModel ) { - R_AddDrawSurf( (void *)surface, shader, fogNum, 0 ); + R_AddDrawSurf( (void *)surface, shader, fogNum, 0, 0 ); } surface++; Index: code/renderer/tr_scene.c =================================================================== --- code/renderer/tr_scene.c (revision 2095) +++ code/renderer/tr_scene.c (working copy) @@ -105,7 +105,7 @@ for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) { sh = R_GetShaderByHandle( poly->hShader ); - R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex, qfalse ); + R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex, qfalse, qfalse ); } } @@ -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++ ) { @@ -365,6 +367,9 @@ tr.refdef.numPolys = r_numpolys - r_firstScenePoly; tr.refdef.polys = &backEndData[tr.smpFrame]->polys[r_firstScenePoly]; + tr.refdef.num_pshadows = 0; + tr.refdef.pshadows = &backEndData[tr.smpFrame]->pshadows[0]; + // turn off dynamic lighting globally by clearing all the // dlights if it needs to be disabled or if vertex lighting is enabled if ( r_dynamiclight->integer == 0 || @@ -381,6 +386,22 @@ tr.frameSceneNum++; tr.sceneCount++; + // SmileTheory: playing with shadow mapping + if (!( fd->rdflags & RDF_NOWORLDMODEL ) && tr.refdef.num_dlights && r_dlightMode->integer >= 2 + && glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer + && glRefConfig.glsl && r_arb_shader_objects->integer) + { + R_RenderDlightCubemaps(fd); + } + + /* playing with more shadows */ + if(!( fd->rdflags & RDF_NOWORLDMODEL ) && r_shadows->integer == 4 + && glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer + && glRefConfig.glsl && r_arb_shader_objects->integer) + { + R_RenderPshadowMaps(fd); + } + // setup view parms for the initial view // // set up viewport Index: code/renderer/tr_shade.c =================================================================== --- code/renderer/tr_shade.c (revision 2095) +++ code/renderer/tr_shade.c (working copy) @@ -200,6 +200,31 @@ } +static void R_DrawElementsVBO( int numIndexes, int firstIndex ) +{ + qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE))); +} + + +static void R_DrawMultiElementsVBO( int multiDrawPrimitives, const GLvoid **multiDrawFirstIndex, GLsizei *multiDrawNumIndexes) +{ + if (glRefConfig.multiDrawArrays && r_ext_multi_draw_arrays->integer) + { + qglMultiDrawElementsEXT(GL_TRIANGLES, multiDrawNumIndexes, GL_INDEX_TYPE, multiDrawFirstIndex, multiDrawPrimitives); + } + else + { + int i; + + for (i = 0; i < multiDrawPrimitives; i++) + { + qglDrawElements(GL_TRIANGLES, multiDrawNumIndexes[i], GL_INDEX_TYPE, multiDrawFirstIndex[i]); + } + } +} + + + /* ============================================================= @@ -213,21 +238,24 @@ /* ================= -R_BindAnimatedImage +R_BindAnimatedImageToTMU ================= */ -static void R_BindAnimatedImage( textureBundle_t *bundle ) { +static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { int index; if ( bundle->isVideoMap ) { + int oldtmu = glState.currenttmu; + GL_SelectTexture(tmu); ri.CIN_RunCinematic(bundle->videoMapHandle); ri.CIN_UploadCinematic(bundle->videoMapHandle); + GL_SelectTexture(oldtmu); return; } if ( bundle->numImageAnimations <= 1 ) { - GL_Bind( bundle->image[0] ); + GL_BindToTMU( bundle->image[0], tmu); return; } @@ -241,10 +269,21 @@ } index %= bundle->numImageAnimations; - GL_Bind( bundle->image[ index ] ); + GL_BindToTMU( bundle->image[ index ], tmu ); } /* +================= +R_BindAnimatedImage + +================= +*/ +static void R_BindAnimatedImage( textureBundle_t *bundle ) { + R_BindAnimatedImageToTMU( bundle, glState.currenttmu ); +} + + +/* ================ DrawTris @@ -253,26 +292,58 @@ */ 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 (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + shaderProgram_t *sp = &tr.textureColorShader; + vec4_t color; - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD + GLSL_VertexAttribsState(ATTR_POSITION); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + VectorSet4(color, 1, 1, 1, 1); + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); - if (qglLockArraysEXT) { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)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 +357,8 @@ ================ */ static void DrawNormals (shaderCommands_t *input) { + //FIXME: implement this +#if 0 int i; vec3_t temp; @@ -303,6 +376,7 @@ qglEnd (); qglDepthRange( 0, 1 ); +#endif } /* @@ -319,23 +393,232 @@ 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 + tess.pshadowBits = 0; // will be OR'd in by surface functions tess.xstages = state->stages; tess.numPasses = state->numUnfoggedPasses; tess.currentStageIteratorFunc = state->optimalStageIteratorFunc; + tess.useInternalVBO = qtrue; tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) { tess.shaderTime = tess.shader->clampTime; } + if (backEnd.viewParms.isShadowmap) + { + tess.currentStageIteratorFunc = RB_StageIteratorGenericVBO; + } +} + + +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: + RB_CalcTurbulentTexMatrix( &bundle->texMods[tm].wave, + matrix ); + outmatrix[12] = matrix[12]; + outmatrix[13] = matrix[13]; + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_ENTITY_TRANSLATE: + RB_CalcScrollTexMatrix( backEnd.currentEntity->e.shaderTexCoord, + matrix ); + //Matrix16Multiply(currentmatrix, matrix, outmatrix); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_SCROLL: + RB_CalcScrollTexMatrix( bundle->texMods[tm].scroll, + matrix ); + //Matrix16Multiply(currentmatrix, matrix, outmatrix); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_SCALE: + RB_CalcScaleTexMatrix( bundle->texMods[tm].scale, + matrix ); + //Matrix16Multiply(currentmatrix, matrix, outmatrix); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_STRETCH: + RB_CalcStretchTexMatrix( &bundle->texMods[tm].wave, + matrix ); + //Matrix16Multiply(currentmatrix, matrix, outmatrix); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_TRANSFORM: + RB_CalcTransformTexMatrix( &bundle->texMods[tm], + matrix ); + //Matrix16Multiply(currentmatrix, matrix, outmatrix); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_ROTATE: + RB_CalcRotateTexMatrix( bundle->texMods[tm].rotateSpeed, + matrix ); + //Matrix16Multiply(currentmatrix, matrix, outmatrix); + 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 +677,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] ); + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)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 @@ -748,7 +1107,396 @@ ProjectDlightTexture_scalar(); } +static void ComputeDeformValues(int *deformGen, vec5_t deformParams); +static void ProjectDlightTextureVBOGLSL( void ) { + int l; + vec3_t origin; + float scale; + float radius; + int deformGen; + vec5_t deformParams; + + if ( !backEnd.refdef.num_dlights ) { + return; + } + + ComputeDeformValues(&deformGen, deformParams); + + for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { + dlight_t *dl; + shaderProgram_t *sp; + vec4_t vector; + + if ( !( tess.dlightBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this light + } + + dl = &backEnd.refdef.dlights[l]; + VectorCopy( dl->transformed, origin ); + radius = dl->radius; + scale = 1.0f / radius; + + sp = &tr.dlightallShader; + + backEnd.pc.c_dlightDraws++; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + GLSL_SetUniformFloat(sp, DLIGHT_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, DLIGHT_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, DLIGHT_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, DLIGHT_UNIFORM_TIME, tess.shaderTime); + } + + vector[0] = dl->color[0]; + vector[1] = dl->color[1]; + vector[2] = dl->color[2]; + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, DLIGHT_UNIFORM_COLOR, vector); + + vector[0] = origin[0]; + vector[1] = origin[1]; + vector[2] = origin[2]; + vector[3] = scale; + GLSL_SetUniformVec4(sp, DLIGHT_UNIFORM_DLIGHTINFO, vector); + + GL_Bind( tr.dlightImage ); + + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + if ( dl->additive ) { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + else { + GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + + if (tess.multiDrawPrimitives) + { + R_DrawMultiElementsVBO(tess.multiDrawPrimitives, (const GLvoid **)tess.multiDrawFirstIndex, tess.multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex); + } + + backEnd.pc.c_totalIndexes += tess.numIndexes; + backEnd.pc.c_dlightIndexes += tess.numIndexes; + } +} + + +static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t vertColor ); +static void ComputeFogColorMask( shaderStage_t *pStage, vec4_t fogColorMask ); + +static void ComputeFogValues(vec4_t fogDistanceVector, vec4_t fogDepthVector, float *eyeT) +{ + // from RB_CalcFogTexCoords() + fog_t *fog; + vec3_t local; + + if (!tess.fogNum) + return; + + fog = tr.world->fogs + tess.fogNum; + + 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 + VectorScale4(fogDistanceVector, fog->tcScale, fogDistanceVector); + + // 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 + } +} + +static void ComputeDeformValues(int *deformGen, vec5_t deformParams) +{ + // u_DeformGen + *deformGen = DGEN_NONE; + if(!ShaderRequiresCPUDeforms(tess.shader)) + { + deformStage_t *ds; + + // only support the first one + ds = &tess.shader->deforms[0]; + + switch (ds->deformation) + { + case DEFORM_WAVE: + *deformGen = ds->deformationWave.func; + + deformParams[0] = ds->deformationWave.base; + deformParams[1] = ds->deformationWave.amplitude; + deformParams[2] = ds->deformationWave.phase; + deformParams[3] = ds->deformationWave.frequency; + deformParams[4] = ds->deformationSpread; + break; + + case DEFORM_BULGE: + *deformGen = DGEN_BULGE; + + deformParams[0] = 0; + deformParams[1] = ds->bulgeHeight; // amplitude + deformParams[2] = ds->bulgeWidth; // phase + deformParams[3] = ds->bulgeSpeed; // frequency + deformParams[4] = 0; + break; + + default: + break; + } + } +} + +static void ForwardDlightVBOGLSL( void ) { + int l; + vec3_t origin; + float scale; + float radius; + + int deformGen; + vec5_t deformParams; + + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT = 0; + + shaderCommands_t *input = &tess; + shaderStage_t *pStage = tess.xstages[0]; + + if ( !backEnd.refdef.num_dlights ) { + return; + } + + ComputeDeformValues(&deformGen, deformParams); + + ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); + + for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { + dlight_t *dl; + shaderProgram_t *sp; + vec4_t vector; + matrix_t matrix; + + if ( !( tess.dlightBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this light + } + + dl = &backEnd.refdef.dlights[l]; + VectorCopy( dl->transformed, origin ); + radius = dl->radius; + scale = 1.0f / radius; + + //if (pStage->glslShaderGroup == tr.lightallShader) + { + int index = pStage->glslShaderIndex; + + index &= ~(LIGHTDEF_USE_LIGHTMAP | LIGHTDEF_USE_DELUXEMAP); + index |= LIGHTDEF_USE_LIGHT_VECTOR; + + sp = &tr.lightallShader[index]; + } + + backEnd.pc.c_lightallDraws++; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.or.viewOrigin); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); + } + + if ( input->fogNum ) { + vec4_t fogColorMask; + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); + + ComputeFogColorMask(pStage, fogColorMask); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); + } + + { + vec4_t baseColor; + vec4_t vertColor; + + ComputeShaderColors(pStage, baseColor, vertColor); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); + } + + if (pStage->alphaGen == AGEN_PORTAL) + { + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); + } + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); + + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, dl->color); + + VectorSet(vector, 0, 0, 0); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, vector); + + VectorCopy(dl->origin, vector); + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vector); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, radius); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_SPECULARREFLECTANCE, pStage->specularReflectance); + + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + + Matrix16Identity(matrix); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, matrix); + + if (pStage->bundle[TB_DIFFUSEMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_DIFFUSEMAP], TB_DIFFUSEMAP); + + if (pStage->bundle[TB_NORMALMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_NORMALMAP], TB_NORMALMAP); + + if (pStage->bundle[TB_SPECULARMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_SPECULARMAP], TB_SPECULARMAP); + + if (r_dlightMode->integer >= 2) + { + GL_SelectTexture(TB_SHADOWMAP); + GL_BindCubemap(tr.shadowCubemaps[l]); + GL_SelectTexture(0); + } + + ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, matrix); + + // + // draw + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + + backEnd.pc.c_totalIndexes += tess.numIndexes; + backEnd.pc.c_dlightIndexes += tess.numIndexes; + } +} + + +static void ProjectPshadowVBOGLSL( void ) { + int l; + vec3_t origin; + float radius; + + int deformGen; + vec5_t deformParams; + + shaderCommands_t *input = &tess; + + if ( !backEnd.refdef.num_pshadows ) { + return; + } + + ComputeDeformValues(&deformGen, deformParams); + + for ( l = 0 ; l < backEnd.refdef.num_pshadows ; l++ ) { + pshadow_t *ps; + shaderProgram_t *sp; + vec4_t vector; + + if ( !( tess.pshadowBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this shadow + } + + ps = &backEnd.refdef.pshadows[l]; + VectorCopy( ps->lightOrigin, origin ); + radius = ps->lightRadius; + + sp = &tr.pshadowShader; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + VectorCopy(origin, vector); + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, PSHADOW_UNIFORM_LIGHTORIGIN, vector); + + VectorScale(ps->lightViewAxis[0], 1.0f / ps->viewRadius, vector); + GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTFORWARD, vector); + + VectorScale(ps->lightViewAxis[1], 1.0f / ps->viewRadius, vector); + GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTRIGHT, vector); + + VectorScale(ps->lightViewAxis[2], 1.0f / ps->viewRadius, vector); + GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTUP, vector); + + GLSL_SetUniformFloat(sp, PSHADOW_UNIFORM_LIGHTRADIUS, radius); + + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); + + GL_BindToTMU( tr.pshadowMaps[l], TB_DIFFUSEMAP ); + + // + // draw + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + + backEnd.pc.c_totalIndexes += tess.numIndexes; + //backEnd.pc.c_dlightIndexes += tess.numIndexes; + } +} + + /* =================== RB_FogPass @@ -786,6 +1534,71 @@ } /* +=================== +RB_FogPassVBOGLSL + +Blends a fog texture on top of everything else +=================== +*/ +static void RB_FogPassVBOGLSL( void ) { + fog_t *fog; + vec4_t color; + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT = 0; + shaderProgram_t *sp = &tr.fogShader; + + int deformGen; + vec5_t deformParams; + + ComputeDeformValues(&deformGen, deformParams); + + backEnd.pc.c_fogDraws++; + + GLSL_BindProgram(sp); + + fog = tr.world->fogs + tess.fogNum; + + GLSL_SetUniformMatrix16(sp, FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, FOGPASS_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, FOGPASS_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_TIME, tess.shaderTime); + } + + 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_SetUniformVec4(sp, FOGPASS_UNIFORM_COLOR, color); + + ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); + + GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_FOGDISTANCE, fogDistanceVector); + GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_FOGDEPTH, fogDepthVector); + GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_FOGEYET, eyeT); + + 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 ); + } + + if (tess.multiDrawPrimitives) + { + R_DrawMultiElementsVBO(tess.multiDrawPrimitives, (const GLvoid **)tess.multiDrawFirstIndex, tess.multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex); + } +} + + +/* =============== ComputeColors =============== @@ -955,6 +1768,9 @@ } } break; + case AGEN_FRESNEL: + // FIXME: implement me! + break; } // @@ -1002,8 +1818,604 @@ } } +static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t vertColor ) +{ + // + // rgbGen + // + switch ( pStage->rgbGen ) + { + case CGEN_IDENTITY: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_IDENTITY_LIGHTING: + baseColor[0] = + baseColor[1] = + baseColor[2] = tr.identityLight; + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_EXACT_VERTEX: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 0.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 1.0f; + break; + case CGEN_CONST: + baseColor[0] = pStage->constantColor[0] / 255.0f; + baseColor[1] = pStage->constantColor[1] / 255.0f; + baseColor[2] = pStage->constantColor[2] / 255.0f; + baseColor[3] = pStage->constantColor[3] / 255.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_VERTEX: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 0.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = tr.identityLight; + vertColor[3] = 1.0f; + break; + case CGEN_ONE_MINUS_VERTEX: + baseColor[0] = + baseColor[1] = + baseColor[2] = tr.identityLight; + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = -tr.identityLight; + vertColor[3] = 0.0f; + break; + case CGEN_FOG: + { + fog_t *fog; + + fog = tr.world->fogs + tess.fogNum; + + baseColor[0] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; + baseColor[1] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; + baseColor[2] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; + baseColor[3] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; + } + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.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; + } + + baseColor[0] = + baseColor[1] = + baseColor[2] = glow; + baseColor[3] = 1.0f; + } + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_ENTITY: + if (backEnd.currentEntity) + { + baseColor[0] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + baseColor[1] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + baseColor[2] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + baseColor[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_ONE_MINUS_ENTITY: + if (backEnd.currentEntity) + { + baseColor[0] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + baseColor[1] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + baseColor[2] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + baseColor[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_LIGHTING_DIFFUSE: + case CGEN_BAD: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + } + + // + // alphaGen + // + switch ( pStage->alphaGen ) + { + case AGEN_SKIP: + break; + case AGEN_IDENTITY: + baseColor[3] = 1.0f; + vertColor[3] = 0.0f; + break; + case AGEN_CONST: + baseColor[3] = pStage->constantColor[3] / 255.0f; + vertColor[3] = 0.0f; + break; + case AGEN_WAVEFORM: + // From RB_CalcWaveAlpha + { + float glow; + waveForm_t *wf = &pStage->alphaWave; + glow = EvalWaveFormClamped( wf ); + baseColor[3] = glow; + } + vertColor[3] = 0.0f; + break; + case AGEN_ENTITY: + if (backEnd.currentEntity) + { + baseColor[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + vertColor[3] = 0.0f; + break; + case AGEN_ONE_MINUS_ENTITY: + if (backEnd.currentEntity) + { + baseColor[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + vertColor[3] = 0.0f; + break; + case AGEN_VERTEX: + baseColor[3] = 0.0f; + vertColor[3] = 1.0f; + break; + case AGEN_ONE_MINUS_VERTEX: + baseColor[3] = 1.0f; + vertColor[3] = -1.0f; + break; + case AGEN_LIGHTING_SPECULAR: + case AGEN_PORTAL: + case AGEN_FRESNEL: + // Done entirely in vertex program + baseColor[3] = 1.0f; + vertColor[3] = 0.0f; + break; + } + + // FIXME: find some way to implement this. +#if 0 + // 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 +} + +static void ComputeFogColorMask( shaderStage_t *pStage, vec4_t fogColorMask ) +{ + switch(pStage->adjustColorsForFog) + { + case ACFF_MODULATE_RGB: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = 1.0f; + fogColorMask[3] = 0.0f; + break; + case ACFF_MODULATE_ALPHA: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = 0.0f; + fogColorMask[3] = 1.0f; + break; + case ACFF_MODULATE_RGBA: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = + fogColorMask[3] = 1.0f; + break; + default: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = + fogColorMask[3] = 0.0f; + break; + } +} + + /* =============== +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; + case AGEN_FRESNEL: + // FIXME: implement me! + 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 =============== */ @@ -1048,6 +2460,8 @@ break; case TCGEN_BAD: return; + default: + break; } // @@ -1169,6 +2583,465 @@ /* +** 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 + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)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; + } + } +} + +#if 0 +static void DrawMultitexturedVBOGLSL( shaderProgram_t *sp, shaderCommands_t *input, int stage ) { + shaderStage_t *pStage; + matrix_t matrix; + vec4_t vector; + + pStage = tess.xstages[stage]; + + // + // base + // + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); + if (pStage->bundle[0].tcGen == TCGEN_VECTOR) + { + vector[3] = 0.0f; + VectorCopy(pStage->bundle[0].tcGenVectors[0], vector); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_TCGEN0VECTOR0, vector); + VectorCopy(pStage->bundle[0].tcGenVectors[1], vector); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_TCGEN0VECTOR1, vector); + } + + ComputeTexMatrix( pStage, 0, matrix ); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, matrix); + + R_BindAnimatedImageToTMU( &pStage->bundle[0], 0 ); + + // + // lightmap/secondary pass + // + if ( r_lightmap->integer ) { + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, GL_REPLACE); + } else { + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, tess.shader->multitextureEnv); + } + + R_BindAnimatedImageToTMU( &pStage->bundle[1], 1 ); + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } +} +#endif + +static unsigned int RB_CalcShaderVertexAttribs( shaderCommands_t *input ) +{ + unsigned int vertexAttribs = input->shader->vertexAttribs; + + if(glState.vertexAttribsInterpolation > 0.0f) + { + vertexAttribs |= ATTR_POSITION2; + if (vertexAttribs & ATTR_NORMAL) + { + vertexAttribs |= ATTR_NORMAL2; + vertexAttribs |= ATTR_TANGENT2; + vertexAttribs |= ATTR_BITANGENT2; + } + } + + return vertexAttribs; +} + + +static void RB_IterateStagesGenericVBOGLSL( shaderCommands_t *input ) +{ + int stage; + matrix_t matrix; + + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT = 0; + + int deformGen; + vec5_t deformParams; + + ComputeDeformValues(&deformGen, deformParams); + + ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = input->xstages[stage]; + shaderProgram_t *sp; + + if ( !pStage ) + { + break; + } + + if (pStage->glslShaderGroup) + { + int index = pStage->glslShaderIndex; + + if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + index |= LIGHTDEF_ENTITY; + } + + if (r_lightmap->integer && index & LIGHTDEF_USE_LIGHTMAP) + { + index = LIGHTDEF_USE_LIGHTMAP; + } + + sp = &pStage->glslShaderGroup[index]; + + if (pStage->glslShaderGroup == tr.lightallShader) + { + backEnd.pc.c_lightallDraws++; + } + } + else + { + sp = GLSL_GetGenericShaderProgram(stage); + + backEnd.pc.c_genericDraws++; + } + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.or.viewOrigin); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); + } + + if ( input->fogNum ) { + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); + } + + GL_State( pStage->stateBits ); + + { + vec4_t baseColor; + vec4_t vertColor; + + ComputeShaderColors(pStage, baseColor, vertColor); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); + } + + if (pStage->rgbGen == CGEN_LIGHTING_DIFFUSE) + { + vec4_t vec; + + VectorScale(backEnd.currentEntity->ambientLight, 1.0f / 255.0f, vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, vec); + + VectorScale(backEnd.currentEntity->directedLight, 1.0f / 255.0f, vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, vec); + + VectorCopy(backEnd.currentEntity->lightDir, vec); + vec[3] = 0.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vec); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, 999999.0f); + } + + if (pStage->alphaGen == AGEN_PORTAL) + { + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); + } + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); + + if ( input->fogNum ) + { + vec4_t fogColorMask; + + ComputeFogColorMask(pStage, fogColorMask); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); + } + + ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, matrix); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); + if (pStage->bundle[0].tcGen == TCGEN_VECTOR) + { + vec3_t vec; + + VectorCopy(pStage->bundle[0].tcGenVectors[0], vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_TCGEN0VECTOR0, vec); + VectorCopy(pStage->bundle[0].tcGenVectors[1], vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_TCGEN0VECTOR1, vec); + } + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_SPECULARREFLECTANCE, pStage->specularReflectance); + + // + // do multitexture + // + if ( pStage->glslShaderGroup ) + { + int i; + + if (r_lightmap->integer && pStage->bundle[TB_LIGHTMAP].image[0]) + { + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + if (i == TB_LIGHTMAP) + { + R_BindAnimatedImageToTMU( &pStage->bundle[i], i); + } + else if (pStage->bundle[i].image[0]) + { + GL_BindToTMU( tr.whiteImage, i); + } + } + } + else + { + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + if (pStage->bundle[i].image[0]) + { + R_BindAnimatedImageToTMU( &pStage->bundle[i], i); + } + } + } + } + else if ( pStage->bundle[1].image[0] != 0 ) + { + R_BindAnimatedImageToTMU( &pStage->bundle[0], 0 ); + + // + // lightmap/secondary pass + // + if ( r_lightmap->integer ) { + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, GL_REPLACE); + } else { + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, tess.shader->multitextureEnv); + } + + R_BindAnimatedImageToTMU( &pStage->bundle[1], 1 ); + } + else + { + // + // set state + // + if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) + { + GL_BindToTMU( tr.whiteImage, 0 ); + } + else + R_BindAnimatedImageToTMU( &pStage->bundle[0], 0 ); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, 0); + } + + // + // draw + // + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)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; + } + } +} + + +static void RB_RenderShadowmap( shaderCommands_t *input ) +{ + int deformGen; + vec5_t deformParams; + + ComputeDeformValues(&deformGen, deformParams); + + { + shaderProgram_t *sp = &tr.shadowmapShader; + + vec4_t vector; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); + } + else + { + matrix_t matrix; + Matrix16Identity(matrix); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, matrix); + } + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); + } + + VectorCopy(backEnd.viewParms.or.origin, vector); + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vector); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, backEnd.viewParms.zFar); + + GL_State( 0 ); + + // + // do multitexture + // + //if ( pStage->glslShaderGroup ) + { + // + // draw + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, (const GLvoid **)input->multiDrawFirstIndex, input->multiDrawNumIndexes); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex); + } + } + } +} + + +/* ** RB_StageIteratorGeneric */ void RB_StageIteratorGeneric( void ) @@ -1176,6 +3049,11 @@ shaderCommands_t *input; input = &tess; + + if (!input->numVertexes || !input->numIndexes) + { + return; + } RB_DeformTessGeometry(); @@ -1283,6 +3161,159 @@ /* +** RB_StageIteratorGenericVBO +*/ +void RB_StageIteratorGenericVBO( void ) +{ + shaderCommands_t *input; + unsigned int vertexAttribs = 0; + + input = &tess; + + if (!input->numVertexes || !input->numIndexes) + { + return; + } + + if (tess.useInternalVBO) + { + RB_DeformTessGeometry(); + } + + vertexAttribs = RB_CalcShaderVertexAttribs( input ); + + if (tess.useInternalVBO) + { + RB_UpdateVBOs(vertexAttribs); + } + else + { + backEnd.pc.c_staticVboDraws++; + } + + // + // log this call + // + if ( r_logFile->integer ) + { + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogComment( va("--- RB_StageIteratorGenericVBO( %s ) ---\n", tess.shader->name) ); + } + + // + // set face culling appropriately + // + GL_Cull( input->shader->cullType ); + + // set polygon offset if necessary + if ( input->shader->polygonOffset ) + { + qglEnable( GL_POLYGON_OFFSET_FILL ); + qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); + } + + // + // Set vertex attribs and pointers + // + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + GLSL_VertexAttribsState(vertexAttribs); + } + + + // + // render shadowmap if in shadowmap mode + // + if (backEnd.viewParms.isShadowmap) + { + if ( glRefConfig.glsl && r_arb_shader_objects->integer && input->shader->sort == SS_OPAQUE ) + { + RB_RenderShadowmap( input ); + } + // + // reset polygon offset + // + if ( input->shader->polygonOffset ) + { + qglDisable( GL_POLYGON_OFFSET_FILL ); + } + + return; + } + + // + // call shader function + // + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + RB_IterateStagesGenericVBOGLSL( input ); + } + else + { + RB_IterateStagesGenericVBO( input ); + } + + // + // pshadows! + // + if ( tess.pshadowBits && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + ProjectPshadowVBOGLSL(); + } + } + + + // + // now do any dynamic lighting needed + // + if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + if (tess.shader->numUnfoggedPasses == 1 && tess.xstages[0]->glslShaderGroup == tr.lightallShader + && r_dlightMode->integer) + { + ForwardDlightVBOGLSL(); + } + else + { + ProjectDlightTextureVBOGLSL(); + } + } + else + { + // FIXME + } + } + + // + // now do fog + // + if ( tess.fogNum && tess.shader->fogPass ) { + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + RB_FogPassVBOGLSL(); + } + else + { + // FIXME: figure out a way to do fog without GLSL + } + } + + // + // reset polygon offset + // + if ( input->shader->polygonOffset ) + { + qglDisable( GL_POLYGON_OFFSET_FILL ); + } +} + + +/* ** RB_StageIteratorVertexLitTexture */ void RB_StageIteratorVertexLitTexture( void ) @@ -1290,6 +3321,13 @@ shaderCommands_t *input; shader_t *shader; + if(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + //R_BindNullVBO(); + //R_BindNullIBO(); + return; + } + input = &tess; shader = input->shader; @@ -1363,9 +3401,17 @@ //define REPLACE_MODE + void RB_StageIteratorLightmappedMultitexture( void ) { shaderCommands_t *input; + if(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + //R_BindNullVBO(); + //R_BindNullIBO(); + return; + } + input = &tess; // @@ -1473,7 +3519,7 @@ input = &tess; - if (input->numIndexes == 0) { + if (input->numIndexes == 0 || input->numVertexes == 0) { return; } @@ -1516,9 +3562,21 @@ if ( r_shownormals->integer ) { DrawNormals (input); } + + if (glRefConfig.vertexBufferObject) + { + //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 2095) +++ 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,15 @@ int i; deformStage_t *ds; + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + 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 ]; @@ -934,6 +963,21 @@ } } +void RB_CalcTurbulentTexMatrix( const waveForm_t *wf, matrix_t matrix ) +{ + float now; + + now = ( wf->phase + tess.shaderTime * wf->frequency ); + + // bit of a hack here, hide amplitude and now in the matrix + // the vertex program will extract them and perform a turbulent pass last if it's nonzero + + matrix[ 0] = 1.0f; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = wf->amplitude; + matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = 0.0f; matrix[13] = now; + 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_CalcScaleTexCoords */ @@ -948,6 +992,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 +1024,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] = adjustedScrollS; matrix[12] = 0.0f; + matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = adjustedScrollT; 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_CalcTransformTexCoords */ @@ -989,6 +1061,14 @@ } } +void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ) +{ + matrix[ 0] = tmi->matrix[0][0]; matrix[ 4] = tmi->matrix[1][0]; matrix[ 8] = tmi->translate[0]; matrix[12] = 0.0f; + matrix[ 1] = tmi->matrix[0][1]; matrix[ 5] = tmi->matrix[1][1]; matrix[ 9] = tmi->translate[1]; 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_CalcRotateTexCoords */ @@ -1017,7 +1097,30 @@ 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 ); +} /* ** RB_CalcSpecularAlpha ** @@ -1214,3 +1317,5 @@ RB_CalcDiffuseColor_scalar( colors ); } + + Index: code/renderer/tr_shader.c =================================================================== --- code/renderer/tr_shader.c (revision 2095) +++ code/renderer/tr_shader.c (working copy) @@ -634,6 +634,22 @@ } continue; } + else if ( !Q_stricmp( token, "$deluxemap" ) ) + { + if (!tr.worldDeluxeMapping) + { + ri.Printf( PRINT_WARNING, "WARNING: shader '%s' wants a deluxe map in a map compiled without them\n", shader.name ); + return qfalse; + } + + stage->bundle[0].isLightmap = qtrue; + if ( shader.lightmapIndex < 0 ) { + stage->bundle[0].image[0] = tr.whiteImage; + } else { + stage->bundle[0].image[0] = tr.deluxemaps[shader.lightmapIndex]; + } + continue; + } else { stage->bundle[0].image[0] = R_FindImageFile( token, !shader.noMipMaps, !shader.noPicMip, GL_REPEAT ); @@ -642,6 +658,27 @@ ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); return qfalse; } + + //if ( r_autoFindNormalMap->integer ) + { + char filename[MAX_QPATH]; + + COM_StripExtension(token, filename, sizeof(filename)); + Q_strcat(filename, sizeof(filename), "_normal"); + + stage->bundle[TB_NORMALMAP].image[0] = R_FindImageFile( filename, !shader.noMipMaps, !shader.noPicMip, GL_REPEAT ); + } + + //if ( r_autoFindSpecularMap->integer ) + { + char filename[MAX_QPATH]; + + COM_StripExtension(token, filename, sizeof(filename)); + Q_strcat(filename, sizeof(filename), "_specular"); + + stage->bundle[TB_SPECULARMAP].image[0] = R_FindImageFile( filename, !shader.noMipMaps, !shader.noPicMip, GL_REPEAT ); + stage->specularReflectance = 0.04f; + } } } // @@ -800,6 +837,54 @@ } } // + // stage + // + else if(!Q_stricmp(token, "stage")) + { + token = COM_ParseExt(text, qfalse); + if(token[0] == 0) + { + ri.Printf(PRINT_WARNING, "WARNING: missing parameters for stage in shader '%s'\n", shader.name); + continue; + } + + if(!Q_stricmp(token, "diffuseMap")) + { + stage->type = ST_DIFFUSEMAP; + } + else if(!Q_stricmp(token, "normalMap") || !Q_stricmp(token, "bumpMap")) + { + stage->type = ST_NORMALMAP; + } + else if(!Q_stricmp(token, "normalParallaxMap") || !Q_stricmp(token, "bumpParallaxMap")) + { + stage->type = ST_NORMALPARALLAXMAP; + } + else if(!Q_stricmp(token, "specularMap")) + { + stage->type = ST_SPECULARMAP; + stage->specularReflectance = 0.04f; + } + else + { + ri.Printf(PRINT_WARNING, "WARNING: unknown stage parameter '%s' in shader '%s'\n", token, shader.name); + continue; + } + } + // + // specularReflectance + // + else if (!Q_stricmp(token, "specularreflectance")) + { + token = COM_ParseExt(text, qfalse); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular reflectance in shader '%s'\n", shader.name ); + continue; + } + stage->specularReflectance = atof( token ); + } + // // rgbGen // else if ( !Q_stricmp( token, "rgbGen" ) ) @@ -929,6 +1014,10 @@ shader.portalRange = atof( token ); } } + else if ( !Q_stricmp( token, "fresnel" ) ) + { + stage->alphaGen = AGEN_FRESNEL; + } else { ri.Printf( PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name ); @@ -1551,6 +1640,7 @@ else if ( !Q_stricmp(token, "portal") ) { shader.sort = SS_PORTAL; + shader.isPortal = qtrue; continue; } // skyparms @@ -1634,6 +1724,11 @@ { shader.optimalStageIteratorFunc = RB_StageIteratorGeneric; + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + shader.optimalStageIteratorFunc = RB_StageIteratorGenericVBO; + } + // // see if this should go into the sky path // @@ -1648,6 +1743,12 @@ return; } + if ( glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer ) + { + // VBOs have alternate methods for doing fast path + return; + } + // // see if this can go into the vertex lit fast path // @@ -1704,6 +1805,149 @@ return; } +/* +=================== +ComputeVertexAttribs + +Check which vertex attributes we only need, so we +don't need to submit/copy all of them. +=================== +*/ +static void ComputeVertexAttribs(void) +{ + int i, stage; + + // dlights always need ATTR_NORMAL + shader.vertexAttribs = ATTR_POSITION | ATTR_NORMAL; + + // portals always need normals, for SurfIsOffscreen() + if (shader.isPortal) + { + shader.vertexAttribs |= ATTR_NORMAL; + } + + if (shader.defaultShader) + { + shader.vertexAttribs |= ATTR_TEXCOORD; + return; + } + + if(shader.numDeforms) + { + for ( i = 0; i < shader.numDeforms; i++) + { + deformStage_t *ds = &shader.deforms[i]; + + switch (ds->deformation) + { + case DEFORM_BULGE: + shader.vertexAttribs |= ATTR_NORMAL | ATTR_TEXCOORD; + break; + + case DEFORM_AUTOSPRITE: + shader.vertexAttribs |= ATTR_NORMAL | ATTR_COLOR; + break; + + case DEFORM_WAVE: + case DEFORM_NORMALS: + case DEFORM_TEXT0: + case DEFORM_TEXT1: + case DEFORM_TEXT2: + case DEFORM_TEXT3: + case DEFORM_TEXT4: + case DEFORM_TEXT5: + case DEFORM_TEXT6: + case DEFORM_TEXT7: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + default: + case DEFORM_NONE: + case DEFORM_MOVE: + case DEFORM_PROJECTION_SHADOW: + case DEFORM_AUTOSPRITE2: + break; + } + } + } + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) + { + break; + } + + if (pStage->glslShaderGroup == tr.lightallShader) + { + shader.vertexAttribs |= ATTR_NORMAL; + + if (pStage->glslShaderIndex & LIGHTDEF_USE_NORMALMAP) + { + shader.vertexAttribs |= ATTR_BITANGENT | ATTR_TANGENT; + } + } + + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + if ( pStage->bundle[i].image[0] == 0 ) + { + continue; + } + + switch(pStage->bundle[i].tcGen) + { + case TCGEN_TEXTURE: + shader.vertexAttribs |= ATTR_TEXCOORD; + break; + case TCGEN_LIGHTMAP: + shader.vertexAttribs |= ATTR_LIGHTCOORD; + break; + case TCGEN_ENVIRONMENT_MAPPED: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + default: + break; + } + } + + switch(pStage->rgbGen) + { + case CGEN_EXACT_VERTEX: + case CGEN_VERTEX: + case CGEN_ONE_MINUS_VERTEX: + shader.vertexAttribs |= ATTR_COLOR; + break; + + case CGEN_LIGHTING_DIFFUSE: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + default: + break; + } + + switch(pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + case AGEN_FRESNEL: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + case AGEN_VERTEX: + case AGEN_ONE_MINUS_VERTEX: + shader.vertexAttribs |= ATTR_COLOR; + break; + + default: + break; + } + } +} + typedef struct { int blendA; int blendB; @@ -1860,6 +2104,315 @@ return qtrue; } +static void CollapseStagesToLightall(shaderStage_t *diffuse, + shaderStage_t *normal, shaderStage_t *specular, shaderStage_t *lightmap, + qboolean useLightVector, qboolean parallax, qboolean environment) +{ + int defs = 0; + + //ri.Printf(PRINT_ALL, "shader %s has diffuse %s", shader.name, diffuse->bundle[0].image[0]->imgName); + + // reuse diffuse, mark others inactive + diffuse->type = ST_GLSL; + + if (lightmap) + { + //ri.Printf(PRINT_ALL, ", lightmap"); + diffuse->bundle[TB_LIGHTMAP] = lightmap->bundle[0]; + defs |= LIGHTDEF_USE_LIGHTMAP; + } + + if ((tr.worldDeluxeMapping || r_deluxeMapping->integer == 2) && lightmap) + { + //ri.Printf(PRINT_ALL, ", deluxemap"); + diffuse->bundle[TB_DELUXEMAP] = lightmap->bundle[0]; + diffuse->bundle[TB_DELUXEMAP].image[0] = tr.deluxemaps[shader.lightmapIndex]; + defs |= LIGHTDEF_USE_DELUXEMAP; + } + + if (normal) + { + //ri.Printf(PRINT_ALL, ", normalmap %s", normal->bundle[0].image[0]->imgName); + diffuse->bundle[TB_NORMALMAP] = normal->bundle[0]; + defs |= LIGHTDEF_USE_NORMALMAP; + if (parallax) + defs |= LIGHTDEF_USE_PARALLAXMAP; + } + else if (diffuse->bundle[TB_NORMALMAP].image[0]) + { + image_t *tmpImg = diffuse->bundle[TB_NORMALMAP].image[0]; + diffuse->bundle[TB_NORMALMAP] = diffuse->bundle[TB_DIFFUSEMAP]; + diffuse->bundle[TB_NORMALMAP].image[0] = tmpImg; + defs |= LIGHTDEF_USE_NORMALMAP; + } + + if (specular) + { + //ri.Printf(PRINT_ALL, ", specularmap %s", specular->bundle[0].image[0]->imgName); + diffuse->bundle[TB_SPECULARMAP] = specular->bundle[0]; + diffuse->specularReflectance = specular->specularReflectance; + defs |= LIGHTDEF_USE_SPECULARMAP; + } + else if (diffuse->bundle[TB_SPECULARMAP].image[0]) + { + image_t *tmpImg = diffuse->bundle[TB_SPECULARMAP].image[0]; + diffuse->bundle[TB_SPECULARMAP] = diffuse->bundle[TB_DIFFUSEMAP]; + diffuse->bundle[TB_SPECULARMAP].image[0] = tmpImg; + defs |= LIGHTDEF_USE_SPECULARMAP; + } + + if (useLightVector) + { + defs |= LIGHTDEF_USE_LIGHT_VECTOR; + } + + if (environment) + { + defs |= LIGHTDEF_TCGEN_ENVIRONMENT; + } + + //ri.Printf(PRINT_ALL, ".\n"); + + diffuse->glslShaderGroup = tr.lightallShader; + diffuse->glslShaderIndex = defs; +} + + +static qboolean CollapseStagesToGLSL(void) +{ + int i, j, numStages; + qboolean skip = qfalse; + + // skip shaders with deforms + if (!(shader.numDeforms == 0 && glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && glRefConfig.glsl && r_arb_shader_objects->integer)) + { + skip = qtrue; + } + + if (!skip) + { + // if 2+ stages and first stage is lightmap, switch them + // this makes it easier for the later bits to process + if (stages[0].active && stages[0].bundle[0].isLightmap && stages[1].active) + { + int blendBits = stages[1].stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + if (blendBits == (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) + || blendBits == (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) + { + int stateBits0 = stages[0].stateBits; + int stateBits1 = stages[1].stateBits; + shaderStage_t swapStage; + + swapStage = stages[0]; + stages[0] = stages[1]; + stages[1] = swapStage; + + stages[0].stateBits = stateBits0; + stages[1].stateBits = stateBits1; + } + } + } + + if (!skip) + { + // scan for shaders that aren't supported + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + + if (!pStage->active) + continue; + + if (pStage->adjustColorsForFog) + { + skip = qtrue; + break; + } + + if (pStage->bundle[0].isLightmap) + { + int blendBits = pStage->stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + if (blendBits != (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) + && blendBits != (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) + { + skip = qtrue; + break; + } + } + + switch(pStage->bundle[0].tcGen) + { + case TCGEN_TEXTURE: + case TCGEN_LIGHTMAP: + case TCGEN_ENVIRONMENT_MAPPED: + break; + default: + skip = qtrue; + break; + } + + switch(pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + case AGEN_PORTAL: + case AGEN_FRESNEL: + skip = qtrue; + break; + default: + break; + } + } + } + + if (!skip) + { + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + shaderStage_t *diffuse, *normal, *specular, *lightmap; + qboolean parallax, environment, entity; + + if (!pStage->active) + continue; + + // skip normal and specular maps + if (pStage->type != ST_COLORMAP) + continue; + + // skip lightmaps + if (pStage->bundle[0].isLightmap) + continue; + + diffuse = pStage; + normal = NULL; + parallax = qfalse; + specular = NULL; + lightmap = NULL; + + // we have a diffuse map, find matching normal, specular, and lightmap + for (j = i + 1; j < MAX_SHADER_STAGES; j++) + { + shaderStage_t *pStage2 = &stages[j]; + + if (!pStage2->active) + continue; + + switch(pStage2->type) + { + case ST_NORMALMAP: + if (!normal) + { + normal = pStage2; + } + break; + + case ST_NORMALPARALLAXMAP: + if (!normal) + { + normal = pStage2; + parallax = qtrue; + } + break; + + case ST_SPECULARMAP: + if (!specular) + { + specular = pStage2; + } + break; + + case ST_COLORMAP: + if (pStage2->bundle[0].isLightmap) + { + lightmap = pStage2; + } + break; + + default: + break; + } + } + + environment = qfalse; + if (diffuse->bundle[0].tcGen == TCGEN_ENVIRONMENT_MAPPED) + { + environment = qtrue; + } + + entity = qfalse; + if (diffuse->rgbGen == CGEN_LIGHTING_DIFFUSE) + { + entity = qtrue; + } + + CollapseStagesToLightall(diffuse, normal, specular, lightmap, entity, parallax, environment); + } + + // deactivate lightmap stages + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + + if (!pStage->active) + continue; + + if (pStage->bundle[0].isLightmap) + { + pStage->active = qfalse; + } + } + } + + // deactivate normal and specular stages + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + + if (!pStage->active) + continue; + + if (pStage->type == ST_NORMALMAP) + { + pStage->active = qfalse; + } + + if (pStage->type == ST_NORMALPARALLAXMAP) + { + pStage->active = qfalse; + } + + if (pStage->type == ST_SPECULARMAP) + { + pStage->active = qfalse; + } + } + + // remove inactive stages + numStages = 0; + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + if (!stages[i].active) + continue; + + if (i == numStages) + { + numStages++; + continue; + } + + stages[numStages] = stages[i]; + stages[i].active = qfalse; + numStages++; + } + + if (numStages == i && i >= 2 && CollapseMultitexture()) + numStages--; + + return numStages; +} + /* ============= @@ -1901,15 +2454,16 @@ int fogNum; int entityNum; int dlightMap; + int pshadowMap; int sortedIndex; const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd; for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) { - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap ); + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap, &pshadowMap ); sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1)); if( sortedIndex >= newShader ) { sortedIndex++; - drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; + drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | ( (int)pshadowMap << QSORT_PSHADOW_SHIFT) | (int)dlightMap; } } curCmd = (const void *)(ds_cmd + 1); @@ -2277,16 +2831,15 @@ // // look for multitexture potential // - if ( stage > 1 && CollapseMultitexture() ) { - stage--; - } + stage = CollapseStagesToGLSL(); if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { if (vertexLightmap) { ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); } else { ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); - shader.lightmapIndex = LIGHTMAP_NONE; + // Don't set this, it will just add duplicate shaders to the hash + //shader.lightmapIndex = LIGHTMAP_NONE; } } @@ -2303,6 +2856,9 @@ // determine which stage iterator function is appropriate ComputeStageIteratorFunc(); + // determine which vertex attributes this shader needs + ComputeVertexAttribs(); + return GeneratePermanentShader(); } @@ -2488,12 +3044,6 @@ stages[i].bundle[0].texMods = texMods[i]; } - // FIXME: set these "need" values apropriately - shader.needsNormal = qtrue; - shader.needsST1 = qtrue; - shader.needsST2 = qtrue; - shader.needsColor = qtrue; - // // attempt to define shader from an explicit parameter file // @@ -2624,12 +3174,6 @@ stages[i].bundle[0].texMods = texMods[i]; } - // FIXME: set these "need" values apropriately - shader.needsNormal = qtrue; - shader.needsST1 = qtrue; - shader.needsST2 = qtrue; - shader.needsColor = qtrue; - // // create the default shading commands // @@ -2850,6 +3394,8 @@ if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { ri.Printf( PRINT_ALL, "gen " ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorGenericVBO ) { + ri.Printf( PRINT_ALL, "genv" ); } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { ri.Printf( PRINT_ALL, "sky " ); } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorLightmappedMultitexture ) { Index: code/renderer/tr_sky.c =================================================================== --- code/renderer/tr_sky.c (revision 2095) +++ code/renderer/tr_sky.c (working copy) @@ -384,6 +384,97 @@ } } +static void DrawSkySideVBO( struct image_s *image, const int mins[2], const int maxs[2] ) +{ + int s, t; + int firstVertex = tess.numVertexes; + //int firstIndex = tess.numIndexes; + vec4_t color; + + //tess.numVertexes = 0; + //tess.numIndexes = 0; + tess.firstIndex = tess.numIndexes; + + GL_Bind( image ); + GL_Cull( CT_TWO_SIDED ); + + 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++ ) + { + tess.xyz[tess.numVertexes][0] = s_skyPoints[t][s][0]; + tess.xyz[tess.numVertexes][1] = s_skyPoints[t][s][1]; + tess.xyz[tess.numVertexes][2] = s_skyPoints[t][s][2]; + tess.xyz[tess.numVertexes][3] = 1.0; + + tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; + tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; + + tess.numVertexes++; + + if(tess.numVertexes >= SHADER_MAX_VERTEXES) + { + ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()\n"); + } + } + } + + for ( t = 0; t < maxs[1] - mins[1]; t++ ) + { + for ( s = 0; s < maxs[0] - mins[0]; s++ ) + { + if (tess.numIndexes + 6 >= SHADER_MAX_INDEXES) + { + ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()\n"); + } + + tess.indexes[tess.numIndexes++] = s + t * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; + + tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = (s + 1) + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; + } + } + + // 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 (glRefConfig.glsl && r_arb_shader_objects->integer) + { + shaderProgram_t *sp = &tr.textureColorShader; + + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + color[0] = + color[1] = + color[2] = tr.identityLight; + color[3] = 1.0f; + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); + } + else + { + qglEnableClientState( GL_VERTEX_ARRAY ); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + qglDisableClientState( GL_COLOR_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 - tess.firstIndex, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex * sizeof(GL_INDEX_TYPE))); + + //R_BindNullVBO(); + //R_BindNullIBO(); + + tess.numIndexes = tess.firstIndex; + tess.numVertexes = firstVertex; + tess.firstIndex = 0; +} + static void DrawSkyBox( shader_t *shader ) { int i; @@ -447,9 +538,19 @@ } } - DrawSkySide( shader->sky.outerbox[sky_texorder[i]], - sky_mins_subd, - sky_maxs_subd ); + if (glRefConfig.vertexBufferObject && 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 +718,7 @@ // set up for drawing tess.numIndexes = 0; tess.numVertexes = 0; + tess.firstIndex = 0; if ( input->shader->sky.cloudHeight ) { @@ -708,9 +810,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,22 +927,45 @@ // 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 (!(glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer && + glRefConfig.glsl && 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 // by the generic shader routine R_BuildCloudData( &tess ); - RB_StageIteratorGeneric(); + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + RB_StageIteratorGenericVBO(); + } + else + { + RB_StageIteratorGeneric(); + } // draw the inner skybox @@ -843,3 +977,5 @@ backEnd.skyRenderedThisView = qtrue; } + + Index: code/renderer/tr_surface.c =================================================================== --- code/renderer/tr_surface.c (revision 2095) +++ code/renderer/tr_surface.c (working copy) @@ -67,6 +67,21 @@ } +void RB_CheckVBOandIBO(VBO_t *vbo, IBO_t *ibo) +{ + 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); + } + + tess.useInternalVBO = qfalse; +} + + /* ============== RB_AddQuadStampExt @@ -148,8 +163,91 @@ RB_AddQuadStampExt( origin, left, up, color, 0, 0, 1, 1 ); } + /* ============== +RB_InstantQuad + +based on Tess_InstantQuad from xreal +============== +*/ +void RB_InstantQuad(vec4_t quadVerts[4]) +{ + 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; + + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); + + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + shaderProgram_t *sp = &tr.textureColorShader; + vec4_t color; + + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + VectorSet4(color, 1, 1, 1, 1); + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); + } + 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_BindNullVBO(); + //R_BindNullIBO(); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; +} + + +/* +============== RB_SurfaceSprite ============== */ @@ -217,40 +315,37 @@ 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 pshadowBits) +{ int i; - drawVert_t *dv; - float *xyz, *normal, *texCoords; + srfTriangle_t *tri; + srfVert_t *dv; + float *xyz, *normal, *tangent, *bitangent, *texCoords; + glIndex_t *index; byte *color; - int dlightBits; qboolean needsNormal; - dlightBits = srf->dlightBits[backEnd.smpFrame]; - tess.dlightBits |= dlightBits; + RB_CHECKOVERFLOW( numVerts, numTriangles * 3 ); - RB_CHECKOVERFLOW( srf->numVerts, srf->numIndexes ); - - 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 ]; + tri = triangles; + index = &tess.indexes[ tess.numIndexes ]; + for ( i = 0 ; i < numTriangles ; i++, tri++ ) { + *index++ = tess.numVertexes + tri->indexes[0]; + *index++ = tess.numVertexes + tri->indexes[1]; + *index++ = tess.numVertexes + tri->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 ]; + tangent = tess.tangent[ tess.numVertexes ]; + bitangent = tess.bitangent[ tess.numVertexes ]; texCoords = tess.texCoords[ tess.numVertexes ][0]; color = tess.vertexColors[ tess.numVertexes ]; - needsNormal = tess.shader->needsNormal; + needsNormal = tess.shader->vertexAttribs & ATTR_NORMAL; - 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]; @@ -259,6 +354,12 @@ normal[0] = dv->normal[0]; normal[1] = dv->normal[1]; normal[2] = dv->normal[2]; + tangent[0] = dv->tangent[0]; + tangent[1] = dv->tangent[1]; + tangent[2] = dv->tangent[2]; + bitangent[0] = dv->bitangent[0]; + bitangent[1] = dv->bitangent[1]; + bitangent[2] = dv->bitangent[2]; } texCoords[0] = dv->st[0]; @@ -267,19 +368,134 @@ 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++ ) { - tess.vertexDlightBits[ tess.numVertexes + i] = dlightBits; +#if 0 // nothing even uses vertex dlightbits + for ( i = 0 ; i < numVerts ; i++ ) { + tess.vertexDlightBits[ tess.numVertexes + i ] = dlightBits; } +#endif - tess.numVertexes += srf->numVerts; + tess.dlightBits |= dlightBits; + tess.pshadowBits |= pshadowBits; + + tess.numVertexes += numVerts; } +static qboolean RB_SurfaceHelperVBO(VBO_t *vbo, IBO_t *ibo, int numVerts, int numIndexes, int firstIndex, int dlightBits, int pshadowBits, qboolean shaderCheck) +{ + if( glRefConfig.vertexBufferObject && 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; + } + RB_CheckVBOandIBO(vbo, ibo); + + tess.dlightBits |= dlightBits; + tess.pshadowBits |= pshadowBits; + + // 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] = (byte *)tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack] * sizeof(GL_INDEX_TYPE); + backEnd.pc.c_multidrawsMerged++; + } + else if (mergeBack == -1 && mergeForward != -1) + { + tess.multiDrawNumIndexes[mergeForward] += numIndexes; + tess.multiDrawFirstIndex[mergeForward] = firstIndexOffset; + tess.multiDrawLastIndex[mergeForward] = (byte *)tess.multiDrawFirstIndex[mergeForward] + tess.multiDrawNumIndexes[mergeForward] * sizeof(GL_INDEX_TYPE); + backEnd.pc.c_multidrawsMerged++; + } + else if (mergeBack != -1 && mergeForward != -1) + { + tess.multiDrawNumIndexes[mergeBack] += numIndexes + tess.multiDrawNumIndexes[mergeForward]; + tess.multiDrawLastIndex[mergeBack] = (byte *)tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack] * sizeof(GL_INDEX_TYPE); + tess.multiDrawPrimitives--; + + if (mergeForward != tess.multiDrawPrimitives) + { + tess.multiDrawNumIndexes[mergeForward] = tess.multiDrawNumIndexes[tess.multiDrawPrimitives]; + tess.multiDrawFirstIndex[mergeForward] = tess.multiDrawFirstIndex[tess.multiDrawPrimitives]; + } + backEnd.pc.c_multidrawsMerged += 2; + } + 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_multidraws++; + + 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, srf->dlightBits[backEnd.smpFrame], srf->pshadowBits[backEnd.smpFrame], qtrue ) ) + { + return; + } + + RB_SurfaceHelper(srf->numVerts, srf->verts, srf->numTriangles, srf->triangles, srf->dlightBits[backEnd.smpFrame], srf->pshadowBits[backEnd.smpFrame]); +} + + + +/* ============== RB_SurfaceBeam ============== @@ -293,6 +509,7 @@ vec3_t direction, normalized_direction; vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; vec3_t oldorigin, origin; + vec4_t color; e = &backEnd.currentEntity->e; @@ -326,14 +543,69 @@ GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - qglColor3f( 1, 0, 0 ); + // FIXME: Quake3 doesn't use this, so I never tested it + if (glRefConfig.vertexBufferObject && r_arb_vertex_buffer_object->integer) + { + tess.numVertexes = 0; + tess.numIndexes = 0; + tess.firstIndex = 0; - qglBegin( GL_TRIANGLE_STRIP ); - for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { - qglVertex3fv( start_points[ i % NUM_BEAM_SEGS] ); - qglVertex3fv( end_points[ i % NUM_BEAM_SEGS] ); + for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { + VectorCopy(start_points[ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); + VectorCopy(end_points [ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); + } + + for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { + tess.indexes[tess.numIndexes++] = i * 2; + tess.indexes[tess.numIndexes++] = (i + 1) * 2; + tess.indexes[tess.numIndexes++] = 1 + i * 2; + + tess.indexes[tess.numIndexes++] = 1 + i * 2; + tess.indexes[tess.numIndexes++] = (i + 1) * 2; + tess.indexes[tess.numIndexes++] = 1 + (i + 1) * 2; + } + + // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function + RB_UpdateVBOs(ATTR_POSITION); + + if (glRefConfig.glsl && r_arb_shader_objects->integer) + { + shaderProgram_t *sp = &tr.textureColorShader; + + GLSL_VertexAttribsState(ATTR_POSITION); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + color[0] = 1.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 1.0f; + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); + } + else + { + qglEnableClientState( GL_VERTEX_ARRAY ); + qglVertexPointer(3, GL_FLOAT, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz)); + } + + qglDrawElements(GL_TRIANGLES, tess.numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex)); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; } - qglEnd(); + else + { + qglColor3f( 1, 0, 0 ); + + qglBegin( GL_TRIANGLE_STRIP ); + for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { + qglVertex3fv( start_points[ i % NUM_BEAM_SEGS] ); + qglVertex3fv( end_points[ i % NUM_BEAM_SEGS] ); + } + qglEnd(); + } } //================================================================================ @@ -740,8 +1012,9 @@ } #endif -static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) +static void LerpMeshVertexes_scalar(mdvSurface_t *surf, float backlerp) { +#if 0 short *oldXyz, *newXyz, *oldNormals, *newNormals; float *outXyz, *outNormal; float oldXyzScale, newXyzScale; @@ -836,10 +1109,59 @@ } VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); } +#endif + float *outXyz, *outNormal; + mdvVertex_t *newVerts; + int vertNum; + + newVerts = surf->verts + backEnd.currentEntity->e.frame * surf->numVerts; + + outXyz = tess.xyz[tess.numVertexes]; + outNormal = tess.normal[tess.numVertexes]; + + if (backlerp == 0) + { + // + // just copy the vertexes + // + + for (vertNum=0 ; vertNum < surf->numVerts ; vertNum++) + { + VectorCopy(newVerts->xyz, outXyz); + VectorCopy(newVerts->normal, outNormal); + newVerts++; + outXyz += 4; + outNormal += 4; + } + } + else + { + // + // interpolate and copy the vertex and normal + // + + mdvVertex_t *oldVerts; + + oldVerts = surf->verts + backEnd.currentEntity->e.oldframe * surf->numVerts; + + for (vertNum=0 ; vertNum < surf->numVerts ; vertNum++) + { + VectorLerp(newVerts->xyz, oldVerts->xyz, backlerp, outXyz); + VectorLerp(newVerts->normal, oldVerts->normal, backlerp, outNormal); + //VectorNormalize(outNormal); + newVerts++; + oldVerts++; + outXyz += 4; + outNormal += 4; + } + VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], surf->numVerts); + } + } -static void LerpMeshVertexes(md3Surface_t *surf, float backlerp) +static void LerpMeshVertexes(mdvSurface_t *surf, float backlerp) { +#if 0 #if idppc_altivec if (com_altivec->integer) { // must be in a seperate function or G3 systems will crash. @@ -847,6 +1169,7 @@ return; } #endif // idppc_altivec +#endif LerpMeshVertexes_scalar( surf, backlerp ); } @@ -856,11 +1179,11 @@ RB_SurfaceMesh ============= */ -static void RB_SurfaceMesh(md3Surface_t *surface) { +static void RB_SurfaceMesh(mdvSurface_t *surface) { int j; float backlerp; - int *triangles; - float *texCoords; + srfTriangle_t *triangles; + mdvSt_t *texCoords; int indexes; int Bob, Doug; int numVerts; @@ -875,21 +1198,23 @@ LerpMeshVertexes (surface, backlerp); - triangles = (int *) ((byte *)surface + surface->ofsTriangles); + triangles = surface->triangles; indexes = surface->numTriangles * 3; Bob = tess.numIndexes; Doug = tess.numVertexes; - for (j = 0 ; j < indexes ; j++) { - tess.indexes[Bob + j] = Doug + triangles[j]; + for (j = 0 ; j < surface->numTriangles ; j++) { + tess.indexes[Bob + j*3 + 0] = Doug + triangles[j].indexes[0]; + tess.indexes[Bob + j*3 + 1] = Doug + triangles[j].indexes[1]; + tess.indexes[Bob + j*3 + 2] = Doug + triangles[j].indexes[2]; } tess.numIndexes += indexes; - texCoords = (float *) ((byte *)surface + surface->ofsSt); + texCoords = surface->st; numVerts = surface->numVerts; for ( j = 0; j < numVerts; j++ ) { - tess.texCoords[Doug + j][0][0] = texCoords[j*2+0]; - tess.texCoords[Doug + j][0][1] = texCoords[j*2+1]; + tess.texCoords[Doug + j][0][0] = texCoords[j].st[0]; + tess.texCoords[Doug + j][0][1] = texCoords[j].st[1]; // FIXME: fill in lightmapST for completeness? } @@ -903,56 +1228,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, srf->dlightBits[backEnd.smpFrame], srf->pshadowBits[backEnd.smpFrame], 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], srf->pshadowBits[backEnd.smpFrame]); } @@ -993,13 +1275,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; + float *normal, *tangent, *bitangent; unsigned char *color; - drawVert_t *dv; + srfVert_t *dv; int rows, irows, vrows; int used; int widthTable[MAX_GRID_SIZE]; @@ -1008,37 +1290,46 @@ int lodWidth, lodHeight; int numVertexes; int dlightBits; - int *vDlightBits; + int pshadowBits; + //int *vDlightBits; qboolean needsNormal; - dlightBits = cv->dlightBits[backEnd.smpFrame]; + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, srf->firstIndex, srf->dlightBits[backEnd.smpFrame], srf->pshadowBits[backEnd.smpFrame], qtrue ) ) + { + return; + } + + dlightBits = srf->dlightBits[backEnd.smpFrame]; tess.dlightBits |= dlightBits; + pshadowBits = srf->pshadowBits[backEnd.smpFrame]; + tess.pshadowBits |= pshadowBits; + // 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++; @@ -1074,14 +1365,16 @@ xyz = tess.xyz[numVertexes]; normal = tess.normal[numVertexes]; + tangent = tess.tangent[numVertexes]; + bitangent = tess.bitangent[numVertexes]; texCoords = tess.texCoords[numVertexes][0]; color = ( unsigned char * ) &tess.vertexColors[numVertexes]; - vDlightBits = &tess.vertexDlightBits[numVertexes]; - needsNormal = tess.shader->needsNormal; + //vDlightBits = &tess.vertexDlightBits[numVertexes]; + needsNormal = tess.shader->vertexAttribs & ATTR_NORMAL; 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]; @@ -1095,9 +1388,15 @@ normal[0] = dv->normal[0]; normal[1] = dv->normal[1]; normal[2] = dv->normal[2]; + tangent[0] = dv->tangent[0]; + tangent[1] = dv->tangent[1]; + tangent[2] = dv->tangent[2]; + bitangent[0] = dv->bitangent[0]; + bitangent[1] = dv->bitangent[1]; + bitangent[2] = dv->bitangent[2]; } - * ( unsigned int * ) color = * ( unsigned int * ) dv->color; - *vDlightBits++ = dlightBits; + * ( unsigned int * ) color = * ( unsigned int * ) dv->vertexColors; + //*vDlightBits++ = dlightBits; xyz += 4; normal += 4; texCoords += 4; @@ -1161,6 +1460,8 @@ =================== */ static void RB_SurfaceAxis( void ) { + // FIXME: implement this +#if 0 GL_Bind( tr.whiteImage ); qglLineWidth( 3 ); qglBegin( GL_LINES ); @@ -1175,6 +1476,7 @@ qglVertex3f( 0,0,16 ); qglEnd(); qglLineWidth( 1 ); +#endif } //=========================================================================== @@ -1220,6 +1522,57 @@ RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); } +static void RB_SurfaceVBOMesh(srfVBOMesh_t * srf) +{ + RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numIndexes, srf->firstIndex, srf->dlightBits[backEnd.smpFrame], srf->pshadowBits[backEnd.smpFrame], qfalse ); +} + +void RB_SurfaceVBOMDVMesh(srfVBOMDVMesh_t * surface) +{ + mdvModel_t *mdvModel; + mdvSurface_t *mdvSurface; + refEntity_t *refEnt; + + GLimp_LogComment("--- RB_SurfaceVBOMDVMesh ---\n"); + + if(!surface->vbo || !surface->ibo) + return; + + //RB_CheckVBOandIBO(surface->vbo, surface->ibo); + RB_EndSurface(); + RB_BeginSurface(tess.shader, tess.fogNum); + + R_BindVBO(surface->vbo); + R_BindIBO(surface->ibo); + + tess.useInternalVBO = qfalse; + + tess.numIndexes += surface->numIndexes; + tess.numVertexes += surface->numVerts; + + mdvModel = surface->mdvModel; + mdvSurface = surface->mdvSurface; + + refEnt = &backEnd.currentEntity->e; + + if(refEnt->oldframe == refEnt->frame) + { + glState.vertexAttribsInterpolation = 0; + } + else + { + glState.vertexAttribsInterpolation = refEnt->backlerp; + } + + glState.vertexAttribsOldFrame = refEnt->oldframe; + glState.vertexAttribsNewFrame = refEnt->frame; + + RB_EndSurface(); + + // So we don't lerp surfaces that shouldn't be lerped + glState.vertexAttribsInterpolation = 0; +} + static void RB_SurfaceDisplayList( srfDisplayList_t *surf ) { // all apropriate state must be set in RB_BeginSurface // this isn't implemented yet... @@ -1237,7 +1590,7 @@ (void(*)(void*))RB_SurfaceGrid, // SF_GRID, (void(*)(void*))RB_SurfaceTriangles, // SF_TRIANGLES, (void(*)(void*))RB_SurfacePolychain, // SF_POLY, - (void(*)(void*))RB_SurfaceMesh, // SF_MD3, + (void(*)(void*))RB_SurfaceMesh, // SF_MDV, (void(*)(void*))RB_SurfaceAnim, // SF_MD4, #ifdef RAVENMD4 (void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR, @@ -1245,5 +1598,7 @@ (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM, (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, + (void(*)(void*))RB_SurfaceVBOMDVMesh, // SF_VBO_MDVMESH }; Index: code/renderer/tr_types.h =================================================================== --- code/renderer/tr_types.h (revision 2095) +++ code/renderer/tr_types.h (working copy) @@ -27,6 +27,9 @@ #define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces #define MAX_ENTITIES 1023 // can't be increased without changing drawsurf bit packing +#define MAX_CALC_PSHADOWS 64 +#define MAX_DRAWN_PSHADOWS 16 // do not increase past 32, because bit flags are used on surfaces + // renderfx flags #define RF_MINLIGHT 0x0001 // allways have some light (viewmodel, some items) #define RF_THIRD_PERSON 0x0002 // don't draw through eyes, only mirrors (player bodies, chat sprites) Index: code/renderer/tr_vbo.c =================================================================== --- code/renderer/tr_vbo.c (revision 0) +++ code/renderer/tr_vbo.c (revision 0) @@ -0,0 +1,867 @@ +/* +=========================================================================== +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++; + + memset(vbo, 0, sizeof(*vbo)); + + Q_strncpyz(vbo->name, name, sizeof(vbo->name)); + + 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); + + glState.currentVBO = NULL; + + 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++; + + memset(vbo, 0, sizeof(*vbo)); + + 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_TANGENT) + { + vbo->ofs_tangent = dataSize; + dataSize += sizeof(verts[0].tangent); + } + + if(stateBits & ATTR_BITANGENT) + { + vbo->ofs_bitangent = dataSize; + dataSize += sizeof(verts[0].bitangent); + } + + 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_tangent = dataSize; + vbo->stride_bitangent = 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); + } + + // tangent + if(stateBits & ATTR_TANGENT) + { + memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent)); + dataOfs += sizeof(verts[i].tangent); + } + + // bitangent + if(stateBits & ATTR_BITANGENT) + { + memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent)); + dataOfs += sizeof(verts[i].bitangent); + } + + // 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_TANGENT) + { + dataSize += sizeof(verts[0].tangent); + } + + if(stateBits & ATTR_BITANGENT) + { + dataSize += sizeof(verts[0].bitangent); + } + + 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_tangent = 0; + vbo->ofs_bitangent = 0; + vbo->ofs_st = 0; + vbo->ofs_lightmap = 0; + vbo->ofs_vertexcolor = 0; + + vbo->stride_xyz = sizeof(verts[0].xyz); + vbo->stride_normal = sizeof(verts[0].normal); + vbo->stride_tangent = sizeof(verts[0].tangent); + vbo->stride_bitangent = sizeof(verts[0].bitangent); + vbo->stride_vertexcolor = sizeof(verts[0].vertexColors); + vbo->stride_st = sizeof(verts[0].st); + vbo->stride_lightmap = sizeof(verts[0].lightmap); + + //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); + } + } + + // tangent + if(stateBits & ATTR_TANGENT) + { + vbo->ofs_tangent = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent)); + dataOfs += sizeof(verts[i].tangent); + } + } + + // bitangent + if(stateBits & ATTR_BITANGENT) + { + vbo->ofs_bitangent = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent)); + dataOfs += sizeof(verts[i].bitangent); + } + } + + // 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); + + glState.currentVBO = NULL; + + 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); + + glState.currentIBO = NULL; + + 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); + + glState.currentIBO = NULL; + + 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; + + glState.vertexAttribsInterpolation = 0; + glState.vertexAttribsOldFrame = 0; + glState.vertexAttribsNewFrame = 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.tangent[0]); + dataSize += sizeof(tess.bitangent[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; + tess.vbo->ofs_tangent = tess.vbo->ofs_normal + sizeof(tess.normal[0]) * SHADER_MAX_VERTEXES; + tess.vbo->ofs_bitangent = tess.vbo->ofs_tangent + sizeof(tess.tangent[0]) * SHADER_MAX_VERTEXES; + // these next two are actually interleaved + tess.vbo->ofs_st = tess.vbo->ofs_bitangent + sizeof(tess.bitangent[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_tangent = sizeof(tess.tangent[0]); + tess.vbo->stride_bitangent = sizeof(tess.bitangent[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; + 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); + } + + tr.numVBOs = 0; + tr.numIBOs = 0; +} + +/* +============ +R_VBOList_f +============ +*/ +void R_VBOList_f(void) +{ + int i; + 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; + } + + 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)); +} + + +/* +============== +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"); + + backEnd.pc.c_dynamicVboDraws++; + + // 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_TANGENT) + { + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); + } + + if(attribBits & ATTR_BITANGENT) + { + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent); + } + + 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_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent); + 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); + } +} Index: code/renderer/tr_world.c =================================================================== --- code/renderer/tr_world.c (revision 2095) +++ code/renderer/tr_world.c (working copy) @@ -24,135 +24,105 @@ /* -================= -R_CullTriSurf +================ +R_CullSurface -Returns true if the grid is completely culled away. -Also sets the clipped hint bit in tess -================= +Tries to cull surfaces before they are lighted or +added to the sorting list. +================ */ -static qboolean R_CullTriSurf( srfTriangles_t *cv ) { - int boxCull; - - boxCull = R_CullLocalBox( cv->bounds ); - - if ( boxCull == CULL_OUT ) { - return qtrue; +static qboolean R_CullSurface( msurface_t *surf ) { + if ( r_nocull->integer || surf->cullinfo.type == CULLINFO_NONE) { + return qfalse; } - return qfalse; -} -/* -================= -R_CullGrid + if (surf->cullinfo.type & CULLINFO_PLANE) + { + // Only true for SF_FACE, so treat like its own function + float d; + cullType_t ct; -Returns true if the grid is completely culled away. -Also sets the clipped hint bit in tess -================= -*/ -static qboolean R_CullGrid( srfGridMesh_t *cv ) { - int boxCull; - int sphereCull; + if ( !r_facePlaneCull->integer ) { + return qfalse; + } - if ( r_nocurves->integer ) { - return qtrue; - } + ct = surf->shader->cullType; - if ( tr.currentEntityNum != ENTITYNUM_WORLD ) { - sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius ); - } else { - sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius ); - } - boxCull = CULL_OUT; - - // check for trivial reject - if ( sphereCull == CULL_OUT ) - { - tr.pc.c_sphere_cull_patch_out++; - return qtrue; - } - // check bounding box if necessary - else if ( sphereCull == CULL_CLIP ) - { - tr.pc.c_sphere_cull_patch_clip++; - - boxCull = R_CullLocalBox( cv->meshBounds ); - - if ( boxCull == CULL_OUT ) + if (ct == CT_TWO_SIDED) { - tr.pc.c_box_cull_patch_out++; - return qtrue; + return qfalse; } - else if ( boxCull == CULL_IN ) + + // shadowmaps draw back surfaces + if ( tr.viewParms.isShadowmap ) { - tr.pc.c_box_cull_patch_in++; + if (ct == CT_FRONT_SIDED) + { + ct = CT_BACK_SIDED; + } + else + { + ct = CT_FRONT_SIDED; + } } - else - { - tr.pc.c_box_cull_patch_clip++; - } - } - else - { - tr.pc.c_sphere_cull_patch_in++; - } - return qfalse; -} + d = DotProduct (tr.or.viewOrigin, surf->cullinfo.plane.normal); + // don't cull exactly on the plane, because there are levels of rounding + // through the BSP, ICD, and hardware that may cause pixel gaps if an + // epsilon isn't allowed here + if ( ct == CT_FRONT_SIDED ) { + if ( d < surf->cullinfo.plane.dist - 8 ) { + return qtrue; + } + } else { + if ( d > surf->cullinfo.plane.dist + 8 ) { + return qtrue; + } + } -/* -================ -R_CullSurface - -Tries to back face cull surfaces before they are lighted or -added to the sorting list. - -This will also allow mirrors on both sides of a model without recursion. -================ -*/ -static qboolean R_CullSurface( surfaceType_t *surface, shader_t *shader ) { - srfSurfaceFace_t *sface; - float d; - - if ( r_nocull->integer ) { return qfalse; } - if ( *surface == SF_GRID ) { - return R_CullGrid( (srfGridMesh_t *)surface ); - } + if (surf->cullinfo.type & CULLINFO_SPHERE) + { + int sphereCull; - if ( *surface == SF_TRIANGLES ) { - return R_CullTriSurf( (srfTriangles_t *)surface ); - } + if ( tr.currentEntityNum != ENTITYNUM_WORLD ) { + sphereCull = R_CullLocalPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius ); + } else { + sphereCull = R_CullPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius ); + } - if ( *surface != SF_FACE ) { - return qfalse; - } + if ( sphereCull == CULL_OUT ) + { + return qtrue; + } - if ( shader->cullType == CT_TWO_SIDED ) { - return qfalse; + if ( sphereCull == CULL_IN ) + { + return qfalse; + } } - // face culling - if ( !r_facePlaneCull->integer ) { - return qfalse; - } + if (surf->cullinfo.type & CULLINFO_BOX) + { + int boxCull; - sface = ( srfSurfaceFace_t * ) surface; - d = DotProduct (tr.or.viewOrigin, sface->plane.normal); + if ( tr.currentEntityNum != ENTITYNUM_WORLD ) { + boxCull = R_CullLocalBox( surf->cullinfo.bounds ); + } else { + boxCull = R_CullBox( surf->cullinfo.bounds ); + } - // don't cull exactly on the plane, because there are levels of rounding - // through the BSP, ICD, and hardware that may cause pixel gaps if an - // epsilon isn't allowed here - if ( shader->cullType == CT_FRONT_SIDED ) { - if ( d < sface->plane.dist - 8 ) { + if ( boxCull == CULL_OUT ) + { return qtrue; } - } else { - if ( d > sface->plane.dist + 8 ) { - return qtrue; + + if ( boxCull == CULL_IN ) + { + return qfalse; } } @@ -160,137 +130,184 @@ } -static int R_DlightFace( srfSurfaceFace_t *face, int dlightBits ) { - float d; - int i; - dlight_t *dl; +/* +==================== +R_DlightSurface - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; +The given surface is going to be drawn, and it touches a leaf +that is touched by one or more dlights, so try to throw out +more dlights if possible. +==================== +*/ +static int R_DlightSurface( msurface_t *surf, int dlightBits ) { + float d; + int i; + dlight_t *dl; + + if ( surf->cullinfo.type & CULLINFO_PLANE ) + { + int i; + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + d = DotProduct( dl->origin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist; + if ( d < -dl->radius || d > dl->radius ) { + // dlight doesn't reach the plane + dlightBits &= ~( 1 << i ); + } } - dl = &tr.refdef.dlights[i]; - d = DotProduct( dl->origin, face->plane.normal ) - face->plane.dist; - if ( d < -dl->radius || d > dl->radius ) { - // dlight doesn't reach the plane - dlightBits &= ~( 1 << i ); + } + + if ( surf->cullinfo.type & CULLINFO_BOX ) + { + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + if ( dl->origin[0] - dl->radius > surf->cullinfo.bounds[1][0] + || dl->origin[0] + dl->radius < surf->cullinfo.bounds[0][0] + || dl->origin[1] - dl->radius > surf->cullinfo.bounds[1][1] + || dl->origin[1] + dl->radius < surf->cullinfo.bounds[0][1] + || dl->origin[2] - dl->radius > surf->cullinfo.bounds[1][2] + || dl->origin[2] + dl->radius < surf->cullinfo.bounds[0][2] ) { + // dlight doesn't reach the bounds + dlightBits &= ~( 1 << i ); + } } } - if ( !dlightBits ) { - tr.pc.c_dlightSurfacesCulled++; + if ( surf->cullinfo.type & CULLINFO_SPHERE ) + { + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + if (!SpheresIntersect(dl->origin, dl->radius, surf->cullinfo.localOrigin, surf->cullinfo.radius)) + { + // dlight doesn't reach the bounds + dlightBits &= ~( 1 << i ); + } + } } - face->dlightBits[ tr.smpFrame ] = dlightBits; - return dlightBits; -} - -static int R_DlightGrid( srfGridMesh_t *grid, int dlightBits ) { - int i; - dlight_t *dl; - - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; - } - dl = &tr.refdef.dlights[i]; - if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0] - || dl->origin[0] + dl->radius < grid->meshBounds[0][0] - || dl->origin[1] - dl->radius > grid->meshBounds[1][1] - || dl->origin[1] + dl->radius < grid->meshBounds[0][1] - || dl->origin[2] - dl->radius > grid->meshBounds[1][2] - || dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) { - // dlight doesn't reach the bounds - dlightBits &= ~( 1 << i ); - } + if ( *surf->data == SF_FACE ) { + ((srfSurfaceFace_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits; + } else if ( *surf->data == SF_GRID ) { + ((srfGridMesh_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits; + } else if ( *surf->data == SF_TRIANGLES ) { + ((srfTriangles_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits; + } else if ( *surf->data == SF_VBO_MESH ) { + ((srfVBOMesh_t *)surf->data)->dlightBits[ tr.smpFrame ] = dlightBits; + } else { + dlightBits = 0; } - if ( !dlightBits ) { - tr.pc.c_dlightSurfacesCulled++; + if ( dlightBits ) { + tr.pc.c_dlightSurfaces++; } - grid->dlightBits[ tr.smpFrame ] = dlightBits; return dlightBits; } -static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) { - // FIXME: more dlight culling to trisurfs... - surf->dlightBits[ tr.smpFrame ] = dlightBits; - return dlightBits; -#if 0 - int i; - dlight_t *dl; +/* +==================== +R_PshadowSurface - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; +Just like R_DlightSurface, cull any we can +==================== +*/ +static int R_PshadowSurface( msurface_t *surf, int pshadowBits ) { + float d; + int i; + pshadow_t *ps; + + if ( surf->cullinfo.type & CULLINFO_PLANE ) + { + int i; + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + if ( ! ( pshadowBits & ( 1 << i ) ) ) { + continue; + } + ps = &tr.refdef.pshadows[i]; + d = DotProduct( ps->lightOrigin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist; + if ( d < -ps->lightRadius || d > ps->lightRadius ) { + // pshadow doesn't reach the plane + pshadowBits &= ~( 1 << i ); + } } - dl = &tr.refdef.dlights[i]; - if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0] - || dl->origin[0] + dl->radius < grid->meshBounds[0][0] - || dl->origin[1] - dl->radius > grid->meshBounds[1][1] - || dl->origin[1] + dl->radius < grid->meshBounds[0][1] - || dl->origin[2] - dl->radius > grid->meshBounds[1][2] - || dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) { - // dlight doesn't reach the bounds - dlightBits &= ~( 1 << i ); + } + + if ( surf->cullinfo.type & CULLINFO_BOX ) + { + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + if ( ! ( pshadowBits & ( 1 << i ) ) ) { + continue; + } + ps = &tr.refdef.pshadows[i]; + if ( ps->lightOrigin[0] - ps->lightRadius > surf->cullinfo.bounds[1][0] + || ps->lightOrigin[0] + ps->lightRadius < surf->cullinfo.bounds[0][0] + || ps->lightOrigin[1] - ps->lightRadius > surf->cullinfo.bounds[1][1] + || ps->lightOrigin[1] + ps->lightRadius < surf->cullinfo.bounds[0][1] + || ps->lightOrigin[2] - ps->lightRadius > surf->cullinfo.bounds[1][2] + || ps->lightOrigin[2] + ps->lightRadius < surf->cullinfo.bounds[0][2] + || BoxOnPlaneSide(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1], &ps->cullPlane) == 2 ) { + // pshadow doesn't reach the bounds + pshadowBits &= ~( 1 << i ); + } } } - if ( !dlightBits ) { - tr.pc.c_dlightSurfacesCulled++; + if ( surf->cullinfo.type & CULLINFO_SPHERE ) + { + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + if ( ! ( pshadowBits & ( 1 << i ) ) ) { + continue; + } + ps = &tr.refdef.pshadows[i]; + if (!SpheresIntersect(ps->viewOrigin, ps->viewRadius, surf->cullinfo.localOrigin, surf->cullinfo.radius) + || DotProduct( surf->cullinfo.localOrigin, ps->cullPlane.normal ) - ps->cullPlane.dist < -surf->cullinfo.radius) + { + // pshadow doesn't reach the bounds + pshadowBits &= ~( 1 << i ); + } + } } - grid->dlightBits[ tr.smpFrame ] = dlightBits; - return dlightBits; -#endif -} - -/* -==================== -R_DlightSurface - -The given surface is going to be drawn, and it touches a leaf -that is touched by one or more dlights, so try to throw out -more dlights if possible. -==================== -*/ -static int R_DlightSurface( msurface_t *surf, int dlightBits ) { if ( *surf->data == SF_FACE ) { - dlightBits = R_DlightFace( (srfSurfaceFace_t *)surf->data, dlightBits ); + ((srfSurfaceFace_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits; } else if ( *surf->data == SF_GRID ) { - dlightBits = R_DlightGrid( (srfGridMesh_t *)surf->data, dlightBits ); + ((srfGridMesh_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits; } else if ( *surf->data == SF_TRIANGLES ) { - dlightBits = R_DlightTrisurf( (srfTriangles_t *)surf->data, dlightBits ); + ((srfTriangles_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits; + } else if ( *surf->data == SF_VBO_MESH ) { + ((srfVBOMesh_t *)surf->data)->pshadowBits[ tr.smpFrame ] = pshadowBits; } else { - dlightBits = 0; + pshadowBits = 0; } - if ( dlightBits ) { - tr.pc.c_dlightSurfaces++; + if ( pshadowBits ) { + //tr.pc.c_dlightSurfaces++; } - return dlightBits; + return pshadowBits; } - /* ====================== R_AddWorldSurface ====================== */ -static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) { - if ( surf->viewCount == tr.viewCount ) { - return; // already in this view - } - - surf->viewCount = tr.viewCount; +static void R_AddWorldSurface( msurface_t *surf, int dlightBits, int pshadowBits ) { // FIXME: bmodel fog? // try to cull before dlighting or adding - if ( R_CullSurface( surf->data, surf->shader ) ) { + if ( R_CullSurface( surf ) ) { return; } @@ -300,7 +317,13 @@ dlightBits = ( dlightBits != 0 ); } - R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits ); + // check for pshadows + /*if ( pshadowBits ) */{ + pshadowBits = R_PshadowSurface( surf, pshadowBits); + pshadowBits = ( pshadowBits != 0 ); + } + + R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits, pshadowBits ); } /* @@ -335,7 +358,13 @@ R_DlightBmodel( bmodel ); for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights ); + int surf = bmodel->firstSurface + i; + + if (tr.world->surfacesViewCount[surf] != tr.viewCount) + { + tr.world->surfacesViewCount[surf] = tr.viewCount; + R_AddWorldSurface( tr.world->surfaces + surf, tr.currentEntity->needDlights, 0 ); + } } } @@ -354,13 +383,14 @@ R_RecursiveWorldNode ================ */ -static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) { +static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits, int pshadowBits ) { do { int newDlights[2]; + unsigned int newPShadows[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; } @@ -410,6 +440,15 @@ } } + if ( planeBits & 16 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[4]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~16; // all descendants will also be in front + } + } } if ( node->contents != -1 ) { @@ -443,18 +482,42 @@ } } + newPShadows[0] = 0; + newPShadows[1] = 0; + if ( pshadowBits ) { + int i; + + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + pshadow_t *shadow; + float dist; + + if ( pshadowBits & ( 1 << i ) ) { + shadow = &tr.refdef.pshadows[i]; + dist = DotProduct( shadow->lightOrigin, node->plane->normal ) - node->plane->dist; + + if ( dist > -shadow->lightRadius ) { + newPShadows[0] |= ( 1 << i ); + } + if ( dist < shadow->lightRadius ) { + newPShadows[1] |= ( 1 << i ); + } + } + } + } + // recurse down the children, front side first - R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0] ); + R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0], newPShadows[0] ); // tail recurse node = node->children[1]; dlightBits = newDlights[1]; + pshadowBits = newPShadows[1]; } while ( 1 ); { // leaf node, so add mark surfaces int c; - msurface_t *surf, **mark; + int surf, *view; tr.pc.c_leafs++; @@ -479,15 +542,45 @@ tr.viewParms.visBounds[1][2] = node->maxs[2]; } - // add the individual surfaces - mark = node->firstmarksurface; + // add merged and unmerged surfaces + if (tr.world->viewSurfaces) + view = tr.world->viewSurfaces + node->firstmarksurface; + else + view = tr.world->marksurfaces + node->firstmarksurface; + c = node->nummarksurfaces; while (c--) { - // the surface may have already been added if it - // spans multiple leafs - surf = *mark; - R_AddWorldSurface( surf, dlightBits ); - mark++; + // just mark it as visible, so we don't jump out of the cache derefencing the surface + surf = *view; + if (surf < 0) + { + if (tr.world->mergedSurfacesViewCount[-surf - 1] != tr.viewCount) + { + tr.world->mergedSurfacesViewCount[-surf - 1] = tr.viewCount; + tr.world->mergedSurfacesDlightBits[-surf - 1] = dlightBits; + tr.world->mergedSurfacesPshadowBits[-surf - 1] = pshadowBits; + } + else + { + tr.world->mergedSurfacesDlightBits[-surf - 1] |= dlightBits; + tr.world->mergedSurfacesPshadowBits[-surf - 1] |= pshadowBits; + } + } + else + { + if (tr.world->surfacesViewCount[surf] != tr.viewCount) + { + tr.world->surfacesViewCount[surf] = tr.viewCount; + tr.world->surfacesDlightBits[surf] = dlightBits; + tr.world->surfacesPshadowBits[surf] = pshadowBits; + } + else + { + tr.world->surfacesDlightBits[surf] |= dlightBits; + tr.world->surfacesPshadowBits[surf] |= pshadowBits; + } + } + view++; } } @@ -584,12 +677,37 @@ // 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; } + // if the areamask was modified, invalidate all visclusters + // this caused doors to open into undrawn areas + if (tr.refdef.areamaskModified) + { + memset(tr.visClusters, -2, sizeof(tr.visClusters)); + } + + 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 ) { @@ -597,19 +715,18 @@ } } - tr.visCount++; - tr.viewCluster = cluster; - - if ( r_novis->integer || tr.viewCluster == -1 ) { + // set all nodes to visible if there is no vis + // this caused some levels to simply not render + if (r_novis->integer || !tr.world->vis || 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; @@ -629,9 +746,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); } @@ -661,9 +778,49 @@ // clear out the visible min/max ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] ); - // perform frustum culling and add all the potentially visible surfaces + // perform frustum culling and flag all the potentially visible surfaces if ( tr.refdef.num_dlights > 32 ) { tr.refdef.num_dlights = 32 ; } - R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 ); + + if ( tr.refdef.num_pshadows > 32 ) { + tr.refdef.num_pshadows = 32 ; + } + + if ( tr.viewParms.isShadowmap) + { + R_RecursiveWorldNode( tr.world->nodes, 31, ( 1 << tr.refdef.num_dlights ) - 1, 0 ); + } + else + { + R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1, ( 1 << tr.refdef.num_pshadows ) - 1 ); + } + + // now add all the potentially visible surfaces + // also mask invisible dlights for next frame + { + int i; + + tr.refdef.dlightMask = 0; + + for (i = 0; i < tr.world->numWorldSurfaces; i++) + { + if (tr.world->surfacesViewCount[i] != tr.viewCount) + continue; + + R_AddWorldSurface( tr.world->surfaces + i, tr.world->surfacesDlightBits[i], tr.world->surfacesPshadowBits[i] ); + tr.refdef.dlightMask |= tr.world->surfacesDlightBits[i]; + } + + for (i = 0; i < tr.world->numMergedSurfaces; i++) + { + if (tr.world->mergedSurfacesViewCount[i] != tr.viewCount) + continue; + + R_AddWorldSurface( tr.world->mergedSurfaces + i, tr.world->mergedSurfacesDlightBits[i], tr.world->mergedSurfacesPshadowBits[i] ); + tr.refdef.dlightMask |= tr.world->mergedSurfacesDlightBits[i]; + } + + tr.refdef.dlightMask = ~tr.refdef.dlightMask; + } } Index: code/sdl/sdl_glimp.c =================================================================== --- code/sdl/sdl_glimp.c (revision 2095) +++ 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,40 @@ 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 +#endif + typedef enum { RSERR_OK, @@ -83,6 +124,110 @@ 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, const 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 * qglUniform1fvARB) (GLint location, GLsizei count, const GLfloat * value); +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); + +// GL_ARB_texture_compression +void (APIENTRY * qglCompressedTexImage3DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexImage2DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexImage1DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, + GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexSubImage3DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, + GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglGetCompressedTexImageARB)(GLenum target, GLint lod, + GLvoid *img); + + +#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 @@ -96,6 +241,7 @@ screen = NULL; Com_Memset( &glConfig, 0, sizeof( glConfig ) ); + Com_Memset( &glRefConfig, 0, sizeof( glRefConfig ) ); Com_Memset( &glState, 0, sizeof( glState ) ); } @@ -119,6 +265,7 @@ */ void GLimp_LogComment( char *comment ) { + //ri.Printf(PRINT_ALL, comment); } /* @@ -426,6 +573,76 @@ break; } + // try to initialize an OpenGL 3.1 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, + //WGL_CONTEXT_PROFILE_MASK_ARB, + //WGL_CONTEXT_COMPATIBILITY_PROFILE_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) @@ -527,6 +744,14 @@ { glConfig.textureCompression = TC_S3TC_ARB; ri.Printf( PRINT_ALL, "...using GL_EXT_texture_compression_s3tc\n" ); + + qglCompressedTexImage3DARB = SDL_GL_GetProcAddress( "glCompressedTexImage3DARB" ); + qglCompressedTexImage2DARB = SDL_GL_GetProcAddress( "glCompressedTexImage2DARB" ); + qglCompressedTexImage1DARB = SDL_GL_GetProcAddress( "glCompressedTexImage1DARB" ); + qglCompressedTexSubImage3DARB = SDL_GL_GetProcAddress( "glCompressedTexSubImage3DARB" ); + qglCompressedTexSubImage2DARB = SDL_GL_GetProcAddress( "glCompressedTexSubImage2DARB" ); + qglCompressedTexSubImage1DARB = SDL_GL_GetProcAddress( "glCompressedTexSubImage1DARB" ); + qglGetCompressedTexImageARB = SDL_GL_GetProcAddress( "glGetCompressedTexImageARB" ); } else { @@ -643,19 +868,19 @@ ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); } - textureFilterAnisotropic = qfalse; + glRefConfig.textureFilterAnisotropic = qfalse; if ( GLimp_HaveExtension( "GL_EXT_texture_filter_anisotropic" ) ) { if ( r_ext_texture_filter_anisotropic->integer ) { - qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisotropy ); - if ( maxAnisotropy <= 0 ) { + qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&glRefConfig.maxAnisotropy ); + if ( glRefConfig.maxAnisotropy <= 0 ) { ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" ); - maxAnisotropy = 0; + glRefConfig.maxAnisotropy = 0; } else { - ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy ); - textureFilterAnisotropic = qtrue; + ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", glRefConfig.maxAnisotropy ); + glRefConfig.textureFilterAnisotropic = qtrue; } } else @@ -667,6 +892,218 @@ { ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); } + + // GL_EXT_multi_draw_arrays + glRefConfig.multiDrawArrays = qfalse; + qglMultiDrawArraysEXT = NULL; + qglMultiDrawElementsEXT = NULL; + if( GLimp_HaveExtension( "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"); + glRefConfig.multiDrawArrays = qtrue; + } + else + { + ri.Printf(PRINT_ALL, "...GL_EXT_multi_draw_arrays not found\n"); + } + + // GL_ARB_vertex_program + glRefConfig.vertexProgram = qfalse; + qglVertexAttrib4fARB = NULL; + qglVertexAttrib4fvARB = NULL; + qglVertexAttribPointerARB = NULL; + qglEnableVertexAttribArrayARB = NULL; + qglDisableVertexAttribArrayARB = NULL; + if( GLimp_HaveExtension( "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"); + glRefConfig.vertexProgram = qtrue; + } + else + { + ri.Printf(PRINT_ALL, "...GL_ARB_vertex_program not found\n"); + } + + // GL_ARB_vertex_buffer_object + glRefConfig.vertexBufferObject = qfalse; + qglBindBufferARB = NULL; + qglDeleteBuffersARB = NULL; + qglGenBuffersARB = NULL; + qglIsBufferARB = NULL; + qglBufferDataARB = NULL; + qglBufferSubDataARB = NULL; + qglGetBufferSubDataARB = NULL; + qglGetBufferParameterivARB = NULL; + qglGetBufferPointervARB = NULL; + if( GLimp_HaveExtension( "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"); + glRefConfig.vertexBufferObject = qtrue; + } + else + { + ri.Printf(PRINT_ALL, "...GL_ARB_vertex_buffer_object not found\n"); + } + + // GL_ARB_shader_objects + glRefConfig.shaderObjects = qfalse; + 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; + qglUniform1fvARB = 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( GLimp_HaveExtension( "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"); + qglUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) SDL_GL_GetProcAddress("glUniform1fvARB"); + 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"); + glRefConfig.shaderObjects = qtrue; + } + else + { + ri.Printf(PRINT_ALL, "...GL_ARB_shader_objects not found\n"); + } + + // GL_ARB_vertex_shader + glRefConfig.vertexShader = qfalse; + qglBindAttribLocationARB = NULL; + qglGetActiveAttribARB = NULL; + qglGetAttribLocationARB = NULL; + if( GLimp_HaveExtension( "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"); + glRefConfig.vertexShader = qtrue; + } + else + { + ri.Printf(PRINT_ALL, "...GL_ARB_vertex_shader not found\n"); + } + + glRefConfig.glsl = glRefConfig.vertexProgram && glRefConfig.shaderObjects && glRefConfig.vertexShader; + + glRefConfig.memInfo = MI_NONE; + + if( GLimp_HaveExtension( "GL_NVX_gpu_memory_info")) + { + glRefConfig.memInfo = MI_NVX; + } + else if( GLimp_HaveExtension( "GL_ATI_meminfo")) + { + glRefConfig.memInfo = MI_ATI; + } + } #define R_MODE_FALLBACK 3 // 640 * 480 @@ -734,13 +1171,41 @@ glConfig.deviceSupportsGamma = SDL_SetGamma( 1.0f, 1.0f, 1.0f ) >= 0; // get our config strings - Q_strncpyz( glConfig.vendor_string, (char *) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); - Q_strncpyz( glConfig.renderer_string, (char *) qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) ); - 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; + + string = (char *) qglGetString (GL_VENDOR); + if (string) + Q_strncpyz( glConfig.vendor_string, (char *) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); + else + glConfig.vendor_string[0] = '\0'; + string = (char *) qglGetString (GL_RENDERER); + if (string) + Q_strncpyz( glConfig.renderer_string, (char *) qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) ); + else + glConfig.renderer_string[0] = '\0'; + + if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') + glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; + + string = (char *) qglGetString(GL_VERSION); + if (string) + Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); + else + glConfig.version_string[0] = '\0'; + + 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: code/sys/sys_main.c =================================================================== --- code/sys/sys_main.c (revision 2095) +++ code/sys/sys_main.c (working copy) @@ -576,11 +576,11 @@ CON_Init( ); - signal( SIGILL, Sys_SigHandler ); - signal( SIGFPE, Sys_SigHandler ); - signal( SIGSEGV, Sys_SigHandler ); - signal( SIGTERM, Sys_SigHandler ); - signal( SIGINT, Sys_SigHandler ); + //signal( SIGILL, Sys_SigHandler ); + //signal( SIGFPE, Sys_SigHandler ); + //signal( SIGSEGV, Sys_SigHandler ); + //signal( SIGTERM, Sys_SigHandler ); + //signal( SIGINT, Sys_SigHandler ); while( 1 ) { Index: Makefile =================================================================== --- Makefile (revision 2095) +++ Makefile (working copy) @@ -1417,8 +1417,10 @@ $(B)/client/tr_bsp.o \ $(B)/client/tr_cmds.o \ $(B)/client/tr_curve.o \ + $(B)/client/tr_extramath.o \ $(B)/client/tr_flares.o \ $(B)/client/tr_font.o \ + $(B)/client/tr_glsl.o \ $(B)/client/tr_image.o \ $(B)/client/tr_image_png.o \ $(B)/client/tr_image_jpg.o \ @@ -1441,6 +1443,7 @@ $(B)/client/tr_sky.o \ $(B)/client/tr_surface.o \ $(B)/client/tr_world.o \ + $(B)/client/tr_vbo.o \ \ $(B)/client/sdl_gamma.o \ $(B)/client/sdl_input.o \ Index: misc/msvc/quake3.vcproj =================================================================== --- misc/msvc/quake3.vcproj (revision 2095) +++ misc/msvc/quake3.vcproj (working copy) @@ -1,9 +1,10 @@ @@ -2970,6 +2971,10 @@ > + + @@ -2978,6 +2983,10 @@ > + + @@ -3062,6 +3071,10 @@ > + +