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;
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;

View file

@ -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;

View file

@ -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

View file

@ -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;