summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZack Middleton <zturtleman@gmail.com>2013-10-29 22:09:06 -0500
committerTim Angus <tim@ngus.net>2014-06-17 17:43:35 +0100
commitd8225d0663c8eda410138c7ca086fa65912e07ae (patch)
tree79db0327d1ecc0a2c45f7f98d061249d611b2c57
parent16a2c5bed3771b7aedceeef18f15819b7b162c6b (diff)
Add support for IQM int blend indices and float blend weights
Integer blend indices are converted to bytes at load, ioq3 doesn't allow more than 128 joints. Heavily based on code by @zippers.
-rw-r--r--src/renderergl1/tr_local.h10
-rw-r--r--src/renderergl1/tr_model_iqm.c88
-rw-r--r--src/renderergl2/tr_local.h10
-rw-r--r--src/renderergl2/tr_model_iqm.c88
4 files changed, 150 insertions, 46 deletions
diff --git a/src/renderergl1/tr_local.h b/src/renderergl1/tr_local.h
index 96fa6445..59058eaf 100644
--- a/src/renderergl1/tr_local.h
+++ b/src/renderergl1/tr_local.h
@@ -619,10 +619,18 @@ typedef struct {
float *normals;
float *tangents;
byte *blendIndexes;
- byte *blendWeights;
+ union {
+ float *f;
+ byte *b;
+ } blendWeights;
byte *colors;
int *triangles;
+ // 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_BYTE or IQM_FLOAT
+
int *jointParents;
float *jointMats;
float *poseMats;
diff --git a/src/renderergl1/tr_model_iqm.c b/src/renderergl1/tr_model_iqm.c
index 1340c7ae..0636bb46 100644
--- a/src/renderergl1/tr_model_iqm.c
+++ b/src/renderergl1/tr_model_iqm.c
@@ -151,6 +151,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmData_t *iqmData;
srfIQModel_t *surface;
char meshName[MAX_QPATH];
+ byte blendIndexesType, blendWeightsType;
if( filesize < sizeof(iqmHeader_t) ) {
return qfalse;
@@ -272,11 +273,20 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
}
break;
case IQM_BLENDINDEXES:
+ if( (vertexarray->format != IQM_INT &&
+ vertexarray->format != IQM_UBYTE) ||
+ vertexarray->size != 4 ) {
+ return qfalse;
+ }
+ blendIndexesType = vertexarray->format;
+ break;
case IQM_BLENDWEIGHTS:
- if( vertexarray->format != IQM_UBYTE ||
+ if( (vertexarray->format != IQM_FLOAT &&
+ vertexarray->format != IQM_UBYTE) ||
vertexarray->size != 4 ) {
return qfalse;
}
+ blendWeightsType = vertexarray->format;
break;
case IQM_COLOR:
if( vertexarray->format != IQM_UBYTE ||
@@ -459,12 +469,18 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
size += header->num_vertexes * 3 * sizeof(float); // normals
size += header->num_vertexes * 4 * sizeof(float); // tangents
size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes
- size += header->num_vertexes * 4 * sizeof(byte); // blendWeights
size += header->num_vertexes * 4 * sizeof(byte); // colors
size += header->num_joints * sizeof(int); // parents
size += header->num_triangles * 3 * sizeof(int); // triangles
size += joint_names; // joint names
+ // blendWeights
+ if (blendWeightsType == IQM_FLOAT) {
+ size += header->num_vertexes * 4 * sizeof(float);
+ } else {
+ size += header->num_vertexes * 4 * sizeof(byte);
+ }
+
mod->type = MOD_IQM;
iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low );
mod->modelData = iqmData;
@@ -476,6 +492,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmData->num_surfaces = header->num_meshes;
iqmData->num_joints = header->num_joints;
iqmData->num_poses = header->num_poses;
+ iqmData->blendWeightsType = blendWeightsType;
iqmData->surfaces = (srfIQModel_t *)(iqmData + 1);
iqmData->jointMats = (float *) (iqmData->surfaces + iqmData->num_surfaces);
iqmData->poseMats = iqmData->jointMats + 12 * header->num_joints;
@@ -490,8 +507,15 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes;
iqmData->tangents = iqmData->normals + 3 * header->num_vertexes;
iqmData->blendIndexes = (byte *)(iqmData->tangents + 4 * header->num_vertexes);
- iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes;
- iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes;
+
+ if(blendWeightsType == IQM_FLOAT) {
+ iqmData->blendWeights.f = (float *)(iqmData->blendIndexes + 4 * header->num_vertexes);
+ iqmData->colors = (byte *)(iqmData->blendWeights.f + 4 * header->num_vertexes);
+ } else {
+ iqmData->blendWeights.b = iqmData->blendIndexes + 4 * header->num_vertexes;
+ iqmData->colors = iqmData->blendWeights.b + 4 * header->num_vertexes;
+ }
+
iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes);
iqmData->triangles = iqmData->jointParents + header->num_joints;
iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles);
@@ -637,14 +661,28 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
n * sizeof(float) );
break;
case IQM_BLENDINDEXES:
- Com_Memcpy( iqmData->blendIndexes,
- (byte *)header + vertexarray->offset,
- n * sizeof(byte) );
+ if( blendIndexesType == IQM_INT ) {
+ int *data = (int*)((byte*)header + vertexarray->offset);
+ for ( j = 0; j < n; j++ ) {
+ iqmData->blendIndexes[j] = (byte)LittleLong( data[j] );
+ }
+ } else {
+ Com_Memcpy( iqmData->blendIndexes,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(byte) );
+ }
break;
case IQM_BLENDWEIGHTS:
- Com_Memcpy( iqmData->blendWeights,
- (byte *)header + vertexarray->offset,
- n * sizeof(byte) );
+ if( blendWeightsType == IQM_FLOAT ) {
+ float *data = (float*)((byte*)header + vertexarray->offset);
+ for ( j = 0; j < n; j++ ) {
+ iqmData->blendWeights.f[j] = LittleFloat( data[j] );
+ }
+ } else {
+ Com_Memcpy( iqmData->blendWeights.b,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(byte) );
+ }
break;
case IQM_COLOR:
Com_Memcpy( iqmData->colors,
@@ -1014,25 +1052,31 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) {
float vtxMat[12];
float nrmMat[9];
int vtx = i + surf->first_vertex;
+ float blendWeights[4];
+ int numWeights;
- if ( data->num_poses == 0 || data->blendWeights[4*vtx] <= 0 ) {
+ for ( numWeights = 0; numWeights < 4; numWeights++ ) {
+ if ( data->blendWeightsType == IQM_FLOAT )
+ blendWeights[numWeights] = data->blendWeights.f[4*vtx + numWeights];
+ else // IQM_BYTE
+ blendWeights[numWeights] = (float)data->blendWeights.b[4*vtx + numWeights] / 255.0f;
+
+ if ( blendWeights[numWeights] <= 0 )
+ 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
- for( k = 0; k < 12; k++ )
- vtxMat[k] = data->blendWeights[4*vtx]
- * jointMats[12*data->blendIndexes[4*vtx] + k];
- for( j = 1; j < 4; j++ ) {
- if( data->blendWeights[4*vtx + j] <= 0 )
- break;
- for( k = 0; k < 12; k++ )
- vtxMat[k] += data->blendWeights[4*vtx + j]
- * jointMats[12*data->blendIndexes[4*vtx + j] + k];
+ 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];
+ }
}
- for( k = 0; k < 12; k++ )
- vtxMat[k] *= 1.0f / 255.0f;
}
// compute the normal matrix as transpose of the adjoint
diff --git a/src/renderergl2/tr_local.h b/src/renderergl2/tr_local.h
index 75b44a11..05662f99 100644
--- a/src/renderergl2/tr_local.h
+++ b/src/renderergl2/tr_local.h
@@ -1095,10 +1095,18 @@ typedef struct {
float *normals;
float *tangents;
byte *blendIndexes;
- byte *blendWeights;
+ union {
+ float *f;
+ byte *b;
+ } blendWeights;
byte *colors;
int *triangles;
+ // 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_BYTE or IQM_FLOAT
+
int *jointParents;
float *jointMats;
float *poseMats;
diff --git a/src/renderergl2/tr_model_iqm.c b/src/renderergl2/tr_model_iqm.c
index 382f76e0..3086293d 100644
--- a/src/renderergl2/tr_model_iqm.c
+++ b/src/renderergl2/tr_model_iqm.c
@@ -150,6 +150,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmData_t *iqmData;
srfIQModel_t *surface;
char meshName[MAX_QPATH];
+ byte blendIndexesType, blendWeightsType;
if( filesize < sizeof(iqmHeader_t) ) {
return qfalse;
@@ -271,11 +272,20 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
}
break;
case IQM_BLENDINDEXES:
+ if( (vertexarray->format != IQM_INT &&
+ vertexarray->format != IQM_UBYTE) ||
+ vertexarray->size != 4 ) {
+ return qfalse;
+ }
+ blendIndexesType = vertexarray->format;
+ break;
case IQM_BLENDWEIGHTS:
- if( vertexarray->format != IQM_UBYTE ||
+ if( (vertexarray->format != IQM_FLOAT &&
+ vertexarray->format != IQM_UBYTE) ||
vertexarray->size != 4 ) {
return qfalse;
}
+ blendWeightsType = vertexarray->format;
break;
case IQM_COLOR:
if( vertexarray->format != IQM_UBYTE ||
@@ -458,12 +468,18 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
size += header->num_vertexes * 3 * sizeof(float); // normals
size += header->num_vertexes * 4 * sizeof(float); // tangents
size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes
- size += header->num_vertexes * 4 * sizeof(byte); // blendWeights
size += header->num_vertexes * 4 * sizeof(byte); // colors
size += header->num_joints * sizeof(int); // parents
size += header->num_triangles * 3 * sizeof(int); // triangles
size += joint_names; // joint names
+ // blendWeights
+ if (blendWeightsType == IQM_FLOAT) {
+ size += header->num_vertexes * 4 * sizeof(float);
+ } else {
+ size += header->num_vertexes * 4 * sizeof(byte);
+ }
+
mod->type = MOD_IQM;
iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low );
mod->modelData = iqmData;
@@ -475,6 +491,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmData->num_surfaces = header->num_meshes;
iqmData->num_joints = header->num_joints;
iqmData->num_poses = header->num_poses;
+ iqmData->blendWeightsType = blendWeightsType;
iqmData->surfaces = (srfIQModel_t *)(iqmData + 1);
iqmData->jointMats = (float *) (iqmData->surfaces + iqmData->num_surfaces);
iqmData->poseMats = iqmData->jointMats + 12 * header->num_joints;
@@ -489,8 +506,15 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes;
iqmData->tangents = iqmData->normals + 3 * header->num_vertexes;
iqmData->blendIndexes = (byte *)(iqmData->tangents + 4 * header->num_vertexes);
- iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes;
- iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes;
+
+ if(blendWeightsType == IQM_FLOAT) {
+ iqmData->blendWeights.f = (float *)(iqmData->blendIndexes + 4 * header->num_vertexes);
+ iqmData->colors = (byte *)(iqmData->blendWeights.f + 4 * header->num_vertexes);
+ } else {
+ iqmData->blendWeights.b = iqmData->blendIndexes + 4 * header->num_vertexes;
+ iqmData->colors = iqmData->blendWeights.b + 4 * header->num_vertexes;
+ }
+
iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes);
iqmData->triangles = iqmData->jointParents + header->num_joints;
iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles);
@@ -636,14 +660,28 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
n * sizeof(float) );
break;
case IQM_BLENDINDEXES:
- Com_Memcpy( iqmData->blendIndexes,
- (byte *)header + vertexarray->offset,
- n * sizeof(byte) );
+ if( blendIndexesType == IQM_INT ) {
+ int *data = (int*)((byte*)header + vertexarray->offset);
+ for ( j = 0; j < n; j++ ) {
+ iqmData->blendIndexes[j] = (byte)LittleLong( data[j] );
+ }
+ } else {
+ Com_Memcpy( iqmData->blendIndexes,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(byte) );
+ }
break;
case IQM_BLENDWEIGHTS:
- Com_Memcpy( iqmData->blendWeights,
- (byte *)header + vertexarray->offset,
- n * sizeof(byte) );
+ if( blendWeightsType == IQM_FLOAT ) {
+ float *data = (float*)((byte*)header + vertexarray->offset);
+ for ( j = 0; j < n; j++ ) {
+ iqmData->blendWeights.f[j] = LittleFloat( data[j] );
+ }
+ } else {
+ Com_Memcpy( iqmData->blendWeights.b,
+ (byte *)header + vertexarray->offset,
+ n * sizeof(byte) );
+ }
break;
case IQM_COLOR:
Com_Memcpy( iqmData->colors,
@@ -1016,25 +1054,31 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) {
float vtxMat[12];
float nrmMat[9];
int vtx = i + surf->first_vertex;
+ float blendWeights[4];
+ int numWeights;
- if ( data->num_poses == 0 || data->blendWeights[4*vtx] <= 0 ) {
+ for ( numWeights = 0; numWeights < 4; numWeights++ ) {
+ if ( data->blendWeightsType == IQM_FLOAT )
+ blendWeights[numWeights] = data->blendWeights.f[4*vtx + numWeights];
+ else // IQM_BYTE
+ blendWeights[numWeights] = (float)data->blendWeights.b[4*vtx + numWeights] / 255.0f;
+
+ if ( blendWeights[numWeights] <= 0 )
+ 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
- for( k = 0; k < 12; k++ )
- vtxMat[k] = data->blendWeights[4*vtx]
- * jointMats[12*data->blendIndexes[4*vtx] + k];
- for( j = 1; j < 4; j++ ) {
- if( data->blendWeights[4*vtx + j] <= 0 )
- break;
- for( k = 0; k < 12; k++ )
- vtxMat[k] += data->blendWeights[4*vtx + j]
- * jointMats[12*data->blendIndexes[4*vtx + j] + k];
+ 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];
+ }
}
- for( k = 0; k < 12; k++ )
- vtxMat[k] *= 1.0f / 255.0f;
}
// compute the normal matrix as transpose of the adjoint