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.
This commit is contained in:
Zack Middleton 2018-07-27 17:40:21 -05:00
parent fdc08e860e
commit cccd283be8
4 changed files with 601 additions and 254 deletions

View file

@ -600,21 +600,26 @@ typedef struct {
struct srfIQModel_s *surfaces; struct srfIQModel_s *surfaces;
int *triangles; int *triangles;
// vertex arrays
float *positions; float *positions;
float *texcoords; float *texcoords;
float *normals; float *normals;
float *tangents; 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 { union {
float *f; float *f;
byte *b; byte *b;
} blendWeights; } influenceBlendWeights; // [num_influences]
byte *colors;
// depending upon the exporter, blend indices and weights might be int/float // depending upon the exporter, blend indices and weights might be int/float
// as opposed to the recommended byte/byte, for example Noesis exports // as opposed to the recommended byte/byte, for example Noesis exports
// int/float whereas the official IQM tool exports byte/byte // 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; char *jointNames;
int *jointParents; int *jointParents;
@ -631,6 +636,7 @@ typedef struct srfIQModel_s {
iqmData_t *data; iqmData_t *data;
int first_vertex, num_vertexes; int first_vertex, num_vertexes;
int first_triangle, num_triangles; int first_triangle, num_triangles;
int first_influence, num_influences;
} srfIQModel_t; } srfIQModel_t;

View file

