Example #1
0
/*
=================
R_LoadIQM

Load an IQM model and compute the joint matrices for every frame.
=================
*/
qboolean R_LoadIQM(model_t* mod, void* buffer, int filesize, const char* mod_name) {
    iqmHeader_t*     header;
    iqmVertexArray_t*    vertexarray;
    iqmTriangle_t*       triangle;
    iqmMesh_t*       mesh;
    iqmJoint_t*      joint;
    iqmPose_t*       pose;
    iqmBounds_t*     bounds;
    unsigned short*      framedata;
    char*            str;
    int         i, j;
    float           jointMats[IQM_MAX_JOINTS * 2 * 12];
    float*           mat;
    size_t          size, joint_names;
    iqmData_t*       iqmData;
    srfIQModel_t*        surface;

    if (filesize < sizeof(iqmHeader_t)) {
        return qfalse;
    }

    header = (iqmHeader_t*)buffer;
    if (Q_strncmp(header->magic, IQM_MAGIC, sizeof(header->magic))) {
        return qfalse;
    }

    LL(header->version);
    if (header->version != IQM_VERSION) {
        ri.Printf(PRINT_WARNING, "R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.\n",
                  mod_name, header->version, IQM_VERSION);
        return qfalse;
    }

    LL(header->filesize);
    if (header->filesize > filesize || header->filesize > 16 << 20) {
        return qfalse;
    }

    LL(header->flags);
    LL(header->num_text);
    LL(header->ofs_text);
    LL(header->num_meshes);
    LL(header->ofs_meshes);
    LL(header->num_vertexarrays);
    LL(header->num_vertexes);
    LL(header->ofs_vertexarrays);
    LL(header->num_triangles);
    LL(header->ofs_triangles);
    LL(header->ofs_adjacency);
    LL(header->num_joints);
    LL(header->ofs_joints);
    LL(header->num_poses);
    LL(header->ofs_poses);
    LL(header->num_anims);
    LL(header->ofs_anims);
    LL(header->num_frames);
    LL(header->num_framechannels);
    LL(header->ofs_frames);
    LL(header->ofs_bounds);
    LL(header->num_comment);
    LL(header->ofs_comment);
    LL(header->num_extensions);
    LL(header->ofs_extensions);

    // check ioq3 joint limit
    if (header->num_joints > IQM_MAX_JOINTS) {
        ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n",
                  mod_name, IQM_MAX_JOINTS, header->num_joints);
        return qfalse;
    }

    // check and swap vertex arrays
    if (IQM_CheckRange(header, header->ofs_vertexarrays,
                       header->num_vertexarrays,
                       sizeof(iqmVertexArray_t))) {
        return qfalse;
    }
    vertexarray = (iqmVertexArray_t*)((byte*)header + header->ofs_vertexarrays);
    for (i = 0; i < header->num_vertexarrays; i++, vertexarray++) {
        int j, n, *intPtr;

        if (vertexarray->size <= 0 || vertexarray->size > 4) {
            return qfalse;
        }

        // total number of values
        n = header->num_vertexes * vertexarray->size;

        switch (vertexarray->format) {
            case IQM_BYTE:
            case IQM_UBYTE:
                // 1 byte, no swapping necessary
                if (IQM_CheckRange(header, vertexarray->offset,
                                   n, sizeof(byte))) {
                    return qfalse;
                }
                break;
            case IQM_INT:
            case IQM_UINT:
            case IQM_FLOAT:
                // 4-byte swap
                if (IQM_CheckRange(header, vertexarray->offset,
                                   n, sizeof(float))) {
                    return qfalse;
                }
                intPtr = (int*)((byte*)header + vertexarray->offset);
                for (j = 0; j < n; j++, intPtr++) {
                    LL(*intPtr);
                }
                break;
            default:
                // not supported
                return qfalse;
                break;
        }

        switch (vertexarray->type) {
            case IQM_POSITION:
            case IQM_NORMAL:
                if (vertexarray->format != IQM_FLOAT ||
                        vertexarray->size != 3) {
                    return qfalse;
                }
                break;
            case IQM_TANGENT:
                if (vertexarray->format != IQM_FLOAT ||
                        vertexarray->size != 4) {
                    return qfalse;
                }
                break;
            case IQM_TEXCOORD:
                if (vertexarray->format != IQM_FLOAT ||
                        vertexarray->size != 2) {
                    return qfalse;
                }
                break;
            case IQM_BLENDINDEXES:
            case IQM_BLENDWEIGHTS:
                if (vertexarray->format != IQM_UBYTE ||
                        vertexarray->size != 4) {
                    return qfalse;
                }
                break;
            case IQM_COLOR:
                if (vertexarray->format != IQM_UBYTE ||
                        vertexarray->size != 4) {
                    return qfalse;
                }
                break;
        }
    }

    // check and swap triangles
    if (IQM_CheckRange(header, header->ofs_triangles,
                       header->num_triangles, sizeof(iqmTriangle_t))) {
        return qfalse;
    }
    triangle = (iqmTriangle_t*)((byte*)header + header->ofs_triangles);
    for (i = 0; i < header->num_triangles; i++, triangle++) {
        LL(triangle->vertex[0]);
        LL(triangle->vertex[1]);
        LL(triangle->vertex[2]);

        if (triangle->vertex[0] > header->num_vertexes ||
                triangle->vertex[1] > header->num_vertexes ||
                triangle->vertex[2] > header->num_vertexes) {
            return qfalse;
        }
    }

    // check and swap meshes
    if (IQM_CheckRange(header, header->ofs_meshes,
                       header->num_meshes, sizeof(iqmMesh_t))) {
        return qfalse;
    }
    mesh = (iqmMesh_t*)((byte*)header + header->ofs_meshes);
    for (i = 0; i < header->num_meshes; i++, mesh++) {
        LL(mesh->name);
        LL(mesh->material);
        LL(mesh->first_vertex);
        LL(mesh->num_vertexes);
        LL(mesh->first_triangle);
        LL(mesh->num_triangles);

        // check ioq3 limits
        if (mesh->num_vertexes > SHADER_MAX_VERTEXES) {
            ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n",
                      mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes);
            return qfalse;
        }
        if (mesh->num_triangles * 3 > SHADER_MAX_INDEXES) {
            ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n",
                      mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles);
            return qfalse;
        }

        if (mesh->first_vertex >= header->num_vertexes ||
                mesh->first_vertex + mesh->num_vertexes > header->num_vertexes ||
                mesh->first_triangle >= header->num_triangles ||
                mesh->first_triangle + mesh->num_triangles > header->num_triangles ||
                mesh->name >= header->num_text ||
                mesh->material >= header->num_text) {
            return qfalse;
        }
    }

    // check and swap joints
    if (IQM_CheckRange(header, header->ofs_joints,
                       header->num_joints, sizeof(iqmJoint_t))) {
        return qfalse;
    }
    joint = (iqmJoint_t*)((byte*)header + header->ofs_joints);
    joint_names = 0;
    for (i = 0; i < header->num_joints; i++, joint++) {
        LL(joint->name);
        LL(joint->parent);
        LL(joint->translate[0]);
        LL(joint->translate[1]);
        LL(joint->translate[2]);
        LL(joint->rotate[0]);
        LL(joint->rotate[1]);
        LL(joint->rotate[2]);
        LL(joint->rotate[3]);
        LL(joint->scale[0]);
        LL(joint->scale[1]);
        LL(joint->scale[2]);

        if (joint->parent < -1 ||
                joint->parent >= (int)header->num_joints ||
                joint->name >= (int)header->num_text) {
            return qfalse;
        }
        joint_names += strlen((char*)header + header->ofs_text +
                              joint->name) + 1;
    }

    // check and swap poses
    if (header->num_poses != header->num_joints) {
        return qfalse;
    }
    if (IQM_CheckRange(header, header->ofs_poses,
                       header->num_poses, sizeof(iqmPose_t))) {
        return qfalse;
    }
    pose = (iqmPose_t*)((byte*)header + header->ofs_poses);
    for (i = 0; i < header->num_poses; i++, pose++) {
        LL(pose->parent);
        LL(pose->mask);
        LL(pose->channeloffset[0]);
        LL(pose->channeloffset[1]);
        LL(pose->channeloffset[2]);
        LL(pose->channeloffset[3]);
        LL(pose->channeloffset[4]);
        LL(pose->channeloffset[5]);
        LL(pose->channeloffset[6]);
        LL(pose->channeloffset[7]);
        LL(pose->channeloffset[8]);
        LL(pose->channeloffset[9]);
        LL(pose->channelscale[0]);
        LL(pose->channelscale[1]);
        LL(pose->channelscale[2]);
        LL(pose->channelscale[3]);
        LL(pose->channelscale[4]);
        LL(pose->channelscale[5]);
        LL(pose->channelscale[6]);
        LL(pose->channelscale[7]);
        LL(pose->channelscale[8]);
        LL(pose->channelscale[9]);
    }

    if (header->ofs_bounds) {
        // check and swap model bounds
        if (IQM_CheckRange(header, header->ofs_bounds,
                           header->num_frames, sizeof(*bounds))) {
            return qfalse;
        }
        bounds = (iqmBounds_t*)((byte*) header + header->ofs_bounds);
        for (i = 0; i < header->num_frames; i++) {
            LL(bounds->bbmin[0]);
            LL(bounds->bbmin[1]);
            LL(bounds->bbmin[2]);
            LL(bounds->bbmax[0]);
            LL(bounds->bbmax[1]);
            LL(bounds->bbmax[2]);

            bounds++;
        }
    }

    // allocate the model and copy the data
    size = sizeof(iqmData_t);
    size += header->num_meshes * sizeof(srfIQModel_t);
    size += header->num_joints * header->num_frames * 12 * sizeof(float);
    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 * 2 * sizeof(float);   // texcoords
    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

    mod->type = MOD_IQM;
    iqmData = (iqmData_t*)ri.Hunk_Alloc(size, h_low);
    mod->modelData = iqmData;

    // fill header
    iqmData->num_vertexes = header->num_vertexes;
    iqmData->num_triangles = header->num_triangles;
    iqmData->num_frames   = header->num_frames;
    iqmData->num_surfaces = header->num_meshes;
    iqmData->num_joints   = header->num_joints;
    iqmData->surfaces     = (srfIQModel_t*)(iqmData + 1);
    iqmData->poseMats     = (float*)(iqmData->surfaces + iqmData->num_surfaces);
    if (header->ofs_bounds) {
        iqmData->bounds       = iqmData->poseMats + 12 * header->num_joints * header->num_frames;
        iqmData->positions    = iqmData->bounds + 6 * header->num_frames;
    } else
        iqmData->positions    = iqmData->poseMats + 12 * header->num_joints * 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);
    iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes;
    iqmData->colors       = iqmData->blendWeights + 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);

    // calculate joint matrices and their inverses
    // they are needed only until the pose matrices are calculated
    mat = jointMats;
    joint = (iqmJoint_t*)((byte*)header + header->ofs_joints);
    for (i = 0; i < header->num_joints; i++, joint++) {
        float baseFrame[12], invBaseFrame[12];

        JointToMatrix(joint->rotate, joint->scale, joint->translate, baseFrame);
        Matrix34Invert(baseFrame, invBaseFrame);

        if (joint->parent >= 0) {
            Matrix34Multiply(jointMats + 2 * 12 * joint->parent, baseFrame, mat);
            mat += 12;
            Matrix34Multiply(invBaseFrame, jointMats + 2 * 12 * joint->parent + 12, mat);
            mat += 12;
        } else {
            Com_Memcpy(mat, baseFrame,    sizeof(baseFrame));
            mat += 12;
            Com_Memcpy(mat, invBaseFrame, sizeof(invBaseFrame));
            mat += 12;
        }
    }

    // calculate pose matrices
    framedata = (unsigned short*)((byte*)header + header->ofs_frames);
    mat = iqmData->poseMats;
    for (i = 0; i < header->num_frames; i++) {
        pose = (iqmPose_t*)((byte*)header + header->ofs_poses);
        for (j = 0; j < header->num_poses; j++, pose++) {
            vec3_t  translate;
            vec4_t  rotate;
            vec3_t  scale;
            float   mat1[12], mat2[12];

            translate[0] = pose->channeloffset[0];
            if (pose->mask & 0x001)
                translate[0] += *framedata++ * pose->channelscale[0];
            translate[1] = pose->channeloffset[1];
            if (pose->mask & 0x002)
                translate[1] += *framedata++ * pose->channelscale[1];
            translate[2] = pose->channeloffset[2];
            if (pose->mask & 0x004)
                translate[2] += *framedata++ * pose->channelscale[2];

            rotate[0] = pose->channeloffset[3];
            if (pose->mask & 0x008)
                rotate[0] += *framedata++ * pose->channelscale[3];
            rotate[1] = pose->channeloffset[4];
            if (pose->mask & 0x010)
                rotate[1] += *framedata++ * pose->channelscale[4];
            rotate[2] = pose->channeloffset[5];
            if (pose->mask & 0x020)
                rotate[2] += *framedata++ * pose->channelscale[5];
            rotate[3] = pose->channeloffset[6];
            if (pose->mask & 0x040)
                rotate[3] += *framedata++ * pose->channelscale[6];

            scale[0] = pose->channeloffset[7];
            if (pose->mask & 0x080)
                scale[0] += *framedata++ * pose->channelscale[7];
            scale[1] = pose->channeloffset[8];
            if (pose->mask & 0x100)
                scale[1] += *framedata++ * pose->channelscale[8];
            scale[2] = pose->channeloffset[9];
            if (pose->mask & 0x200)
                scale[2] += *framedata++ * pose->channelscale[9];

            // construct transformation matrix
            JointToMatrix(rotate, scale, translate, mat1);

            if (pose->parent >= 0) {
                Matrix34Multiply(jointMats + 12 * 2 * pose->parent,
                                 mat1, mat2);
            } else {
                Com_Memcpy(mat2, mat1, sizeof(mat1));
            }

            Matrix34Multiply(mat2, jointMats + 12 * (2 * j + 1), mat);
            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:
                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));
                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
    if (header->ofs_bounds) {
        mat = iqmData->bounds;
        bounds = (iqmBounds_t*)((byte*) header + header->ofs_bounds);
        for (i = 0; i < header->num_frames; i++) {
            mat[0] = bounds->bbmin[0];
            mat[1] = bounds->bbmin[1];
            mat[2] = bounds->bbmin[2];
            mat[3] = bounds->bbmax[0];
            mat[4] = bounds->bbmax[1];
            mat[5] = bounds->bbmax[2];

            mat += 6;
            bounds++;
        }
    }

    return qtrue;
}
Example #2
0
static bool LoadIQMFile( void *buffer, unsigned filesize, const char *mod_name,
			     size_t *len_names )
{
	iqmHeader_t		*header;
	iqmVertexArray_t	*vertexarray;
	iqmTriangle_t		*triangle;
	iqmMesh_t		*mesh;
	iqmJoint_t		*joint;
	iqmPose_t		*pose;
	iqmBounds_t		*bounds;
	iqmAnim_t		*anim;

	if( filesize < sizeof(iqmHeader_t) ) {
		Log::Warn("R_LoadIQModel: file size of %s is too small.",
			  mod_name );
		return false;
	}

	header = (iqmHeader_t *)buffer;
	if( Q_strncmp( header->magic, IQM_MAGIC, sizeof(header->magic) ) ) {
		Log::Warn("R_LoadIQModel: file %s doesn't contain an IQM header.",
			  mod_name );
		return false;
	}

	LL( header->version );
	if( header->version != IQM_VERSION ) {
		Log::Warn("R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.",
			  mod_name, header->version, IQM_VERSION);
		return false;
	}

	LL( header->filesize );
	if( header->filesize > filesize || header->filesize > 1<<24 ) {
		Log::Warn("R_LoadIQM: %s has an invalid file size %d.",
			  mod_name, header->filesize );
		return false;
	}

	LL( header->flags );
	LL( header->num_text );
	LL( header->ofs_text );
	LL( header->num_meshes );
	LL( header->ofs_meshes );
	LL( header->num_vertexarrays );
	LL( header->num_vertexes );
	LL( header->ofs_vertexarrays );
	LL( header->num_triangles );
	LL( header->ofs_triangles );
	LL( header->ofs_adjacency );
	LL( header->num_joints );
	LL( header->ofs_joints );
	LL( header->num_poses );
	LL( header->ofs_poses );
	LL( header->num_anims );
	LL( header->ofs_anims );
	LL( header->num_frames );
	LL( header->num_framechannels );
	LL( header->ofs_frames );
	LL( header->ofs_bounds );
	LL( header->num_comment );
	LL( header->ofs_comment );
	LL( header->num_extensions );
	LL( header->ofs_extensions );

	// check and swap vertex arrays
	if( IQM_CheckRange( header, header->ofs_vertexarrays,
			    header->num_vertexarrays,
			    sizeof(iqmVertexArray_t),
			    mod_name, "vertexarray" ) ) {
		return false;
	}
	vertexarray = ( iqmVertexArray_t* )IQMPtr( header, header->ofs_vertexarrays );
	for(unsigned i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) {
		int	j, n, *intPtr;

		if( vertexarray->size <= 0 || vertexarray->size > 4 ) {
			Log::Warn("R_LoadIQM: %s contains an invalid vertexarray size.",
				  mod_name );
			return false;
		}

		// total number of values
		n = header->num_vertexes * vertexarray->size;

		switch( vertexarray->format ) {
		case IQM_BYTE:
		case IQM_UBYTE:
			// 1 byte, no swapping necessary
			if( IQM_CheckRange( header, vertexarray->offset, n,
					    sizeof(byte), mod_name, "vertexarray" ) ) {
				return false;
			}
			break;
		case IQM_INT:
		case IQM_UINT:
		case IQM_FLOAT:
			// 4-byte swap
			if( IQM_CheckRange( header, vertexarray->offset, n,
					    sizeof(float), mod_name, "vertexarray" ) ) {
				return false;
			}
			intPtr = ( int* )IQMPtr( header, vertexarray->offset );
			for( j = 0; j < n; j++, intPtr++ ) {
				LL( *intPtr );
			}
			break;
		default:
			// not supported
			Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.",
				  mod_name );
			return false;
			break;
		}

		switch( vertexarray->type ) {
		case IQM_POSITION:
		case IQM_NORMAL:
			if( vertexarray->format != IQM_FLOAT ||
			    vertexarray->size != 3 ) {
				Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.",
					  mod_name );
				return false;
			}
			break;
		case IQM_TANGENT:
			if( vertexarray->format != IQM_FLOAT ||
			    vertexarray->size != 4 ) {
				Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.",
					  mod_name );
				return false;
			}
			break;
		case IQM_TEXCOORD:
			if( vertexarray->format != IQM_FLOAT ||
			    vertexarray->size != 2 ) {
				Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.",
					  mod_name );
				return false;
			}
			break;
		case IQM_BLENDINDEXES:
		case IQM_BLENDWEIGHTS:
			if( vertexarray->format != IQM_UBYTE ||
			    vertexarray->size != 4 ) {
				Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.",
					  mod_name );
				return false;
			}
			break;
		case IQM_COLOR:
			if( vertexarray->format != IQM_UBYTE ||
			    vertexarray->size != 4 ) {
				Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.",
					  mod_name );
				return false;
			}
			break;
		}
	}

	// check and swap triangles
	if( IQM_CheckRange( header, header->ofs_triangles,
			    header->num_triangles, sizeof(iqmTriangle_t),
			    mod_name, "triangle" ) ) {
		return false;
	}
	triangle = ( iqmTriangle_t* )IQMPtr( header, header->ofs_triangles );
	for(unsigned i = 0; i < header->num_triangles; i++, triangle++ ) {
		LL( triangle->vertex[0] );
		LL( triangle->vertex[1] );
		LL( triangle->vertex[2] );

		if( triangle->vertex[0] > header->num_vertexes ||
		    triangle->vertex[1] > header->num_vertexes ||
		    triangle->vertex[2] > header->num_vertexes ) {
			return false;
		}
	}

	*len_names = 0;

	// check and swap meshes
	if( IQM_CheckRange( header, header->ofs_meshes,
			    header->num_meshes, sizeof(iqmMesh_t),
			    mod_name, "mesh" ) ) {
		return false;
	}
	mesh = ( iqmMesh_t* )IQMPtr( header, header->ofs_meshes );
	for(unsigned i = 0; i < header->num_meshes; i++, mesh++) {
		LL( mesh->name );
		LL( mesh->material );
		LL( mesh->first_vertex );
		LL( mesh->num_vertexes );
		LL( mesh->first_triangle );
		LL( mesh->num_triangles );

		if( mesh->first_vertex >= header->num_vertexes ||
		    mesh->first_vertex + mesh->num_vertexes > header->num_vertexes ||
		    mesh->first_triangle >= header->num_triangles ||
		    mesh->first_triangle + mesh->num_triangles > header->num_triangles ||
		    mesh->name >= header->num_text ||
		    mesh->material >= header->num_text ) {
			Log::Warn("R_LoadIQM: file %s contains an invalid mesh.",
				  mod_name );
			return false;
		}
		*len_names += strlen( ( char* )IQMPtr( header, header->ofs_text
					      + mesh->name ) ) + 1;
	}

	// check and swap joints
	if( header->num_joints != 0 &&
	    IQM_CheckRange( header, header->ofs_joints,
			    header->num_joints, sizeof(iqmJoint_t),
			    mod_name, "joint" ) ) {
		return false;
	}
	joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints );
	for(unsigned i = 0; i < header->num_joints; i++, joint++ ) {
		LL( joint->name );
		LL( joint->parent );
		LL( joint->translate[0] );
		LL( joint->translate[1] );
		LL( joint->translate[2] );
		LL( joint->rotate[0] );
		LL( joint->rotate[1] );
		LL( joint->rotate[2] );
		LL( joint->rotate[3] );
		LL( joint->scale[0] );
		LL( joint->scale[1] );
		LL( joint->scale[2] );

		if( joint->parent < -1 ||
		    joint->parent >= (int)header->num_joints ||
		    joint->name >= header->num_text ) {
			Log::Warn("R_LoadIQM: file %s contains an invalid joint.",
				  mod_name );
			return false;
		}
		if( joint->scale[0] < 0.0f ||
			(int)( joint->scale[0] - joint->scale[1] ) ||
			(int)( joint->scale[1] - joint->scale[2] ) ) {
			Log::Warn("R_LoadIQM: file %s contains an invalid scale: %f %f %f",
				  mod_name, joint->scale[0], joint->scale[1], joint->scale[2] );
			return false;
		}
		*len_names += strlen( ( char* )IQMPtr( header, header->ofs_text
					      + joint->name ) ) + 1;
	}

	// check and swap poses
	if( header->ofs_poses > 0 ) {
		if( IQM_CheckRange( header, header->ofs_poses,
				    header->num_poses, sizeof(iqmPose_t),
				    mod_name, "pose" ) ) {
			return false;
		}
		pose = ( iqmPose_t* )IQMPtr( header, header->ofs_poses );
		for(unsigned i = 0; i < header->num_poses; i++, pose++ ) {
			LL( pose->parent );
			LL( pose->mask );
			LL( pose->channeloffset[0] );
			LL( pose->channeloffset[1] );
			LL( pose->channeloffset[2] );
			LL( pose->channeloffset[3] );
			LL( pose->channeloffset[4] );
			LL( pose->channeloffset[5] );
			LL( pose->channeloffset[6] );
			LL( pose->channeloffset[7] );
			LL( pose->channeloffset[8] );
			LL( pose->channeloffset[9] );
			LL( pose->channelscale[0] );
			LL( pose->channelscale[1] );
			LL( pose->channelscale[2] );
			LL( pose->channelscale[3] );
			LL( pose->channelscale[4] );
			LL( pose->channelscale[5] );
			LL( pose->channelscale[6] );
			LL( pose->channelscale[7] );
			LL( pose->channelscale[8] );
			LL( pose->channelscale[9] );
		}
	}

	if( header->ofs_bounds )
	{
		// check and swap model bounds
		if(IQM_CheckRange(header, header->ofs_bounds,
				  header->num_frames, sizeof(*bounds),
				  mod_name, "bounds" ))
		{
			return false;
		}
		bounds = ( iqmBounds_t* )IQMPtr( header, header->ofs_bounds );
		for(unsigned i = 0; i < header->num_poses; i++)
		{
			LL(bounds->bbmin[0]);
			LL(bounds->bbmin[1]);
			LL(bounds->bbmin[2]);
			LL(bounds->bbmax[0]);
			LL(bounds->bbmax[1]);
			LL(bounds->bbmax[2]);

			bounds++;
		}
	}

	// check and swap animations
	if( header->ofs_anims ) {
		if( IQM_CheckRange( header, header->ofs_anims,
				    header->num_anims, sizeof(iqmAnim_t),
				    mod_name, "animation" ) ) {
			return false;
		}
		anim = ( iqmAnim_t* )IQMPtr( header, header->ofs_anims );
		for(unsigned i = 0; i < header->num_anims; i++, anim++ ) {
			LL( anim->name );
			LL( anim->first_frame );
			LL( anim->num_frames );
			LL( anim->framerate );
			LL( anim->flags );

			*len_names += strlen( ( char* )IQMPtr( header, header->ofs_text
							       + anim->name ) ) + 1;
		}
	}

	return true;
}