/* ============== Tess_SurfaceVertsAndTris ============== */ static void Tess_SurfaceVertsAndTris( const srfVert_t *verts, const srfTriangle_t *triangles, int numVerts, int numTriangles ) { int i; const srfTriangle_t *tri = triangles; const srfVert_t *vert = verts; const int numIndexes = numTriangles * 3; Tess_CheckOverflow( numVerts, numIndexes ); for ( i = 0; i < numIndexes; i+=3, tri++ ) { tess.indexes[ tess.numIndexes + i + 0 ] = tess.numVertexes + tri->indexes[ 0 ]; tess.indexes[ tess.numIndexes + i + 1 ] = tess.numVertexes + tri->indexes[ 1 ]; tess.indexes[ tess.numIndexes + i + 2 ] = tess.numVertexes + tri->indexes[ 2 ]; } tess.numIndexes += numIndexes; for ( i = 0; i < numVerts; i++, vert++ ) { VectorCopy( vert->xyz, tess.verts[ tess.numVertexes + i ].xyz ); Vector4Copy( vert->qtangent, tess.verts[ tess.numVertexes + i ].qtangents ); tess.verts[ tess.numVertexes + i ].texCoords[ 0 ] = floatToHalf( vert->st[ 0 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 1 ] = floatToHalf( vert->st[ 1 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 2 ] = floatToHalf( vert->lightmap[ 0 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 3 ] = floatToHalf( vert->lightmap[ 1 ] ); Vector4Copy( vert->lightColor, tess.verts[ tess.numVertexes + i ].color ); } tess.numVertexes += numVerts; tess.attribsSet = ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR | ATTR_QTANGENT; }
// ydnar: decal surfaces void Tess_SurfaceDecal( srfDecal_t *srf ) { int i; GLimp_LogComment( "--- Tess_SurfaceDecal ---\n" ); Tess_CheckOverflow( srf->numVerts, 3 * ( srf->numVerts - 2 ) ); // fan triangles into the tess array for ( i = 0; i < srf->numVerts; i++ ) { VectorCopy( srf->verts[ i ].xyz, tess.verts[ tess.numVertexes + i ].xyz ); tess.verts[ tess.numVertexes + i ].texCoords[ 0 ] = floatToHalf( srf->verts[ i ].st[ 0 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 1 ] = floatToHalf( srf->verts[ i ].st[ 1 ] ); Vector4Copy( srf->verts[ i ].modulate, tess.verts[ tess.numVertexes + i ].color ); } // generate fan indexes into the tess array for ( i = 0; i < srf->numVerts - 2; i++ ) { tess.indexes[ tess.numIndexes + 0 ] = tess.numVertexes; tess.indexes[ tess.numIndexes + 1 ] = tess.numVertexes + i + 1; tess.indexes[ tess.numIndexes + 2 ] = tess.numVertexes + i + 2; tess.numIndexes += 3; } tess.attribsSet |= ATTR_POSITION | ATTR_COLOR | ATTR_TEXCOORD; tess.numVertexes += srf->numVerts; }
static void FillCloudySkySide( const int mins[ 2 ], const int maxs[ 2 ], bool addIndexes ) { int s, t; int vertexStart = tess.numVertexes; int tHeight, sWidth; tHeight = maxs[ 1 ] - mins[ 1 ] + 1; sWidth = maxs[ 0 ] - mins[ 0 ] + 1; for ( t = mins[ 1 ] + HALF_SKY_SUBDIVISIONS; t <= maxs[ 1 ] + HALF_SKY_SUBDIVISIONS; t++ ) { for ( s = mins[ 0 ] + HALF_SKY_SUBDIVISIONS; s <= maxs[ 0 ] + HALF_SKY_SUBDIVISIONS; s++ ) { VectorAdd( s_skyPoints[ t ][ s ], backEnd.viewParms.orientation.origin, tess.verts[ tess.numVertexes ].xyz ); tess.verts[ tess.numVertexes ].texCoords[ 0 ] = floatToHalf( s_skyTexCoords[ t ][ s ][ 0 ] ); tess.verts[ tess.numVertexes ].texCoords[ 1 ] = floatToHalf( s_skyTexCoords[ t ][ s ][ 1 ] ); tess.numVertexes++; if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) { ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" ); } } } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD; // only add indexes for one pass, otherwise it would draw multiple times for each pass if ( addIndexes ) { for ( t = 0; t < tHeight - 1; t++ ) { for ( s = 0; s < sWidth - 1; s++ ) { tess.indexes[ tess.numIndexes ] = vertexStart + s + t * ( sWidth ); tess.numIndexes++; tess.indexes[ tess.numIndexes ] = vertexStart + s + ( t + 1 ) * ( sWidth ); tess.numIndexes++; tess.indexes[ tess.numIndexes ] = vertexStart + s + 1 + t * ( sWidth ); tess.numIndexes++; tess.indexes[ tess.numIndexes ] = vertexStart + s + ( t + 1 ) * ( sWidth ); tess.numIndexes++; tess.indexes[ tess.numIndexes ] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); tess.numIndexes++; tess.indexes[ tess.numIndexes ] = vertexStart + s + 1 + t * ( sWidth ); tess.numIndexes++; } } } }
void Tess_SurfacePolybuffer( srfPolyBuffer_t *surf ) { int i; int numIndexes; int numVertexes; glIndex_t *indices; float *xyzw; float *st; byte *color; GLimp_LogComment( "--- Tess_SurfacePolybuffer ---\n" ); Tess_CheckOverflow( surf->pPolyBuffer->numVerts, surf->pPolyBuffer->numIndicies ); numIndexes = std::min( surf->pPolyBuffer->numIndicies, MAX_PB_INDICIES ); indices = surf->pPolyBuffer->indicies; for ( i = 0; i < numIndexes; i++ ) { tess.indexes[ tess.numIndexes + i ] = tess.numVertexes + indices[ i ]; } tess.numIndexes += numIndexes; numVertexes = std::min( surf->pPolyBuffer->numVerts, MAX_PB_VERTS ); xyzw = &surf->pPolyBuffer->xyz[ 0 ][ 0 ]; st = &surf->pPolyBuffer->st[ 0 ][ 0 ]; color = &surf->pPolyBuffer->color[ 0 ][ 0 ]; for ( i = 0; i < numVertexes; i++, xyzw += 4, st += 2, color += 4 ) { VectorCopy( xyzw, tess.verts[ tess.numVertexes + i ].xyz ); tess.verts[ tess.numVertexes + i ].texCoords[ 0 ] = floatToHalf( st[ 0 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 1 ] = floatToHalf( st[ 1 ] ); Vector4Copy( color, tess.verts[ tess.numVertexes + i ].color ); } tess.attribsSet |= ATTR_POSITION | ATTR_COLOR | ATTR_TEXCOORD; tess.numVertexes += numVertexes; }
/* ============== Tess_InstantQuad ============== */ void Tess_InstantQuad( vec4_t quadVerts[ 4 ] ) { GLimp_LogComment( "--- Tess_InstantQuad ---\n" ); tess.multiDrawPrimitives = 0; tess.numVertexes = 0; tess.numIndexes = 0; tess.attribsSet = 0; Tess_MapVBOs( false ); VectorCopy( quadVerts[ 0 ], tess.verts[ tess.numVertexes ].xyz ); Vector4Set( tess.verts[ tess.numVertexes ].color, 255, 255, 255, 255 ); tess.verts[ tess.numVertexes ].texCoords[ 0 ] = floatToHalf( 0.0f ); tess.verts[ tess.numVertexes ].texCoords[ 1 ] = floatToHalf( 0.0f ); tess.numVertexes++; VectorCopy( quadVerts[ 1 ], tess.verts[ tess.numVertexes ].xyz ); Vector4Set( tess.verts[ tess.numVertexes ].color, 255, 255, 255, 255 ); tess.verts[ tess.numVertexes ].texCoords[ 0 ] = floatToHalf( 1.0f ); tess.verts[ tess.numVertexes ].texCoords[ 1 ] = floatToHalf( 0.0f ); tess.numVertexes++; VectorCopy( quadVerts[ 2 ], tess.verts[ tess.numVertexes ].xyz ); Vector4Set( tess.verts[ tess.numVertexes ].color, 255, 255, 255, 255 ); tess.verts[ tess.numVertexes ].texCoords[ 0 ] = floatToHalf( 1.0f ); tess.verts[ tess.numVertexes ].texCoords[ 1 ] = floatToHalf( 1.0f ); tess.numVertexes++; VectorCopy( quadVerts[ 3 ], tess.verts[ tess.numVertexes ].xyz ); Vector4Set( tess.verts[ tess.numVertexes ].color, 255, 255, 255, 255 ); tess.verts[ tess.numVertexes ].texCoords[ 0 ] = floatToHalf( 0.0f ); tess.verts[ tess.numVertexes ].texCoords[ 1 ] = floatToHalf( 1.0f ); tess.numVertexes++; tess.indexes[ tess.numIndexes++ ] = 0; tess.indexes[ tess.numIndexes++ ] = 1; tess.indexes[ tess.numIndexes++ ] = 2; tess.indexes[ tess.numIndexes++ ] = 0; tess.indexes[ tess.numIndexes++ ] = 2; tess.indexes[ tess.numIndexes++ ] = 3; Tess_UpdateVBOs( ); GL_VertexAttribsState( ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR ); Tess_DrawElements(); tess.multiDrawPrimitives = 0; tess.numVertexes = 0; tess.numIndexes = 0; tess.attribsSet = 0; GL_CheckErrors(); }
void Tess_AddSprite( const vec3_t center, const u8vec4_t color, float radius, float rotation ) { int i; int ndx; GLimp_LogComment( "--- Tess_AddSprite ---\n" ); Tess_CheckOverflow( 4, 6 ); ndx = tess.numVertexes; // triangle indexes for a simple quad tess.indexes[ tess.numIndexes ] = ndx; tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; for ( i = 0; i < 4; i++ ) { vec4_t texCoord; vec4_t orientation; Vector4Set( texCoord, 0.5f * (i & 2), 0.5f * ( (i + 1) & 2 ), 0.5f * (i & 2), 0.5f * ( (i + 1) & 2 ) ); VectorCopy( center, tess.verts[ ndx + i ].xyz ); Vector4Copy( color, tess.verts[ ndx + i ].color ); floatToHalf( texCoord, tess.verts[ ndx + i ].texCoords ); Vector4Set( orientation, rotation, 0.0f, 0.0f, radius ); floatToHalf( orientation, tess.verts[ ndx + i ].spriteOrientation ); } tess.numVertexes += 4; tess.numIndexes += 6; tess.attribsSet |= ATTR_POSITION | ATTR_COLOR | ATTR_TEXCOORD | ATTR_ORIENTATION; }
/* ============= Tess_SurfaceMDV ============= */ static void Tess_SurfaceMDV( mdvSurface_t *srf ) { int i, j; int numIndexes = 0; int numVertexes; mdvXyz_t *oldVert, *newVert; mdvNormal_t *oldNormal, *newNormal; mdvSt_t *st; srfTriangle_t *tri; float backlerp; float oldXyzScale, newXyzScale; GLimp_LogComment( "--- Tess_SurfaceMDV ---\n" ); if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { backlerp = 0; } else { backlerp = backEnd.currentEntity->e.backlerp; } newXyzScale = ( 1.0f - backlerp ); oldXyzScale = backlerp; Tess_CheckOverflow( srf->numVerts, srf->numTriangles * 3 ); numIndexes = srf->numTriangles * 3; for ( i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = tess.numVertexes + tri->indexes[ 0 ]; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = tess.numVertexes + tri->indexes[ 1 ]; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = tess.numVertexes + tri->indexes[ 2 ]; } newVert = srf->verts + ( backEnd.currentEntity->e.frame * srf->numVerts ); oldVert = srf->verts + ( backEnd.currentEntity->e.oldframe * srf->numVerts ); newNormal = srf->normals + ( backEnd.currentEntity->e.frame * srf->numVerts ); oldNormal = srf->normals + ( backEnd.currentEntity->e.oldframe * srf->numVerts ); st = srf->st; numVertexes = srf->numVerts; for ( j = 0; j < numVertexes; j++, newVert++, oldVert++, st++ ) { vec3_t tmpVert; if ( backlerp == 0 ) { // just copy VectorCopy( newVert->xyz, tmpVert ); } else { // interpolate the xyz VectorScale( oldVert->xyz, oldXyzScale, tmpVert ); VectorMA( tmpVert, newXyzScale, newVert->xyz, tmpVert ); } tess.verts[ tess.numVertexes + j ].xyz[ 0 ] = tmpVert[ 0 ]; tess.verts[ tess.numVertexes + j ].xyz[ 1 ] = tmpVert[ 1 ]; tess.verts[ tess.numVertexes + j ].xyz[ 2 ] = tmpVert[ 2 ]; tess.verts[ tess.numVertexes + j ].texCoords[ 0 ] = floatToHalf( st->st[ 0 ] ); tess.verts[ tess.numVertexes + j ].texCoords[ 1 ] = floatToHalf( st->st[ 1 ] ); } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD; // calc tangent spaces if ( !tess.skipTangentSpaces ) { int i; float *v; const float *v0, *v1, *v2; const int16_t *t0, *t1, *t2; vec3_t tangent, *tangents; vec3_t binormal, *binormals; vec3_t *normals; glIndex_t *indices; tess.attribsSet |= ATTR_QTANGENT; tangents = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); binormals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); normals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); for ( i = 0; i < numVertexes; i++ ) { VectorClear( tangents[ i ] ); VectorClear( binormals[ i ] ); if ( backlerp == 0 ) { // just copy VectorCopy( newNormal->normal, normals[ i ] ); } else { // interpolate the xyz VectorScale( oldNormal->normal, oldXyzScale, normals[ i ] ); VectorMA( normals[ i ], newXyzScale, newNormal->normal, normals[ i ] ); VectorNormalizeFast( normals[ i ] ); } } for ( i = 0, indices = tess.indexes + tess.numIndexes; i < numIndexes; i += 3, indices += 3 ) { v0 = tess.verts[ indices[ 0 ] ].xyz; v1 = tess.verts[ indices[ 1 ] ].xyz; v2 = tess.verts[ indices[ 2 ] ].xyz; t0 = tess.verts[ indices[ 0 ] ].texCoords; t1 = tess.verts[ indices[ 1 ] ].texCoords; t2 = tess.verts[ indices[ 2 ] ].texCoords; R_CalcTangents( tangent, binormal, v0, v1, v2, t0, t1, t2 ); for ( j = 0; j < 3; j++ ) { v = tangents[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, tangent, v ); v = binormals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, binormal, v ); } } for ( i = 0; i < numVertexes; i++ ) { R_TBNtoQtangents( tangents[ i ], binormals[ i ], normals[ i ], tess.verts[ numVertexes + i ].qtangents ); } ri.Hunk_FreeTempMemory( normals ); ri.Hunk_FreeTempMemory( binormals ); ri.Hunk_FreeTempMemory( tangents ); } tess.numIndexes += numIndexes; tess.numVertexes += numVertexes; }
/* ============= Tess_SurfacePolychain ============= */ static void Tess_SurfacePolychain( srfPoly_t *p ) { int i, j; int numVertexes; int numIndexes; GLimp_LogComment( "--- Tess_SurfacePolychain ---\n" ); Tess_CheckOverflow( p->numVerts, 3 * ( p->numVerts - 2 ) ); // fan triangles into the tess array numVertexes = 0; for ( i = 0; i < p->numVerts; i++ ) { VectorCopy( p->verts[ i ].xyz, tess.verts[ tess.numVertexes + i ].xyz ); tess.verts[ tess.numVertexes + i ].texCoords[ 0 ] = floatToHalf( p->verts[ i ].st[ 0 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 1 ] = floatToHalf( p->verts[ i ].st[ 1 ] ); Vector4Copy( p->verts[ i ].modulate, tess.verts[ tess.numVertexes + i ].color ); numVertexes++; } // generate fan indexes into the tess array numIndexes = 0; for ( i = 0; i < p->numVerts - 2; i++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = tess.numVertexes; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = tess.numVertexes + i + 1; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = tess.numVertexes + i + 2; numIndexes += 3; } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR; // calc tangent spaces if ( tess.surfaceShader->interactLight && !tess.skipTangentSpaces ) { int i; float *v; const float *v0, *v1, *v2; const int16_t *t0, *t1, *t2; vec3_t tangent, *tangents; vec3_t binormal, *binormals; vec3_t normal, *normals; glIndex_t *indices; tangents = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); binormals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); normals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); for ( i = 0; i < numVertexes; i++ ) { VectorClear( tangents[ i ] ); VectorClear( binormals[ i ] ); VectorClear( normals[ i ] ); } for ( i = 0, indices = tess.indexes + tess.numIndexes; i < numIndexes; i += 3, indices += 3 ) { v0 = tess.verts[ indices[ 0 ] ].xyz; v1 = tess.verts[ indices[ 1 ] ].xyz; v2 = tess.verts[ indices[ 2 ] ].xyz; t0 = tess.verts[ indices[ 0 ] ].texCoords; t1 = tess.verts[ indices[ 1 ] ].texCoords; t2 = tess.verts[ indices[ 2 ] ].texCoords; R_CalcFaceNormal( normal, v0, v1, v2 ); R_CalcTangents( tangent, binormal, v0, v1, v2, t0, t1, t2 ); for ( j = 0; j < 3; j++ ) { v = tangents[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, tangent, v ); v = binormals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, binormal, v ); v = normals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, normal, v ); } } for ( i = 0; i < numVertexes; i++ ) { VectorNormalizeFast( normals[ i ] ); R_TBNtoQtangents( tangents[ i ], binormals[ i ], normals[ i ], tess.verts[ tess.numVertexes + i ].qtangents ); } ri.Hunk_FreeTempMemory( normals ); ri.Hunk_FreeTempMemory( binormals ); ri.Hunk_FreeTempMemory( tangents ); tess.attribsSet |= ATTR_QTANGENT; } tess.numIndexes += numIndexes; tess.numVertexes += numVertexes; }
/* ============== Tess_AddQuadStampExt2 ============== */ void Tess_AddQuadStampExt2( vec4_t quadVerts[ 4 ], const vec4_t color, float s1, float t1, float s2, float t2 ) { int i; vec3_t normal, tangent, binormal; int ndx; GLimp_LogComment( "--- Tess_AddQuadStampExt2 ---\n" ); Tess_CheckOverflow( 4, 6 ); ndx = tess.numVertexes; // triangle indexes for a simple quad tess.indexes[ tess.numIndexes ] = ndx; tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; VectorCopy( quadVerts[ 0 ], tess.verts[ ndx + 0 ].xyz ); VectorCopy( quadVerts[ 1 ], tess.verts[ ndx + 1 ].xyz ); VectorCopy( quadVerts[ 2 ], tess.verts[ ndx + 2 ].xyz ); VectorCopy( quadVerts[ 3 ], tess.verts[ ndx + 3 ].xyz ); tess.attribsSet |= ATTR_POSITION | ATTR_COLOR | ATTR_TEXCOORD | ATTR_QTANGENT; // constant normal all the way around vec2_t st[ 3 ] = { { s1, t1 }, { s2, t1 }, { s2, t2 } }; R_CalcFaceNormal( normal, quadVerts[ 0 ], quadVerts[ 1 ], quadVerts[ 2 ] ); R_CalcTangents( tangent, binormal, quadVerts[ 0 ], quadVerts[ 1 ], quadVerts[ 2 ], st[ 0 ], st[ 1 ], st[ 2 ] ); //if ( !calcNormals ) //{ // VectorNegate( backEnd.viewParms.orientation.axis[ 0 ], normal ); //} R_TBNtoQtangents( tangent, binormal, normal, tess.verts[ ndx ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 1 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 2 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 3 ].qtangents ); // standard square texture coordinates tess.verts[ ndx ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 1 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 1 ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 2 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 2 ].texCoords[ 1 ] = floatToHalf( t2 ); tess.verts[ ndx + 3 ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx + 3 ].texCoords[ 1 ] = floatToHalf( t2 ); // constant color all the way around // should this be identity and let the shader specify from entity? u8vec4_t iColor; floatToUnorm8( color, iColor ); for ( i = 0; i < 4; i++ ) { Vector4Copy( iColor, tess.verts[ ndx + i ].color ); } tess.numVertexes += 4; tess.numIndexes += 6; }
/* ============== Tess_AddQuadStampExt ============== */ void Tess_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, const vec4_t color, float s1, float t1, float s2, float t2 ) { int i; vec3_t normal; int ndx; GLimp_LogComment( "--- Tess_AddQuadStampExt ---\n" ); Tess_CheckOverflow( 4, 6 ); ndx = tess.numVertexes; // triangle indexes for a simple quad tess.indexes[ tess.numIndexes ] = ndx; tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; tess.verts[ ndx ].xyz[ 0 ] = origin[ 0 ] + left[ 0 ] + up[ 0 ]; tess.verts[ ndx ].xyz[ 1 ] = origin[ 1 ] + left[ 1 ] + up[ 1 ]; tess.verts[ ndx ].xyz[ 2 ] = origin[ 2 ] + left[ 2 ] + up[ 2 ]; tess.verts[ ndx + 1 ].xyz[ 0 ] = origin[ 0 ] - left[ 0 ] + up[ 0 ]; tess.verts[ ndx + 1 ].xyz[ 1 ] = origin[ 1 ] - left[ 1 ] + up[ 1 ]; tess.verts[ ndx + 1 ].xyz[ 2 ] = origin[ 2 ] - left[ 2 ] + up[ 2 ]; tess.verts[ ndx + 2 ].xyz[ 0 ] = origin[ 0 ] - left[ 0 ] - up[ 0 ]; tess.verts[ ndx + 2 ].xyz[ 1 ] = origin[ 1 ] - left[ 1 ] - up[ 1 ]; tess.verts[ ndx + 2 ].xyz[ 2 ] = origin[ 2 ] - left[ 2 ] - up[ 2 ]; tess.verts[ ndx + 3 ].xyz[ 0 ] = origin[ 0 ] + left[ 0 ] - up[ 0 ]; tess.verts[ ndx + 3 ].xyz[ 1 ] = origin[ 1 ] + left[ 1 ] - up[ 1 ]; tess.verts[ ndx + 3 ].xyz[ 2 ] = origin[ 2 ] + left[ 2 ] - up[ 2 ]; // constant normal all the way around VectorSubtract( vec3_origin, backEnd.viewParms.orientation.axis[ 0 ], normal ); R_TBNtoQtangents( left, up, normal, tess.verts[ ndx ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 1 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 2 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 3 ].qtangents ); // standard square texture coordinates tess.verts[ ndx ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 1 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 1 ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 2 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 2 ].texCoords[ 1 ] = floatToHalf( t2 ); tess.verts[ ndx + 3 ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx + 3 ].texCoords[ 1 ] = floatToHalf( t2 ); // constant color all the way around // should this be identity and let the shader specify from entity? u8vec4_t iColor; floatToUnorm8( color, iColor ); for ( i = 0; i < 4; i++ ) { Vector4Copy( iColor, tess.verts[ ndx + i ].color ); } tess.numVertexes += 4; tess.numIndexes += 6; tess.attribsSet |= ATTR_POSITION | ATTR_QTANGENT | ATTR_COLOR | ATTR_TEXCOORD; }
/* ============== Tess_SurfaceMD5 ============== */ static void Tess_SurfaceMD5( md5Surface_t *srf ) { int j; int numIndexes = 0; int numVertexes; md5Model_t *model; md5Vertex_t *v; srfTriangle_t *tri; GLimp_LogComment( "--- Tess_SurfaceMD5 ---\n" ); Tess_CheckOverflow( srf->numVerts, srf->numTriangles * 3 ); model = srf->model; numIndexes = srf->numTriangles * 3; tri = srf->triangles; for (unsigned i = 0; i < srf->numTriangles; i++, tri++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = tess.numVertexes + tri->indexes[ 0 ]; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = tess.numVertexes + tri->indexes[ 1 ]; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = tess.numVertexes + tri->indexes[ 2 ]; } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD; if ( tess.skipTangentSpaces ) { // convert bones back to matrices for (unsigned i = 0; i < model->numBones; i++ ) { if ( backEnd.currentEntity->e.skeleton.type == SK_ABSOLUTE ) { refBone_t *bone = &backEnd.currentEntity->e.skeleton.bones[ i ]; TransInitRotationQuat( model->bones[ i ].rotation, &bones[ i ] ); TransAddTranslation( model->bones[ i ].origin, &bones[ i ] ); TransInverse( &bones[ i ], &bones[ i ] ); TransCombine( &bones[ i ], &bone->t, &bones[ i ] ); TransAddScale( backEnd.currentEntity->e.skeleton.scale, &bones[ i ] ); } else { TransInitRotationQuat( model->bones[ i ].rotation, &bones[i] ); TransAddTranslation( model->bones[ i ].origin, &bones[ i ] ); } TransInsScale( model->internalScale, &bones[ i ] ); } // deform the vertices by the lerped bones numVertexes = srf->numVerts; for ( j = 0, v = srf->verts; j < numVertexes; j++, v++ ) { vec3_t tmp; VectorClear( tess.verts[ tess.numVertexes + j ].xyz ); for (unsigned k = 0; k < v->numWeights; k++ ) { TransformPoint( &bones[ v->boneIndexes[ k ] ], v->position, tmp ); VectorMA( tess.verts[ tess.numVertexes + j ].xyz, v->boneWeights[ k ], tmp, tess.verts[ tess.numVertexes + j ].xyz ); } tess.verts[ tess.numVertexes + j ].texCoords[ 0 ] = floatToHalf( v->texCoords[ 0 ] ); tess.verts[ tess.numVertexes + j ].texCoords[ 1 ] = floatToHalf( v->texCoords[ 1 ] ); } } else { tess.attribsSet |= ATTR_QTANGENT; // convert bones back to matrices for (unsigned i = 0; i < model->numBones; i++ ) { if ( backEnd.currentEntity->e.skeleton.type == SK_ABSOLUTE ) { refBone_t *bone = &backEnd.currentEntity->e.skeleton.bones[ i ]; TransInitRotationQuat( model->bones[ i ].rotation, &bones[ i ] ); TransAddTranslation( model->bones[ i ].origin, &bones[ i ] ); TransInverse( &bones[ i ], &bones[ i ] ); TransCombine( &bones[ i ], &bone->t, &bones[ i ] ); TransAddScale( backEnd.currentEntity->e.skeleton.scale, &bones[ i ] ); } else { TransInitScale( backEnd.currentEntity->e.skeleton.scale, &bones[ i ] ); } TransInsScale( model->internalScale, &bones[ i ] ); } // deform the vertices by the lerped bones numVertexes = srf->numVerts; for ( j = 0, v = srf->verts; j < numVertexes; j++, v++ ) { vec3_t tangent, binormal, normal, tmp; VectorClear( tess.verts[ tess.numVertexes + j ].xyz ); VectorClear( normal ); VectorClear( binormal ); VectorClear( tangent ); for(unsigned k = 0; k < v->numWeights; k++ ) { TransformPoint( &bones[ v->boneIndexes[ k ] ], v->position, tmp ); VectorMA( tess.verts[ tess.numVertexes + j ].xyz, v->boneWeights[ k ], tmp, tess.verts[ tess.numVertexes + j ].xyz ); TransformNormalVector( &bones[ v->boneIndexes[ k ] ], v->normal, tmp ); VectorMA( normal, v->boneWeights[ k ], tmp, normal ); TransformNormalVector( &bones[ v->boneIndexes[ k ] ], v->tangent, tmp ); VectorMA( tangent, v->boneWeights[ k ], tmp, tangent ); TransformNormalVector( &bones[ v->boneIndexes[ k ] ], v->binormal, tmp ); VectorMA( binormal, v->boneWeights[ k ], tmp, binormal ); } VectorNormalize( normal ); VectorNormalize( tangent ); VectorNormalize( binormal ); R_TBNtoQtangents( tangent, binormal, normal, tess.verts[ tess.numVertexes + j ].qtangents ); tess.verts[ tess.numVertexes + j ].texCoords[ 0 ] = floatToHalf( v->texCoords[ 0 ] ); tess.verts[ tess.numVertexes + j ].texCoords[ 1 ] = floatToHalf( v->texCoords[ 1 ] ); } } tess.numIndexes += numIndexes; tess.numVertexes += numVertexes; }
/* ================= R_LoadIQModel Load an IQM model and compute the joint matrices for every frame. ================= */ bool R_LoadIQModel( model_t *mod, void *buffer, int filesize, const char *mod_name ) { iqmHeader_t *header; iqmVertexArray_t *vertexarray; iqmTriangle_t *triangle; iqmMesh_t *mesh; iqmJoint_t *joint; iqmPose_t *pose; iqmAnim_t *anim; unsigned short *framedata; char *str, *name; int len; transform_t *trans, *poses; float *bounds; size_t size, len_names; IQModel_t *IQModel; IQAnim_t *IQAnim; srfIQModel_t *surface; vboData_t vboData; float *weightbuf; int *indexbuf; i16vec4_t *qtangentbuf; VBO_t *vbo; IBO_t *ibo; void *ptr; u8vec4_t *weights; if( !LoadIQMFile( buffer, filesize, mod_name, &len_names ) ) { return false; } header = (iqmHeader_t *)buffer; // compute required space size = sizeof(IQModel_t); size += header->num_meshes * sizeof( srfIQModel_t ); size += header->num_anims * sizeof( IQAnim_t ); size += header->num_joints * sizeof( transform_t ); size = PAD( size, 16 ); size += header->num_joints * header->num_frames * sizeof( transform_t ); if(header->ofs_bounds) size += header->num_frames * 6 * sizeof(float); // model bounds size += header->num_vertexes * 3 * sizeof(float); // positions size += header->num_vertexes * 3 * sizeof(float); // normals size += header->num_vertexes * 3 * sizeof(float); // tangents size += header->num_vertexes * 3 * sizeof(float); // bitangents size += header->num_vertexes * 2 * sizeof(int16_t); // texcoords size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes size += header->num_vertexes * 4 * sizeof(byte); // blendWeights size += header->num_vertexes * 4 * sizeof(byte); // colors size += header->num_triangles * 3 * sizeof(int); // triangles size += header->num_joints * sizeof(int); // parents size += len_names; // joint and anim names IQModel = (IQModel_t *)ri.Hunk_Alloc( size, ha_pref::h_low ); mod->type = modtype_t::MOD_IQM; mod->iqm = IQModel; ptr = IQModel + 1; // fill header and setup pointers IQModel->num_vertexes = header->num_vertexes; IQModel->num_triangles = header->num_triangles; IQModel->num_frames = header->num_frames; IQModel->num_surfaces = header->num_meshes; IQModel->num_joints = header->num_joints; IQModel->num_anims = header->num_anims; IQModel->surfaces = (srfIQModel_t *)ptr; ptr = IQModel->surfaces + header->num_meshes; if( header->ofs_anims ) { IQModel->anims = (IQAnim_t *)ptr; ptr = IQModel->anims + header->num_anims; } else { IQModel->anims = nullptr; } IQModel->joints = (transform_t *)PADP(ptr, 16); ptr = IQModel->joints + header->num_joints; if( header->ofs_poses ) { poses = (transform_t *)ptr; ptr = poses + header->num_poses * header->num_frames; } else { poses = nullptr; } if( header->ofs_bounds ) { bounds = (float *)ptr; ptr = bounds + 6 * header->num_frames; } else { bounds = nullptr; } IQModel->positions = (float *)ptr; ptr = IQModel->positions + 3 * header->num_vertexes; IQModel->normals = (float *)ptr; ptr = IQModel->normals + 3 * header->num_vertexes; IQModel->tangents = (float *)ptr; ptr = IQModel->tangents + 3 * header->num_vertexes; IQModel->bitangents = (float *)ptr; ptr = IQModel->bitangents + 3 * header->num_vertexes; IQModel->texcoords = (int16_t *)ptr; ptr = IQModel->texcoords + 2 * header->num_vertexes; IQModel->blendIndexes = (byte *)ptr; ptr = IQModel->blendIndexes + 4 * header->num_vertexes; IQModel->blendWeights = (byte *)ptr; ptr = IQModel->blendWeights + 4 * header->num_vertexes; IQModel->colors = (byte *)ptr; ptr = IQModel->colors + 4 * header->num_vertexes; IQModel->jointParents = (int *)ptr; ptr = IQModel->jointParents + header->num_joints; IQModel->triangles = (int *)ptr; ptr = IQModel->triangles + 3 * header->num_triangles; str = (char *)ptr; IQModel->jointNames = str; // copy joint names joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints ); for(unsigned i = 0; i < header->num_joints; i++, joint++ ) { name = ( char* )IQMPtr( header, header->ofs_text + joint->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } // setup animations IQAnim = IQModel->anims; anim = ( iqmAnim_t* )IQMPtr( header, header->ofs_anims ); for(int i = 0; i < IQModel->num_anims; i++, IQAnim++, anim++ ) { IQAnim->num_frames = anim->num_frames; IQAnim->framerate = anim->framerate; IQAnim->num_joints = header->num_joints; IQAnim->flags = anim->flags; IQAnim->jointParents = IQModel->jointParents; if( poses ) { IQAnim->poses = poses + anim->first_frame * header->num_poses; } else { IQAnim->poses = nullptr; } if( bounds ) { IQAnim->bounds = bounds + anim->first_frame * 6; } else { IQAnim->bounds = nullptr; } IQAnim->name = str; IQAnim->jointNames = IQModel->jointNames; name = ( char* )IQMPtr( header, header->ofs_text + anim->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } // calculate joint transforms trans = IQModel->joints; joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints ); for(unsigned i = 0; i < header->num_joints; i++, joint++, trans++ ) { if( joint->parent >= (int) i ) { Log::Warn("R_LoadIQModel: file %s contains an invalid parent joint number.", mod_name ); return false; } TransInitRotationQuat( joint->rotate, trans ); TransAddScale( joint->scale[0], trans ); TransAddTranslation( joint->translate, trans ); if( joint->parent >= 0 ) { TransCombine( trans, &IQModel->joints[ joint->parent ], trans ); } IQModel->jointParents[i] = joint->parent; } // calculate pose transforms framedata = ( short unsigned int* )IQMPtr( header, header->ofs_frames ); trans = poses; for(unsigned i = 0; i < header->num_frames; i++ ) { pose = ( iqmPose_t* )IQMPtr( header, header->ofs_poses ); for(unsigned j = 0; j < header->num_poses; j++, pose++, trans++ ) { vec3_t translate; quat_t rotate; vec3_t scale; translate[0] = pose->channeloffset[0]; if( pose->mask & 0x001) translate[0] += *framedata++ * pose->channelscale[0]; translate[1] = pose->channeloffset[1]; if( pose->mask & 0x002) translate[1] += *framedata++ * pose->channelscale[1]; translate[2] = pose->channeloffset[2]; if( pose->mask & 0x004) translate[2] += *framedata++ * pose->channelscale[2]; rotate[0] = pose->channeloffset[3]; if( pose->mask & 0x008) rotate[0] += *framedata++ * pose->channelscale[3]; rotate[1] = pose->channeloffset[4]; if( pose->mask & 0x010) rotate[1] += *framedata++ * pose->channelscale[4]; rotate[2] = pose->channeloffset[5]; if( pose->mask & 0x020) rotate[2] += *framedata++ * pose->channelscale[5]; rotate[3] = pose->channeloffset[6]; if( pose->mask & 0x040) rotate[3] += *framedata++ * pose->channelscale[6]; scale[0] = pose->channeloffset[7]; if( pose->mask & 0x080) scale[0] += *framedata++ * pose->channelscale[7]; scale[1] = pose->channeloffset[8]; if( pose->mask & 0x100) scale[1] += *framedata++ * pose->channelscale[8]; scale[2] = pose->channeloffset[9]; if( pose->mask & 0x200) scale[2] += *framedata++ * pose->channelscale[9]; if( scale[0] < 0.0f || (int)( scale[0] - scale[1] ) || (int)( scale[1] - scale[2] ) ) { Log::Warn("R_LoadIQM: file %s contains an invalid scale.", mod_name ); return false; } // construct transformation TransInitRotationQuat( rotate, trans ); TransAddScale( scale[0], trans ); TransAddTranslation( translate, trans ); } } // copy vertexarrays and indexes vertexarray = ( iqmVertexArray_t* )IQMPtr( header, header->ofs_vertexarrays ); for(unsigned i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { int n; // total number of values n = header->num_vertexes * vertexarray->size; switch( vertexarray->type ) { case IQM_POSITION: ClearBounds( IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); Com_Memcpy( IQModel->positions, IQMPtr( header, vertexarray->offset ), n * sizeof(float) ); for( int j = 0; j < n; j += vertexarray->size ) { AddPointToBounds( &IQModel->positions[ j ], IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); } IQModel->internalScale = BoundsMaxExtent( IQModel->bounds[ 0 ], IQModel->bounds[ 1 ] ); if( IQModel->internalScale > 0.0f ) { float inverseScale = 1.0f / IQModel->internalScale; for( int j = 0; j < n; j += vertexarray->size ) { VectorScale( &IQModel->positions[ j ], inverseScale, &IQModel->positions[ j ] ); } } break; case IQM_NORMAL: Com_Memcpy( IQModel->normals, IQMPtr( header, vertexarray->offset ), n * sizeof(float) ); break; case IQM_TANGENT: BuildTangents( header->num_vertexes, ( float* )IQMPtr( header, vertexarray->offset ), IQModel->normals, IQModel->tangents, IQModel->bitangents ); break; case IQM_TEXCOORD: for( int j = 0; j < n; j++ ) { IQModel->texcoords[ j ] = floatToHalf( ((float *)IQMPtr( header, vertexarray->offset ))[ j ] ); } break; case IQM_BLENDINDEXES: Com_Memcpy( IQModel->blendIndexes, IQMPtr( header, vertexarray->offset ), n * sizeof(byte) ); break; case IQM_BLENDWEIGHTS: weights = (u8vec4_t *)IQMPtr( header, vertexarray->offset ); for(unsigned j = 0; j < header->num_vertexes; j++ ) { IQModel->blendWeights[ 4 * j + 0 ] = 255 - weights[ j ][ 1 ] - weights[ j ][ 2 ] - weights[ j ][ 3 ]; IQModel->blendWeights[ 4 * j + 1 ] = weights[ j ][ 1 ]; IQModel->blendWeights[ 4 * j + 2 ] = weights[ j ][ 2 ]; IQModel->blendWeights[ 4 * j + 3 ] = weights[ j ][ 3 ]; } break; case IQM_COLOR: Com_Memcpy( IQModel->colors, IQMPtr( header, vertexarray->offset ), n * sizeof(byte) ); break; } } // copy triangles triangle = ( iqmTriangle_t* )IQMPtr( header, header->ofs_triangles ); for(unsigned i = 0; i < header->num_triangles; i++, triangle++ ) { IQModel->triangles[3*i+0] = triangle->vertex[0]; IQModel->triangles[3*i+1] = triangle->vertex[1]; IQModel->triangles[3*i+2] = triangle->vertex[2]; } // convert data where necessary and create VBO if( r_vboModels->integer && glConfig2.vboVertexSkinningAvailable && IQModel->num_joints <= glConfig2.maxVertexSkinningBones ) { if( IQModel->blendIndexes ) { indexbuf = (int *)ri.Hunk_AllocateTempMemory( sizeof(int[4]) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { indexbuf[ 4 * i + 0 ] = IQModel->blendIndexes[ 4 * i + 0 ]; indexbuf[ 4 * i + 1 ] = IQModel->blendIndexes[ 4 * i + 1 ]; indexbuf[ 4 * i + 2 ] = IQModel->blendIndexes[ 4 * i + 2 ]; indexbuf[ 4 * i + 3 ] = IQModel->blendIndexes[ 4 * i + 3 ]; } } else { indexbuf = nullptr; } if( IQModel->blendWeights ) { const float weightscale = 1.0f / 255.0f; weightbuf = (float *)ri.Hunk_AllocateTempMemory( sizeof(vec4_t) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { if( IQModel->blendWeights[ 4 * i + 0 ] == 0 && IQModel->blendWeights[ 4 * i + 1 ] == 0 && IQModel->blendWeights[ 4 * i + 2 ] == 0 && IQModel->blendWeights[ 4 * i + 3 ] == 0 ) IQModel->blendWeights[ 4 * i + 0 ] = 255; weightbuf[ 4 * i + 0 ] = weightscale * IQModel->blendWeights[ 4 * i + 0 ]; weightbuf[ 4 * i + 1 ] = weightscale * IQModel->blendWeights[ 4 * i + 1 ]; weightbuf[ 4 * i + 2 ] = weightscale * IQModel->blendWeights[ 4 * i + 2 ]; weightbuf[ 4 * i + 3 ] = weightscale * IQModel->blendWeights[ 4 * i + 3 ]; } } else { weightbuf = nullptr; } qtangentbuf = (i16vec4_t *)ri.Hunk_AllocateTempMemory( sizeof( i16vec4_t ) * IQModel->num_vertexes ); for(int i = 0; i < IQModel->num_vertexes; i++ ) { R_TBNtoQtangents( &IQModel->tangents[ 3 * i ], &IQModel->bitangents[ 3 * i ], &IQModel->normals[ 3 * i ], qtangentbuf[ i ] ); } vboData.xyz = (vec3_t *)IQModel->positions; vboData.qtangent = qtangentbuf; vboData.numFrames = 0; vboData.color = (u8vec4_t *)IQModel->colors; vboData.st = (i16vec2_t *)IQModel->texcoords; vboData.noLightCoords = true; vboData.boneIndexes = (int (*)[4])indexbuf; vboData.boneWeights = (vec4_t *)weightbuf; vboData.numVerts = IQModel->num_vertexes; vbo = R_CreateStaticVBO( "IQM surface VBO", vboData, vboLayout_t::VBO_LAYOUT_SKELETAL ); if( qtangentbuf ) { ri.Hunk_FreeTempMemory( qtangentbuf ); } if( weightbuf ) { ri.Hunk_FreeTempMemory( weightbuf ); } if( indexbuf ) { ri.Hunk_FreeTempMemory( indexbuf ); } // create IBO ibo = R_CreateStaticIBO( "IQM surface IBO", ( glIndex_t* )IQModel->triangles, IQModel->num_triangles * 3 ); } else { vbo = nullptr; ibo = nullptr; } // register shaders // overwrite the material offset with the shader index mesh = ( iqmMesh_t* )IQMPtr( header, header->ofs_meshes ); surface = IQModel->surfaces; for(unsigned i = 0; i < header->num_meshes; i++, mesh++, surface++ ) { surface->surfaceType = surfaceType_t::SF_IQM; if( mesh->name ) { surface->name = str; name = ( char* )IQMPtr( header, header->ofs_text + mesh->name ); len = strlen( name ) + 1; Com_Memcpy( str, name, len ); str += len; } else { surface->name = nullptr; } surface->shader = R_FindShader( ( char* )IQMPtr(header, header->ofs_text + mesh->material), shaderType_t::SHADER_3D_DYNAMIC, RSF_DEFAULT ); if( surface->shader->defaultShader ) surface->shader = tr.defaultShader; surface->data = IQModel; surface->first_vertex = mesh->first_vertex; surface->num_vertexes = mesh->num_vertexes; surface->first_triangle = mesh->first_triangle; surface->num_triangles = mesh->num_triangles; surface->vbo = vbo; surface->ibo = ibo; } // copy model bounds if(header->ofs_bounds) { iqmBounds_t *ptr = ( iqmBounds_t* )IQMPtr( header, header->ofs_bounds ); for(unsigned i = 0; i < header->num_frames; i++) { VectorCopy( ptr->bbmin, bounds ); bounds += 3; VectorCopy( ptr->bbmax, bounds ); bounds += 3; ptr++; } } // register animations IQAnim = IQModel->anims; if( header->num_anims == 1 ) { RE_RegisterAnimationIQM( mod_name, IQAnim ); } for(unsigned i = 0; i < header->num_anims; i++, IQAnim++ ) { char name[ MAX_QPATH ]; Com_sprintf( name, MAX_QPATH, "%s:%s", mod_name, IQAnim->name ); RE_RegisterAnimationIQM( name, IQAnim ); } // build VBO return true; }