/* ============= 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_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_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_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_SurfaceIQM Compute vertices for this model surface ================= */ void Tess_SurfaceIQM( srfIQModel_t *surf ) { IQModel_t *model = surf->data; int i, j; int offset = tess.numVertexes - surf->first_vertex; GLimp_LogComment( "--- RB_SurfaceIQM ---\n" ); Tess_CheckOverflow( surf->num_vertexes, surf->num_triangles * 3 ); // compute bones for ( i = 0; i < model->num_joints; i++ ) { if ( backEnd.currentEntity->e.skeleton.type == SK_ABSOLUTE ) { refBone_t *bone = &backEnd.currentEntity->e.skeleton.bones[ i ]; TransInverse( &model->joints[ i ], &bones[ i ] ); TransCombine( &bones[ i ], &bone->t, &bones[ i ] ); } else { TransInit( &bones[ i ] ); } TransAddScale( backEnd.currentEntity->e.skeleton.scale, &bones[ i ] ); TransInsScale( model->internalScale, &bones[ i ] ); } if( surf->vbo && surf->ibo ) { if( model->num_joints > 0 ) { Com_Memcpy( tess.bones, bones, model->num_joints * sizeof(transform_t) ); tess.numBones = model->num_joints; } else { TransInitScale( model->internalScale * backEnd.currentEntity->e.skeleton.scale, &tess.bones[ 0 ] ); tess.numBones = 1; } R_BindVBO( surf->vbo ); R_BindIBO( surf->ibo ); tess.vboVertexSkinning = true; tess.multiDrawIndexes[ tess.multiDrawPrimitives ] = ((glIndex_t *)nullptr) + surf->first_triangle * 3; tess.multiDrawCounts[ tess.multiDrawPrimitives ] = surf->num_triangles * 3; tess.multiDrawPrimitives++; Tess_End(); return; } for ( i = 0; i < surf->num_triangles; i++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = offset + model->triangles[ 3 * ( surf->first_triangle + i ) + 0 ]; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = offset + model->triangles[ 3 * ( surf->first_triangle + i ) + 1 ]; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = offset + model->triangles[ 3 * ( surf->first_triangle + i ) + 2 ]; } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD | ATTR_QTANGENT; if( model->num_joints > 0 && model->blendWeights && model->blendIndexes ) { // deform the vertices by the lerped bones for ( i = 0; i < surf->num_vertexes; i++ ) { int idxIn = surf->first_vertex + i; int idxOut = tess.numVertexes + i; const float weightFactor = 1.0f / 255.0f; vec3_t tangent, binormal, normal, tmp; if( model->blendWeights[ 4 * idxIn + 0 ] == 0 && model->blendWeights[ 4 * idxIn + 1 ] == 0 && model->blendWeights[ 4 * idxIn + 2 ] == 0 && model->blendWeights[ 4 * idxIn + 3 ] == 0 ) model->blendWeights[ 4 * idxIn + 0 ] = 255; VectorClear( tess.verts[ idxOut ].xyz ); VectorClear( normal ); VectorClear( tangent ); VectorClear( binormal ); for ( j = 0; j < 4; j++ ) { int bone = model->blendIndexes[ 4 * idxIn + j ]; float weight = weightFactor * model->blendWeights[ 4 * idxIn + j ]; TransformPoint( &bones[ bone ], &model->positions[ 3 * idxIn ], tmp ); VectorMA( tess.verts[ idxOut ].xyz, weight, tmp, tess.verts[ idxOut ].xyz ); TransformNormalVector( &bones[ bone ], &model->normals[ 3 * idxIn ], tmp ); VectorMA( normal, weight, tmp, normal ); TransformNormalVector( &bones[ bone ], &model->tangents[ 3 * idxIn ], tmp ); VectorMA( tangent, weight, tmp, tangent ); TransformNormalVector( &bones[ bone ], &model->bitangents[ 3 * idxIn ], tmp ); VectorMA( binormal, weight, tmp, binormal ); } VectorNormalize( normal ); VectorNormalize( tangent ); VectorNormalize( binormal ); R_TBNtoQtangents( tangent, binormal, normal, tess.verts[ idxOut ].qtangents ); tess.verts[ idxOut ].texCoords[ 0 ] = model->texcoords[ 2 * idxIn + 0 ]; tess.verts[ idxOut ].texCoords[ 1 ] = model->texcoords[ 2 * idxIn + 1 ]; } } else { for ( i = 0; i < surf->num_vertexes; i++ ) { int idxIn = surf->first_vertex + i; int idxOut = tess.numVertexes + i; float scale = model->internalScale * backEnd.currentEntity->e.skeleton.scale; VectorScale( &model->positions[ 3 * idxIn ], scale, tess.verts[ idxOut ].xyz ); R_TBNtoQtangents( &model->tangents[ 3 * idxIn ], &model->bitangents[ 3 * idxIn ], &model->normals[ 3 * idxIn ], tess.verts[ idxOut ].qtangents ); tess.verts[ idxOut ].texCoords[ 0 ] = model->texcoords[ 2 * idxIn + 0 ]; tess.verts[ idxOut ].texCoords[ 1 ] = model->texcoords[ 2 * idxIn + 1 ]; } } tess.numIndexes += 3 * surf->num_triangles; tess.numVertexes += surf->num_vertexes; }
/* ============== 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; }
/* ================= MakeMeshNormals Handles all the complicated wrapping and degenerate cases ================= */ static void MakeMeshNormals( int width, int height, srfVert_t ctrl[ MAX_GRID_SIZE ][ MAX_GRID_SIZE ] ) { int i, j, k, dist; vec3_t tangent, binormal, normal; vec3_t sum, sumTangents, sumBinormals; int count; vec3_t base; vec3_t delta; int x, y; srfVert_t *dv; vec3_t around[ 8 ], temp; bool good[ 8 ]; vec2_t st[8]; bool wrapWidth, wrapHeight; float len; static int neighbors[ 8 ][ 2 ] = { { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 } }; wrapWidth = false; for ( i = 0; i < height; i++ ) { VectorSubtract( ctrl[ i ][ 0 ].xyz, ctrl[ i ][ width - 1 ].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == height ) { wrapWidth = true; } wrapHeight = false; for ( i = 0; i < width; i++ ) { VectorSubtract( ctrl[ 0 ][ i ].xyz, ctrl[ height - 1 ][ i ].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == width ) { wrapHeight = true; } for ( i = 0; i < width; i++ ) { for ( j = 0; j < height; j++ ) { count = 0; dv = &ctrl[ j ][ i ]; VectorCopy( dv->xyz, base ); for ( k = 0; k < 8; k++ ) { VectorClear( around[ k ] ); good[ k ] = false; for ( dist = 1; dist <= 3; dist++ ) { x = i + neighbors[ k ][ 0 ] * dist; y = j + neighbors[ k ][ 1 ] * dist; if ( wrapWidth ) { if ( x < 0 ) { x = width - 1 + x; } else if ( x >= width ) { x = 1 + x - width; } } if ( wrapHeight ) { if ( y < 0 ) { y = height - 1 + y; } else if ( y >= height ) { y = 1 + y - height; } } if ( x < 0 || x >= width || y < 0 || y >= height ) { break; // edge of patch } VectorSubtract( ctrl[ y ][ x ].xyz, base, temp ); if ( VectorNormalize2( temp, temp ) == 0 ) { continue; // degenerate edge, get more dist } else { good[ k ] = true; VectorCopy( temp, around[ k ] ); Vector2Copy( ctrl[ y ][ x ].st, st[ k ] ); break; // good edge } } } VectorClear( sum ); VectorClear( sumTangents ); VectorClear( sumBinormals ); for ( k = 0; k < 8; k++ ) { if ( !good[ k ] || !good[( k + 1 ) & 7 ] ) { continue; // didn't get two points } CrossProduct( around[( k + 1 ) & 7 ], around[ k ], normal ); if ( VectorNormalize2( normal, normal ) == 0 ) { continue; } R_CalcTangents( tangent, binormal, vec3_origin, around[ k ], around[ ( k + 1 ) & 7 ], dv->st, st[ k ], st[ ( k + 1 ) & 7 ] ); VectorAdd( normal, sum, sum ); VectorAdd( tangent, sumTangents, sumTangents ); VectorAdd( binormal, sumBinormals, sumBinormals ); count++; } if ( count == 0 ) { VectorSet( dv->normal, 0.0f, 0.0f, 1.0f ); } else { VectorNormalize2( sum, dv->normal ); } R_TBNtoQtangents( sumTangents, sumBinormals, dv->normal, dv->qtangent ); } } }