@ -143,7 +143,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmBounds_t *bounds; iqmBounds_t *bounds;
unsigned short *framedata; unsigned short *framedata;
char *str; char *str;
int i, j; int i, j, k;
float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f}; float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f};
float *mat, *matInv; float *mat, *matInv;
size_t size, joint_names; 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; srfIQModel_t *surface;
char meshName[MAX_QPATH]; char meshName[MAX_QPATH];
int vertexArrayFormat[IQM_COLOR+1]; int vertexArrayFormat[IQM_COLOR+1];
int allocateInfluences;
byte *blendIndexes;
union {
byte *b;
float *f;
} blendWeights;
if( filesize < sizeof(iqmHeader_t) ) { if( filesize < sizeof(iqmHeader_t) ) {
return qfalse; return qfalse;
@ -211,6 +217,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
vertexArrayFormat[i] = -1; vertexArrayFormat[i] = -1;
} }
blendIndexes = NULL;
blendWeights.b = NULL;
allocateInfluences = 0;
if ( header->num_meshes ) if ( header->num_meshes )
{ {
// check and swap vertex arrays // 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 ) { vertexarray->size != 4 ) {
return qfalse; return qfalse;
} }
blendIndexes = (byte*)header + vertexarray->offset;
break; break;
case IQM_BLENDWEIGHTS: case IQM_BLENDWEIGHTS:
if( (vertexarray->format != IQM_FLOAT && 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 ) { vertexarray->size != 4 ) {
return qfalse; return qfalse;
} }
if( vertexarray->format == IQM_FLOAT ) {
blendWeights.f = (float*)( (byte*)header + vertexarray->offset );
} else {
blendWeights.b = (byte*)header + vertexarray->offset;
}
break; break;
case IQM_COLOR: case IQM_COLOR:
if( vertexarray->format != IQM_UBYTE || 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 // opengl1 renderer doesn't use tangents
vertexArrayFormat[IQM_TANGENT] = -1; vertexArrayFormat[IQM_TANGENT] = -1;
// FIXME: don't require colors array when drawing
vertexArrayFormat[IQM_COLOR] = IQM_UBYTE;
// check and swap triangles // check and swap triangles
if( IQM_CheckRange( header, header->ofs_triangles, if( IQM_CheckRange( header, header->ofs_triangles,
header->num_triangles, sizeof(iqmTriangle_t) ) ) { 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 ) { mesh->material >= header->num_text ) {
return qfalse; 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 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 ) { if ( vertexArrayFormat[IQM_COLOR] != -1 ) {
size += header->num_vertexes * 4 * sizeof(byte); // colors 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 ) { if( header->num_joints ) {
size += joint_names; // joint names 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 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 ) { if ( vertexArrayFormat[IQM_COLOR] != -1 ) {
iqmData->colors = (byte*)dataPtr; iqmData->colors = (byte*)dataPtr;
dataPtr += header->num_vertexes * 4 * sizeof(byte); // colors 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 ) { if( header->num_joints ) {
iqmData->jointNames = (char*)dataPtr; 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) ); n * sizeof(float) );
break; break;
case IQM_BLENDINDEXES: 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: 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; break;
case IQM_COLOR: case IQM_COLOR:
Com_Memcpy( iqmData->colors, Com_Memcpy( iqmData->colors,
@ -691,6 +721,68 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
break; 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 ) if( header->num_joints )
@ -1103,8 +1195,14 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) {
srfIQModel_t *surf = (srfIQModel_t *)surface; srfIQModel_t *surf = (srfIQModel_t *)surface;
iqmData_t *data = surf->data; iqmData_t *data = surf->data;
float jointMats[IQM_MAX_JOINTS * 12]; float jointMats[IQM_MAX_JOINTS * 12];
float influenceVtxMat[SHADER_MAX_VERTEXES * 12];
float influenceNrmMat[SHADER_MAX_VERTEXES * 9];
int i; int i;
float *xyz;
float *normal;
float *texCoords;
byte *color;
vec4_t *outXYZ; vec4_t *outXYZ;
vec4_t *outNormal; vec4_t *outNormal;
vec2_t (*outTexCoord)[2]; vec2_t (*outTexCoord)[2];
@ -1120,102 +1218,165 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) {
RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); 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]; outXYZ = &tess.xyz[tess.numVertexes];
outNormal = &tess.normal[tess.numVertexes]; outNormal = &tess.normal[tess.numVertexes];
outTexCoord = &tess.texCoords[tess.numVertexes]; outTexCoord = &tess.texCoords[tess.numVertexes];
outColor = &tess.vertexColors[tess.numVertexes]; outColor = &tess.vertexColors[tess.numVertexes];
// compute interpolated joint matrices
if ( data->num_poses > 0 ) { if ( data->num_poses > 0 ) {
// compute interpolated joint matrices
ComputePoseMats( data, frame, oldframe, backlerp, jointMats ); ComputePoseMats( data, frame, oldframe, backlerp, jointMats );
}
// transform vertexes and fill other data // compute vertex blend influence matricies
for( i = 0; i < surf->num_vertexes; for( i = 0; i < surf->num_influences; i++ ) {
i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { int influence = surf->first_influence + i;
int j, k; float *vtxMat = &influenceVtxMat[12*i];
float vtxMat[12]; float *nrmMat = &influenceNrmMat[9*i];
float nrmMat[9]; int j;
int vtx = i + surf->first_vertex; float blendWeights[4];
float blendWeights[4]; int numWeights;
int numWeights;
for ( numWeights = 0; numWeights < 4; numWeights++ ) { for ( numWeights = 0; numWeights < 4; numWeights++ ) {
if ( data->blendWeightsType == IQM_FLOAT ) if ( data->blendWeightsType == IQM_FLOAT )
blendWeights[numWeights] = data->blendWeights.f[4*vtx + numWeights]; blendWeights[numWeights] = data->influenceBlendWeights.f[4*influence + numWeights];
else else
blendWeights[numWeights] = (float)data->blendWeights.b[4*vtx + numWeights] / 255.0f; blendWeights[numWeights] = (float)data->influenceBlendWeights.b[4*influence + numWeights] / 255.0f;
if ( blendWeights[numWeights] <= 0 ) if ( blendWeights[numWeights] <= 0.0f )
break; break;
} }
if ( data->num_poses == 0 || numWeights == 0 ) { if ( numWeights == 0 ) {
// no blend joint, use identity matrix. // no blend joint, use identity matrix.
Com_Memcpy( vtxMat, identityMatrix, 12 * sizeof (float) ); vtxMat[0] = identityMatrix[0];
} else { vtxMat[1] = identityMatrix[1];
// compute the vertex matrix by blending the up to vtxMat[2] = identityMatrix[2];
// four blend weights vtxMat[3] = identityMatrix[3];
Com_Memset( vtxMat, 0, 12 * sizeof (float) ); vtxMat[4] = identityMatrix[4];
for( j = 0; j < numWeights; j++ ) { vtxMat[5] = identityMatrix[5];
for( k = 0; k < 12; k++ ) { vtxMat[6] = identityMatrix[6];
vtxMat[k] += blendWeights[j] * jointMats[12*data->blendIndexes[4*vtx + j] + k]; 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 // transform vertexes and fill other data
// of the vertex matrix for( i = 0; i < surf->num_vertexes;
nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; i++, xyz+=3, normal+=3, texCoords+=2,
nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; outXYZ++, outNormal++, outTexCoord++ ) {
nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; int influence = data->influences[surf->first_vertex + i] - surf->first_influence;
nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; float *vtxMat = &influenceVtxMat[12*influence];
nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; float *nrmMat = &influenceNrmMat[9*influence];
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];
(*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; (*outTexCoord)[0][0] = texCoords[0];
(*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; (*outTexCoord)[0][1] = texCoords[1];
(*outTexCoord)[1][0] = (*outTexCoord)[0][0];
(*outTexCoord)[1][1] = (*outTexCoord)[0][1];
(*outXYZ)[0] = (*outXYZ)[0] =
vtxMat[ 0] * data->positions[3*vtx+0] + vtxMat[ 0] * xyz[0] +
vtxMat[ 1] * data->positions[3*vtx+1] + vtxMat[ 1] * xyz[1] +
vtxMat[ 2] * data->positions[3*vtx+2] + vtxMat[ 2] * xyz[2] +
vtxMat[ 3]; vtxMat[ 3];
(*outXYZ)[1] = (*outXYZ)[1] =
vtxMat[ 4] * data->positions[3*vtx+0] + vtxMat[ 4] * xyz[0] +
vtxMat[ 5] * data->positions[3*vtx+1] + vtxMat[ 5] * xyz[1] +
vtxMat[ 6] * data->positions[3*vtx+2] + vtxMat[ 6] * xyz[2] +
vtxMat[ 7]; vtxMat[ 7];
(*outXYZ)[2] = (*outXYZ)[2] =
vtxMat[ 8] * data->positions[3*vtx+0] + vtxMat[ 8] * xyz[0] +
vtxMat[ 9] * data->positions[3*vtx+1] + vtxMat[ 9] * xyz[1] +
vtxMat[10] * data->positions[3*vtx+2] + vtxMat[10] * xyz[2] +
vtxMat[11]; vtxMat[11];
(*outXYZ)[3] = 1.0f;
(*outNormal)[0] = (*outNormal)[0] =
nrmMat[ 0] * data->normals[3*vtx+0] + nrmMat[ 0] * normal[0] +
nrmMat[ 1] * data->normals[3*vtx+1] + nrmMat[ 1] * normal[1] +
nrmMat[ 2] * data->normals[3*vtx+2]; nrmMat[ 2] * normal[2];
(*outNormal)[1] = (*outNormal)[1] =
nrmMat[ 3] * data->normals[3*vtx+0] + nrmMat[ 3] * normal[0] +
nrmMat[ 4] * data->normals[3*vtx+1] + nrmMat[ 4] * normal[1] +
nrmMat[ 5] * data->normals[3*vtx+2]; nrmMat[ 5] * normal[2];
(*outNormal)[2] = (*outNormal)[2] =
nrmMat[ 6] * data->normals[3*vtx+0] + nrmMat[ 6] * normal[0] +
nrmMat[ 7] * data->normals[3*vtx+1] + nrmMat[ 7] * normal[1] +
nrmMat[ 8] * data->normals[3*vtx+2]; nrmMat[ 8] * normal[2];
(*outNormal)[3] = 0.0f; }
} 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]; (*outXYZ)[0] = xyz[0];
(*outColor)[1] = data->colors[4*vtx+1]; (*outXYZ)[1] = xyz[1];
(*outColor)[2] = data->colors[4*vtx+2]; (*outXYZ)[2] = xyz[2];
(*outColor)[3] = data->colors[4*vtx+3];
(*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; tri = data->triangles + 3 * surf->first_triangle;

View file

@ -950,21 +950,26 @@ typedef struct {
struct srfIQModel_s *surfaces; struct srfIQModel_s *surfaces;
int *triangles; int *triangles;
// vertex arrays
float *positions; float *positions;
float *texcoords; float *texcoords;
float *normals; float *normals;
float *tangents; 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 { union {
float *f; float *f;
byte *b; byte *b;
} blendWeights; } influenceBlendWeights; // [num_influences]
byte *colors;
// depending upon the exporter, blend indices and weights might be int/float // depending upon the exporter, blend indices and weights might be int/float
// as opposed to the recommended byte/byte, for example Noesis exports // as opposed to the recommended byte/byte, for example Noesis exports
// int/float whereas the official IQM tool exports byte/byte // 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; char *jointNames;
int *jointParents; int *jointParents;
@ -981,6 +986,7 @@ typedef struct srfIQModel_s {
iqmData_t *data; iqmData_t *data;
int first_vertex, num_vertexes; int first_vertex, num_vertexes;
int first_triangle, num_triangles; int first_triangle, num_triangles;
int first_influence, num_influences;
} srfIQModel_t; } srfIQModel_t;
typedef struct srfVaoMdvMesh_s typedef struct srfVaoMdvMesh_s

View file

@ -143,7 +143,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmBounds_t *bounds; iqmBounds_t *bounds;
unsigned short *framedata; unsigned short *framedata;
char *str; char *str;
int i, j; int i, j, k;
float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f}; float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f};
float *mat, *matInv; float *mat, *matInv;
size_t size, joint_names; 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; srfIQModel_t *surface;
char meshName[MAX_QPATH]; char meshName[MAX_QPATH];
int vertexArrayFormat[IQM_COLOR+1]; int vertexArrayFormat[IQM_COLOR+1];
int allocateInfluences;
byte *blendIndexes;
union {
byte *b;
float *f;
} blendWeights;
if( filesize < sizeof(iqmHeader_t) ) { if( filesize < sizeof(iqmHeader_t) ) {
return qfalse; return qfalse;
@ -211,6 +217,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
vertexArrayFormat[i] = -1; vertexArrayFormat[i] = -1;
} }
blendIndexes = NULL;
blendWeights.b = NULL;
allocateInfluences = 0;
if ( header->num_meshes ) if ( header->num_meshes )
{ {
// check and swap vertex arrays // 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 ) { vertexarray->size != 4 ) {
return qfalse; return qfalse;
} }
blendIndexes = (byte*)header + vertexarray->offset;
break; break;
case IQM_BLENDWEIGHTS: case IQM_BLENDWEIGHTS:
if( (vertexarray->format != IQM_FLOAT && 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 ) { vertexarray->size != 4 ) {
return qfalse; return qfalse;
} }
if( vertexarray->format == IQM_FLOAT ) {
blendWeights.f = (float*)( (byte*)header + vertexarray->offset );
} else {
blendWeights.b = (byte*)header + vertexarray->offset;
}
break; break;
case IQM_COLOR: case IQM_COLOR:
if( vertexarray->format != IQM_UBYTE || 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; return qfalse;
} }
// FIXME: don't require colors array when drawing
vertexArrayFormat[IQM_COLOR] = IQM_UBYTE;
// check and swap triangles // check and swap triangles
if( IQM_CheckRange( header, header->ofs_triangles, if( IQM_CheckRange( header, header->ofs_triangles,
header->num_triangles, sizeof(iqmTriangle_t) ) ) { 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 ) { mesh->material >= header->num_text ) {
return qfalse; 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 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 ) { if ( vertexArrayFormat[IQM_COLOR] != -1 ) {
size += header->num_vertexes * 4 * sizeof(byte); // colors 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 ) { if( header->num_joints ) {
size += joint_names; // joint names 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 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 ) { if ( vertexArrayFormat[IQM_COLOR] != -1 ) {
iqmData->colors = (byte*)dataPtr; iqmData->colors = (byte*)dataPtr;
dataPtr += header->num_vertexes * 4 * sizeof(byte); // colors 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 ) { if( header->num_joints ) {
iqmData->jointNames = (char*)dataPtr; 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) ); n * sizeof(float) );
break; break;
case IQM_BLENDINDEXES: 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: 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; break;
case IQM_COLOR: case IQM_COLOR:
Com_Memcpy( iqmData->colors, Com_Memcpy( iqmData->colors,
@ -694,6 +724,68 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
break; 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 ) if( header->num_joints )
@ -1110,8 +1202,15 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) {
srfIQModel_t *surf = (srfIQModel_t *)surface; srfIQModel_t *surf = (srfIQModel_t *)surface;
iqmData_t *data = surf->data; iqmData_t *data = surf->data;
float jointMats[IQM_MAX_JOINTS * 12]; float jointMats[IQM_MAX_JOINTS * 12];
float influenceVtxMat[SHADER_MAX_VERTEXES * 12];
float influenceNrmMat[SHADER_MAX_VERTEXES * 9];
int i; int i;
float *xyz;
float *normal;
float *tangent;
float *texCoords;
byte *color;
vec4_t *outXYZ; vec4_t *outXYZ;
int16_t *outNormal; int16_t *outNormal;
int16_t *outTangent; int16_t *outTangent;
@ -1128,106 +1227,181 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) {
RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); 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]; outXYZ = &tess.xyz[tess.numVertexes];
outNormal = tess.normal[tess.numVertexes]; outNormal = tess.normal[tess.numVertexes];
outTangent = tess.tangent[tess.numVertexes]; outTangent = tess.tangent[tess.numVertexes];
outTexCoord = &tess.texCoords[tess.numVertexes]; outTexCoord = &tess.texCoords[tess.numVertexes];
outColor = tess.color[tess.numVertexes]; outColor = tess.color[tess.numVertexes];
// compute interpolated joint matrices
if ( data->num_poses > 0 ) { if ( data->num_poses > 0 ) {
// compute interpolated joint matrices
ComputePoseMats( data, frame, oldframe, backlerp, jointMats ); ComputePoseMats( data, frame, oldframe, backlerp, jointMats );
}
// transform vertexes and fill other data // compute vertex blend influence matricies
for( i = 0; i < surf->num_vertexes; for( i = 0; i < surf->num_influences; i++ ) {
i++, outXYZ++, outNormal+=4, outTexCoord++, outColor+=4 ) { int influence = surf->first_influence + i;
int j, k; float *vtxMat = &influenceVtxMat[12*i];
float vtxMat[12]; float *nrmMat = &influenceNrmMat[9*i];
float nrmMat[9]; int j;
int vtx = i + surf->first_vertex; float blendWeights[4];
float blendWeights[4]; int numWeights;
int numWeights;
for ( numWeights = 0; numWeights < 4; numWeights++ ) { for ( numWeights = 0; numWeights < 4; numWeights++ ) {
if ( data->blendWeightsType == IQM_FLOAT ) if ( data->blendWeightsType == IQM_FLOAT )
blendWeights[numWeights] = data->blendWeights.f[4*vtx + numWeights]; blendWeights[numWeights] = data->influenceBlendWeights.f[4*influence + numWeights];
else else
blendWeights[numWeights] = (float)data->blendWeights.b[4*vtx + numWeights] / 255.0f; blendWeights[numWeights] = (float)data->influenceBlendWeights.b[4*influence + numWeights] / 255.0f;
if ( blendWeights[numWeights] <= 0 ) if ( blendWeights[numWeights] <= 0.0f )
break; break;
} }
if ( data->num_poses == 0 || numWeights == 0 ) { if ( numWeights == 0 ) {
// no blend joint, use identity matrix. // no blend joint, use identity matrix.
Com_Memcpy( vtxMat, identityMatrix, 12 * sizeof (float) ); vtxMat[0] = identityMatrix[0];
} else { vtxMat[1] = identityMatrix[1];
// compute the vertex matrix by blending the up to vtxMat[2] = identityMatrix[2];
// four blend weights vtxMat[3] = identityMatrix[3];
Com_Memset( vtxMat, 0, 12 * sizeof (float) ); vtxMat[4] = identityMatrix[4];
for( j = 0; j < numWeights; j++ ) { vtxMat[5] = identityMatrix[5];
for( k = 0; k < 12; k++ ) { vtxMat[6] = identityMatrix[6];
vtxMat[k] += blendWeights[j] * jointMats[12*data->blendIndexes[4*vtx + j] + k]; 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 // transform vertexes and fill other data
// of the vertex matrix for( i = 0; i < surf->num_vertexes;
nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; i++, xyz+=3, normal+=3, tangent+=4, texCoords+=2,
nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; outXYZ++, outNormal+=4, outTangent+=4, outTexCoord++ ) {
nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; int influence = data->influences[surf->first_vertex + i] - surf->first_influence;
nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; float *vtxMat = &influenceVtxMat[12*influence];
nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; float *nrmMat = &influenceNrmMat[9*influence];
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];
(*outTexCoord)[0] = data->texcoords[2*vtx + 0]; (*outTexCoord)[0] = texCoords[0];
(*outTexCoord)[1] = data->texcoords[2*vtx + 1]; (*outTexCoord)[1] = texCoords[1];
(*outXYZ)[0] = (*outXYZ)[0] =
vtxMat[ 0] * data->positions[3*vtx+0] + vtxMat[ 0] * xyz[0] +
vtxMat[ 1] * data->positions[3*vtx+1] + vtxMat[ 1] * xyz[1] +
vtxMat[ 2] * data->positions[3*vtx+2] + vtxMat[ 2] * xyz[2] +
vtxMat[ 3]; vtxMat[ 3];
(*outXYZ)[1] = (*outXYZ)[1] =
vtxMat[ 4] * data->positions[3*vtx+0] + vtxMat[ 4] * xyz[0] +
vtxMat[ 5] * data->positions[3*vtx+1] + vtxMat[ 5] * xyz[1] +
vtxMat[ 6] * data->positions[3*vtx+2] + vtxMat[ 6] * xyz[2] +
vtxMat[ 7]; vtxMat[ 7];
(*outXYZ)[2] = (*outXYZ)[2] =
vtxMat[ 8] * data->positions[3*vtx+0] + vtxMat[ 8] * xyz[0] +
vtxMat[ 9] * data->positions[3*vtx+1] + vtxMat[ 9] * xyz[1] +
vtxMat[10] * data->positions[3*vtx+2] + vtxMat[10] * xyz[2] +
vtxMat[11]; vtxMat[11];
(*outXYZ)[3] = 1.0f;
{ {
vec3_t normal; vec3_t unpackedNormal;
vec4_t tangent; vec4_t unpackedTangent;
normal[0] = DotProduct(&nrmMat[0], &data->normals[3*vtx]); unpackedNormal[0] = DotProduct(&nrmMat[0], normal);
normal[1] = DotProduct(&nrmMat[3], &data->normals[3*vtx]); unpackedNormal[1] = DotProduct(&nrmMat[3], normal);
normal[2] = DotProduct(&nrmMat[6], &data->normals[3*vtx]); 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); 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); R_VaoPackTangent(outTangent, tangent);
outTangent+=4;
} }
}
outColor[0] = data->colors[4*vtx+0] * 257; if ( color ) {
outColor[1] = data->colors[4*vtx+1] * 257; for( i = 0; i < surf->num_vertexes; i++, color+=4, outColor+=4 ) {
outColor[2] = data->colors[4*vtx+2] * 257; outColor[0] = color[0] * 257;
outColor[3] = data->colors[4*vtx+3] * 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; tri = data->triangles + 3 * surf->first_triangle;