/* ============ R_ShutdownVBOs ============ */ void R_ShutdownVBOs(void) { int i, j; VBO_t *vbo; IBO_t *ibo; ri.Printf(PRINT_ALL, "------- R_ShutdownVBOs -------\n"); R_BindNullVBO(); R_BindNullIBO(); for(i = 0; i < tr.vbos.currentElements; i++) { vbo = (VBO_t *) Com_GrowListElement(&tr.vbos, i); if(vbo->vertexesVBO) { glDeleteBuffersARB(1, &vbo->vertexesVBO); } } for(i = 0; i < tr.ibos.currentElements; i++) { ibo = (IBO_t *) Com_GrowListElement(&tr.ibos, i); if(ibo->indexesVBO) { glDeleteBuffersARB(1, &ibo->indexesVBO); } } #if defined(USE_BSP_CLUSTERSURFACE_MERGING) if(tr.world) { for(j = 0; j < MAX_VISCOUNTS; j++) { // FIXME: clean up this code for(i = 0; i < tr.world->clusterVBOSurfaces[j].currentElements; i++) { srfVBOMesh_t *vboSurf; vboSurf = (srfVBOMesh_t *) Com_GrowListElement(&tr.world->clusterVBOSurfaces[j], i); ibo = vboSurf->ibo; if(ibo->indexesVBO) { glDeleteBuffersARB(1, &ibo->indexesVBO); } } Com_DestroyGrowList(&tr.world->clusterVBOSurfaces[j]); } } #endif // #if defined(USE_BSP_CLUSTERSURFACE_MERGING) Com_DestroyGrowList(&tr.vbos); Com_DestroyGrowList(&tr.ibos); }
/* ============ R_InitVBOs ============ */ void R_InitVBOs(void) { int dataSize; int offset; ri.Printf(PRINT_ALL, "------- R_InitVBOs -------\n"); tr.numVBOs = 0; tr.numIBOs = 0; dataSize = sizeof(tess.xyz[0]); dataSize += sizeof(tess.normal[0]); #ifdef USE_VERT_TANGENT_SPACE dataSize += sizeof(tess.tangent[0]); dataSize += sizeof(tess.bitangent[0]); #endif dataSize += sizeof(tess.vertexColors[0]); dataSize += sizeof(tess.texCoords[0][0]) * 2; dataSize += sizeof(tess.lightdir[0]); dataSize *= SHADER_MAX_VERTEXES; tess.vbo = R_CreateVBO("tessVertexArray_VBO", NULL, dataSize, VBO_USAGE_DYNAMIC); offset = 0; tess.vbo->ofs_xyz = offset; offset += sizeof(tess.xyz[0]) * SHADER_MAX_VERTEXES; tess.vbo->ofs_normal = offset; offset += sizeof(tess.normal[0]) * SHADER_MAX_VERTEXES; #ifdef USE_VERT_TANGENT_SPACE tess.vbo->ofs_tangent = offset; offset += sizeof(tess.tangent[0]) * SHADER_MAX_VERTEXES; tess.vbo->ofs_bitangent = offset; offset += sizeof(tess.bitangent[0]) * SHADER_MAX_VERTEXES; #endif // these next two are actually interleaved tess.vbo->ofs_st = offset; tess.vbo->ofs_lightmap = offset + sizeof(tess.texCoords[0][0]); offset += sizeof(tess.texCoords[0][0]) * 2 * SHADER_MAX_VERTEXES; tess.vbo->ofs_vertexcolor = offset; offset += sizeof(tess.vertexColors[0]) * SHADER_MAX_VERTEXES; tess.vbo->ofs_lightdir = offset; tess.vbo->stride_xyz = sizeof(tess.xyz[0]); tess.vbo->stride_normal = sizeof(tess.normal[0]); #ifdef USE_VERT_TANGENT_SPACE tess.vbo->stride_tangent = sizeof(tess.tangent[0]); tess.vbo->stride_bitangent = sizeof(tess.bitangent[0]); #endif 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; tess.vbo->stride_lightdir = sizeof(tess.lightdir[0]); dataSize = sizeof(tess.indexes[0]) * SHADER_MAX_INDEXES; tess.ibo = R_CreateIBO("tessVertexArray_IBO", NULL, dataSize, VBO_USAGE_DYNAMIC); R_BindNullVBO(); R_BindNullIBO(); GL_CheckErrors(); }
/* ============ R_InitVBOs ============ */ void R_InitVBOs( void ) { int dataSize; ri.Printf( PRINT_DEVELOPER, "------- R_InitVBOs -------\n" ); Com_InitGrowList( &tr.vbos, 100 ); Com_InitGrowList( &tr.ibos, 100 ); #if !defined( COMPAT_Q3A ) && !defined( COMPAT_ET ) dataSize = sizeof( vec4_t ) * SHADER_MAX_VERTEXES * 11; #else dataSize = sizeof( vec4_t ) * SHADER_MAX_VERTEXES * 10; #endif tess.vbo = R_CreateVBO( "tessVertexArray_VBO", NULL, dataSize, VBO_USAGE_DYNAMIC ); tess.vbo->ofsXYZ = 0; tess.vbo->ofsTexCoords = tess.vbo->ofsXYZ + sizeof( tess.xyz ); tess.vbo->ofsLightCoords = tess.vbo->ofsTexCoords + sizeof( tess.texCoords ); tess.vbo->ofsTangents = tess.vbo->ofsLightCoords + sizeof( tess.lightCoords ); tess.vbo->ofsBinormals = tess.vbo->ofsTangents + sizeof( tess.tangents ); tess.vbo->ofsNormals = tess.vbo->ofsBinormals + sizeof( tess.binormals ); tess.vbo->ofsColors = tess.vbo->ofsNormals + sizeof( tess.normals ); #if !defined( COMPAT_Q3A ) && !defined( COMPAT_ET ) tess.vbo->ofsPaintColors = tess.vbo->ofsColors + sizeof( tess.colors ); tess.vbo->ofsAmbientLight = tess.vbo->ofsPaintColors + sizeof( tess.PaintColors ); #endif tess.vbo->ofsAmbientLight = tess.vbo->ofsColors + sizeof( tess.colors ); tess.vbo->ofsDirectedLight = tess.vbo->ofsAmbientLight + sizeof( tess.ambientLights ); tess.vbo->ofsLightDirections = tess.vbo->ofsDirectedLight + sizeof( tess.directedLights ); tess.vbo->sizeXYZ = sizeof( tess.xyz ); tess.vbo->sizeTangents = sizeof( tess.tangents ); tess.vbo->sizeBinormals = sizeof( tess.binormals ); tess.vbo->sizeNormals = sizeof( tess.normals ); dataSize = sizeof( tess.indexes ); tess.ibo = R_CreateIBO( "tessVertexArray_IBO", NULL, dataSize, VBO_USAGE_DYNAMIC ); R_InitUnitCubeVBO(); // allocate a PBO for color grade map transfers glGenBuffers( 1, &tr.colorGradePBO ); glBindBuffer( GL_PIXEL_PACK_BUFFER, tr.colorGradePBO ); glBufferData( GL_PIXEL_PACK_BUFFER, REF_COLORGRADEMAP_STORE_SIZE * sizeof(color4ub_t), NULL, GL_STREAM_COPY ); glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 ); R_BindNullVBO(); R_BindNullIBO(); GL_CheckErrors(); }
/* ============ R_CreateVBO ============ */ VBO_t *R_CreateStaticVBO( const char *name, vboData_t data, vboLayout_t layout ) { VBO_t *vbo; byte *outData; if ( strlen( name ) >= MAX_QPATH ) { ri.Error( ERR_DROP, "R_CreateVBO: \"%s\" is too long", name ); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = (VBO_t*) ri.Hunk_Alloc( sizeof( *vbo ), h_low ); memset( vbo, 0, sizeof( *vbo ) ); Com_AddToGrowList( &tr.vbos, vbo ); Q_strncpyz( vbo->name, name, sizeof( vbo->name ) ); vbo->layout = layout; vbo->vertexesNum = data.numVerts; vbo->framesNum = data.numFrames; vbo->attribBits = R_DeriveAttrBits( data ); vbo->usage = GL_STATIC_DRAW; R_SetVBOAttributeLayouts( vbo ); glGenBuffers( 1, &vbo->vertexesVBO ); R_BindVBO( vbo ); #ifdef GLEW_ARB_buffer_storage if( glConfig2.bufferStorageAvailable ) { outData = (byte *)ri.Hunk_AllocateTempMemory( vbo->vertexesSize ); R_CopyVertexData( vbo, outData, data ); glBufferStorage( GL_ARRAY_BUFFER, vbo->vertexesSize, outData, 0 ); ri.Hunk_FreeTempMemory( outData ); } else #endif { glBufferData( GL_ARRAY_BUFFER, vbo->vertexesSize, nullptr, vbo->usage ); outData = (byte *)glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); R_CopyVertexData( vbo, outData, data ); glUnmapBuffer( GL_ARRAY_BUFFER ); } R_BindNullVBO(); GL_CheckErrors(); return vbo; }
VBO_t *R_CreateDynamicVBO( const char *name, int numVertexes, uint32_t stateBits, vboLayout_t layout ) { VBO_t *vbo; if ( !numVertexes ) { return nullptr; } if ( strlen( name ) >= MAX_QPATH ) { ri.Error( ERR_DROP, "R_CreateDynamicVBO: \"%s\" is too long", name ); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = (VBO_t*) ri.Hunk_Alloc( sizeof( *vbo ), h_low ); memset( vbo, 0, sizeof( *vbo ) ); Com_AddToGrowList( &tr.vbos, vbo ); Q_strncpyz( vbo->name, name, sizeof( vbo->name ) ); vbo->layout = layout; vbo->framesNum = 0; vbo->vertexesNum = numVertexes; vbo->attribBits = stateBits; vbo->usage = GL_DYNAMIC_DRAW; R_SetVBOAttributeLayouts( vbo ); glGenBuffers( 1, &vbo->vertexesVBO ); R_BindVBO( vbo ); #if defined( GLEW_ARB_buffer_storage ) && defined( GLEW_ARB_sync ) if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { R_InitRingbuffer( GL_ARRAY_BUFFER, sizeof( shaderVertex_t ), numVertexes, &tess.vertexRB ); } else #endif { glBufferData( GL_ARRAY_BUFFER, vbo->vertexesSize, nullptr, vbo->usage ); } R_BindNullVBO(); GL_CheckErrors(); return vbo; }
/* ============ R_CreateVBO2 ============ */ VBO_t *R_CreateStaticVBO2( const char *name, int numVertexes, shaderVertex_t *verts, unsigned int stateBits ) { VBO_t *vbo; if ( !numVertexes ) { return nullptr; } if ( strlen( name ) >= MAX_QPATH ) { ri.Error( ERR_DROP, "R_CreateVBO2: \"%s\" is too long", name ); } // make sure the render thread is stopped R_SyncRenderThread(); vbo = ( VBO_t * ) ri.Hunk_Alloc( sizeof( *vbo ), h_low ); memset( vbo, 0, sizeof( *vbo ) ); Com_AddToGrowList( &tr.vbos, vbo ); Q_strncpyz( vbo->name, name, sizeof( vbo->name ) ); vbo->layout = VBO_LAYOUT_STATIC; vbo->framesNum = 0; vbo->vertexesNum = numVertexes; vbo->attribBits = stateBits; vbo->usage = GL_STATIC_DRAW; R_SetVBOAttributeLayouts( vbo ); glGenBuffers( 1, &vbo->vertexesVBO ); R_BindVBO( vbo ); #ifdef GLEW_ARB_buffer_storage if( glConfig2.bufferStorageAvailable ) { glBufferStorage( GL_ARRAY_BUFFER, vbo->vertexesSize, verts, 0 ); } else #endif { glBufferData( GL_ARRAY_BUFFER, vbo->vertexesSize, verts, vbo->usage ); } R_BindNullVBO(); GL_CheckErrors(); return vbo; }
/* ============ R_InitVBOs ============ */ void R_InitVBOs( void ) { int dataSize; byte *data; ri.Printf( PRINT_ALL, "------- R_InitVBOs -------\n" ); Com_InitGrowList( &tr.vbos, 100 ); Com_InitGrowList( &tr.ibos, 100 ); dataSize = sizeof( vec4_t ) * SHADER_MAX_VERTEXES * 11; data = Com_Allocate( dataSize ); memset( data, 0, dataSize ); tess.vbo = R_CreateVBO( "tessVertexArray_VBO", data, dataSize, VBO_USAGE_DYNAMIC ); tess.vbo->ofsXYZ = 0; tess.vbo->ofsTexCoords = tess.vbo->ofsXYZ + sizeof( tess.xyz ); tess.vbo->ofsLightCoords = tess.vbo->ofsTexCoords + sizeof( tess.texCoords ); tess.vbo->ofsTangents = tess.vbo->ofsLightCoords + sizeof( tess.lightCoords ); tess.vbo->ofsBinormals = tess.vbo->ofsTangents + sizeof( tess.tangents ); tess.vbo->ofsNormals = tess.vbo->ofsBinormals + sizeof( tess.binormals ); tess.vbo->ofsColors = tess.vbo->ofsNormals + sizeof( tess.normals ); #if !defined( COMPAT_Q3A ) && !defined( COMPAT_ET ) tess.vbo->ofsPaintColors = tess.vbo->ofsColors + sizeof( tess.colors ); tess.vbo->ofsLightDirections = tess.vbo->ofsPaintColors + sizeof( tess.paintColors ); #endif tess.vbo->sizeXYZ = sizeof( tess.xyz ); tess.vbo->sizeTangents = sizeof( tess.tangents ); tess.vbo->sizeBinormals = sizeof( tess.binormals ); tess.vbo->sizeNormals = sizeof( tess.normals ); Com_Dealloc( data ); dataSize = sizeof( tess.indexes ); data = Com_Allocate( dataSize ); memset( data, 0, dataSize ); tess.ibo = R_CreateIBO( "tessVertexArray_IBO", data, dataSize, VBO_USAGE_DYNAMIC ); Com_Dealloc( data ); R_InitUnitCubeVBO(); R_BindNullVBO(); R_BindNullIBO(); GL_CheckErrors(); }
/* ============ R_InitVBOs ============ */ void R_InitVBOs(void) { int dataSize; byte *data; Ren_Print("------- R_InitVBOs -------\n"); Com_InitGrowList(&tr.vbos, 100); Com_InitGrowList(&tr.ibos, 100); dataSize = sizeof(vec4_t) * SHADER_MAX_VERTEXES * 11; data = (byte *)Com_Allocate(dataSize); memset(data, 0, dataSize); tess.vbo = R_CreateVBO("tessVertexArray_VBO", data, dataSize, VBO_USAGE_DYNAMIC); tess.vbo->ofsXYZ = 0; tess.vbo->ofsTexCoords = tess.vbo->ofsXYZ + sizeof(tess.xyz); tess.vbo->ofsLightCoords = tess.vbo->ofsTexCoords + sizeof(tess.texCoords); tess.vbo->ofsTangents = tess.vbo->ofsLightCoords + sizeof(tess.lightCoords); tess.vbo->ofsBinormals = tess.vbo->ofsTangents + sizeof(tess.tangents); tess.vbo->ofsNormals = tess.vbo->ofsBinormals + sizeof(tess.binormals); tess.vbo->ofsColors = tess.vbo->ofsNormals + sizeof(tess.normals); tess.vbo->sizeXYZ = sizeof(tess.xyz); tess.vbo->sizeTangents = sizeof(tess.tangents); tess.vbo->sizeBinormals = sizeof(tess.binormals); tess.vbo->sizeNormals = sizeof(tess.normals); Com_Dealloc(data); dataSize = sizeof(tess.indexes); data = (byte *)Com_Allocate(dataSize); memset(data, 0, dataSize); tess.ibo = R_CreateIBO("tessVertexArray_IBO", data, dataSize, VBO_USAGE_DYNAMIC); Com_Dealloc(data); R_InitUnitCubeVBO(); 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; }
/** * @brief Returns the number of msec spent in the back end * @param[out] frontEndMsec * @param[out] backEndMsec */ void RE_EndFrame(int *frontEndMsec, int *backEndMsec) { swapBuffersCommand_t *cmd; if (!tr.registered) { return; } cmd = (swapBuffersCommand_t *)R_GetCommandBuffer(sizeof(*cmd)); if (!cmd) { return; } cmd->commandId = RC_SWAP_BUFFERS; R_BindNullVBO(); R_BindNullIBO(); R_IssueRenderCommands(qtrue); // use the other buffers next frame, because another CPU // may still be rendering into the current ones R_InitNextFrame(); if (frontEndMsec) { *frontEndMsec = tr.frontEndMsec; } tr.frontEndMsec = 0; if (backEndMsec) { *backEndMsec = backEnd.pc.msec; } backEnd.pc.msec = 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 *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_strnicmp( ( const char * ) buffer, "MD5Version", 10 ) ) { loaded = R_LoadMD5( mod, buffer, bufferLen, name ); } else if ( !Q_strnicmp( ( 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; }
/* ============ R_ShutdownVBOs ============ */ void R_ShutdownVBOs() { int i; VBO_t *vbo; IBO_t *ibo; ri.Printf( PRINT_DEVELOPER, "------- R_ShutdownVBOs -------\n" ); if( !glConfig2.mapBufferRangeAvailable ) { // nothing } #if defined( GLEW_ARB_buffer_storage ) && defined( GLEW_ARB_sync ) else if( glConfig2.bufferStorageAvailable && glConfig2.syncAvailable ) { R_BindVBO( tess.vbo ); R_ShutdownRingbuffer( GL_ARRAY_BUFFER, &tess.vertexRB ); R_BindIBO( tess.ibo ); R_ShutdownRingbuffer( GL_ELEMENT_ARRAY_BUFFER, &tess.indexRB ); } #endif else { if( tess.verts != nullptr && tess.verts != tess.vertsBuffer ) { R_BindVBO( tess.vbo ); glUnmapBuffer( GL_ARRAY_BUFFER ); } if( tess.indexes != nullptr && tess.indexes != tess.indexesBuffer ) { R_BindIBO( tess.ibo ); glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER ); } } R_BindNullVBO(); R_BindNullIBO(); glDeleteBuffers( 1, &tr.colorGradePBO ); for ( i = 0; i < tr.vbos.currentElements; i++ ) { vbo = ( VBO_t * ) Com_GrowListElement( &tr.vbos, i ); if ( vbo->vertexesVBO ) { glDeleteBuffers( 1, &vbo->vertexesVBO ); } } for ( i = 0; i < tr.ibos.currentElements; i++ ) { ibo = ( IBO_t * ) Com_GrowListElement( &tr.ibos, i ); if ( ibo->indexesVBO ) { glDeleteBuffers( 1, &ibo->indexesVBO ); } } Com_DestroyGrowList( &tr.vbos ); Com_DestroyGrowList( &tr.ibos ); Com_Free_Aligned( tess.vertsBuffer ); Com_Free_Aligned( tess.indexesBuffer ); tess.verts = tess.vertsBuffer = nullptr; tess.indexes = tess.indexesBuffer = nullptr; }