/* ============ 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, j; byte *data; int dataSize; int dataOfs; int glUsage; switch (usage) { case VBO_USAGE_STATIC: glUsage = GL_STATIC_DRAW; break; case VBO_USAGE_DYNAMIC: glUsage = GL_DYNAMIC_DRAW; break; default: glUsage = 0; Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); } if (!numVertexes) { return NULL; } if (strlen(name) >= MAX_QPATH) { ri.Error(ERR_DROP, "R_CreateVBO2: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = (VBO_t *)ri.Hunk_Alloc(sizeof(*vbo), h_low); Com_AddToGrowList(&tr.vbos, vbo); Q_strncpyz(vbo->name, name, sizeof(vbo->name)); vbo->ofsXYZ = 0; vbo->ofsTexCoords = 0; vbo->ofsLightCoords = 0; vbo->ofsBinormals = 0; vbo->ofsTangents = 0; vbo->ofsNormals = 0; vbo->ofsColors = 0; vbo->ofsPaintColors = 0; vbo->ofsLightDirections = 0; vbo->ofsBoneIndexes = 0; vbo->ofsBoneWeights = 0; vbo->sizeXYZ = 0; vbo->sizeTangents = 0; vbo->sizeBinormals = 0; vbo->sizeNormals = 0; // create VBO dataSize = numVertexes * (sizeof(vec4_t) * 9); data = (byte *)ri.Hunk_AllocateTempMemory(dataSize); dataOfs = 0; // since this is all float, point tmp directly into data // 2-entry -> { memb[0], memb[1], 0, 1 } // 3-entry -> { memb[0], memb[1], memb[2], 1 } #define VERTEXSIZE(memb) (sizeof(verts->memb) / sizeof(verts->memb[0])) #define VERTEXCOPY(memb) \ do { \ vec_t *tmp = (vec_t *) (data + dataOfs); \ for (i = 0; i < numVertexes; i++) \ { \ for (j = 0; j < VERTEXSIZE(memb); j++) { *tmp++ = verts[i].memb[j]; } \ if (VERTEXSIZE(memb) < 3) { *tmp++ = 0; } \ if (VERTEXSIZE(memb) < 4) { *tmp++ = 1; } \ } \ dataOfs += i * sizeof(vec4_t); \ } while (0) // set up xyz array VERTEXCOPY(xyz); // feed vertex texcoords if (stateBits & ATTR_TEXCOORD) { vbo->ofsTexCoords = dataOfs; VERTEXCOPY(st); } // feed vertex lightmap texcoords if (stateBits & ATTR_LIGHTCOORD) { vbo->ofsLightCoords = dataOfs; VERTEXCOPY(lightmap); } // feed vertex tangents if (stateBits & ATTR_TANGENT) { vbo->ofsTangents = dataOfs; VERTEXCOPY(tangent); } // feed vertex binormals if (stateBits & ATTR_BINORMAL) { vbo->ofsBinormals = dataOfs; VERTEXCOPY(binormal); } // feed vertex normals if (stateBits & ATTR_NORMAL) { vbo->ofsNormals = dataOfs; VERTEXCOPY(normal); } // feed vertex colors if (stateBits & ATTR_COLOR) { vbo->ofsColors = dataOfs; VERTEXCOPY(lightColor); } vbo->vertexesSize = dataSize; vbo->vertexesNum = numVertexes; glGenBuffers(1, &vbo->vertexesVBO); glBindBuffer(GL_ARRAY_BUFFER, vbo->vertexesVBO); glBufferData(GL_ARRAY_BUFFER, dataSize, data, glUsage); glBindBuffer(GL_ARRAY_BUFFER, 0); GL_CheckErrors(); ri.Hunk_FreeTempMemory(data); return vbo; }
/* ============ R_InitFBOs ============ */ void R_InitFBOs(void) { int i; int width, height; ri.Printf(PRINT_ALL, "------- R_InitFBOs -------\n"); if(!glConfig2.framebufferObjectAvailable) return; tr.numFBOs = 0; #if !defined(USE_D3D10) GL_CheckErrors(); #endif // make sure the render thread is stopped R_SyncRenderThread(); #if defined(USE_D3D10) // TODO #else if(DS_STANDARD_ENABLED()) { // geometricRender FBO as G-Buffer for deferred shading ri.Printf(PRINT_ALL, "Deferred Shading enabled\n"); if(glConfig2.textureNPOTAvailable) { width = glConfig.vidWidth; height = glConfig.vidHeight; } else { width = NearestPowerOfTwo(glConfig.vidWidth); height = NearestPowerOfTwo(glConfig.vidHeight); } tr.geometricRenderFBO = R_CreateFBO("_geometricRender", width, height); R_BindFBO(tr.geometricRenderFBO); #if 0 if(glConfig2.framebufferPackedDepthStencilAvailable) { R_CreateFBOPackedDepthStencilBuffer(tr.geometricRenderFBO, GL_DEPTH24_STENCIL8_EXT); R_AttachFBOTexturePackedDepthStencil(tr.depthRenderImage->texnum); } else if(glConfig.hardwareType == GLHW_ATI || glConfig.hardwareType == GLHW_ATI_DX10)// || glConfig.hardwareType == GLHW_NV_DX10) { R_CreateFBODepthBuffer(tr.geometricRenderFBO, GL_DEPTH_COMPONENT16_ARB); R_AttachFBOTextureDepth(tr.depthRenderImage->texnum); } else #endif { R_CreateFBODepthBuffer(tr.geometricRenderFBO, GL_DEPTH_COMPONENT24_ARB); R_AttachFBOTextureDepth(tr.depthRenderImage->texnum); } // enable all attachments as draw buffers //glDrawBuffersARB(4, geometricRenderTargets); R_CreateFBOColorBuffer(tr.geometricRenderFBO, GL_RGBA, 0); R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.deferredRenderFBOImage->texnum, 0); R_CreateFBOColorBuffer(tr.geometricRenderFBO, GL_RGBA, 1); R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.deferredDiffuseFBOImage->texnum, 1); R_CreateFBOColorBuffer(tr.geometricRenderFBO, GL_RGBA, 2); R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.deferredNormalFBOImage->texnum, 2); R_CreateFBOColorBuffer(tr.geometricRenderFBO, GL_RGBA, 3); R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.deferredSpecularFBOImage->texnum, 3); R_CheckFBO(tr.geometricRenderFBO); } else { // forward shading if(glConfig2.textureNPOTAvailable) { width = glConfig.vidWidth; height = glConfig.vidHeight; } else { width = NearestPowerOfTwo(glConfig.vidWidth); height = NearestPowerOfTwo(glConfig.vidHeight); } // deferredRender FBO for the HDR or LDR context tr.deferredRenderFBO = R_CreateFBO("_deferredRender", width, height); R_BindFBO(tr.deferredRenderFBO); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.deferredRenderFBO, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.deferredRenderFBO, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.deferredRenderFBOImage->texnum, 0); #if 0 if(glConfig2.framebufferPackedDepthStencilAvailable) { R_CreateFBOPackedDepthStencilBuffer(tr.deferredRenderFBO, GL_DEPTH24_STENCIL8_EXT); R_AttachFBOTexturePackedDepthStencil(tr.depthRenderImage->texnum); } else if(glConfig.hardwareType == GLHW_ATI || glConfig.hardwareType == GLHW_ATI_DX10)// || glConfig.hardwareType == GLHW_NV_DX10) { R_CreateFBODepthBuffer(tr.deferredRenderFBO, GL_DEPTH_COMPONENT16_ARB); R_AttachFBOTextureDepth(tr.depthRenderImage->texnum); } else #endif { R_CreateFBODepthBuffer(tr.deferredRenderFBO, GL_DEPTH_COMPONENT24_ARB); R_AttachFBOTextureDepth(tr.depthRenderImage->texnum); } R_CheckFBO(tr.deferredRenderFBO); } if(glConfig2.framebufferBlitAvailable) { if(glConfig2.textureNPOTAvailable) { width = glConfig.vidWidth; height = glConfig.vidHeight; } else { width = NearestPowerOfTwo(glConfig.vidWidth); height = NearestPowerOfTwo(glConfig.vidHeight); } tr.occlusionRenderFBO = R_CreateFBO("_occlusionRender", width, height); R_BindFBO(tr.occlusionRenderFBO); if(glConfig.hardwareType == GLHW_ATI_DX10) { //R_CreateFBOColorBuffer(tr.occlusionRenderFBO, GL_ALPHA16F_ARB, 0); R_CreateFBODepthBuffer(tr.occlusionRenderFBO, GL_DEPTH_COMPONENT16_ARB); } else if(glConfig.hardwareType == GLHW_NV_DX10) { //R_CreateFBOColorBuffer(tr.occlusionRenderFBO, GL_ALPHA32F_ARB, 0); R_CreateFBODepthBuffer(tr.occlusionRenderFBO, GL_DEPTH_COMPONENT24_ARB); } else if(glConfig2.framebufferPackedDepthStencilAvailable) { //R_CreateFBOColorBuffer(tr.occlusionRenderFBO, GL_ALPHA32F_ARB, 0); R_CreateFBOPackedDepthStencilBuffer(tr.occlusionRenderFBO, GL_DEPTH24_STENCIL8_EXT); } else { //R_CreateFBOColorBuffer(tr.occlusionRenderFBO, GL_RGBA, 0); R_CreateFBODepthBuffer(tr.occlusionRenderFBO, GL_DEPTH_COMPONENT24_ARB); } R_CreateFBOColorBuffer(tr.occlusionRenderFBO, GL_RGBA, 0); R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.occlusionRenderFBOImage->texnum, 0); R_CheckFBO(tr.occlusionRenderFBO); } if(r_shadows->integer >= SHADOWING_ESM16 && glConfig2.textureFloatAvailable) { // shadowMap FBOs for shadow mapping offscreen rendering for(i = 0; i < MAX_SHADOWMAPS; i++) { width = height = shadowMapResolutions[i]; tr.shadowMapFBO[i] = R_CreateFBO(va("_shadowMap%d", i), width, height); R_BindFBO(tr.shadowMapFBO[i]); if((glConfig.driverType == GLDRV_OPENGL3) || (glConfig.hardwareType == GLHW_NV_DX10 || glConfig.hardwareType == GLHW_ATI_DX10)) { if(r_shadows->integer == SHADOWING_ESM32) { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_ALPHA32F_ARB, 0); } else if(r_shadows->integer == SHADOWING_VSM32) { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_LUMINANCE_ALPHA32F_ARB, 0); } else if(r_shadows->integer == SHADOWING_EVSM32) { if(r_evsmPostProcess->integer) { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_ALPHA32F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_RGBA32F_ARB, 0); } } else { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_RGBA16F_ARB, 0); } } else { if(r_shadows->integer == SHADOWING_ESM16) { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_ALPHA16F_ARB, 0); } else if(r_shadows->integer == SHADOWING_VSM16) { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_LUMINANCE_ALPHA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.shadowMapFBO[i], GL_RGBA16F_ARB, 0); } } R_CreateFBODepthBuffer(tr.shadowMapFBO[i], GL_DEPTH_COMPONENT24_ARB); R_CheckFBO(tr.shadowMapFBO[i]); } // sun requires different resolutions for(i = 0; i < MAX_SHADOWMAPS; i++) { width = height = sunShadowMapResolutions[i]; tr.sunShadowMapFBO[i] = R_CreateFBO(va("_sunShadowMap%d", i), width, height); R_BindFBO(tr.sunShadowMapFBO[i]); if((glConfig.driverType == GLDRV_OPENGL3) || (glConfig.hardwareType == GLHW_NV_DX10 || glConfig.hardwareType == GLHW_ATI_DX10)) { if(r_shadows->integer == SHADOWING_ESM32) { R_CreateFBOColorBuffer(tr.sunShadowMapFBO[i], GL_ALPHA32F_ARB, 0); } else if(r_shadows->integer == SHADOWING_VSM32) { R_CreateFBOColorBuffer(tr.sunShadowMapFBO[i], GL_LUMINANCE_ALPHA32F_ARB, 0); } else if(r_shadows->integer == SHADOWING_EVSM32) { if(!r_evsmPostProcess->integer) { R_CreateFBOColorBuffer(tr.sunShadowMapFBO[i], GL_RGBA32F_ARB, 0); } } else { R_CreateFBOColorBuffer(tr.sunShadowMapFBO[i], GL_RGBA16F_ARB, 0); } } else { if(r_shadows->integer == SHADOWING_ESM16) { R_CreateFBOColorBuffer(tr.sunShadowMapFBO[i], GL_ALPHA16F_ARB, 0); } else if(r_shadows->integer == SHADOWING_VSM16) { R_CreateFBOColorBuffer(tr.sunShadowMapFBO[i], GL_LUMINANCE_ALPHA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.sunShadowMapFBO[i], GL_RGBA16F_ARB, 0); } } R_CreateFBODepthBuffer(tr.sunShadowMapFBO[i], GL_DEPTH_COMPONENT24_ARB); if(r_shadows->integer == SHADOWING_EVSM32 && r_evsmPostProcess->integer) { R_AttachFBOTextureDepth(tr.sunShadowMapFBOImage[i]->texnum); /* Since we don’t have a color attachment the framebuffer will be considered incomplete. Consequently, we must inform the driver that we do not wish to render to the color buffer. We do this with a call to set the draw-buffer and read-buffer to GL_NONE: */ glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); } R_CheckFBO(tr.sunShadowMapFBO[i]); } } { if(glConfig2.textureNPOTAvailable) { width = glConfig.vidWidth; height = glConfig.vidHeight; } else { width = NearestPowerOfTwo(glConfig.vidWidth); height = NearestPowerOfTwo(glConfig.vidHeight); } // portalRender FBO for portals, mirrors, water, cameras et cetera tr.portalRenderFBO = R_CreateFBO("_portalRender", width, height); R_BindFBO(tr.portalRenderFBO); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.portalRenderFBO, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.portalRenderFBO, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.portalRenderImage->texnum, 0); R_CheckFBO(tr.portalRenderFBO); } { if(glConfig2.textureNPOTAvailable) { width = glConfig.vidWidth * 0.25f; height = glConfig.vidHeight * 0.25f; } else { width = NearestPowerOfTwo(glConfig.vidWidth * 0.25f); height = NearestPowerOfTwo(glConfig.vidHeight * 0.25f); } tr.downScaleFBO_quarter = R_CreateFBO("_downScale_quarter", width, height); R_BindFBO(tr.downScaleFBO_quarter); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.downScaleFBO_quarter, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.downScaleFBO_quarter, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.downScaleFBOImage_quarter->texnum, 0); R_CheckFBO(tr.downScaleFBO_quarter); tr.downScaleFBO_64x64 = R_CreateFBO("_downScale_64x64", 64, 64); R_BindFBO(tr.downScaleFBO_64x64); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.downScaleFBO_64x64, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.downScaleFBO_64x64, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.downScaleFBOImage_64x64->texnum, 0); R_CheckFBO(tr.downScaleFBO_64x64); #if 0 tr.downScaleFBO_16x16 = R_CreateFBO("_downScale_16x16", 16, 16); R_BindFBO(tr.downScaleFBO_16x16); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.downScaleFBO_16x16, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.downScaleFBO_16x16, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.downScaleFBOImage_16x16->texnum, 0); R_CheckFBO(tr.downScaleFBO_16x16); tr.downScaleFBO_4x4 = R_CreateFBO("_downScale_4x4", 4, 4); R_BindFBO(tr.downScaleFBO_4x4); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.downScaleFBO_4x4, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.downScaleFBO_4x4, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.downScaleFBOImage_4x4->texnum, 0); R_CheckFBO(tr.downScaleFBO_4x4); tr.downScaleFBO_1x1 = R_CreateFBO("_downScale_1x1", 1, 1); R_BindFBO(tr.downScaleFBO_1x1); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.downScaleFBO_1x1, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.downScaleFBO_1x1, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.downScaleFBOImage_1x1->texnum, 0); R_CheckFBO(tr.downScaleFBO_1x1); #endif if(glConfig2.textureNPOTAvailable) { width = glConfig.vidWidth * 0.25f; height = glConfig.vidHeight * 0.25f; } else { width = NearestPowerOfTwo(glConfig.vidWidth * 0.25f); height = NearestPowerOfTwo(glConfig.vidHeight * 0.25f); } tr.contrastRenderFBO = R_CreateFBO("_contrastRender", width, height); R_BindFBO(tr.contrastRenderFBO); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.contrastRenderFBO, GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.contrastRenderFBO, GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.contrastRenderFBOImage->texnum, 0); R_CheckFBO(tr.contrastRenderFBO); for(i = 0; i < 2; i++) { tr.bloomRenderFBO[i] = R_CreateFBO(va("_bloomRender%d", i), width, height); R_BindFBO(tr.bloomRenderFBO[i]); if(r_hdrRendering->integer && glConfig2.textureFloatAvailable) { R_CreateFBOColorBuffer(tr.bloomRenderFBO[i], GL_RGBA16F_ARB, 0); } else { R_CreateFBOColorBuffer(tr.bloomRenderFBO[i], GL_RGBA, 0); } R_AttachFBOTexture2D(GL_TEXTURE_2D, tr.bloomRenderFBOImage[i]->texnum, 0); R_CheckFBO(tr.bloomRenderFBO[i]); } } GL_CheckErrors(); #endif // defined(USE_D3D10) R_BindNullFBO(); }
/* ==================== RE_BeginFrame If running in stereo, RE_BeginFrame will be called twice for each RE_EndFrame ==================== */ void RE_BeginFrame( stereoFrame_t stereoFrame ) { drawBufferCommand_t *cmd; if ( !tr.registered ) { return; } glState.finishCalled = qfalse; tr.frameCount++; tr.frameSceneNum = 0; // // do overdraw measurement // if ( r_measureOverdraw->integer ) { if ( glConfig.stencilBits < 4 ) { ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = qfalse; } else if ( r_shadows->integer == 2 ) { ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = qfalse; } else { R_SyncRenderThread(); qglEnable( GL_STENCIL_TEST ); qglStencilMask( ~0U ); qglClearStencil( 0U ); qglStencilFunc( GL_ALWAYS, 0U, ~0U ); qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); } r_measureOverdraw->modified = qfalse; } else { // this is only reached if it was on and is now off if ( r_measureOverdraw->modified ) { R_SyncRenderThread(); qglDisable( GL_STENCIL_TEST ); } r_measureOverdraw->modified = qfalse; } // // texturemode stuff // if ( r_textureMode->modified ) { R_SyncRenderThread(); GL_TextureMode( r_textureMode->string ); r_textureMode->modified = qfalse; } // // gamma stuff // if ( r_gamma->modified ) { r_gamma->modified = qfalse; R_SyncRenderThread(); R_SetColorMappings(); } // check for errors if ( !r_ignoreGLErrors->integer ) { int err; R_SyncRenderThread(); if ( ( err = qglGetError() ) != GL_NO_ERROR ) { ri.Error( ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err ); } } // // draw buffer stuff // cmd = R_GetCommandBuffer( sizeof( *cmd ) ); if ( !cmd ) { return; } cmd->commandId = RC_DRAW_BUFFER; if ( glConfig.stereoEnabled ) { if ( stereoFrame == STEREO_LEFT ) { cmd->buffer = (int)GL_BACK_LEFT; } else if ( stereoFrame == STEREO_RIGHT ) { cmd->buffer = (int)GL_BACK_RIGHT; } else { ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); } } else { if ( stereoFrame != STEREO_CENTER ) { ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); } if ( !Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) ) { cmd->buffer = (int)GL_FRONT; } else { cmd->buffer = (int)GL_BACK; } } }
/* ============= RE_StretchRaw FIXME: not exactly backend Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. Used for cinematics. ============= */ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { int i, j; int start, end; #ifdef PANDORA vec2_t texcoords[4]; vec2_t verts[4]; glIndex_t indicies[6] = { 0, 1, 2, 0, 3, 2 }; #endif if ( !tr.registered ) { return; } R_SyncRenderThread(); // we definately want to sync every frame for the cinematics qglFinish(); start = end = 0; if ( r_speeds->integer ) { start = ri.Milliseconds(); } // make sure rows and cols are powers of 2 for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { } for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { } if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); } GL_Bind( tr.scratchImage[client] ); // if the scratchImage isn't in the format we want, specify it as a new texture if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; #ifdef PANDORA qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); #else qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); #endif qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); } else { if (dirty) { // otherwise, just subimage upload it so that drivers can tell we are going to be changing // it and don't try and do a texture compression qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); } } if ( r_speeds->integer ) { end = ri.Milliseconds(); ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); } RB_SetGL2D(); #ifdef PANDORA glColor4f( tr.identityLight, tr.identityLight, tr.identityLight, 1.0f ); verts[0][0] = x; verts[0][1] = y; verts[1][0] = x+w; verts[1][1] = y; verts[2][0] = x+w; verts[2][1] = y+h; verts[3][0] = x; verts[3][1] = y+h; texcoords[0][0] = 0.5f/cols; texcoords[0][1] = 0.5f/rows; texcoords[1][0] = (cols-0.5f)/cols; texcoords[1][1] = 0.5f/rows; texcoords[2][0] = (cols-0.5f)/cols; texcoords[2][1] = (rows-0.5f)/rows; texcoords[3][0] = 0.5f/cols; texcoords[3][1] = (rows-0.5f)/rows; qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); qglTexCoordPointer( 2, GL_FLOAT, 0, texcoords ); qglVertexPointer ( 2, GL_FLOAT, 0, verts ); qglDrawElements( GL_TRIANGLE_STRIP, 6, GL_INDEX_TYPE, indicies ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); #else qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); 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 (); #endif }
/* ==================== RE_RegisterModel Loads in a model for the given name Zero will be returned if the model fails to load. An entry will be retained for failed models as an optimization to prevent disk rescanning if they are asked for again. ==================== */ qhandle_t RE_RegisterModel( const char *name ) { model_t *mod; unsigned *buffer; int bufferLen = 0; int lod; int ident; qboolean loaded; qhandle_t hModel; int numLoaded; if ( !name || !name[ 0 ] ) { ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { Com_Printf( "Model name exceeds MAX_QPATH\n" ); return 0; } // search the currently loaded models for ( hModel = 1; hModel < tr.numModels; hModel++ ) { mod = tr.models[ hModel ]; if ( !strcmp( mod->name, name ) ) { if ( mod->type == MOD_BAD ) { return 0; } return hModel; } } // allocate a new model_t if ( ( mod = R_AllocModel() ) == NULL ) { ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name ); return 0; } // only set the name after the model has been successfully loaded Q_strncpyz( mod->name, name, sizeof( mod->name ) ); // make sure the render thread is stopped R_SyncRenderThread(); mod->numLods = 0; // load the files numLoaded = 0; #if defined( COMPAT_ET ) if ( strstr( name, ".mds" ) || strstr( name, ".mdm" ) || strstr( name, ".mdx" ) || strstr( name, ".md5mesh" ) || strstr( name, ".psk" ) ) #else if ( strstr( name, ".md5mesh" ) || strstr( name, ".psk" ) ) #endif { // try loading skeletal file loaded = qfalse; bufferLen = ri.FS_ReadFile( name, ( void ** ) &buffer ); if ( buffer ) { loadmodel = mod; ident = LittleLong( * ( unsigned * ) buffer ); #if defined( COMPAT_ET ) #if 0 if ( ident == MDS_IDENT ) { loaded = R_LoadMDS( mod, buffer, name ); } else #endif if ( ident == MDM_IDENT ) { loaded = R_LoadMDM( mod, buffer, name ); } else if ( ident == MDX_IDENT ) { loaded = R_LoadMDX( mod, buffer, name ); } #endif #if defined( USE_REFENTITY_ANIMATIONSYSTEM ) if ( !Q_stricmpn( ( const char * ) buffer, "MD5Version", 10 ) ) { loaded = R_LoadMD5( mod, buffer, bufferLen, name ); } else if ( !Q_stricmpn( ( const char * ) buffer, PSK_IDENTSTRING, PSK_IDENTLEN ) ) { loaded = R_LoadPSK( mod, buffer, bufferLen, name ); } #endif ri.FS_FreeFile( buffer ); } if ( loaded ) { // make sure the VBO glState entries are save R_BindNullVBO(); R_BindNullIBO(); return mod->index; } } for ( lod = MD3_MAX_LODS - 1; lod >= 0; lod-- ) { char filename[ 1024 ]; strcpy( filename, name ); if ( lod != 0 ) { char namebuf[ 80 ]; if ( strrchr( filename, '.' ) ) { *strrchr( filename, '.' ) = 0; } sprintf( namebuf, "_%d.md3", lod ); strcat( filename, namebuf ); } filename[ strlen( filename ) - 1 ] = '3'; // try MD3 first ri.FS_ReadFile( filename, ( void ** ) &buffer ); if ( !buffer ) { filename[ strlen( filename ) - 1 ] = 'c'; // try MDC second ri.FS_ReadFile( filename, ( void ** ) &buffer ); if ( !buffer ) { continue; } } loadmodel = mod; ident = LittleLong( * ( unsigned * ) buffer ); if ( ident == MD3_IDENT ) { loaded = R_LoadMD3( mod, lod, buffer, bufferLen, name ); ri.FS_FreeFile( buffer ); } #if defined( COMPAT_ET ) else if ( ident == MDC_IDENT ) { loaded = R_LoadMDC( mod, lod, buffer, bufferLen, name ); ri.FS_FreeFile( buffer ); } #endif else { ri.FS_FreeFile( buffer ); ri.Printf( PRINT_WARNING, "RE_RegisterModel: unknown fileid for %s\n", name ); goto fail; } if ( !loaded ) { if ( lod == 0 ) { goto fail; } else { break; } } else { // make sure the VBO glState entries are save R_BindNullVBO(); R_BindNullIBO(); mod->numLods++; numLoaded++; // if we have a valid model and are biased // so that we won't see any higher detail ones, // stop loading them // if ( lod <= r_lodbias->integer ) { // break; // } } } // make sure the VBO glState entries are save R_BindNullVBO(); R_BindNullIBO(); if ( numLoaded ) { // duplicate into higher lod spots that weren't // loaded, in case the user changes r_lodbias on the fly for ( lod--; lod >= 0; lod-- ) { mod->numLods++; mod->mdv[ lod ] = mod->mdv[ lod + 1 ]; } return mod->index; } #ifndef NDEBUG else { ri.Printf( PRINT_WARNING, "couldn't load '%s'\n", name ); } #endif fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; // make sure the VBO glState entries are save R_BindNullVBO(); R_BindNullIBO(); return 0; }
/* ==================== RE_RegisterModel Loads in a model for the given name Zero will be returned if the model fails to load. An entry will be retained for failed models as an optimization to prevent disk rescanning if they are asked for again. ==================== */ qhandle_t RE_RegisterModel( const char *name ) { model_t *mod; unsigned *buf; int lod; int ident; qboolean loaded = qfalse; qhandle_t hModel; int numLoaded; char *fext, defex[] = "md3", filename[MAX_QPATH], namebuf[MAX_QPATH+20]; if ( !name || !name[0] ) { ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { Com_Printf( "Model name exceeds MAX_QPATH\n" ); return 0; } // // search the currently loaded models // for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { mod = tr.models[hModel]; if ( !strcmp( mod->name, name ) ) { if( mod->type == MOD_BAD ) { return 0; } return hModel; } } // allocate a new model_t if ( ( mod = R_AllocModel() ) == NULL ) { ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); return 0; } // only set the name after the model has been successfully loaded Q_strncpyz( mod->name, name, sizeof( mod->name ) ); // make sure the render thread is stopped R_SyncRenderThread(); mod->numLods = 0; // // load the files // numLoaded = 0; strcpy(filename, name); fext = strchr(filename, '.'); if(!fext) fext = defex; else { *fext = '\0'; fext++; } #ifdef RAVENMD4 if(!Q_stricmp(fext, "mdr")) { int filesize; filesize = ri.FS_ReadFile(name, (void **) &buf); if(!buf) { ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name); mod->type = MOD_BAD; return 0; } ident = LittleLong(*(unsigned *)buf); if(ident == MDR_IDENT) loaded = R_LoadMDR(mod, buf, filesize, name); ri.FS_FreeFile (buf); if(!loaded) { ri.Printf(PRINT_WARNING,"RE_RegisterModel: couldn't load mdr file %s\n", name); mod->type = MOD_BAD; return 0; } return mod->index; } #endif fext = defex; for ( lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod-- ) { if ( lod ) Com_sprintf(namebuf, sizeof(namebuf), "%s_%d.%s", filename, lod, fext); else Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext); ri.FS_ReadFile( namebuf, (void **)&buf ); if ( !buf ) { continue; } loadmodel = mod; ident = LittleLong(*(unsigned *)buf); if ( ident == MD4_IDENT ) { loaded = R_LoadMD4( mod, buf, name ); } else { if ( ident != MD3_IDENT ) { ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", name); goto fail; } loaded = R_LoadMD3( mod, lod, buf, name ); } ri.FS_FreeFile (buf); if ( !loaded ) { if ( lod == 0 ) { goto fail; } else { break; } } else { mod->numLods++; numLoaded++; // if we have a valid model and are biased // so that we won't see any higher detail ones, // stop loading them // if ( lod <= r_lodbias->integer ) { // break; // } } } if ( numLoaded ) { // duplicate into higher lod spots that weren't // loaded, in case the user changes r_lodbias on the fly for ( lod-- ; lod >= 0 ; lod-- ) { mod->numLods++; mod->md3[lod] = mod->md3[lod+1]; } return mod->index; } #ifdef _DEBUG else { ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name); } #endif fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; return 0; }
/* ============= RE_EndRegistration Touch all images to make sure they are resident ============= */ void RE_EndRegistration(void) { R_SyncRenderThread(); if (!ri.Sys_LowPhysicalMemory()) { RB_ShowImages(); } }
/* =============== RE_Shutdown =============== */ void RE_Shutdown( qboolean destroyWindow ) { ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); ri.Cmd_RemoveCommand( "modellist" ); ri.Cmd_RemoveCommand( "screenshotJPEG" ); ri.Cmd_RemoveCommand( "screenshot" ); ri.Cmd_RemoveCommand( "imagelist" ); ri.Cmd_RemoveCommand( "shaderlist" ); ri.Cmd_RemoveCommand( "skinlist" ); ri.Cmd_RemoveCommand( "gfxinfo" ); ri.Cmd_RemoveCommand( "modelist" ); ri.Cmd_RemoveCommand( "shaderstate" ); ri.Cmd_RemoveCommand( "taginfo" ); // Ridah ri.Cmd_RemoveCommand( "cropimages" ); // done. R_ShutdownCommandBuffers(); // Ridah, keep a backup of the current images if possible // clean out any remaining unused media from the last backup R_PurgeShaders( 9999999 ); R_PurgeBackupImages( 9999999 ); R_PurgeModels( 9999999 ); if ( r_cache->integer ) { if ( tr.registered ) { if ( destroyWindow ) { R_SyncRenderThread(); R_ShutdownCommandBuffers(); R_DeleteTextures(); } else { // backup the current media R_ShutdownCommandBuffers(); R_BackupModels(); R_BackupShaders(); R_BackupImages(); } } } else if ( tr.registered ) { R_SyncRenderThread(); R_ShutdownCommandBuffers(); R_DeleteTextures(); } R_DoneFreeType(); // shut down platform specific OpenGL stuff if ( destroyWindow ) { GLimp_Shutdown(); // Ridah, release the virtual memory R_Hunk_End(); R_FreeImageBuffer(); //ri.Tag_Free(); // wipe all render alloc'd zone memory } tr.registered = qfalse; }
/* ============ R_InitFBOs ============ */ void R_InitFBOs() { int i; int width, height; ri.Printf( PRINT_DEVELOPER, "------- R_InitFBOs -------\n" ); if ( !glConfig2.framebufferObjectAvailable ) { return; } tr.numFBOs = 0; GL_CheckErrors(); // make sure the render thread is stopped R_SyncRenderThread(); if ( glConfig2.textureNPOTAvailable ) { width = glConfig.vidWidth; height = glConfig.vidHeight; } else { width = NearestPowerOfTwo( glConfig.vidWidth ); height = NearestPowerOfTwo( glConfig.vidHeight ); } tr.occlusionRenderFBO = R_CreateFBO( "_occlusionRender", width, height ); R_BindFBO( tr.occlusionRenderFBO ); if ( glConfig.hardwareType == GLHW_ATI_DX10 ) { R_CreateFBODepthBuffer( tr.occlusionRenderFBO, GL_DEPTH_COMPONENT16 ); } else if ( glConfig.hardwareType == GLHW_NV_DX10 ) { R_CreateFBODepthBuffer( tr.occlusionRenderFBO, GL_DEPTH_COMPONENT24 ); } else if ( glConfig2.framebufferPackedDepthStencilAvailable ) { R_CreateFBOPackedDepthStencilBuffer( tr.occlusionRenderFBO, GL_DEPTH24_STENCIL8_EXT ); } else { R_CreateFBODepthBuffer( tr.occlusionRenderFBO, GL_DEPTH_COMPONENT24 ); } R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.occlusionRenderFBOImage->texnum, 0 ); R_CheckFBO( tr.occlusionRenderFBO ); if ( r_shadows->integer >= SHADOWING_ESM16 && glConfig2.textureFloatAvailable ) { // shadowMap FBOs for shadow mapping offscreen rendering for ( i = 0; i < MAX_SHADOWMAPS; i++ ) { width = height = shadowMapResolutions[ i ]; tr.shadowMapFBO[ i ] = R_CreateFBO( va( "_shadowMap%d", i ), width, height ); R_BindFBO( tr.shadowMapFBO[ i ] ); R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.shadowMapFBOImage[ i ]->texnum, 0 ); R_CreateFBODepthBuffer( tr.shadowMapFBO[ i ], GL_DEPTH_COMPONENT24 ); R_CheckFBO( tr.shadowMapFBO[ i ] ); } // sun requires different resolutions for ( i = 0; i < MAX_SHADOWMAPS; i++ ) { width = height = sunShadowMapResolutions[ i ]; tr.sunShadowMapFBO[ i ] = R_CreateFBO( va( "_sunShadowMap%d", i ), width, height ); R_BindFBO( tr.sunShadowMapFBO[ i ] ); R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.sunShadowMapFBOImage[ i ]->texnum, 0 ); R_CreateFBODepthBuffer( tr.sunShadowMapFBO[ i ], GL_DEPTH_COMPONENT24 ); if ( r_shadows->integer == SHADOWING_EVSM32 && r_evsmPostProcess->integer ) { R_AttachFBOTextureDepth( tr.sunShadowMapFBOImage[ i ]->texnum ); /* Since we don't have a color attachment, the framebuffer will be considered incomplete. Consequently, we must inform the driver that we do not wish to render to the color buffer. We do this with a call to set the draw-buffer and read-buffer to GL_NONE: */ glDrawBuffer( GL_NONE ); glReadBuffer( GL_NONE ); } R_CheckFBO( tr.sunShadowMapFBO[ i ] ); } } { if ( glConfig2.textureNPOTAvailable ) { width = glConfig.vidWidth; height = glConfig.vidHeight; } else { width = NearestPowerOfTwo( glConfig.vidWidth ); height = NearestPowerOfTwo( glConfig.vidHeight ); } // portalRender FBO for portals, mirrors, water, cameras et cetera tr.portalRenderFBO = R_CreateFBO( "_portalRender", width, height ); R_BindFBO( tr.portalRenderFBO ); R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.portalRenderImage->texnum, 0 ); R_CheckFBO( tr.portalRenderFBO ); } { if ( glConfig2.textureNPOTAvailable ) { width = glConfig.vidWidth * 0.25f; height = glConfig.vidHeight * 0.25f; } else { width = NearestPowerOfTwo( glConfig.vidWidth * 0.25f ); height = NearestPowerOfTwo( glConfig.vidHeight * 0.25f ); } tr.downScaleFBO_quarter = R_CreateFBO( "_downScale_quarter", width, height ); R_BindFBO( tr.downScaleFBO_quarter ); R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.downScaleFBOImage_quarter->texnum, 0 ); R_CheckFBO( tr.downScaleFBO_quarter ); tr.downScaleFBO_64x64 = R_CreateFBO( "_downScale_64x64", 64, 64 ); R_BindFBO( tr.downScaleFBO_64x64 ); R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.downScaleFBOImage_64x64->texnum, 0 ); R_CheckFBO( tr.downScaleFBO_64x64 ); if ( glConfig2.textureNPOTAvailable ) { width = glConfig.vidWidth * 0.25f; height = glConfig.vidHeight * 0.25f; } else { width = NearestPowerOfTwo( glConfig.vidWidth * 0.25f ); height = NearestPowerOfTwo( glConfig.vidHeight * 0.25f ); } tr.contrastRenderFBO = R_CreateFBO( "_contrastRender", width, height ); R_BindFBO( tr.contrastRenderFBO ); R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.contrastRenderFBOImage->texnum, 0 ); R_CheckFBO( tr.contrastRenderFBO ); for ( i = 0; i < 2; i++ ) { tr.bloomRenderFBO[ i ] = R_CreateFBO( va( "_bloomRender%d", i ), width, height ); R_BindFBO( tr.bloomRenderFBO[ i ] ); R_AttachFBOTexture2D( GL_TEXTURE_2D, tr.bloomRenderFBOImage[ i ]->texnum, 0 ); R_CheckFBO( tr.bloomRenderFBO[ i ] ); } } GL_CheckErrors(); R_BindNullFBO(); }
/* =============== RE_Shutdown =============== */ void RE_Shutdown( qboolean destroyWindow ) { // Com_Printf ("RE_Shutdown( %i )\n", destroyWindow ); Cmd_RemoveCommand ("imagelist"); Cmd_RemoveCommand ("shaderlist"); Cmd_RemoveCommand ("skinlist"); Cmd_RemoveCommand ("screenshot"); Cmd_RemoveCommand ("screenshot_tga"); Cmd_RemoveCommand ("gfxinfo"); Cmd_RemoveCommand ("r_we"); Cmd_RemoveCommand ("imagecacheinfo"); Cmd_RemoveCommand ("modellist"); Cmd_RemoveCommand ("modelist"); Cmd_RemoveCommand ("modelcacheinfo"); #ifndef DEDICATED #ifndef _XBOX // GLOWXXX if ( r_DynamicGlow && r_DynamicGlow->integer ) { // Release the Glow Vertex Shader. if ( tr.glowVShader ) { qglDeleteProgramsARB( 1, &tr.glowVShader ); } // Release Pixel Shader. if ( tr.glowPShader ) { if ( qglCombinerParameteriNV ) { // Release the Glow Regcom call list. qglDeleteLists( tr.glowPShader, 1 ); } else if ( qglGenProgramsARB ) { // Release the Glow Fragment Shader. qglDeleteProgramsARB( 1, &tr.glowPShader ); } } // Release the scene glow texture. qglDeleteTextures( 1, &tr.screenGlow ); // Release the scene texture. qglDeleteTextures( 1, &tr.sceneImage ); // Release the blur texture. qglDeleteTextures( 1, &tr.blurImage ); } #endif /* R_TerrainShutdown(); //rwwRMG - added */ R_ShutdownFonts(); if ( tr.registered ) { R_SyncRenderThread(); R_ShutdownCommandBuffers(); //#ifndef _XBOX if (destroyWindow) //#endif { R_DeleteTextures(); // only do this for vid_restart now, not during things like map load } } // shut down platform specific OpenGL stuff if ( destroyWindow ) { GLimp_Shutdown(); } #endif //!DEDICATED tr.registered = qfalse; }
/* =============== RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin( const char *name ) { qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; skinModel_t *model; //----(SA) added char *text, *text_p; char *token; char surfName[ MAX_QPATH ]; if ( !name || !name[ 0 ] ) { Com_Printf( "Empty name passed to RE_RegisterSkin\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { Com_Printf( "Skin name exceeds MAX_QPATH\n" ); return 0; } // see if the skin is already loaded for ( hSkin = 1; hSkin < tr.numSkins; hSkin++ ) { skin = tr.skins[ hSkin ]; if ( !Q_stricmp( skin->name, name ) ) { if ( skin->numSurfaces == 0 ) { return 0; // default skin } return hSkin; } } // allocate a new skin if ( tr.numSkins == MAX_SKINS ) { ri.Printf( PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name ); return 0; } //----(SA) moved things around slightly to fix the problem where you restart // a map that has ai characters who had invalid skin names entered // in thier "skin" or "head" field // make sure the render thread is stopped R_SyncRenderThread(); #if 0 // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; skin->surfaces[ 0 ] = ri.Hunk_Alloc( sizeof( skin->surfaces[ 0 ] ), h_low ); skin->surfaces[ 0 ]->shader = R_FindShader( name, SHADER_3D_DYNAMIC, qtrue ); return hSkin; } #endif // load and parse the skin file ri.FS_ReadFile( name, ( void ** ) &text ); if ( !text ) { return 0; } tr.numSkins++; skin = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); tr.skins[ hSkin ] = skin; Q_strncpyz( skin->name, name, sizeof( skin->name ) ); skin->numSurfaces = 0; skin->numModels = 0; //----(SA) added //----(SA) end text_p = text; while ( text_p && *text_p ) { // get surface name token = CommaParse( &text_p ); Q_strncpyz( surfName, token, sizeof( surfName ) ); if ( !token[ 0 ] ) { break; } // lowercase the surface name so skin compares are faster Q_strlwr( surfName ); if ( *text_p == ',' ) { text_p++; } if ( !Q_stricmpn( token, "tag_", 4 ) ) { continue; } if ( !Q_stricmpn( token, "md3_", 4 ) ) { // this is specifying a model model = skin->models[ skin->numModels ] = ri.Hunk_Alloc( sizeof( *skin->models[ 0 ] ), h_low ); Q_strncpyz( model->type, token, sizeof( model->type ) ); model->hash = Com_HashKey( model->type, sizeof( model->type ) ); // get the model name token = CommaParse( &text_p ); Q_strncpyz( model->model, token, sizeof( model->model ) ); skin->numModels++; continue; } // parse the shader name token = CommaParse( &text_p ); surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[ 0 ] ), h_low ); Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); // RB: bspSurface_t does not have ::hash yet // surf->hash = Com_HashKey(surf->name, sizeof(surf->name)); surf->shader = R_FindShader( token, SHADER_3D_DYNAMIC, qtrue ); skin->numSurfaces++; } ri.FS_FreeFile( text ); // never let a skin have 0 shaders if ( skin->numSurfaces == 0 ) { return 0; // use default skin } return hSkin; }
// Loads in a model for the given name // Zero will be returned if the model fails to load. An entry will be // retained for failed models as an optimization to prevent disk rescanning // if they are asked for again. qhandle_t R_RegisterModel( const char* name, idSkinTranslation* skinTranslation ) { if ( !name || !name[ 0 ] ) { common->Printf( "R_RegisterModel: NULL name\n" ); return 0; } if ( String::Length( name ) >= MAX_QPATH ) { common->Printf( "Model name exceeds MAX_QPATH\n" ); return 0; } // Ridah, caching if ( r_cacheGathering->integer ) { Cbuf_ExecuteText( EXEC_NOW, va( "cache_usedfile model %s\n", name ) ); } // // search the currently loaded models // for ( int hModel = 1; hModel < tr.numModels; hModel++ ) { idRenderModel* mod = tr.models[ hModel ]; if ( !String::ICmp( mod->name, name ) ) { if ( mod->type == MOD_BAD ) { return 0; } return hModel; } } // make sure the render thread is stopped R_SyncRenderThread(); idList<byte> buf; int modfilelen = FS_ReadFile( name, buf ); if ( modfilelen < 0 && ( GGameType & ( GAME_WolfSP | GAME_WolfMP | GAME_ET ) ) ) { char filename[ 1024 ]; String::Cpy( filename, name ); // try MDC first filename[ String::Length( filename ) - 1 ] = 'c'; modfilelen = FS_ReadFile( filename, buf ); if ( modfilelen < 0 ) { // try MD3 second filename[ String::Length( filename ) - 1 ] = '3'; modfilelen = FS_ReadFile( filename, buf ); } } if ( modfilelen < 0 ) { common->Printf( S_COLOR_YELLOW "R_RegisterModel: couldn't load %s\n", name ); // we still keep the idRenderModel around, so if the model name is asked for // again, we won't bother scanning the filesystem idRenderModel* mod = new idRenderModelBad(); R_AddModel( mod ); mod->type = MOD_BAD; String::NCpyZ( mod->name, name, sizeof ( mod->name ) ); return 0; } // allocate a new idRenderModel idRenderModel* mod; switch ( LittleLong( *( qint32* )buf.Ptr() ) ) { case BSP29_VERSION: mod = new idRenderModelBSP29NonMap(); break; case IDSPRITE1HEADER: mod = new idRenderModelSPR(); break; case IDSPRITE2HEADER: mod = new idRenderModelSP2(); break; case IDPOLYHEADER: mod = new idRenderModelMDL(); break; case RAPOLYHEADER: mod = new idRenderModelMDLNew(); break; case IDMESH2HEADER: mod = new idRenderModelMD2(); break; case MD3_IDENT: mod = new idRenderModelMD3(); break; case MD4_IDENT: mod = new idRenderModelMD4(); break; case MDC_IDENT: mod = new idRenderModelMDC(); break; case MDS_IDENT: mod = new idRenderModelMDS(); break; case MDM_IDENT: mod = new idRenderModelMDM(); break; case MDX_IDENT: mod = new idRenderModelMDX(); break; default: common->Printf( S_COLOR_YELLOW "R_RegisterModel: unknown fileid for %s\n", name ); mod = new idRenderModelBad; break; } R_AddModel( mod ); // only set the name after the model has been successfully loaded String::NCpyZ( mod->name, name, sizeof ( mod->name ) ); loadmodel = mod; // call the apropriate loader bool loaded = mod->Load( buf, skinTranslation ); R_LoadModelShadow( mod ); // GR - by default models are not tessellated mod->q3_ATI_tess = false; // GR - check if can be tessellated... // make sure to tessellate model heads if ( GGameType & GAME_WolfSP && strstr( name, "head" ) ) { mod->q3_ATI_tess = true; } if ( !loaded ) { common->Printf( S_COLOR_YELLOW "R_RegisterModel: couldn't load %s\n", name ); // we still keep the idRenderModel around, so if the model name is asked for // again, we won't bother scanning the filesystem delete mod; mod = new idRenderModelBad(); R_AddModel( mod ); mod->type = MOD_BAD; String::NCpyZ( mod->name, name, sizeof ( mod->name ) ); return 0; } return mod->index; }
/* ==================== RE_RegisterModel Loads in a model for the given name Zero will be returned if the model fails to load. An entry will be retained for failed models as an optimization to prevent disk rescanning if they are asked for again. ==================== */ qhandle_t RE_RegisterModel( const char *name ) { model_t *mod; qhandle_t hModel; qboolean orgNameFailed = qfalse; int orgLoader = -1; int i; char localName[ MAX_QPATH ]; const char *ext; char altName[ MAX_QPATH ]; if ( !name || !name[0] ) { ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { Com_Printf( "Model name exceeds MAX_QPATH\n" ); return 0; } // // search the currently loaded models // for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { mod = tr.models[hModel]; if ( !strcmp( mod->name, name ) ) { if( mod->type == MOD_BAD ) { return 0; } return hModel; } } // allocate a new model_t if ( ( mod = R_AllocModel() ) == NULL ) { ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); return 0; } // only set the name after the model has been successfully loaded Q_strncpyz( mod->name, name, sizeof( mod->name ) ); // make sure the render thread is stopped R_SyncRenderThread(); mod->type = MOD_BAD; mod->numLods = 0; // // load the files // Q_strncpyz( localName, name, MAX_QPATH ); ext = COM_GetExtension( localName ); if( *ext ) { // Look for the correct loader and use it for( i = 0; i < numModelLoaders; i++ ) { if( !Q_stricmp( ext, modelLoaders[ i ].ext ) ) { // Load hModel = modelLoaders[ i ].ModelLoader( localName, mod ); break; } } // A loader was found if( i < numModelLoaders ) { if( !hModel ) { // Loader failed, most likely because the file isn't there; // try again without the extension orgNameFailed = qtrue; orgLoader = i; COM_StripExtension( name, localName, MAX_QPATH ); } else { // Something loaded return mod->index; } } } // Try and find a suitable match using all // the model formats supported for( i = 0; i < numModelLoaders; i++ ) { if (i == orgLoader) continue; Com_sprintf( altName, sizeof (altName), "%s.%s", localName, modelLoaders[ i ].ext ); // Load hModel = modelLoaders[ i ].ModelLoader( altName, mod ); if( hModel ) { if( orgNameFailed ) { ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", name, altName ); } break; } } return hModel; }
/* ==================== RE_BeginFrame If running in stereo, RE_BeginFrame will be called twice for each RE_EndFrame ==================== */ void RE_BeginFrame(stereoFrame_t stereoFrame) { drawBufferCommand_t *cmd; if(!tr.registered) { return; } glState.finishCalled = false; tr.frameCount++; tr.frameSceneNum = 0; // // do overdraw measurement // if(r_measureOverdraw->integer) { if(glConfig.stencilBits < 4) { ri.Printf(PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits); ri.Cvar_Set("r_measureOverdraw", "0"); r_measureOverdraw->modified = false; } else if(r_shadows->integer == 2) { ri.Printf(PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n"); ri.Cvar_Set("r_measureOverdraw", "0"); r_measureOverdraw->modified = false; } else { R_SyncRenderThread(); glEnable(GL_STENCIL_TEST); glStencilMask(~0U); glClearStencil(0U); glStencilFunc(GL_ALWAYS, 0U, ~0U); glStencilOp(GL_KEEP, GL_INCR, GL_INCR); } r_measureOverdraw->modified = false; } else { // this is only reached if it was on and is now off if(r_measureOverdraw->modified) { R_SyncRenderThread(); glDisable(GL_STENCIL_TEST); } r_measureOverdraw->modified = false; } // // texturemode stuff // if(r_textureMode->modified) { R_SyncRenderThread(); GL_TextureMode(r_textureMode->string); r_textureMode->modified = false; } // // anisotropic filtering stuff // if(r_textureAnisotropy->modified) { R_SyncRenderThread(); GL_TextureAnisotropy(r_textureAnisotropy->value); r_textureAnisotropy->modified = false; } // // NVidia stuff // // fog control if(glConfig.NVFogAvailable && r_nv_fogdist_mode->modified) { r_nv_fogdist_mode->modified = false; if(!Q_stricmp(r_nv_fogdist_mode->string, "GL_EYE_PLANE_ABSOLUTE_NV")) { glConfig.NVFogMode = (int)GL_EYE_PLANE_ABSOLUTE_NV; } else if(!Q_stricmp(r_nv_fogdist_mode->string, "GL_EYE_PLANE")) { glConfig.NVFogMode = (int)GL_EYE_PLANE; } else if(!Q_stricmp(r_nv_fogdist_mode->string, "GL_EYE_RADIAL_NV")) { glConfig.NVFogMode = (int)GL_EYE_RADIAL_NV; } else { // in case this was really 'else', store a valid value for next time glConfig.NVFogMode = (int)GL_EYE_RADIAL_NV; ri.Cvar_Set("r_nv_fogdist_mode", "GL_EYE_RADIAL_NV"); } } // // gamma stuff // if(r_gamma->modified) { r_gamma->modified = false; if( !glConfig.deviceSupportsGamma ) { Com_Printf( "r_gamma will be changed upon restarting.\n" ); } else { R_SyncRenderThread(); R_SetColorMappings(); } } // check for errors if(!r_ignoreGLErrors->integer) { int err; R_SyncRenderThread(); if((err = glGetError()) != GL_NO_ERROR) { ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err); } } // // draw buffer stuff // cmd = (drawBufferCommand_t*)R_GetCommandBuffer(sizeof(*cmd)); if(!cmd) { return; } cmd->commandId = RC_DRAW_BUFFER; if(glConfig.stereoEnabled) { if(stereoFrame == STEREO_LEFT) { cmd->buffer = (int)GL_BACK_LEFT; } else if(stereoFrame == STEREO_RIGHT) { cmd->buffer = (int)GL_BACK_RIGHT; } else { ri.Error(ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame); } } else { if(stereoFrame != STEREO_CENTER) { ri.Error(ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame); } if(!Q_stricmp(r_drawBuffer->string, "GL_FRONT")) { cmd->buffer = (int)GL_FRONT; } else { cmd->buffer = (int)GL_BACK; } } }
/* ============ 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; break; case VBO_USAGE_DYNAMIC: glUsage = GL_DYNAMIC_DRAW; break; default: glUsage = 0; Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); } if (!numTriangles) { return NULL; } if (strlen(name) >= MAX_QPATH) { ri.Error(ERR_DROP, "R_CreateIBO2: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_SyncRenderThread(); ibo = (IBO_t *)ri.Hunk_Alloc(sizeof(*ibo), h_low); Com_AddToGrowList(&tr.ibos, ibo); Q_strncpyz(ibo->name, name, sizeof(ibo->name)); indexesSize = numTriangles * 3 * sizeof(glIndex_t); indexes = (byte *)ri.Hunk_AllocateTempMemory(indexesSize); indexesOfs = 0; //ri.Printf(PRINT_ALL, "sizeof(glIndex_t) = %i\n", sizeof(glIndex_t)); 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; ibo->indexesNum = numTriangles * 3; glGenBuffers(1, &ibo->indexesVBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->indexesVBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexesSize, indexes, glUsage); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL_CheckErrors(); ri.Hunk_FreeTempMemory(indexes); return ibo; }
/* ============= RE_StretchRaw FIXME: not exactly backend Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. Used for cinematics. ============= */ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { int i, j; int start, end; if ( !tr.registered ) { return; } R_SyncRenderThread(); // we definately want to sync every frame for the cinematics qglFinish(); start = 0; if ( r_speeds->integer ) { start = ri.Milliseconds(); } // make sure rows and cols are powers of 2 for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { } for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { } if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); } GL_Bind( tr.scratchImage[client] ); // if the scratchImage isn't in the format we want, specify it as a new texture if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); } else { if (dirty) { // otherwise, just subimage upload it so that drivers can tell we are going to be changing // it and don't try and do a texture compression qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); } } if ( r_speeds->integer ) { end = ri.Milliseconds(); ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); } RB_SetGL2D(); qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); 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 (); }
/* ============ 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; break; case VBO_USAGE_DYNAMIC: glUsage = GL_DYNAMIC_DRAW; break; default: glUsage = 0; //Prevents warning Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); } if (strlen(name) >= MAX_QPATH) { ri.Error(ERR_DROP, "R_CreateVBO: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = (VBO_t *)ri.Hunk_Alloc(sizeof(*vbo), h_low); Com_AddToGrowList(&tr.vbos, vbo); Q_strncpyz(vbo->name, name, sizeof(vbo->name)); vbo->ofsXYZ = 0; vbo->ofsTexCoords = 0; vbo->ofsLightCoords = 0; vbo->ofsBinormals = 0; vbo->ofsTangents = 0; vbo->ofsNormals = 0; vbo->ofsColors = 0; vbo->ofsPaintColors = 0; vbo->ofsLightDirections = 0; vbo->ofsBoneIndexes = 0; vbo->ofsBoneWeights = 0; vbo->sizeXYZ = 0; vbo->sizeTangents = 0; vbo->sizeBinormals = 0; vbo->sizeNormals = 0; vbo->vertexesSize = vertexesSize; glGenBuffers(1, &vbo->vertexesVBO); glBindBuffer(GL_ARRAY_BUFFER, vbo->vertexesVBO); glBufferData(GL_ARRAY_BUFFER, vertexesSize, vertexes, glUsage); glBindBuffer(GL_ARRAY_BUFFER, 0); GL_CheckErrors(); return vbo; }
/* ============ R_CreateVBO2 RB: OPTIMIZE rewrite to not use memcpy ============ */ VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * verts, unsigned int stateBits, vboUsage_t usage) { VBO_t *vbo; int i, j; byte *data; int dataSize; int dataOfs; vec4_t tmp; 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); } if(!numVertexes) return NULL; if(strlen(name) >= MAX_QPATH) { ri.Error(ERR_DROP, "R_CreateVBO2: \"%s\" is too long\n", name); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = ri.Hunk_Alloc(sizeof(*vbo), h_low); Com_AddToGrowList(&tr.vbos, vbo); Q_strncpyz(vbo->name, name, sizeof(vbo->name)); vbo->ofsXYZ = 0; vbo->ofsTexCoords = 0; vbo->ofsLightCoords = 0; vbo->ofsBinormals = 0; vbo->ofsTangents = 0; vbo->ofsNormals = 0; vbo->ofsColors = 0; vbo->ofsPaintColors = 0; vbo->ofsLightDirections = 0; vbo->ofsBoneIndexes = 0; vbo->ofsBoneWeights = 0; vbo->sizeXYZ = 0; vbo->sizeTangents = 0; vbo->sizeBinormals = 0; vbo->sizeNormals = 0; // create VBO dataSize = numVertexes * (sizeof(vec4_t) * 9); data = ri.Hunk_AllocateTempMemory(dataSize); dataOfs = 0; // set up xyz array for(i = 0; i < numVertexes; i++) { for(j = 0; j < 3; j++) { tmp[j] = verts[i].xyz[j]; } tmp[3] = 1; memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed vertex texcoords if(stateBits & ATTR_TEXCOORD) { vbo->ofsTexCoords = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 2; j++) { tmp[j] = verts[i].st[j]; } tmp[2] = 0; tmp[3] = 1; memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } // feed vertex lightmap texcoords if(stateBits & ATTR_LIGHTCOORD) { vbo->ofsLightCoords = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 2; j++) { tmp[j] = verts[i].lightmap[j]; } tmp[2] = 0; tmp[3] = 1; memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } // feed vertex tangents if(stateBits & ATTR_TANGENT) { vbo->ofsTangents = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 3; j++) { tmp[j] = verts[i].tangent[j]; } tmp[3] = 1; memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } // feed vertex binormals if(stateBits & ATTR_BINORMAL) { vbo->ofsBinormals = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 3; j++) { tmp[j] = verts[i].binormal[j]; } tmp[3] = 1; memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } // feed vertex normals if(stateBits & ATTR_NORMAL) { vbo->ofsNormals = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 3; j++) { tmp[j] = verts[i].normal[j]; } tmp[3] = 1; memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } // feed vertex colors if(stateBits & ATTR_COLOR) { vbo->ofsColors = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 4; j++) { tmp[j] = verts[i].lightColor[j]; } memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } #if !defined(COMPAT_Q3A) && !defined(COMPAT_ET) // feed vertex paint colors if(stateBits & ATTR_PAINTCOLOR) { vbo->ofsPaintColors = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 4; j++) { tmp[j] = verts[i].paintColor[j]; } memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } // feed vertex light directions if(stateBits & ATTR_LIGHTDIRECTION) { vbo->ofsLightDirections = dataOfs; for(i = 0; i < numVertexes; i++) { for(j = 0; j < 3; j++) { tmp[j] = verts[i].lightDirection[j]; } tmp[3] = 1; memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } } #endif vbo->vertexesSize = dataSize; vbo->vertexesNum = numVertexes; glGenBuffersARB(1, &vbo->vertexesVBO); glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); glBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, data, glUsage); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); GL_CheckErrors(); ri.Hunk_FreeTempMemory(data); return vbo; }
/* ==================== RE_BeginFrame If running in stereo, RE_BeginFrame will be called twice for each RE_EndFrame ==================== */ void RE_BeginFrame( stereoFrame_t stereoFrame ) { drawBufferCommand_t *cmd = NULL; colorMaskCommand_t *colcmd = NULL; if ( !tr.registered ) { return; } glState.finishCalled = qfalse; tr.frameCount++; tr.frameSceneNum = 0; // // do overdraw measurement // #ifndef HAVE_GLES if ( r_measureOverdraw->integer ) { if ( glConfig.stencilBits < 4 ) { ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = qfalse; } else if ( r_shadows->integer == 2 ) { ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = qfalse; } else { R_SyncRenderThread(); qglEnable( GL_STENCIL_TEST ); qglStencilMask( ~0U ); qglClearStencil( 0U ); qglStencilFunc( GL_ALWAYS, 0U, ~0U ); qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); } r_measureOverdraw->modified = qfalse; } else { // this is only reached if it was on and is now off if ( r_measureOverdraw->modified ) { R_SyncRenderThread(); qglDisable( GL_STENCIL_TEST ); } r_measureOverdraw->modified = qfalse; } #endif // // texturemode stuff // if ( r_textureMode->modified ) { R_SyncRenderThread(); GL_TextureMode( r_textureMode->string ); r_textureMode->modified = qfalse; } // // gamma stuff // if ( r_gamma->modified ) { r_gamma->modified = qfalse; R_SyncRenderThread(); R_SetColorMappings(); } // check for errors if ( !r_ignoreGLErrors->integer ) { int err; R_SyncRenderThread(); if ((err = qglGetError()) != GL_NO_ERROR) ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!", err); } #ifndef HAVE_GLES if (glConfig.stereoEnabled) { if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) return; cmd->commandId = RC_DRAW_BUFFER; if ( stereoFrame == STEREO_LEFT ) { cmd->buffer = (int)GL_BACK_LEFT; } else if ( stereoFrame == STEREO_RIGHT ) { cmd->buffer = (int)GL_BACK_RIGHT; } else { ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); } } else { if(r_anaglyphMode->integer) { if(r_anaglyphMode->modified) { // clear both, front and backbuffer. qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); qglClearColor(0.0f, 0.0f, 0.0f, 1.0f); qglDrawBuffer(GL_FRONT); qglClear(GL_COLOR_BUFFER_BIT); qglDrawBuffer(GL_BACK); qglClear(GL_COLOR_BUFFER_BIT); r_anaglyphMode->modified = qfalse; } if(stereoFrame == STEREO_LEFT) { if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) return; if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) return; } else if(stereoFrame == STEREO_RIGHT) { clearDepthCommand_t *cldcmd; if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) ) return; cldcmd->commandId = RC_CLEARDEPTH; if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) return; } else ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer); colcmd->commandId = RC_COLORMASK; } else #endif { if(stereoFrame != STEREO_CENTER) ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) return; } if(cmd) { cmd->commandId = RC_DRAW_BUFFER; #ifndef HAVE_GLES if(r_anaglyphMode->modified) { qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); r_anaglyphMode->modified = qfalse; } if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT")) cmd->buffer = (int)GL_FRONT; else #endif cmd->buffer = (int)GL_BACK; } #ifndef HAVE_GLES } #endif tr.refdef.stereoFrame = stereoFrame; }
/* ============ 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; }
/* =============== RE_RegisterAnimation =============== */ qhandle_t RE_RegisterAnimation( const char *name ) { qhandle_t hAnim; skelAnimation_t *anim; char *buffer; int bufferLen; qboolean loaded = qfalse; if ( !name || !name[ 0 ] ) { ri.Printf( PRINT_WARNING, "Empty name passed to RE_RegisterAnimation\n" ); return 0; } //ri.Printf(PRINT_ALL, "RE_RegisterAnimation(%s)\n", name); if ( strlen( name ) >= MAX_QPATH ) { ri.Printf( PRINT_WARNING, "Animation name exceeds MAX_QPATH\n" ); return 0; } // search the currently loaded animations for ( hAnim = 1; hAnim < tr.numAnimations; hAnim++ ) { anim = tr.animations[ hAnim ]; if ( !Q_stricmp( anim->name, name ) ) { if ( anim->type == AT_BAD ) { return 0; } return hAnim; } } // allocate a new model_t if ( ( anim = R_AllocAnimation() ) == NULL ) { ri.Printf( PRINT_WARNING, "RE_RegisterAnimation: R_AllocAnimation() failed for '%s'\n", name ); return 0; } // only set the name after the animation has been successfully allocated Q_strncpyz( anim->name, name, sizeof( anim->name ) ); // make sure the render thread is stopped R_SyncRenderThread(); // load and parse the .md5anim file bufferLen = ri.FS_ReadFile( name, ( void ** ) &buffer ); if ( !buffer ) { return 0; } if ( !Q_stricmpn( ( const char * ) buffer, "MD5Version", 10 ) ) { loaded = R_LoadMD5Anim( anim, buffer, bufferLen, name ); } else { ri.Printf( PRINT_WARNING, "RE_RegisterAnimation: unknown fileid for '%s'\n", name ); } ri.FS_FreeFile( buffer ); if ( !loaded ) { ri.Printf( PRINT_WARNING, "couldn't load '%s'\n", name ); // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem anim->type = AT_BAD; } return anim->index; }
/* ============ 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); } #ifdef USE_VERT_TANGENT_SPACE 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); } #endif 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); } if(stateBits & ATTR_LIGHTDIRECTION) { vbo->ofs_lightdir = dataSize; dataSize += sizeof(verts[0].lightdir); } vbo->stride_xyz = dataSize; vbo->stride_normal = dataSize; #ifdef USE_VERT_TANGENT_SPACE vbo->stride_tangent = dataSize; vbo->stride_bitangent = dataSize; #endif vbo->stride_st = dataSize; vbo->stride_lightmap = dataSize; vbo->stride_vertexcolor = dataSize; vbo->stride_lightdir = 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); } #ifdef USE_VERT_TANGENT_SPACE // 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); } #endif // 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); } // feed vertex light directions if(stateBits & ATTR_LIGHTDIRECTION) { memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir)); dataOfs += sizeof(verts[i].lightdir); } } } 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); } #ifdef USE_VERT_TANGENT_SPACE if(stateBits & ATTR_TANGENT) { dataSize += sizeof(verts[0].tangent); } if(stateBits & ATTR_BITANGENT) { dataSize += sizeof(verts[0].bitangent); } #endif 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); } if(stateBits & ATTR_LIGHTDIRECTION) { dataSize += sizeof(verts[0].lightdir); } // create VBO dataSize *= numVertexes; data = ri.Hunk_AllocateTempMemory(dataSize); dataOfs = 0; vbo->ofs_xyz = 0; vbo->ofs_normal = 0; #ifdef USE_VERT_TANGENT_SPACE vbo->ofs_tangent = 0; vbo->ofs_bitangent = 0; #endif vbo->ofs_st = 0; vbo->ofs_lightmap = 0; vbo->ofs_vertexcolor = 0; vbo->ofs_lightdir = 0; vbo->stride_xyz = sizeof(verts[0].xyz); vbo->stride_normal = sizeof(verts[0].normal); #ifdef USE_VERT_TANGENT_SPACE vbo->stride_tangent = sizeof(verts[0].tangent); vbo->stride_bitangent = sizeof(verts[0].bitangent); #endif vbo->stride_vertexcolor = sizeof(verts[0].vertexColors); vbo->stride_st = sizeof(verts[0].st); vbo->stride_lightmap = sizeof(verts[0].lightmap); vbo->stride_lightdir = sizeof(verts[0].lightdir); //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); } } #ifdef USE_VERT_TANGENT_SPACE // 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); } } #endif // 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); } } // feed vertex lightdirs if(stateBits & ATTR_LIGHTDIRECTION) { vbo->ofs_lightdir = dataOfs; for (i = 0; i < numVertexes; i++) { memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir)); dataOfs += sizeof(verts[i].lightdir); } } } 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; }
/* ==================== RE_BeginFrame If running in stereo, RE_BeginFrame will be called twice for each RE_EndFrame ==================== */ void RE_BeginFrame(stereoFrame_t stereoFrame) { drawBufferCommand_t *cmd; if(!tr.registered) { return; } GLimp_LogComment("--- RE_BeginFrame ---\n"); #if defined(USE_D3D10) // TODO #else glState.finishCalled = qfalse; #endif tr.frameCount++; tr.frameSceneNum = 0; tr.viewCount = 0; #if defined(USE_D3D10) // draw buffer stuff cmd = (drawBufferCommand_t*)R_GetCommandBuffer(sizeof(*cmd)); if(!cmd) { return; } cmd->commandId = RC_DRAW_BUFFER; cmd->buffer = 0; #else // do overdraw measurement if(r_measureOverdraw->integer) { if(glConfig.stencilBits < 4) { ri.Printf(PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits); ri.Cvar_Set("r_measureOverdraw", "0"); r_measureOverdraw->modified = qfalse; } else { R_SyncRenderThread(); glEnable(GL_STENCIL_TEST); glStencilMask(~0U); GL_ClearStencil(0U); glStencilFunc(GL_ALWAYS, 0U, ~0U); glStencilOp(GL_KEEP, GL_INCR, GL_INCR); } r_measureOverdraw->modified = qfalse; } else { // this is only reached if it was on and is now off if(r_measureOverdraw->modified) { R_SyncRenderThread(); glDisable(GL_STENCIL_TEST); } r_measureOverdraw->modified = qfalse; } // texturemode stuff if(r_textureMode->modified) { R_SyncRenderThread(); GL_TextureMode(r_textureMode->string); r_textureMode->modified = qfalse; } // gamma stuff if(r_gamma->modified) { r_gamma->modified = qfalse; if( !glConfig.deviceSupportsGamma ) { Com_Printf( "r_gamma will be changed upon restarting.\n" ); } else { R_SyncRenderThread(); R_SetColorMappings(); } } // check for errors if(!r_ignoreGLErrors->integer) { int err; char s[128]; R_SyncRenderThread(); if((err = glGetError()) != GL_NO_ERROR) { switch (err) { case GL_INVALID_ENUM: strcpy(s, "GL_INVALID_ENUM"); break; case GL_INVALID_VALUE: strcpy(s, "GL_INVALID_VALUE"); break; case GL_INVALID_OPERATION: strcpy(s, "GL_INVALID_OPERATION"); break; case GL_STACK_OVERFLOW: strcpy(s, "GL_STACK_OVERFLOW"); break; case GL_STACK_UNDERFLOW: strcpy(s, "GL_STACK_UNDERFLOW"); break; case GL_OUT_OF_MEMORY: strcpy(s, "GL_OUT_OF_MEMORY"); break; case GL_TABLE_TOO_LARGE: strcpy(s, "GL_TABLE_TOO_LARGE"); break; case GL_INVALID_FRAMEBUFFER_OPERATION_EXT: strcpy(s, "GL_INVALID_FRAMEBUFFER_OPERATION_EXT"); break; default: Com_sprintf(s, sizeof(s), "0x%X", err); break; } //ri.Error(ERR_FATAL, "caught OpenGL error: %s in file %s line %i", s, filename, line); ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (%s)!\n", s); } } // draw buffer stuff cmd = (drawBufferCommand_t*)R_GetCommandBuffer(sizeof(*cmd)); if(!cmd) { return; } cmd->commandId = RC_DRAW_BUFFER; if(glConfig.stereoEnabled) { if(stereoFrame == STEREO_LEFT) { cmd->buffer = (int)GL_BACK_LEFT; } else if(stereoFrame == STEREO_RIGHT) { cmd->buffer = (int)GL_BACK_RIGHT; } else { ri.Error(ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame); } } else { if(stereoFrame != STEREO_CENTER) { ri.Error(ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame); } if(!Q_stricmp(r_drawBuffer->string, "GL_FRONT")) { cmd->buffer = (int)GL_FRONT; } else { cmd->buffer = (int)GL_BACK; } } #endif }
/* ==================== RE_BeginFrame ==================== */ void RE_BeginFrame() { drawBufferCommand_t *cmd; if ( !tr.registered ) { return; } GLimp_LogComment( "--- RE_BeginFrame ---\n" ); tr.frameCount++; tr.frameSceneNum = 0; tr.viewCount = 0; // do overdraw measurement if ( r_measureOverdraw->integer ) { if ( glConfig.stencilBits < 4 ) { ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); ri.Cvar_Set( "r_measureOverdraw", "0" ); r_measureOverdraw->modified = false; } else { R_SyncRenderThread(); glEnable( GL_STENCIL_TEST ); glStencilMask( ~0U ); GL_ClearStencil( 0U ); glStencilFunc( GL_ALWAYS, 0U, ~0U ); glStencilOp( GL_KEEP, GL_INCR, GL_INCR ); } r_measureOverdraw->modified = false; } else { // this is only reached if it was on and is now off if ( r_measureOverdraw->modified ) { R_SyncRenderThread(); glDisable( GL_STENCIL_TEST ); } r_measureOverdraw->modified = false; } // texturemode stuff if ( r_textureMode->modified ) { R_SyncRenderThread(); GL_TextureMode( r_textureMode->string ); r_textureMode->modified = false; } // check for errors if ( !r_ignoreGLErrors->integer ) { int err; char s[ 128 ]; R_SyncRenderThread(); if ( ( err = glGetError() ) != GL_NO_ERROR ) { switch ( err ) { case GL_INVALID_ENUM: strcpy( s, "GL_INVALID_ENUM" ); break; case GL_INVALID_VALUE: strcpy( s, "GL_INVALID_VALUE" ); break; case GL_INVALID_OPERATION: strcpy( s, "GL_INVALID_OPERATION" ); break; case GL_STACK_OVERFLOW: strcpy( s, "GL_STACK_OVERFLOW" ); break; case GL_STACK_UNDERFLOW: strcpy( s, "GL_STACK_UNDERFLOW" ); break; case GL_OUT_OF_MEMORY: strcpy( s, "GL_OUT_OF_MEMORY" ); break; case GL_TABLE_TOO_LARGE: strcpy( s, "GL_TABLE_TOO_LARGE" ); break; case GL_INVALID_FRAMEBUFFER_OPERATION: strcpy( s, "GL_INVALID_FRAMEBUFFER_OPERATION" ); break; default: Com_sprintf( s, sizeof( s ), "0x%X", err ); break; } //ri.Error(ERR_FATAL, "caught OpenGL error: %s in file %s line %i", s, filename, line); ri.Error( ERR_FATAL, "RE_BeginFrame() - glGetError() failed (%s)!", s ); } } // draw buffer stuff cmd = (drawBufferCommand_t*) R_GetCommandBuffer( sizeof( *cmd ) ); if ( !cmd ) { return; } cmd->commandId = RC_DRAW_BUFFER; if ( !Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) ) { cmd->buffer = ( int ) GL_FRONT; } else { cmd->buffer = ( int ) GL_BACK; } }