mirror of
https://github.com/UberGames/lilium-voyager.git
synced 2024-11-10 06:31:47 +00:00
Improve IQM loading
- Only allocate memory for vertex arrays that are present in the IQM file and are actually used (may not have colors or blend index/weights, don't load tangents in opengl1). (Colors is fixed to next commit.) - Explicitly handle loading IQM files without meshes (bones only). - Better IQM validation. Header data offset 0 mean data is not present in file. Check if required vertex arrays are present. This involved a lot of white space changes and moving code around.
This commit is contained in:
parent
6c3d92133d
commit
fdc08e860e
4 changed files with 924 additions and 751 deletions
|
@ -599,6 +599,7 @@ typedef struct {
|
||||||
int num_poses;
|
int num_poses;
|
||||||
struct srfIQModel_s *surfaces;
|
struct srfIQModel_s *surfaces;
|
||||||
|
|
||||||
|
int *triangles;
|
||||||
float *positions;
|
float *positions;
|
||||||
float *texcoords;
|
float *texcoords;
|
||||||
float *normals;
|
float *normals;
|
||||||
|
@ -609,18 +610,17 @@ typedef struct {
|
||||||
byte *b;
|
byte *b;
|
||||||
} blendWeights;
|
} blendWeights;
|
||||||
byte *colors;
|
byte *colors;
|
||||||
int *triangles;
|
|
||||||
|
|
||||||
// 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
|
byte blendWeightsType; // IQM_UBYTE or IQM_FLOAT
|
||||||
|
|
||||||
|
char *jointNames;
|
||||||
int *jointParents;
|
int *jointParents;
|
||||||
float *jointMats;
|
float *jointMats;
|
||||||
float *poseMats;
|
float *poseMats;
|
||||||
float *bounds;
|
float *bounds;
|
||||||
char *names;
|
|
||||||
} iqmData_t;
|
} iqmData_t;
|
||||||
|
|
||||||
// inter-quake-model surface
|
// inter-quake-model surface
|
||||||
|
|
|
@ -33,11 +33,11 @@ static float identityMatrix[12] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static qboolean IQM_CheckRange( iqmHeader_t *header, int offset,
|
static qboolean IQM_CheckRange( iqmHeader_t *header, int offset,
|
||||||
int count,int size ) {
|
int count, int size ) {
|
||||||
// return true if the range specified by offset, count and size
|
// return true if the range specified by offset, count and size
|
||||||
// doesn't fit into the file
|
// doesn't fit into the file
|
||||||
return ( count <= 0 ||
|
return ( count <= 0 ||
|
||||||
offset < 0 ||
|
offset <= 0 ||
|
||||||
offset > header->filesize ||
|
offset > header->filesize ||
|
||||||
offset + count * size < 0 ||
|
offset + count * size < 0 ||
|
||||||
offset + count * size > header->filesize );
|
offset + count * size > header->filesize );
|
||||||
|
@ -147,10 +147,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
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;
|
||||||
|
byte *dataPtr;
|
||||||
iqmData_t *iqmData;
|
iqmData_t *iqmData;
|
||||||
srfIQModel_t *surface;
|
srfIQModel_t *surface;
|
||||||
char meshName[MAX_QPATH];
|
char meshName[MAX_QPATH];
|
||||||
byte blendIndexesType, blendWeightsType;
|
int vertexArrayFormat[IQM_COLOR+1];
|
||||||
|
|
||||||
if( filesize < sizeof(iqmHeader_t) ) {
|
if( filesize < sizeof(iqmHeader_t) ) {
|
||||||
return qfalse;
|
return qfalse;
|
||||||
|
@ -206,8 +207,12 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
blendIndexesType = blendWeightsType = IQM_UBYTE;
|
for ( i = 0; i < ARRAY_LEN( vertexArrayFormat ); i++ ) {
|
||||||
|
vertexArrayFormat[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( header->num_meshes )
|
||||||
|
{
|
||||||
// check and swap vertex arrays
|
// check and swap vertex arrays
|
||||||
if( IQM_CheckRange( header, header->ofs_vertexarrays,
|
if( IQM_CheckRange( header, header->ofs_vertexarrays,
|
||||||
header->num_vertexarrays,
|
header->num_vertexarrays,
|
||||||
|
@ -253,6 +258,10 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( vertexarray->type < ARRAY_LEN( vertexArrayFormat ) ) {
|
||||||
|
vertexArrayFormat[vertexarray->type] = vertexarray->format;
|
||||||
|
}
|
||||||
|
|
||||||
switch( vertexarray->type ) {
|
switch( vertexarray->type ) {
|
||||||
case IQM_POSITION:
|
case IQM_POSITION:
|
||||||
case IQM_NORMAL:
|
case IQM_NORMAL:
|
||||||
|
@ -279,7 +288,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
vertexarray->size != 4 ) {
|
vertexarray->size != 4 ) {
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
blendIndexesType = vertexarray->format;
|
|
||||||
break;
|
break;
|
||||||
case IQM_BLENDWEIGHTS:
|
case IQM_BLENDWEIGHTS:
|
||||||
if( (vertexarray->format != IQM_FLOAT &&
|
if( (vertexarray->format != IQM_FLOAT &&
|
||||||
|
@ -287,7 +295,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
vertexarray->size != 4 ) {
|
vertexarray->size != 4 ) {
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
blendWeightsType = vertexarray->format;
|
|
||||||
break;
|
break;
|
||||||
case IQM_COLOR:
|
case IQM_COLOR:
|
||||||
if( vertexarray->format != IQM_UBYTE ||
|
if( vertexarray->format != IQM_UBYTE ||
|
||||||
|
@ -298,6 +305,29 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for required vertex arrays
|
||||||
|
if( vertexArrayFormat[IQM_POSITION] == -1 || vertexArrayFormat[IQM_NORMAL] == -1 || vertexArrayFormat[IQM_TEXCOORD] == -1 ) {
|
||||||
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s is missing IQM_POSITION, IQM_NORMAL, and/or IQM_TEXCOORD array.\n", mod_name );
|
||||||
|
return qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( header->num_joints ) {
|
||||||
|
if( vertexArrayFormat[IQM_BLENDINDEXES] == -1 || vertexArrayFormat[IQM_BLENDWEIGHTS] == -1 ) {
|
||||||
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s is missing IQM_BLENDINDEXES and/or IQM_BLENDWEIGHTS array.\n", mod_name );
|
||||||
|
return qfalse;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ignore blend arrays if present
|
||||||
|
vertexArrayFormat[IQM_BLENDINDEXES] = -1;
|
||||||
|
vertexArrayFormat[IQM_BLENDWEIGHTS] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// 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) ) ) {
|
||||||
|
@ -337,16 +367,14 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
}
|
}
|
||||||
|
|
||||||
// check ioq3 limits
|
// check ioq3 limits
|
||||||
if ( mesh->num_vertexes >= SHADER_MAX_VERTEXES )
|
if ( mesh->num_vertexes >= SHADER_MAX_VERTEXES ) {
|
||||||
{
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on %s (%i).\n",
|
||||||
ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on %s (%i).\n",
|
|
||||||
mod_name, SHADER_MAX_VERTEXES - 1, meshName[0] ? meshName : "a surface",
|
mod_name, SHADER_MAX_VERTEXES - 1, meshName[0] ? meshName : "a surface",
|
||||||
mesh->num_vertexes );
|
mesh->num_vertexes );
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
if ( mesh->num_triangles*3 >= SHADER_MAX_INDEXES )
|
if ( mesh->num_triangles*3 >= SHADER_MAX_INDEXES ) {
|
||||||
{
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on %s (%i).\n",
|
||||||
ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on %s (%i).\n",
|
|
||||||
mod_name, ( SHADER_MAX_INDEXES / 3 ) - 1, meshName[0] ? meshName : "a surface",
|
mod_name, ( SHADER_MAX_INDEXES / 3 ) - 1, meshName[0] ? meshName : "a surface",
|
||||||
mesh->num_triangles );
|
mesh->num_triangles );
|
||||||
return qfalse;
|
return qfalse;
|
||||||
|
@ -361,9 +389,10 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( header->num_poses != header->num_joints && header->num_poses != 0 ) {
|
if( header->num_poses != header->num_joints && header->num_poses != 0 ) {
|
||||||
ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has %d poses and %d joints, must have the same number or 0 poses\n",
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s has %d poses and %d joints, must have the same number or 0 poses\n",
|
||||||
mod_name, header->num_poses, header->num_joints );
|
mod_name, header->num_poses, header->num_joints );
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
|
@ -460,26 +489,41 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
|
|
||||||
// allocate the model and copy the data
|
// allocate the model and copy the data
|
||||||
size = sizeof(iqmData_t);
|
size = sizeof(iqmData_t);
|
||||||
size += header->num_meshes * sizeof( srfIQModel_t );
|
if( header->num_meshes ) {
|
||||||
size += header->num_joints * 12 * sizeof( float ); // joint mats
|
size += header->num_meshes * sizeof( srfIQModel_t ); // surfaces
|
||||||
size += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats
|
size += header->num_triangles * 3 * sizeof(int); // triangles
|
||||||
if(header->ofs_bounds)
|
|
||||||
size += header->num_frames * 6 * sizeof(float); // model bounds
|
|
||||||
size += header->num_vertexes * 3 * sizeof(float); // positions
|
size += header->num_vertexes * 3 * sizeof(float); // positions
|
||||||
size += header->num_vertexes * 2 * sizeof(float); // texcoords
|
size += header->num_vertexes * 2 * sizeof(float); // texcoords
|
||||||
size += header->num_vertexes * 3 * sizeof(float); // normals
|
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); // colors
|
|
||||||
size += header->num_joints * sizeof(int); // parents
|
|
||||||
size += header->num_triangles * 3 * sizeof(int); // triangles
|
|
||||||
size += joint_names; // joint names
|
|
||||||
|
|
||||||
// blendWeights
|
if ( vertexArrayFormat[IQM_TANGENT] != -1 ) {
|
||||||
if (blendWeightsType == IQM_FLOAT) {
|
size += header->num_vertexes * 4 * sizeof(float); // tangents
|
||||||
size += header->num_vertexes * 4 * sizeof(float);
|
}
|
||||||
} else {
|
|
||||||
size += header->num_vertexes * 4 * sizeof(byte);
|
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( header->num_joints ) {
|
||||||
|
size += joint_names; // joint names
|
||||||
|
size += header->num_joints * sizeof(int); // joint parents
|
||||||
|
size += header->num_joints * 12 * sizeof( float ); // joint mats
|
||||||
|
}
|
||||||
|
if( header->num_poses ) {
|
||||||
|
size += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats
|
||||||
|
}
|
||||||
|
if( header->ofs_bounds ) {
|
||||||
|
size += header->num_frames * 6 * sizeof(float); // model bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
mod->type = MOD_IQM;
|
mod->type = MOD_IQM;
|
||||||
|
@ -487,45 +531,186 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
mod->modelData = iqmData;
|
mod->modelData = iqmData;
|
||||||
|
|
||||||
// fill header
|
// fill header
|
||||||
iqmData->num_vertexes = header->num_vertexes;
|
iqmData->num_vertexes = ( header->num_meshes > 0 ) ? header->num_vertexes : 0;
|
||||||
iqmData->num_triangles = header->num_triangles;
|
iqmData->num_triangles = ( header->num_meshes > 0 ) ? header->num_triangles : 0;
|
||||||
iqmData->num_frames = header->num_frames;
|
iqmData->num_frames = header->num_frames;
|
||||||
iqmData->num_surfaces = header->num_meshes;
|
iqmData->num_surfaces = header->num_meshes;
|
||||||
iqmData->num_joints = header->num_joints;
|
iqmData->num_joints = header->num_joints;
|
||||||
iqmData->num_poses = header->num_poses;
|
iqmData->num_poses = header->num_poses;
|
||||||
iqmData->blendWeightsType = blendWeightsType;
|
iqmData->blendWeightsType = vertexArrayFormat[IQM_BLENDWEIGHTS];
|
||||||
iqmData->surfaces = (srfIQModel_t *)(iqmData + 1);
|
|
||||||
iqmData->jointMats = (float *) (iqmData->surfaces + iqmData->num_surfaces);
|
dataPtr = (byte*)iqmData + sizeof(iqmData_t);
|
||||||
iqmData->poseMats = iqmData->jointMats + 12 * header->num_joints;
|
if( header->num_meshes ) {
|
||||||
if(header->ofs_bounds)
|
iqmData->surfaces = (struct srfIQModel_s*)dataPtr;
|
||||||
|
dataPtr += header->num_meshes * sizeof( srfIQModel_t );
|
||||||
|
|
||||||
|
iqmData->triangles = (int*)dataPtr;
|
||||||
|
dataPtr += header->num_triangles * 3 * sizeof(int); // triangles
|
||||||
|
|
||||||
|
iqmData->positions = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_vertexes * 3 * sizeof(float); // positions
|
||||||
|
|
||||||
|
iqmData->texcoords = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_vertexes * 2 * sizeof(float); // texcoords
|
||||||
|
|
||||||
|
iqmData->normals = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_vertexes * 3 * sizeof(float); // normals
|
||||||
|
|
||||||
|
if ( vertexArrayFormat[IQM_TANGENT] != -1 ) {
|
||||||
|
iqmData->tangents = (float*)dataPtr;
|
||||||
|
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( header->num_joints ) {
|
||||||
|
iqmData->jointNames = (char*)dataPtr;
|
||||||
|
dataPtr += joint_names; // joint names
|
||||||
|
|
||||||
|
iqmData->jointParents = (int*)dataPtr;
|
||||||
|
dataPtr += header->num_joints * sizeof(int); // joint parents
|
||||||
|
|
||||||
|
iqmData->jointMats = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_joints * 12 * sizeof( float ); // joint mats
|
||||||
|
}
|
||||||
|
if( header->num_poses ) {
|
||||||
|
iqmData->poseMats = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats
|
||||||
|
}
|
||||||
|
if( header->ofs_bounds ) {
|
||||||
|
iqmData->bounds = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_frames * 6 * sizeof(float); // model bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
if( header->num_meshes )
|
||||||
{
|
{
|
||||||
iqmData->bounds = iqmData->poseMats + 12 * header->num_poses * header->num_frames;
|
// register shaders
|
||||||
iqmData->positions = iqmData->bounds + 6 * header->num_frames;
|
// overwrite the material offset with the shader index
|
||||||
|
mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes);
|
||||||
|
surface = iqmData->surfaces;
|
||||||
|
str = (char *)header + header->ofs_text;
|
||||||
|
for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) {
|
||||||
|
surface->surfaceType = SF_IQM;
|
||||||
|
Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name));
|
||||||
|
Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster
|
||||||
|
surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue );
|
||||||
|
if( surface->shader->defaultShader )
|
||||||
|
surface->shader = tr.defaultShader;
|
||||||
|
surface->data = iqmData;
|
||||||
|
surface->first_vertex = mesh->first_vertex;
|
||||||
|
surface->num_vertexes = mesh->num_vertexes;
|
||||||
|
surface->first_triangle = mesh->first_triangle;
|
||||||
|
surface->num_triangles = mesh->num_triangles;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
iqmData->positions = iqmData->poseMats + 12 * header->num_poses * header->num_frames;
|
|
||||||
iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes;
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(blendWeightsType == IQM_FLOAT) {
|
// copy triangles
|
||||||
iqmData->blendWeights.f = (float *)(iqmData->blendIndexes + 4 * header->num_vertexes);
|
triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles);
|
||||||
iqmData->colors = (byte *)(iqmData->blendWeights.f + 4 * header->num_vertexes);
|
for( i = 0; i < header->num_triangles; i++, triangle++ ) {
|
||||||
|
iqmData->triangles[3*i+0] = triangle->vertex[0];
|
||||||
|
iqmData->triangles[3*i+1] = triangle->vertex[1];
|
||||||
|
iqmData->triangles[3*i+2] = triangle->vertex[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy vertexarrays and indexes
|
||||||
|
vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays);
|
||||||
|
for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) {
|
||||||
|
int n;
|
||||||
|
|
||||||
|
// skip disabled arrays
|
||||||
|
if( vertexarray->type < ARRAY_LEN( vertexArrayFormat )
|
||||||
|
&& vertexArrayFormat[vertexarray->type] == -1 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// total number of values
|
||||||
|
n = header->num_vertexes * vertexarray->size;
|
||||||
|
|
||||||
|
switch( vertexarray->type ) {
|
||||||
|
case IQM_POSITION:
|
||||||
|
Com_Memcpy( iqmData->positions,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(float) );
|
||||||
|
break;
|
||||||
|
case IQM_NORMAL:
|
||||||
|
Com_Memcpy( iqmData->normals,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(float) );
|
||||||
|
break;
|
||||||
|
case IQM_TANGENT:
|
||||||
|
Com_Memcpy( iqmData->tangents,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(float) );
|
||||||
|
break;
|
||||||
|
case IQM_TEXCOORD:
|
||||||
|
Com_Memcpy( iqmData->texcoords,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
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 {
|
} else {
|
||||||
iqmData->blendWeights.b = iqmData->blendIndexes + 4 * header->num_vertexes;
|
Com_Memcpy( iqmData->blendIndexes,
|
||||||
iqmData->colors = iqmData->blendWeights.b + 4 * header->num_vertexes;
|
(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,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(byte) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes);
|
if( header->num_joints )
|
||||||
iqmData->triangles = iqmData->jointParents + header->num_joints;
|
{
|
||||||
iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles);
|
// copy joint names
|
||||||
|
str = iqmData->jointNames;
|
||||||
|
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
||||||
|
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
||||||
|
char *name = (char *)header + header->ofs_text +
|
||||||
|
joint->name;
|
||||||
|
int len = strlen( name ) + 1;
|
||||||
|
Com_Memcpy( str, name, len );
|
||||||
|
str += len;
|
||||||
|
}
|
||||||
|
|
||||||
if ( header->num_joints == 0 )
|
// copy joint parents
|
||||||
iqmData->jointMats = NULL;
|
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
||||||
|
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
||||||
if ( header->num_poses == 0 )
|
iqmData->jointParents[i] = joint->parent;
|
||||||
iqmData->poseMats = NULL;
|
}
|
||||||
|
|
||||||
// calculate joint matrices and their inverses
|
// calculate joint matrices and their inverses
|
||||||
// joint inverses are needed only until the pose matrices are calculated
|
// joint inverses are needed only until the pose matrices are calculated
|
||||||
|
@ -553,7 +738,10 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
matInv += 12;
|
matInv += 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( header->num_poses )
|
||||||
|
{
|
||||||
// calculate pose matrices
|
// calculate pose matrices
|
||||||
framedata = (unsigned short *)((byte *)header + header->ofs_frames);
|
framedata = (unsigned short *)((byte *)header + header->ofs_frames);
|
||||||
mat = iqmData->poseMats;
|
mat = iqmData->poseMats;
|
||||||
|
@ -612,109 +800,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
mat += 12;
|
mat += 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// register shaders
|
|
||||||
// overwrite the material offset with the shader index
|
|
||||||
mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes);
|
|
||||||
surface = iqmData->surfaces;
|
|
||||||
str = (char *)header + header->ofs_text;
|
|
||||||
for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) {
|
|
||||||
surface->surfaceType = SF_IQM;
|
|
||||||
Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name));
|
|
||||||
Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster
|
|
||||||
surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue );
|
|
||||||
if( surface->shader->defaultShader )
|
|
||||||
surface->shader = tr.defaultShader;
|
|
||||||
surface->data = iqmData;
|
|
||||||
surface->first_vertex = mesh->first_vertex;
|
|
||||||
surface->num_vertexes = mesh->num_vertexes;
|
|
||||||
surface->first_triangle = mesh->first_triangle;
|
|
||||||
surface->num_triangles = mesh->num_triangles;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy vertexarrays and indexes
|
|
||||||
vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays);
|
|
||||||
for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) {
|
|
||||||
int n;
|
|
||||||
|
|
||||||
// total number of values
|
|
||||||
n = header->num_vertexes * vertexarray->size;
|
|
||||||
|
|
||||||
switch( vertexarray->type ) {
|
|
||||||
case IQM_POSITION:
|
|
||||||
Com_Memcpy( iqmData->positions,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_NORMAL:
|
|
||||||
Com_Memcpy( iqmData->normals,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_TANGENT:
|
|
||||||
Com_Memcpy( iqmData->tangents,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_TEXCOORD:
|
|
||||||
Com_Memcpy( iqmData->texcoords,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_BLENDINDEXES:
|
|
||||||
if( blendIndexesType == 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( blendWeightsType == 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,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(byte) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy joint parents
|
|
||||||
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
|
||||||
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
|
||||||
iqmData->jointParents[i] = joint->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy triangles
|
|
||||||
triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles);
|
|
||||||
for( i = 0; i < header->num_triangles; i++, triangle++ ) {
|
|
||||||
iqmData->triangles[3*i+0] = triangle->vertex[0];
|
|
||||||
iqmData->triangles[3*i+1] = triangle->vertex[1];
|
|
||||||
iqmData->triangles[3*i+2] = triangle->vertex[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy joint names
|
|
||||||
str = iqmData->names;
|
|
||||||
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
|
||||||
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
|
||||||
char *name = (char *)header + header->ofs_text +
|
|
||||||
joint->name;
|
|
||||||
int len = strlen( name ) + 1;
|
|
||||||
Com_Memcpy( str, name, len );
|
|
||||||
str += len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy model bounds
|
// copy model bounds
|
||||||
|
@ -1152,7 +1237,7 @@ int R_IQMLerpTag( orientation_t *tag, iqmData_t *data,
|
||||||
float frac, const char *tagName ) {
|
float frac, const char *tagName ) {
|
||||||
float jointMats[IQM_MAX_JOINTS * 12];
|
float jointMats[IQM_MAX_JOINTS * 12];
|
||||||
int joint;
|
int joint;
|
||||||
char *names = data->names;
|
char *names = data->jointNames;
|
||||||
|
|
||||||
// get joint number by reading the joint names
|
// get joint number by reading the joint names
|
||||||
for( joint = 0; joint < data->num_joints; joint++ ) {
|
for( joint = 0; joint < data->num_joints; joint++ ) {
|
||||||
|
|
|
@ -949,6 +949,7 @@ typedef struct {
|
||||||
int num_poses;
|
int num_poses;
|
||||||
struct srfIQModel_s *surfaces;
|
struct srfIQModel_s *surfaces;
|
||||||
|
|
||||||
|
int *triangles;
|
||||||
float *positions;
|
float *positions;
|
||||||
float *texcoords;
|
float *texcoords;
|
||||||
float *normals;
|
float *normals;
|
||||||
|
@ -959,18 +960,17 @@ typedef struct {
|
||||||
byte *b;
|
byte *b;
|
||||||
} blendWeights;
|
} blendWeights;
|
||||||
byte *colors;
|
byte *colors;
|
||||||
int *triangles;
|
|
||||||
|
|
||||||
// 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
|
byte blendWeightsType; // IQM_UBYTE or IQM_FLOAT
|
||||||
|
|
||||||
|
char *jointNames;
|
||||||
int *jointParents;
|
int *jointParents;
|
||||||
float *jointMats;
|
float *jointMats;
|
||||||
float *poseMats;
|
float *poseMats;
|
||||||
float *bounds;
|
float *bounds;
|
||||||
char *names;
|
|
||||||
} iqmData_t;
|
} iqmData_t;
|
||||||
|
|
||||||
// inter-quake-model surface
|
// inter-quake-model surface
|
||||||
|
|
|
@ -33,11 +33,11 @@ static float identityMatrix[12] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static qboolean IQM_CheckRange( iqmHeader_t *header, int offset,
|
static qboolean IQM_CheckRange( iqmHeader_t *header, int offset,
|
||||||
int count,int size ) {
|
int count, int size ) {
|
||||||
// return true if the range specified by offset, count and size
|
// return true if the range specified by offset, count and size
|
||||||
// doesn't fit into the file
|
// doesn't fit into the file
|
||||||
return ( count <= 0 ||
|
return ( count <= 0 ||
|
||||||
offset < 0 ||
|
offset <= 0 ||
|
||||||
offset > header->filesize ||
|
offset > header->filesize ||
|
||||||
offset + count * size < 0 ||
|
offset + count * size < 0 ||
|
||||||
offset + count * size > header->filesize );
|
offset + count * size > header->filesize );
|
||||||
|
@ -147,10 +147,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
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;
|
||||||
|
byte *dataPtr;
|
||||||
iqmData_t *iqmData;
|
iqmData_t *iqmData;
|
||||||
srfIQModel_t *surface;
|
srfIQModel_t *surface;
|
||||||
char meshName[MAX_QPATH];
|
char meshName[MAX_QPATH];
|
||||||
byte blendIndexesType, blendWeightsType;
|
int vertexArrayFormat[IQM_COLOR+1];
|
||||||
|
|
||||||
if( filesize < sizeof(iqmHeader_t) ) {
|
if( filesize < sizeof(iqmHeader_t) ) {
|
||||||
return qfalse;
|
return qfalse;
|
||||||
|
@ -206,8 +207,12 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
blendIndexesType = blendWeightsType = IQM_UBYTE;
|
for ( i = 0; i < ARRAY_LEN( vertexArrayFormat ); i++ ) {
|
||||||
|
vertexArrayFormat[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( header->num_meshes )
|
||||||
|
{
|
||||||
// check and swap vertex arrays
|
// check and swap vertex arrays
|
||||||
if( IQM_CheckRange( header, header->ofs_vertexarrays,
|
if( IQM_CheckRange( header, header->ofs_vertexarrays,
|
||||||
header->num_vertexarrays,
|
header->num_vertexarrays,
|
||||||
|
@ -253,6 +258,10 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( vertexarray->type < ARRAY_LEN( vertexArrayFormat ) ) {
|
||||||
|
vertexArrayFormat[vertexarray->type] = vertexarray->format;
|
||||||
|
}
|
||||||
|
|
||||||
switch( vertexarray->type ) {
|
switch( vertexarray->type ) {
|
||||||
case IQM_POSITION:
|
case IQM_POSITION:
|
||||||
case IQM_NORMAL:
|
case IQM_NORMAL:
|
||||||
|
@ -279,7 +288,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
vertexarray->size != 4 ) {
|
vertexarray->size != 4 ) {
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
blendIndexesType = vertexarray->format;
|
|
||||||
break;
|
break;
|
||||||
case IQM_BLENDWEIGHTS:
|
case IQM_BLENDWEIGHTS:
|
||||||
if( (vertexarray->format != IQM_FLOAT &&
|
if( (vertexarray->format != IQM_FLOAT &&
|
||||||
|
@ -287,7 +295,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
vertexarray->size != 4 ) {
|
vertexarray->size != 4 ) {
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
blendWeightsType = vertexarray->format;
|
|
||||||
break;
|
break;
|
||||||
case IQM_COLOR:
|
case IQM_COLOR:
|
||||||
if( vertexarray->format != IQM_UBYTE ||
|
if( vertexarray->format != IQM_UBYTE ||
|
||||||
|
@ -298,6 +305,32 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for required vertex arrays
|
||||||
|
if( vertexArrayFormat[IQM_POSITION] == -1 || vertexArrayFormat[IQM_NORMAL] == -1 || vertexArrayFormat[IQM_TEXCOORD] == -1 ) {
|
||||||
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s is missing IQM_POSITION, IQM_NORMAL, and/or IQM_TEXCOORD array.\n", mod_name );
|
||||||
|
return qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( header->num_joints ) {
|
||||||
|
if( vertexArrayFormat[IQM_BLENDINDEXES] == -1 || vertexArrayFormat[IQM_BLENDWEIGHTS] == -1 ) {
|
||||||
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s is missing IQM_BLENDINDEXES and/or IQM_BLENDWEIGHTS array.\n", mod_name );
|
||||||
|
return qfalse;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ignore blend arrays if present
|
||||||
|
vertexArrayFormat[IQM_BLENDINDEXES] = -1;
|
||||||
|
vertexArrayFormat[IQM_BLENDWEIGHTS] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// opengl2 renderer requires tangents
|
||||||
|
if( vertexArrayFormat[IQM_TANGENT] == -1 ) {
|
||||||
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s is missing IQM_TANGENT array.\n", mod_name );
|
||||||
|
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) ) ) {
|
||||||
|
@ -337,16 +370,14 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
}
|
}
|
||||||
|
|
||||||
// check ioq3 limits
|
// check ioq3 limits
|
||||||
if ( mesh->num_vertexes >= SHADER_MAX_VERTEXES )
|
if ( mesh->num_vertexes >= SHADER_MAX_VERTEXES ) {
|
||||||
{
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on %s (%i).\n",
|
||||||
ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on %s (%i).\n",
|
|
||||||
mod_name, SHADER_MAX_VERTEXES - 1, meshName[0] ? meshName : "a surface",
|
mod_name, SHADER_MAX_VERTEXES - 1, meshName[0] ? meshName : "a surface",
|
||||||
mesh->num_vertexes );
|
mesh->num_vertexes );
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
if ( mesh->num_triangles*3 >= SHADER_MAX_INDEXES )
|
if ( mesh->num_triangles*3 >= SHADER_MAX_INDEXES ) {
|
||||||
{
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on %s (%i).\n",
|
||||||
ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on %s (%i).\n",
|
|
||||||
mod_name, ( SHADER_MAX_INDEXES / 3 ) - 1, meshName[0] ? meshName : "a surface",
|
mod_name, ( SHADER_MAX_INDEXES / 3 ) - 1, meshName[0] ? meshName : "a surface",
|
||||||
mesh->num_triangles );
|
mesh->num_triangles );
|
||||||
return qfalse;
|
return qfalse;
|
||||||
|
@ -361,9 +392,10 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( header->num_poses != header->num_joints && header->num_poses != 0 ) {
|
if( header->num_poses != header->num_joints && header->num_poses != 0 ) {
|
||||||
ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has %d poses and %d joints, must have the same number or 0 poses\n",
|
ri.Printf( PRINT_WARNING, "R_LoadIQM: %s has %d poses and %d joints, must have the same number or 0 poses\n",
|
||||||
mod_name, header->num_poses, header->num_joints );
|
mod_name, header->num_poses, header->num_joints );
|
||||||
return qfalse;
|
return qfalse;
|
||||||
}
|
}
|
||||||
|
@ -460,26 +492,41 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
|
|
||||||
// allocate the model and copy the data
|
// allocate the model and copy the data
|
||||||
size = sizeof(iqmData_t);
|
size = sizeof(iqmData_t);
|
||||||
size += header->num_meshes * sizeof( srfIQModel_t );
|
if( header->num_meshes ) {
|
||||||
size += header->num_joints * 12 * sizeof( float ); // joint mats
|
size += header->num_meshes * sizeof( srfIQModel_t ); // surfaces
|
||||||
size += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats
|
size += header->num_triangles * 3 * sizeof(int); // triangles
|
||||||
if(header->ofs_bounds)
|
|
||||||
size += header->num_frames * 6 * sizeof(float); // model bounds
|
|
||||||
size += header->num_vertexes * 3 * sizeof(float); // positions
|
size += header->num_vertexes * 3 * sizeof(float); // positions
|
||||||
size += header->num_vertexes * 2 * sizeof(float); // texcoords
|
size += header->num_vertexes * 2 * sizeof(float); // texcoords
|
||||||
size += header->num_vertexes * 3 * sizeof(float); // normals
|
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); // colors
|
|
||||||
size += header->num_joints * sizeof(int); // parents
|
|
||||||
size += header->num_triangles * 3 * sizeof(int); // triangles
|
|
||||||
size += joint_names; // joint names
|
|
||||||
|
|
||||||
// blendWeights
|
if ( vertexArrayFormat[IQM_TANGENT] != -1 ) {
|
||||||
if (blendWeightsType == IQM_FLOAT) {
|
size += header->num_vertexes * 4 * sizeof(float); // tangents
|
||||||
size += header->num_vertexes * 4 * sizeof(float);
|
}
|
||||||
} else {
|
|
||||||
size += header->num_vertexes * 4 * sizeof(byte);
|
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( header->num_joints ) {
|
||||||
|
size += joint_names; // joint names
|
||||||
|
size += header->num_joints * sizeof(int); // joint parents
|
||||||
|
size += header->num_joints * 12 * sizeof( float ); // joint mats
|
||||||
|
}
|
||||||
|
if( header->num_poses ) {
|
||||||
|
size += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats
|
||||||
|
}
|
||||||
|
if( header->ofs_bounds ) {
|
||||||
|
size += header->num_frames * 6 * sizeof(float); // model bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
mod->type = MOD_IQM;
|
mod->type = MOD_IQM;
|
||||||
|
@ -487,45 +534,186 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
mod->modelData = iqmData;
|
mod->modelData = iqmData;
|
||||||
|
|
||||||
// fill header
|
// fill header
|
||||||
iqmData->num_vertexes = header->num_vertexes;
|
iqmData->num_vertexes = ( header->num_meshes > 0 ) ? header->num_vertexes : 0;
|
||||||
iqmData->num_triangles = header->num_triangles;
|
iqmData->num_triangles = ( header->num_meshes > 0 ) ? header->num_triangles : 0;
|
||||||
iqmData->num_frames = header->num_frames;
|
iqmData->num_frames = header->num_frames;
|
||||||
iqmData->num_surfaces = header->num_meshes;
|
iqmData->num_surfaces = header->num_meshes;
|
||||||
iqmData->num_joints = header->num_joints;
|
iqmData->num_joints = header->num_joints;
|
||||||
iqmData->num_poses = header->num_poses;
|
iqmData->num_poses = header->num_poses;
|
||||||
iqmData->blendWeightsType = blendWeightsType;
|
iqmData->blendWeightsType = vertexArrayFormat[IQM_BLENDWEIGHTS];
|
||||||
iqmData->surfaces = (srfIQModel_t *)(iqmData + 1);
|
|
||||||
iqmData->jointMats = (float *) (iqmData->surfaces + iqmData->num_surfaces);
|
dataPtr = (byte*)iqmData + sizeof(iqmData_t);
|
||||||
iqmData->poseMats = iqmData->jointMats + 12 * header->num_joints;
|
if( header->num_meshes ) {
|
||||||
if(header->ofs_bounds)
|
iqmData->surfaces = (struct srfIQModel_s*)dataPtr;
|
||||||
|
dataPtr += header->num_meshes * sizeof( srfIQModel_t );
|
||||||
|
|
||||||
|
iqmData->triangles = (int*)dataPtr;
|
||||||
|
dataPtr += header->num_triangles * 3 * sizeof(int); // triangles
|
||||||
|
|
||||||
|
iqmData->positions = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_vertexes * 3 * sizeof(float); // positions
|
||||||
|
|
||||||
|
iqmData->texcoords = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_vertexes * 2 * sizeof(float); // texcoords
|
||||||
|
|
||||||
|
iqmData->normals = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_vertexes * 3 * sizeof(float); // normals
|
||||||
|
|
||||||
|
if ( vertexArrayFormat[IQM_TANGENT] != -1 ) {
|
||||||
|
iqmData->tangents = (float*)dataPtr;
|
||||||
|
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( header->num_joints ) {
|
||||||
|
iqmData->jointNames = (char*)dataPtr;
|
||||||
|
dataPtr += joint_names; // joint names
|
||||||
|
|
||||||
|
iqmData->jointParents = (int*)dataPtr;
|
||||||
|
dataPtr += header->num_joints * sizeof(int); // joint parents
|
||||||
|
|
||||||
|
iqmData->jointMats = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_joints * 12 * sizeof( float ); // joint mats
|
||||||
|
}
|
||||||
|
if( header->num_poses ) {
|
||||||
|
iqmData->poseMats = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats
|
||||||
|
}
|
||||||
|
if( header->ofs_bounds ) {
|
||||||
|
iqmData->bounds = (float*)dataPtr;
|
||||||
|
dataPtr += header->num_frames * 6 * sizeof(float); // model bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
if( header->num_meshes )
|
||||||
{
|
{
|
||||||
iqmData->bounds = iqmData->poseMats + 12 * header->num_poses * header->num_frames;
|
// register shaders
|
||||||
iqmData->positions = iqmData->bounds + 6 * header->num_frames;
|
// overwrite the material offset with the shader index
|
||||||
|
mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes);
|
||||||
|
surface = iqmData->surfaces;
|
||||||
|
str = (char *)header + header->ofs_text;
|
||||||
|
for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) {
|
||||||
|
surface->surfaceType = SF_IQM;
|
||||||
|
Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name));
|
||||||
|
Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster
|
||||||
|
surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue );
|
||||||
|
if( surface->shader->defaultShader )
|
||||||
|
surface->shader = tr.defaultShader;
|
||||||
|
surface->data = iqmData;
|
||||||
|
surface->first_vertex = mesh->first_vertex;
|
||||||
|
surface->num_vertexes = mesh->num_vertexes;
|
||||||
|
surface->first_triangle = mesh->first_triangle;
|
||||||
|
surface->num_triangles = mesh->num_triangles;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
iqmData->positions = iqmData->poseMats + 12 * header->num_poses * header->num_frames;
|
|
||||||
iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes;
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(blendWeightsType == IQM_FLOAT) {
|
// copy triangles
|
||||||
iqmData->blendWeights.f = (float *)(iqmData->blendIndexes + 4 * header->num_vertexes);
|
triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles);
|
||||||
iqmData->colors = (byte *)(iqmData->blendWeights.f + 4 * header->num_vertexes);
|
for( i = 0; i < header->num_triangles; i++, triangle++ ) {
|
||||||
|
iqmData->triangles[3*i+0] = triangle->vertex[0];
|
||||||
|
iqmData->triangles[3*i+1] = triangle->vertex[1];
|
||||||
|
iqmData->triangles[3*i+2] = triangle->vertex[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy vertexarrays and indexes
|
||||||
|
vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays);
|
||||||
|
for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) {
|
||||||
|
int n;
|
||||||
|
|
||||||
|
// skip disabled arrays
|
||||||
|
if( vertexarray->type < ARRAY_LEN( vertexArrayFormat )
|
||||||
|
&& vertexArrayFormat[vertexarray->type] == -1 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// total number of values
|
||||||
|
n = header->num_vertexes * vertexarray->size;
|
||||||
|
|
||||||
|
switch( vertexarray->type ) {
|
||||||
|
case IQM_POSITION:
|
||||||
|
Com_Memcpy( iqmData->positions,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(float) );
|
||||||
|
break;
|
||||||
|
case IQM_NORMAL:
|
||||||
|
Com_Memcpy( iqmData->normals,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(float) );
|
||||||
|
break;
|
||||||
|
case IQM_TANGENT:
|
||||||
|
Com_Memcpy( iqmData->tangents,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(float) );
|
||||||
|
break;
|
||||||
|
case IQM_TEXCOORD:
|
||||||
|
Com_Memcpy( iqmData->texcoords,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
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 {
|
} else {
|
||||||
iqmData->blendWeights.b = iqmData->blendIndexes + 4 * header->num_vertexes;
|
Com_Memcpy( iqmData->blendIndexes,
|
||||||
iqmData->colors = iqmData->blendWeights.b + 4 * header->num_vertexes;
|
(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,
|
||||||
|
(byte *)header + vertexarray->offset,
|
||||||
|
n * sizeof(byte) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes);
|
if( header->num_joints )
|
||||||
iqmData->triangles = iqmData->jointParents + header->num_joints;
|
{
|
||||||
iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles);
|
// copy joint names
|
||||||
|
str = iqmData->jointNames;
|
||||||
|
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
||||||
|
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
||||||
|
char *name = (char *)header + header->ofs_text +
|
||||||
|
joint->name;
|
||||||
|
int len = strlen( name ) + 1;
|
||||||
|
Com_Memcpy( str, name, len );
|
||||||
|
str += len;
|
||||||
|
}
|
||||||
|
|
||||||
if ( header->num_joints == 0 )
|
// copy joint parents
|
||||||
iqmData->jointMats = NULL;
|
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
||||||
|
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
||||||
if ( header->num_poses == 0 )
|
iqmData->jointParents[i] = joint->parent;
|
||||||
iqmData->poseMats = NULL;
|
}
|
||||||
|
|
||||||
// calculate joint matrices and their inverses
|
// calculate joint matrices and their inverses
|
||||||
// joint inverses are needed only until the pose matrices are calculated
|
// joint inverses are needed only until the pose matrices are calculated
|
||||||
|
@ -553,7 +741,10 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
matInv += 12;
|
matInv += 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( header->num_poses )
|
||||||
|
{
|
||||||
// calculate pose matrices
|
// calculate pose matrices
|
||||||
framedata = (unsigned short *)((byte *)header + header->ofs_frames);
|
framedata = (unsigned short *)((byte *)header + header->ofs_frames);
|
||||||
mat = iqmData->poseMats;
|
mat = iqmData->poseMats;
|
||||||
|
@ -612,109 +803,6 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na
|
||||||
mat += 12;
|
mat += 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// register shaders
|
|
||||||
// overwrite the material offset with the shader index
|
|
||||||
mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes);
|
|
||||||
surface = iqmData->surfaces;
|
|
||||||
str = (char *)header + header->ofs_text;
|
|
||||||
for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) {
|
|
||||||
surface->surfaceType = SF_IQM;
|
|
||||||
Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name));
|
|
||||||
Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster
|
|
||||||
surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue );
|
|
||||||
if( surface->shader->defaultShader )
|
|
||||||
surface->shader = tr.defaultShader;
|
|
||||||
surface->data = iqmData;
|
|
||||||
surface->first_vertex = mesh->first_vertex;
|
|
||||||
surface->num_vertexes = mesh->num_vertexes;
|
|
||||||
surface->first_triangle = mesh->first_triangle;
|
|
||||||
surface->num_triangles = mesh->num_triangles;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy vertexarrays and indexes
|
|
||||||
vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays);
|
|
||||||
for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) {
|
|
||||||
int n;
|
|
||||||
|
|
||||||
// total number of values
|
|
||||||
n = header->num_vertexes * vertexarray->size;
|
|
||||||
|
|
||||||
switch( vertexarray->type ) {
|
|
||||||
case IQM_POSITION:
|
|
||||||
Com_Memcpy( iqmData->positions,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_NORMAL:
|
|
||||||
Com_Memcpy( iqmData->normals,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_TANGENT:
|
|
||||||
Com_Memcpy( iqmData->tangents,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_TEXCOORD:
|
|
||||||
Com_Memcpy( iqmData->texcoords,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(float) );
|
|
||||||
break;
|
|
||||||
case IQM_BLENDINDEXES:
|
|
||||||
if( blendIndexesType == 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( blendWeightsType == 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,
|
|
||||||
(byte *)header + vertexarray->offset,
|
|
||||||
n * sizeof(byte) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy joint parents
|
|
||||||
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
|
||||||
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
|
||||||
iqmData->jointParents[i] = joint->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy triangles
|
|
||||||
triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles);
|
|
||||||
for( i = 0; i < header->num_triangles; i++, triangle++ ) {
|
|
||||||
iqmData->triangles[3*i+0] = triangle->vertex[0];
|
|
||||||
iqmData->triangles[3*i+1] = triangle->vertex[1];
|
|
||||||
iqmData->triangles[3*i+2] = triangle->vertex[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy joint names
|
|
||||||
str = iqmData->names;
|
|
||||||
joint = (iqmJoint_t *)((byte *)header + header->ofs_joints);
|
|
||||||
for( i = 0; i < header->num_joints; i++, joint++ ) {
|
|
||||||
char *name = (char *)header + header->ofs_text +
|
|
||||||
joint->name;
|
|
||||||
int len = strlen( name ) + 1;
|
|
||||||
Com_Memcpy( str, name, len );
|
|
||||||
str += len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy model bounds
|
// copy model bounds
|
||||||
|
@ -1161,7 +1249,7 @@ int R_IQMLerpTag( orientation_t *tag, iqmData_t *data,
|
||||||
float frac, const char *tagName ) {
|
float frac, const char *tagName ) {
|
||||||
float jointMats[IQM_MAX_JOINTS * 12];
|
float jointMats[IQM_MAX_JOINTS * 12];
|
||||||
int joint;
|
int joint;
|
||||||
char *names = data->names;
|
char *names = data->jointNames;
|
||||||
|
|
||||||
// get joint number by reading the joint names
|
// get joint number by reading the joint names
|
||||||
for( joint = 0; joint < data->num_joints; joint++ ) {
|
for( joint = 0; joint < data->num_joints; joint++ ) {
|
||||||
|
|
Loading…
Reference in a new issue