From cccd283be84935c46d473edf0a667efa5707303a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 27 Jul 2018 17:40:21 -0500 Subject: [PATCH] Improve IQM CPU vertex skinning performance Only calculate vertex blend matrix for each unique bone indexes/weights combination once per-surface instead of recalculating for each vertex. For best performance the model surfaces needs to use few vertex bone indexes and weights combinations. Unroll loops so GCC better optimizes them. In my tests drawing animated IQM may take 50% as long in opengl1 and 70% as long in opengl2. It will vary by model though and might not help much at all. Made unanimated IQM models skip matrix math altogether. --- code/renderergl1/tr_local.h | 14 +- code/renderergl1/tr_model_iqm.c | 409 +++++++++++++++++++++---------- code/renderergl2/tr_local.h | 14 +- code/renderergl2/tr_model_iqm.c | 418 ++++++++++++++++++++++---------- 4 files changed, 601 insertions(+), 254 deletions(-) diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index 464d6221..fe42f4de 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -600,21 +600,26 @@ typedef struct { struct srfIQModel_s *surfaces; int *triangles; + + // vertex arrays float *positions; float *texcoords; float *normals; float *tangents; - byte *blendIndexes; + byte *colors; + int *influences; // [num_vertexes] indexes into influenceBlendVertexes + + // unique list of vertex blend indexes/weights for faster CPU vertex skinning + byte *influenceBlendIndexes; // [num_influences] union { float *f; byte *b; - } blendWeights; - byte *colors; + } influenceBlendWeights; // [num_influences] // depending upon the exporter, blend indices and weights might be int/float // as opposed to the recommended byte/byte, for example Noesis exports // int/float whereas the official IQM tool exports byte/byte - byte blendWeightsType; // IQM_UBYTE or IQM_FLOAT + int blendWeightsType; // IQM_UBYTE or IQM_FLOAT char *jointNames; int *jointParents; @@ -631,6 +636,7 @@ typedef struct srfIQModel_s { iqmData_t *data; int first_vertex, num_vertexes; int first_triangle, num_triangles; + int first_influence, num_influences; } srfIQModel_t; diff --git a/code/renderergl1/tr_model_iqm.c b/code/renderergl1/tr_model_iqm.c index 35240ec2..bd256a98 100644 --- a/code/renderergl1/tr_model_iqm.c +++ b/code/renderergl1/tr_model_iqm.c @@ -143,7 +143,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na iqmBounds_t *bounds; unsigned short *framedata; char *str; - int i, j; + int i, j, k; float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f}; float *mat, *matInv; size_t size, joint_names; @@ -152,6 +152,12 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na srfIQModel_t *surface; char meshName[MAX_QPATH]; int vertexArrayFormat[IQM_COLOR+1]; + int allocateInfluences; + byte *blendIndexes; + union { + byte *b; + float *f; + } blendWeights; if( filesize < sizeof(iqmHeader_t) ) { return qfalse; @@ -211,6 +217,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na vertexArrayFormat[i] = -1; } + blendIndexes = NULL; + blendWeights.b = NULL; + + allocateInfluences = 0; + if ( header->num_meshes ) { // check and swap vertex arrays @@ -288,6 +299,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na vertexarray->size != 4 ) { return qfalse; } + blendIndexes = (byte*)header + vertexarray->offset; break; case IQM_BLENDWEIGHTS: if( (vertexarray->format != IQM_FLOAT && @@ -295,6 +307,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na vertexarray->size != 4 ) { return qfalse; } + if( vertexarray->format == IQM_FLOAT ) { + blendWeights.f = (float*)( (byte*)header + vertexarray->offset ); + } else { + blendWeights.b = (byte*)header + vertexarray->offset; + } break; case IQM_COLOR: if( vertexarray->format != IQM_UBYTE || @@ -325,9 +342,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na // opengl1 renderer doesn't use tangents vertexArrayFormat[IQM_TANGENT] = -1; - // FIXME: don't require colors array when drawing - vertexArrayFormat[IQM_COLOR] = IQM_UBYTE; - // check and swap triangles if( IQM_CheckRange( header, header->ofs_triangles, header->num_triangles, sizeof(iqmTriangle_t) ) ) { @@ -388,6 +402,38 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na mesh->material >= header->num_text ) { return qfalse; } + + // find number of unique blend influences per mesh + if( header->num_joints ) { + for( j = 0; j < mesh->num_vertexes; j++ ) { + int vtx = mesh->first_vertex + j; + + for( k = 0; k < j; k++ ) { + int influence = mesh->first_vertex + k; + + if( *(int*)&blendIndexes[4*influence] != *(int*)&blendIndexes[4*vtx] ) { + continue; + } + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + if ( blendWeights.f[4*influence+0] == blendWeights.f[4*vtx+0] && + blendWeights.f[4*influence+1] == blendWeights.f[4*vtx+1] && + blendWeights.f[4*influence+2] == blendWeights.f[4*vtx+2] && + blendWeights.f[4*influence+3] == blendWeights.f[4*vtx+3] ) { + break; + } + } else { + if ( *(int*)&blendWeights.b[4*influence] == *(int*)&blendWeights.b[4*vtx] ) { + break; + } + } + } + + if ( k == j ) { + allocateInfluences++; + } + } + } } } @@ -500,19 +546,20 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na size += header->num_vertexes * 4 * sizeof(float); // tangents } - if ( vertexArrayFormat[IQM_BLENDINDEXES] != -1 ) { - size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes - } - - if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { - size += header->num_vertexes * 4 * sizeof(byte); // blendWeights - } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { - size += header->num_vertexes * 4 * sizeof(float); // blendWeights - } - if ( vertexArrayFormat[IQM_COLOR] != -1 ) { size += header->num_vertexes * 4 * sizeof(byte); // colors } + + if ( allocateInfluences ) { + size += header->num_vertexes * sizeof(int); // influences + size += allocateInfluences * 4 * sizeof(byte); // influenceBlendIndexes + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { + size += allocateInfluences * 4 * sizeof(byte); // influenceBlendWeights + } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + size += allocateInfluences * 4 * sizeof(float); // influenceBlendWeights + } + } } if( header->num_joints ) { size += joint_names; // joint names @@ -561,23 +608,26 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na dataPtr += header->num_vertexes * 4 * sizeof(float); // tangents } - if ( vertexArrayFormat[IQM_BLENDINDEXES] != -1 ) { - iqmData->blendIndexes = (byte*)dataPtr; - dataPtr += header->num_vertexes * 4 * sizeof(byte); // blendIndexes - } - - if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { - iqmData->blendWeights.b = (byte*)dataPtr; - dataPtr += header->num_vertexes * 4 * sizeof(byte); // blendWeights - } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { - iqmData->blendWeights.f = (float*)dataPtr; - dataPtr += header->num_vertexes * 4 * sizeof(float); // blendWeights - } - if ( vertexArrayFormat[IQM_COLOR] != -1 ) { iqmData->colors = (byte*)dataPtr; dataPtr += header->num_vertexes * 4 * sizeof(byte); // colors } + + if ( allocateInfluences ) { + iqmData->influences = (int*)dataPtr; + dataPtr += header->num_vertexes * sizeof(int); // influences + + iqmData->influenceBlendIndexes = (byte*)dataPtr; + dataPtr += allocateInfluences * 4 * sizeof(byte); // influenceBlendIndexes + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { + iqmData->influenceBlendWeights.b = (byte*)dataPtr; + dataPtr += allocateInfluences * 4 * sizeof(byte); // influenceBlendWeights + } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + iqmData->influenceBlendWeights.f = (float*)dataPtr; + dataPtr += allocateInfluences * 4 * sizeof(float); // influenceBlendWeights + } + } } if( header->num_joints ) { iqmData->jointNames = (char*)dataPtr; @@ -662,27 +712,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na n * sizeof(float) ); break; case IQM_BLENDINDEXES: - if( vertexarray->format == IQM_INT ) { - int *data = (int*)((byte*)header + vertexarray->offset); - for ( j = 0; j < n; j++ ) { - iqmData->blendIndexes[j] = (byte)data[j]; - } - } else { - Com_Memcpy( iqmData->blendIndexes, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - } - break; case IQM_BLENDWEIGHTS: - if( vertexarray->format == IQM_FLOAT ) { - Com_Memcpy( iqmData->blendWeights.f, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - } else { - Com_Memcpy( iqmData->blendWeights.b, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - } break; case IQM_COLOR: Com_Memcpy( iqmData->colors, @@ -691,6 +721,68 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na break; } } + + // find unique blend influences per mesh + if( allocateInfluences ) { + int vtx, influence, totalInfluences = 0; + + surface = iqmData->surfaces; + for( i = 0; i < header->num_meshes; i++, surface++ ) { + surface->first_influence = totalInfluences; + surface->num_influences = 0; + + for( j = 0; j < surface->num_vertexes; j++ ) { + vtx = surface->first_vertex + j; + + for( k = 0; k < surface->num_influences; k++ ) { + influence = surface->first_influence + k; + + if( *(int*)&iqmData->influenceBlendIndexes[4*influence] != *(int*)&blendIndexes[4*vtx] ) { + continue; + } + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + if ( iqmData->influenceBlendWeights.f[4*influence+0] == blendWeights.f[4*vtx+0] && + iqmData->influenceBlendWeights.f[4*influence+1] == blendWeights.f[4*vtx+1] && + iqmData->influenceBlendWeights.f[4*influence+2] == blendWeights.f[4*vtx+2] && + iqmData->influenceBlendWeights.f[4*influence+3] == blendWeights.f[4*vtx+3] ) { + break; + } + } else { + if ( *(int*)&iqmData->influenceBlendWeights.b[4*influence] == *(int*)&blendWeights.b[4*vtx] ) { + break; + } + } + } + + iqmData->influences[vtx] = surface->first_influence + k; + + if( k == surface->num_influences ) { + influence = surface->first_influence + k; + + iqmData->influenceBlendIndexes[4*influence+0] = blendIndexes[4*vtx+0]; + iqmData->influenceBlendIndexes[4*influence+1] = blendIndexes[4*vtx+1]; + iqmData->influenceBlendIndexes[4*influence+2] = blendIndexes[4*vtx+2]; + iqmData->influenceBlendIndexes[4*influence+3] = blendIndexes[4*vtx+3]; + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + iqmData->influenceBlendWeights.f[4*influence+0] = blendWeights.f[4*vtx+0]; + iqmData->influenceBlendWeights.f[4*influence+1] = blendWeights.f[4*vtx+1]; + iqmData->influenceBlendWeights.f[4*influence+2] = blendWeights.f[4*vtx+2]; + iqmData->influenceBlendWeights.f[4*influence+3] = blendWeights.f[4*vtx+3]; + } else { + iqmData->influenceBlendWeights.b[4*influence+0] = blendWeights.b[4*vtx+0]; + iqmData->influenceBlendWeights.b[4*influence+1] = blendWeights.b[4*vtx+1]; + iqmData->influenceBlendWeights.b[4*influence+2] = blendWeights.b[4*vtx+2]; + iqmData->influenceBlendWeights.b[4*influence+3] = blendWeights.b[4*vtx+3]; + } + + totalInfluences++; + surface->num_influences++; + } + } + } + } } if( header->num_joints ) @@ -1103,8 +1195,14 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { srfIQModel_t *surf = (srfIQModel_t *)surface; iqmData_t *data = surf->data; float jointMats[IQM_MAX_JOINTS * 12]; + float influenceVtxMat[SHADER_MAX_VERTEXES * 12]; + float influenceNrmMat[SHADER_MAX_VERTEXES * 9]; int i; + float *xyz; + float *normal; + float *texCoords; + byte *color; vec4_t *outXYZ; vec4_t *outNormal; vec2_t (*outTexCoord)[2]; @@ -1120,102 +1218,165 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); + xyz = &data->positions[surf->first_vertex * 3]; + normal = &data->normals[surf->first_vertex * 3]; + texCoords = &data->texcoords[surf->first_vertex * 2]; + + if ( data->colors ) { + color = &data->colors[surf->first_vertex * 4]; + } else { + color = NULL; + } + outXYZ = &tess.xyz[tess.numVertexes]; outNormal = &tess.normal[tess.numVertexes]; outTexCoord = &tess.texCoords[tess.numVertexes]; outColor = &tess.vertexColors[tess.numVertexes]; - // compute interpolated joint matrices if ( data->num_poses > 0 ) { + // compute interpolated joint matrices ComputePoseMats( data, frame, oldframe, backlerp, jointMats ); - } - // transform vertexes and fill other data - for( i = 0; i < surf->num_vertexes; - i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { - int j, k; - float vtxMat[12]; - float nrmMat[9]; - int vtx = i + surf->first_vertex; - float blendWeights[4]; - int numWeights; + // compute vertex blend influence matricies + for( i = 0; i < surf->num_influences; i++ ) { + int influence = surf->first_influence + i; + float *vtxMat = &influenceVtxMat[12*i]; + float *nrmMat = &influenceNrmMat[9*i]; + int j; + float blendWeights[4]; + int numWeights; - for ( numWeights = 0; numWeights < 4; numWeights++ ) { - if ( data->blendWeightsType == IQM_FLOAT ) - blendWeights[numWeights] = data->blendWeights.f[4*vtx + numWeights]; - else - blendWeights[numWeights] = (float)data->blendWeights.b[4*vtx + numWeights] / 255.0f; + for ( numWeights = 0; numWeights < 4; numWeights++ ) { + if ( data->blendWeightsType == IQM_FLOAT ) + blendWeights[numWeights] = data->influenceBlendWeights.f[4*influence + numWeights]; + else + blendWeights[numWeights] = (float)data->influenceBlendWeights.b[4*influence + numWeights] / 255.0f; - if ( blendWeights[numWeights] <= 0 ) - break; - } + if ( blendWeights[numWeights] <= 0.0f ) + break; + } - if ( data->num_poses == 0 || numWeights == 0 ) { - // no blend joint, use identity matrix. - Com_Memcpy( vtxMat, identityMatrix, 12 * sizeof (float) ); - } else { - // compute the vertex matrix by blending the up to - // four blend weights - Com_Memset( vtxMat, 0, 12 * sizeof (float) ); - for( j = 0; j < numWeights; j++ ) { - for( k = 0; k < 12; k++ ) { - vtxMat[k] += blendWeights[j] * jointMats[12*data->blendIndexes[4*vtx + j] + k]; + if ( numWeights == 0 ) { + // no blend joint, use identity matrix. + vtxMat[0] = identityMatrix[0]; + vtxMat[1] = identityMatrix[1]; + vtxMat[2] = identityMatrix[2]; + vtxMat[3] = identityMatrix[3]; + vtxMat[4] = identityMatrix[4]; + vtxMat[5] = identityMatrix[5]; + vtxMat[6] = identityMatrix[6]; + vtxMat[7] = identityMatrix[7]; + vtxMat[8] = identityMatrix[8]; + vtxMat[9] = identityMatrix[9]; + vtxMat[10] = identityMatrix[10]; + vtxMat[11] = identityMatrix[11]; + } else { + // compute the vertex matrix by blending the up to + // four blend weights + vtxMat[0] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 0]; + vtxMat[1] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 1]; + vtxMat[2] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 2]; + vtxMat[3] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 3]; + vtxMat[4] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 4]; + vtxMat[5] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 5]; + vtxMat[6] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 6]; + vtxMat[7] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 7]; + vtxMat[8] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 8]; + vtxMat[9] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 9]; + vtxMat[10] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 10]; + vtxMat[11] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 11]; + + for( j = 1; j < numWeights; j++ ) { + vtxMat[0] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 0]; + vtxMat[1] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 1]; + vtxMat[2] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 2]; + vtxMat[3] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 3]; + vtxMat[4] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 4]; + vtxMat[5] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 5]; + vtxMat[6] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 6]; + vtxMat[7] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 7]; + vtxMat[8] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 8]; + vtxMat[9] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 9]; + vtxMat[10] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 10]; + vtxMat[11] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 11]; } } + + // compute the normal matrix as transpose of the adjoint + // of the vertex matrix + nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; + nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; + nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; + nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; + nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; + nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; + nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; + nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; + nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; } - // compute the normal matrix as transpose of the adjoint - // of the vertex matrix - nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; - nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; - nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; - nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; - nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; - nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; - nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; - nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; - nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; + // transform vertexes and fill other data + for( i = 0; i < surf->num_vertexes; + i++, xyz+=3, normal+=3, texCoords+=2, + outXYZ++, outNormal++, outTexCoord++ ) { + int influence = data->influences[surf->first_vertex + i] - surf->first_influence; + float *vtxMat = &influenceVtxMat[12*influence]; + float *nrmMat = &influenceNrmMat[9*influence]; - (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; - (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; - (*outTexCoord)[1][0] = (*outTexCoord)[0][0]; - (*outTexCoord)[1][1] = (*outTexCoord)[0][1]; + (*outTexCoord)[0][0] = texCoords[0]; + (*outTexCoord)[0][1] = texCoords[1]; - (*outXYZ)[0] = - vtxMat[ 0] * data->positions[3*vtx+0] + - vtxMat[ 1] * data->positions[3*vtx+1] + - vtxMat[ 2] * data->positions[3*vtx+2] + - vtxMat[ 3]; - (*outXYZ)[1] = - vtxMat[ 4] * data->positions[3*vtx+0] + - vtxMat[ 5] * data->positions[3*vtx+1] + - vtxMat[ 6] * data->positions[3*vtx+2] + - vtxMat[ 7]; - (*outXYZ)[2] = - vtxMat[ 8] * data->positions[3*vtx+0] + - vtxMat[ 9] * data->positions[3*vtx+1] + - vtxMat[10] * data->positions[3*vtx+2] + - vtxMat[11]; - (*outXYZ)[3] = 1.0f; + (*outXYZ)[0] = + vtxMat[ 0] * xyz[0] + + vtxMat[ 1] * xyz[1] + + vtxMat[ 2] * xyz[2] + + vtxMat[ 3]; + (*outXYZ)[1] = + vtxMat[ 4] * xyz[0] + + vtxMat[ 5] * xyz[1] + + vtxMat[ 6] * xyz[2] + + vtxMat[ 7]; + (*outXYZ)[2] = + vtxMat[ 8] * xyz[0] + + vtxMat[ 9] * xyz[1] + + vtxMat[10] * xyz[2] + + vtxMat[11]; - (*outNormal)[0] = - nrmMat[ 0] * data->normals[3*vtx+0] + - nrmMat[ 1] * data->normals[3*vtx+1] + - nrmMat[ 2] * data->normals[3*vtx+2]; - (*outNormal)[1] = - nrmMat[ 3] * data->normals[3*vtx+0] + - nrmMat[ 4] * data->normals[3*vtx+1] + - nrmMat[ 5] * data->normals[3*vtx+2]; - (*outNormal)[2] = - nrmMat[ 6] * data->normals[3*vtx+0] + - nrmMat[ 7] * data->normals[3*vtx+1] + - nrmMat[ 8] * data->normals[3*vtx+2]; - (*outNormal)[3] = 0.0f; + (*outNormal)[0] = + nrmMat[ 0] * normal[0] + + nrmMat[ 1] * normal[1] + + nrmMat[ 2] * normal[2]; + (*outNormal)[1] = + nrmMat[ 3] * normal[0] + + nrmMat[ 4] * normal[1] + + nrmMat[ 5] * normal[2]; + (*outNormal)[2] = + nrmMat[ 6] * normal[0] + + nrmMat[ 7] * normal[1] + + nrmMat[ 8] * normal[2]; + } + } else { + // copy vertexes and fill other data + for( i = 0; i < surf->num_vertexes; + i++, xyz+=3, normal+=3, texCoords+=2, + outXYZ++, outNormal++, outTexCoord++ ) { + (*outTexCoord)[0][0] = texCoords[0]; + (*outTexCoord)[0][1] = texCoords[1]; - (*outColor)[0] = data->colors[4*vtx+0]; - (*outColor)[1] = data->colors[4*vtx+1]; - (*outColor)[2] = data->colors[4*vtx+2]; - (*outColor)[3] = data->colors[4*vtx+3]; + (*outXYZ)[0] = xyz[0]; + (*outXYZ)[1] = xyz[1]; + (*outXYZ)[2] = xyz[2]; + + (*outNormal)[0] = normal[0]; + (*outNormal)[1] = normal[1]; + (*outNormal)[2] = normal[2]; + } + } + + if ( color ) { + Com_Memcpy( outColor, color, surf->num_vertexes * sizeof( outColor[0] ) ); + } else { + Com_Memset( outColor, 0, surf->num_vertexes * sizeof( outColor[0] ) ); } tri = data->triangles + 3 * surf->first_triangle; diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 3d45819d..193879c5 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -950,21 +950,26 @@ typedef struct { struct srfIQModel_s *surfaces; int *triangles; + + // vertex arrays float *positions; float *texcoords; float *normals; float *tangents; - byte *blendIndexes; + byte *colors; + int *influences; // [num_vertexes] indexes into influenceBlendVertexes + + // unique list of vertex blend indexes/weights for faster CPU vertex skinning + byte *influenceBlendIndexes; // [num_influences] union { float *f; byte *b; - } blendWeights; - byte *colors; + } influenceBlendWeights; // [num_influences] // depending upon the exporter, blend indices and weights might be int/float // as opposed to the recommended byte/byte, for example Noesis exports // int/float whereas the official IQM tool exports byte/byte - byte blendWeightsType; // IQM_UBYTE or IQM_FLOAT + int blendWeightsType; // IQM_UBYTE or IQM_FLOAT char *jointNames; int *jointParents; @@ -981,6 +986,7 @@ typedef struct srfIQModel_s { iqmData_t *data; int first_vertex, num_vertexes; int first_triangle, num_triangles; + int first_influence, num_influences; } srfIQModel_t; typedef struct srfVaoMdvMesh_s diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index a935c0b7..6fafe367 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -143,7 +143,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na iqmBounds_t *bounds; unsigned short *framedata; char *str; - int i, j; + int i, j, k; float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f}; float *mat, *matInv; size_t size, joint_names; @@ -152,6 +152,12 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na srfIQModel_t *surface; char meshName[MAX_QPATH]; int vertexArrayFormat[IQM_COLOR+1]; + int allocateInfluences; + byte *blendIndexes; + union { + byte *b; + float *f; + } blendWeights; if( filesize < sizeof(iqmHeader_t) ) { return qfalse; @@ -211,6 +217,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na vertexArrayFormat[i] = -1; } + blendIndexes = NULL; + blendWeights.b = NULL; + + allocateInfluences = 0; + if ( header->num_meshes ) { // check and swap vertex arrays @@ -288,6 +299,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na vertexarray->size != 4 ) { return qfalse; } + blendIndexes = (byte*)header + vertexarray->offset; break; case IQM_BLENDWEIGHTS: if( (vertexarray->format != IQM_FLOAT && @@ -295,6 +307,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na vertexarray->size != 4 ) { return qfalse; } + if( vertexarray->format == IQM_FLOAT ) { + blendWeights.f = (float*)( (byte*)header + vertexarray->offset ); + } else { + blendWeights.b = (byte*)header + vertexarray->offset; + } break; case IQM_COLOR: if( vertexarray->format != IQM_UBYTE || @@ -328,9 +345,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na return qfalse; } - // FIXME: don't require colors array when drawing - vertexArrayFormat[IQM_COLOR] = IQM_UBYTE; - // check and swap triangles if( IQM_CheckRange( header, header->ofs_triangles, header->num_triangles, sizeof(iqmTriangle_t) ) ) { @@ -391,6 +405,38 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na mesh->material >= header->num_text ) { return qfalse; } + + // find number of unique blend influences per mesh + if( header->num_joints ) { + for( j = 0; j < mesh->num_vertexes; j++ ) { + int vtx = mesh->first_vertex + j; + + for( k = 0; k < j; k++ ) { + int influence = mesh->first_vertex + k; + + if( *(int*)&blendIndexes[4*influence] != *(int*)&blendIndexes[4*vtx] ) { + continue; + } + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + if ( blendWeights.f[4*influence+0] == blendWeights.f[4*vtx+0] && + blendWeights.f[4*influence+1] == blendWeights.f[4*vtx+1] && + blendWeights.f[4*influence+2] == blendWeights.f[4*vtx+2] && + blendWeights.f[4*influence+3] == blendWeights.f[4*vtx+3] ) { + break; + } + } else { + if ( *(int*)&blendWeights.b[4*influence] == *(int*)&blendWeights.b[4*vtx] ) { + break; + } + } + } + + if ( k == j ) { + allocateInfluences++; + } + } + } } } @@ -503,19 +549,20 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na size += header->num_vertexes * 4 * sizeof(float); // tangents } - if ( vertexArrayFormat[IQM_BLENDINDEXES] != -1 ) { - size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes - } - - if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { - size += header->num_vertexes * 4 * sizeof(byte); // blendWeights - } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { - size += header->num_vertexes * 4 * sizeof(float); // blendWeights - } - if ( vertexArrayFormat[IQM_COLOR] != -1 ) { size += header->num_vertexes * 4 * sizeof(byte); // colors } + + if ( allocateInfluences ) { + size += header->num_vertexes * sizeof(int); // influences + size += allocateInfluences * 4 * sizeof(byte); // influenceBlendIndexes + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { + size += allocateInfluences * 4 * sizeof(byte); // influenceBlendWeights + } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + size += allocateInfluences * 4 * sizeof(float); // influenceBlendWeights + } + } } if( header->num_joints ) { size += joint_names; // joint names @@ -564,23 +611,26 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na dataPtr += header->num_vertexes * 4 * sizeof(float); // tangents } - if ( vertexArrayFormat[IQM_BLENDINDEXES] != -1 ) { - iqmData->blendIndexes = (byte*)dataPtr; - dataPtr += header->num_vertexes * 4 * sizeof(byte); // blendIndexes - } - - if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { - iqmData->blendWeights.b = (byte*)dataPtr; - dataPtr += header->num_vertexes * 4 * sizeof(byte); // blendWeights - } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { - iqmData->blendWeights.f = (float*)dataPtr; - dataPtr += header->num_vertexes * 4 * sizeof(float); // blendWeights - } - if ( vertexArrayFormat[IQM_COLOR] != -1 ) { iqmData->colors = (byte*)dataPtr; dataPtr += header->num_vertexes * 4 * sizeof(byte); // colors } + + if ( allocateInfluences ) { + iqmData->influences = (int*)dataPtr; + dataPtr += header->num_vertexes * sizeof(int); // influences + + iqmData->influenceBlendIndexes = (byte*)dataPtr; + dataPtr += allocateInfluences * 4 * sizeof(byte); // influenceBlendIndexes + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_UBYTE ) { + iqmData->influenceBlendWeights.b = (byte*)dataPtr; + dataPtr += allocateInfluences * 4 * sizeof(byte); // influenceBlendWeights + } else if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + iqmData->influenceBlendWeights.f = (float*)dataPtr; + dataPtr += allocateInfluences * 4 * sizeof(float); // influenceBlendWeights + } + } } if( header->num_joints ) { iqmData->jointNames = (char*)dataPtr; @@ -665,27 +715,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na n * sizeof(float) ); break; case IQM_BLENDINDEXES: - if( vertexarray->format == IQM_INT ) { - int *data = (int*)((byte*)header + vertexarray->offset); - for ( j = 0; j < n; j++ ) { - iqmData->blendIndexes[j] = (byte)data[j]; - } - } else { - Com_Memcpy( iqmData->blendIndexes, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - } - break; case IQM_BLENDWEIGHTS: - if( vertexarray->format == IQM_FLOAT ) { - Com_Memcpy( iqmData->blendWeights.f, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - } else { - Com_Memcpy( iqmData->blendWeights.b, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - } break; case IQM_COLOR: Com_Memcpy( iqmData->colors, @@ -694,6 +724,68 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na break; } } + + // find unique blend influences per mesh + if( allocateInfluences ) { + int vtx, influence, totalInfluences = 0; + + surface = iqmData->surfaces; + for( i = 0; i < header->num_meshes; i++, surface++ ) { + surface->first_influence = totalInfluences; + surface->num_influences = 0; + + for( j = 0; j < surface->num_vertexes; j++ ) { + vtx = surface->first_vertex + j; + + for( k = 0; k < surface->num_influences; k++ ) { + influence = surface->first_influence + k; + + if( *(int*)&iqmData->influenceBlendIndexes[4*influence] != *(int*)&blendIndexes[4*vtx] ) { + continue; + } + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + if ( iqmData->influenceBlendWeights.f[4*influence+0] == blendWeights.f[4*vtx+0] && + iqmData->influenceBlendWeights.f[4*influence+1] == blendWeights.f[4*vtx+1] && + iqmData->influenceBlendWeights.f[4*influence+2] == blendWeights.f[4*vtx+2] && + iqmData->influenceBlendWeights.f[4*influence+3] == blendWeights.f[4*vtx+3] ) { + break; + } + } else { + if ( *(int*)&iqmData->influenceBlendWeights.b[4*influence] == *(int*)&blendWeights.b[4*vtx] ) { + break; + } + } + } + + iqmData->influences[vtx] = surface->first_influence + k; + + if( k == surface->num_influences ) { + influence = surface->first_influence + k; + + iqmData->influenceBlendIndexes[4*influence+0] = blendIndexes[4*vtx+0]; + iqmData->influenceBlendIndexes[4*influence+1] = blendIndexes[4*vtx+1]; + iqmData->influenceBlendIndexes[4*influence+2] = blendIndexes[4*vtx+2]; + iqmData->influenceBlendIndexes[4*influence+3] = blendIndexes[4*vtx+3]; + + if( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + iqmData->influenceBlendWeights.f[4*influence+0] = blendWeights.f[4*vtx+0]; + iqmData->influenceBlendWeights.f[4*influence+1] = blendWeights.f[4*vtx+1]; + iqmData->influenceBlendWeights.f[4*influence+2] = blendWeights.f[4*vtx+2]; + iqmData->influenceBlendWeights.f[4*influence+3] = blendWeights.f[4*vtx+3]; + } else { + iqmData->influenceBlendWeights.b[4*influence+0] = blendWeights.b[4*vtx+0]; + iqmData->influenceBlendWeights.b[4*influence+1] = blendWeights.b[4*vtx+1]; + iqmData->influenceBlendWeights.b[4*influence+2] = blendWeights.b[4*vtx+2]; + iqmData->influenceBlendWeights.b[4*influence+3] = blendWeights.b[4*vtx+3]; + } + + totalInfluences++; + surface->num_influences++; + } + } + } + } } if( header->num_joints ) @@ -1110,8 +1202,15 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { srfIQModel_t *surf = (srfIQModel_t *)surface; iqmData_t *data = surf->data; float jointMats[IQM_MAX_JOINTS * 12]; + float influenceVtxMat[SHADER_MAX_VERTEXES * 12]; + float influenceNrmMat[SHADER_MAX_VERTEXES * 9]; int i; + float *xyz; + float *normal; + float *tangent; + float *texCoords; + byte *color; vec4_t *outXYZ; int16_t *outNormal; int16_t *outTangent; @@ -1128,106 +1227,181 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); + xyz = &data->positions[surf->first_vertex * 3]; + normal = &data->normals[surf->first_vertex * 3]; + tangent = &data->tangents[surf->first_vertex * 4]; + texCoords = &data->texcoords[surf->first_vertex * 2]; + + if ( data->colors ) { + color = &data->colors[surf->first_vertex * 4]; + } else { + color = NULL; + } + outXYZ = &tess.xyz[tess.numVertexes]; outNormal = tess.normal[tess.numVertexes]; outTangent = tess.tangent[tess.numVertexes]; outTexCoord = &tess.texCoords[tess.numVertexes]; outColor = tess.color[tess.numVertexes]; - // compute interpolated joint matrices if ( data->num_poses > 0 ) { + // compute interpolated joint matrices ComputePoseMats( data, frame, oldframe, backlerp, jointMats ); - } - // transform vertexes and fill other data - for( i = 0; i < surf->num_vertexes; - i++, outXYZ++, outNormal+=4, outTexCoord++, outColor+=4 ) { - int j, k; - float vtxMat[12]; - float nrmMat[9]; - int vtx = i + surf->first_vertex; - float blendWeights[4]; - int numWeights; + // compute vertex blend influence matricies + for( i = 0; i < surf->num_influences; i++ ) { + int influence = surf->first_influence + i; + float *vtxMat = &influenceVtxMat[12*i]; + float *nrmMat = &influenceNrmMat[9*i]; + int j; + float blendWeights[4]; + int numWeights; - for ( numWeights = 0; numWeights < 4; numWeights++ ) { - if ( data->blendWeightsType == IQM_FLOAT ) - blendWeights[numWeights] = data->blendWeights.f[4*vtx + numWeights]; - else - blendWeights[numWeights] = (float)data->blendWeights.b[4*vtx + numWeights] / 255.0f; + for ( numWeights = 0; numWeights < 4; numWeights++ ) { + if ( data->blendWeightsType == IQM_FLOAT ) + blendWeights[numWeights] = data->influenceBlendWeights.f[4*influence + numWeights]; + else + blendWeights[numWeights] = (float)data->influenceBlendWeights.b[4*influence + numWeights] / 255.0f; - if ( blendWeights[numWeights] <= 0 ) - break; - } + if ( blendWeights[numWeights] <= 0.0f ) + break; + } - if ( data->num_poses == 0 || numWeights == 0 ) { - // no blend joint, use identity matrix. - Com_Memcpy( vtxMat, identityMatrix, 12 * sizeof (float) ); - } else { - // compute the vertex matrix by blending the up to - // four blend weights - Com_Memset( vtxMat, 0, 12 * sizeof (float) ); - for( j = 0; j < numWeights; j++ ) { - for( k = 0; k < 12; k++ ) { - vtxMat[k] += blendWeights[j] * jointMats[12*data->blendIndexes[4*vtx + j] + k]; + if ( numWeights == 0 ) { + // no blend joint, use identity matrix. + vtxMat[0] = identityMatrix[0]; + vtxMat[1] = identityMatrix[1]; + vtxMat[2] = identityMatrix[2]; + vtxMat[3] = identityMatrix[3]; + vtxMat[4] = identityMatrix[4]; + vtxMat[5] = identityMatrix[5]; + vtxMat[6] = identityMatrix[6]; + vtxMat[7] = identityMatrix[7]; + vtxMat[8] = identityMatrix[8]; + vtxMat[9] = identityMatrix[9]; + vtxMat[10] = identityMatrix[10]; + vtxMat[11] = identityMatrix[11]; + } else { + // compute the vertex matrix by blending the up to + // four blend weights + vtxMat[0] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 0]; + vtxMat[1] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 1]; + vtxMat[2] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 2]; + vtxMat[3] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 3]; + vtxMat[4] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 4]; + vtxMat[5] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 5]; + vtxMat[6] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 6]; + vtxMat[7] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 7]; + vtxMat[8] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 8]; + vtxMat[9] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 9]; + vtxMat[10] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 10]; + vtxMat[11] = blendWeights[0] * jointMats[12 * data->influenceBlendIndexes[4*influence + 0] + 11]; + + for( j = 1; j < numWeights; j++ ) { + vtxMat[0] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 0]; + vtxMat[1] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 1]; + vtxMat[2] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 2]; + vtxMat[3] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 3]; + vtxMat[4] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 4]; + vtxMat[5] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 5]; + vtxMat[6] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 6]; + vtxMat[7] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 7]; + vtxMat[8] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 8]; + vtxMat[9] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 9]; + vtxMat[10] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 10]; + vtxMat[11] += blendWeights[j] * jointMats[12 * data->influenceBlendIndexes[4*influence + j] + 11]; } } + + // compute the normal matrix as transpose of the adjoint + // of the vertex matrix + nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; + nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; + nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; + nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; + nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; + nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; + nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; + nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; + nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; } - // compute the normal matrix as transpose of the adjoint - // of the vertex matrix - nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; - nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; - nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; - nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; - nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; - nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; - nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; - nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; - nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; + // transform vertexes and fill other data + for( i = 0; i < surf->num_vertexes; + i++, xyz+=3, normal+=3, tangent+=4, texCoords+=2, + outXYZ++, outNormal+=4, outTangent+=4, outTexCoord++ ) { + int influence = data->influences[surf->first_vertex + i] - surf->first_influence; + float *vtxMat = &influenceVtxMat[12*influence]; + float *nrmMat = &influenceNrmMat[9*influence]; - (*outTexCoord)[0] = data->texcoords[2*vtx + 0]; - (*outTexCoord)[1] = data->texcoords[2*vtx + 1]; + (*outTexCoord)[0] = texCoords[0]; + (*outTexCoord)[1] = texCoords[1]; - (*outXYZ)[0] = - vtxMat[ 0] * data->positions[3*vtx+0] + - vtxMat[ 1] * data->positions[3*vtx+1] + - vtxMat[ 2] * data->positions[3*vtx+2] + - vtxMat[ 3]; - (*outXYZ)[1] = - vtxMat[ 4] * data->positions[3*vtx+0] + - vtxMat[ 5] * data->positions[3*vtx+1] + - vtxMat[ 6] * data->positions[3*vtx+2] + - vtxMat[ 7]; - (*outXYZ)[2] = - vtxMat[ 8] * data->positions[3*vtx+0] + - vtxMat[ 9] * data->positions[3*vtx+1] + - vtxMat[10] * data->positions[3*vtx+2] + - vtxMat[11]; - (*outXYZ)[3] = 1.0f; + (*outXYZ)[0] = + vtxMat[ 0] * xyz[0] + + vtxMat[ 1] * xyz[1] + + vtxMat[ 2] * xyz[2] + + vtxMat[ 3]; + (*outXYZ)[1] = + vtxMat[ 4] * xyz[0] + + vtxMat[ 5] * xyz[1] + + vtxMat[ 6] * xyz[2] + + vtxMat[ 7]; + (*outXYZ)[2] = + vtxMat[ 8] * xyz[0] + + vtxMat[ 9] * xyz[1] + + vtxMat[10] * xyz[2] + + vtxMat[11]; - { - vec3_t normal; - vec4_t tangent; + { + vec3_t unpackedNormal; + vec4_t unpackedTangent; - normal[0] = DotProduct(&nrmMat[0], &data->normals[3*vtx]); - normal[1] = DotProduct(&nrmMat[3], &data->normals[3*vtx]); - normal[2] = DotProduct(&nrmMat[6], &data->normals[3*vtx]); + unpackedNormal[0] = DotProduct(&nrmMat[0], normal); + unpackedNormal[1] = DotProduct(&nrmMat[3], normal); + unpackedNormal[2] = DotProduct(&nrmMat[6], normal); + + R_VaoPackNormal(outNormal, unpackedNormal); + + unpackedTangent[0] = DotProduct(&nrmMat[0], tangent); + unpackedTangent[1] = DotProduct(&nrmMat[3], tangent); + unpackedTangent[2] = DotProduct(&nrmMat[6], tangent); + unpackedTangent[3] = tangent[3]; + + R_VaoPackTangent(outTangent, unpackedTangent); + } + } + } else { + // copy vertexes and fill other data + for( i = 0; i < surf->num_vertexes; + i++, xyz+=3, normal+=3, tangent+=4, texCoords+=2, + outXYZ++, outNormal+=4, outTangent+=4, outTexCoord++ ) { + (*outTexCoord)[0] = texCoords[0]; + (*outTexCoord)[1] = texCoords[1]; + + (*outXYZ)[0] = xyz[0]; + (*outXYZ)[1] = xyz[1]; + (*outXYZ)[2] = xyz[2]; R_VaoPackNormal(outNormal, normal); - - tangent[0] = DotProduct(&nrmMat[0], &data->tangents[4*vtx]); - tangent[1] = DotProduct(&nrmMat[3], &data->tangents[4*vtx]); - tangent[2] = DotProduct(&nrmMat[6], &data->tangents[4*vtx]); - tangent[3] = data->tangents[4*vtx+3]; - R_VaoPackTangent(outTangent, tangent); - outTangent+=4; } + } - outColor[0] = data->colors[4*vtx+0] * 257; - outColor[1] = data->colors[4*vtx+1] * 257; - outColor[2] = data->colors[4*vtx+2] * 257; - outColor[3] = data->colors[4*vtx+3] * 257; + if ( color ) { + for( i = 0; i < surf->num_vertexes; i++, color+=4, outColor+=4 ) { + outColor[0] = color[0] * 257; + outColor[1] = color[1] * 257; + outColor[2] = color[2] * 257; + outColor[3] = color[3] * 257; + } + } else { + for( i = 0; i < surf->num_vertexes; i++, outColor+=4 ) { + outColor[0] = 0; + outColor[1] = 0; + outColor[2] = 0; + outColor[3] = 0; + } } tri = data->triangles + 3 * surf->first_triangle;