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;