+ loadmodel->surfmesh.blends[index] = Mod_Skeletal_CompressBlend(loadmodel, weightindex, weightinfluence);
+ loadmodel->surfmesh.data_skeletalindex4ub[index*4 ] = weightindex[0];
+ loadmodel->surfmesh.data_skeletalindex4ub[index*4+1] = weightindex[1];
+ loadmodel->surfmesh.data_skeletalindex4ub[index*4+2] = weightindex[2];
+ loadmodel->surfmesh.data_skeletalindex4ub[index*4+3] = weightindex[3];
+ loadmodel->surfmesh.data_skeletalweight4ub[index*4 ] = (unsigned char)(weightinfluence[0]*255.0f);
+ loadmodel->surfmesh.data_skeletalweight4ub[index*4+1] = (unsigned char)(weightinfluence[1]*255.0f);
+ loadmodel->surfmesh.data_skeletalweight4ub[index*4+2] = (unsigned char)(weightinfluence[2]*255.0f);
+ loadmodel->surfmesh.data_skeletalweight4ub[index*4+3] = (unsigned char)(weightinfluence[3]*255.0f);
+ }
+ if (loadmodel->surfmesh.num_blends < loadmodel->surfmesh.num_vertices)
+ loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Realloc(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
+
+ // set up the animscenes based on the anims
+ if (numanims)
+ {
+ for (index = 0, i = 0;index < numanims;index++)
+ {
+ for (j = 0;j < anims[index].numframes;j++, i++)
+ {
+ dpsnprintf(loadmodel->animscenes[i].name, sizeof(loadmodel->animscenes[i].name), "%s_%d", anims[index].name, j);
+ loadmodel->animscenes[i].firstframe = i;
+ loadmodel->animscenes[i].framecount = 1;
+ loadmodel->animscenes[i].loop = true;
+ loadmodel->animscenes[i].framerate = anims[index].fps;
+ }
+ }
+ // calculate the scaling value for bone origins so they can be compressed to short
+ biggestorigin = 0;
+ for (index = 0;index < numanimkeys;index++)
+ {
+ pskanimkeys_t *k = animkeys + index;
+ biggestorigin = max(biggestorigin, fabs(k->origin[0]));
+ biggestorigin = max(biggestorigin, fabs(k->origin[1]));
+ biggestorigin = max(biggestorigin, fabs(k->origin[2]));
+ }
+ loadmodel->num_posescale = biggestorigin / 32767.0f;
+ loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
+
+ // load the poses from the animkeys
+ for (index = 0;index < numanimkeys;index++)
+ {
+ pskanimkeys_t *k = animkeys + index;
+ float quat[4];
+ Vector4Copy(k->quat, quat);
+ if (quat[3] > 0)
+ Vector4Negate(quat, quat);
+ Vector4Normalize2(quat, quat);
+ // compress poses to the short[7] format for longterm storage
+ loadmodel->data_poses7s[index*7+0] = k->origin[0] * loadmodel->num_poseinvscale;
+ loadmodel->data_poses7s[index*7+1] = k->origin[1] * loadmodel->num_poseinvscale;
+ loadmodel->data_poses7s[index*7+2] = k->origin[2] * loadmodel->num_poseinvscale;
+ loadmodel->data_poses7s[index*7+3] = quat[0] * 32767.0f;
+ loadmodel->data_poses7s[index*7+4] = quat[1] * 32767.0f;
+ loadmodel->data_poses7s[index*7+5] = quat[2] * 32767.0f;
+ loadmodel->data_poses7s[index*7+6] = quat[3] * 32767.0f;
+ }
+ }
+ else
+ {
+ strlcpy(loadmodel->animscenes[0].name, "base", sizeof(loadmodel->animscenes[0].name));
+ loadmodel->animscenes[0].firstframe = 0;
+ loadmodel->animscenes[0].framecount = 1;
+ loadmodel->animscenes[0].loop = true;
+ loadmodel->animscenes[0].framerate = 10;
+
+ // calculate the scaling value for bone origins so they can be compressed to short
+ biggestorigin = 0;
+ for (index = 0;index < numbones;index++)
+ {
+ pskboneinfo_t *p = bones + index;
+ biggestorigin = max(biggestorigin, fabs(p->basepose.origin[0]));
+ biggestorigin = max(biggestorigin, fabs(p->basepose.origin[1]));
+ biggestorigin = max(biggestorigin, fabs(p->basepose.origin[2]));
+ }
+ loadmodel->num_posescale = biggestorigin / 32767.0f;
+ loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
+
+ // load the basepose as a frame
+ for (index = 0;index < numbones;index++)
+ {
+ pskboneinfo_t *p = bones + index;
+ float quat[4];
+ Vector4Copy(p->basepose.quat, quat);
+ if (quat[3] > 0)
+ Vector4Negate(quat, quat);
+ Vector4Normalize2(quat, quat);
+ // compress poses to the short[7] format for longterm storage
+ loadmodel->data_poses7s[index*7+0] = p->basepose.origin[0] * loadmodel->num_poseinvscale;
+ loadmodel->data_poses7s[index*7+1] = p->basepose.origin[1] * loadmodel->num_poseinvscale;
+ loadmodel->data_poses7s[index*7+2] = p->basepose.origin[2] * loadmodel->num_poseinvscale;
+ loadmodel->data_poses7s[index*7+3] = quat[0] * 32767.0f;
+ loadmodel->data_poses7s[index*7+4] = quat[1] * 32767.0f;
+ loadmodel->data_poses7s[index*7+5] = quat[2] * 32767.0f;
+ loadmodel->data_poses7s[index*7+6] = quat[3] * 32767.0f;
+ }
+ }
+
+ Mod_FreeSkinFiles(skinfiles);
+ if (animfilebuffer)
+ Mem_Free(animfilebuffer);
+ Mod_MakeSortedSurfaces(loadmodel);
+
+ // compute all the mesh information that was not loaded from the file
+ // TODO: honor smoothing groups somehow?
+ if (loadmodel->surfmesh.data_element3s)
+ for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+ loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+ Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
+ Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0);
+ Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
+ if (loadmodel->surfmesh.data_neighbor3i)
+ Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+ loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
+ if(mod_alias_force_animated.string[0])
+ loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
+
+ // Always make a BIH for the first frame, we can use it where possible.
+ Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+ if (!loadmodel->surfmesh.isanimated)
+ {
+ loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+ loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+ loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+ loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+ loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+ }
+
+ // because shaders can do somewhat unexpected things, check for unusual features now
+ for (i = 0;i < loadmodel->num_textures;i++)
+ {
+ if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+ mod->DrawSky = R_Q1BSP_DrawSky;
+ if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+ mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+ }
+}
+
+void Mod_INTERQUAKEMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
+{
+ unsigned char *data;
+ const char *text;
+ const unsigned char *pbase, *pend;
+ iqmheader_t header;
+ skinfile_t *skinfiles;
+ int i, j, k, meshvertices, meshtriangles;
+ float biggestorigin;
+ const unsigned int *inelements;
+ int *outelements;
+ const int *inneighbors;
+ int *outneighbors;
+ float *outvertex, *outnormal, *outtexcoord, *outsvector, *outtvector, *outcolor;
+ // this pointers into the file data are read only through Little* functions so they can be unaligned memory
+ const float *vnormal = NULL;
+ const float *vposition = NULL;
+ const float *vtangent = NULL;
+ const float *vtexcoord = NULL;
+ const float *vcolor4f = NULL;
+ const unsigned char *vblendindexes = NULL;
+ const unsigned char *vblendweights = NULL;
+ const unsigned char *vcolor4ub = NULL;
+ const unsigned short *framedata = NULL;
+ // temporary memory allocations (because the data in the file may be misaligned)
+ iqmanim_t *anims = NULL;
+ iqmbounds_t *bounds = NULL;
+ iqmjoint1_t *joint1 = NULL;
+ iqmjoint_t *joint = NULL;
+ iqmmesh_t *meshes = NULL;
+ iqmpose1_t *pose1 = NULL;
+ iqmpose_t *pose = NULL;
+ iqmvertexarray_t *vas = NULL;
+
+ pbase = (unsigned char *)buffer;
+ pend = (unsigned char *)bufferend;
+
+ if (pbase + sizeof(iqmheader_t) > pend)
+ Host_Error ("Mod_INTERQUAKEMODEL_Load: %s is not an Inter-Quake Model %d", loadmodel->name, (int)(pend - pbase));
+
+ // copy struct (otherwise it may be misaligned)
+ // LordHavoc: okay it's definitely not misaligned here, but for consistency...
+ memcpy(&header, pbase, sizeof(iqmheader_t));
+
+ if (memcmp(header.id, "INTERQUAKEMODEL", 16))
+ Host_Error ("Mod_INTERQUAKEMODEL_Load: %s is not an Inter-Quake Model", loadmodel->name);
+ if (LittleLong(header.version) != 1 && LittleLong(header.version) != 2)
+ Host_Error ("Mod_INTERQUAKEMODEL_Load: only version 1 and 2 models are currently supported (name = %s)", loadmodel->name);
+
+ loadmodel->modeldatatypestring = "IQM";
+
+ loadmodel->type = mod_alias;
+ loadmodel->synctype = ST_RAND;
+
+ // byteswap header
+ header.version = LittleLong(header.version);
+ header.filesize = LittleLong(header.filesize);
+ header.flags = LittleLong(header.flags);
+ header.num_text = LittleLong(header.num_text);
+ header.ofs_text = LittleLong(header.ofs_text);
+ header.num_meshes = LittleLong(header.num_meshes);
+ header.ofs_meshes = LittleLong(header.ofs_meshes);
+ header.num_vertexarrays = LittleLong(header.num_vertexarrays);
+ header.num_vertexes = LittleLong(header.num_vertexes);
+ header.ofs_vertexarrays = LittleLong(header.ofs_vertexarrays);
+ header.num_triangles = LittleLong(header.num_triangles);
+ header.ofs_triangles = LittleLong(header.ofs_triangles);
+ header.ofs_neighbors = LittleLong(header.ofs_neighbors);
+ header.num_joints = LittleLong(header.num_joints);
+ header.ofs_joints = LittleLong(header.ofs_joints);
+ header.num_poses = LittleLong(header.num_poses);
+ header.ofs_poses = LittleLong(header.ofs_poses);
+ header.num_anims = LittleLong(header.num_anims);
+ header.ofs_anims = LittleLong(header.ofs_anims);
+ header.num_frames = LittleLong(header.num_frames);
+ header.num_framechannels = LittleLong(header.num_framechannels);
+ header.ofs_frames = LittleLong(header.ofs_frames);
+ header.ofs_bounds = LittleLong(header.ofs_bounds);
+ header.num_comment = LittleLong(header.num_comment);
+ header.ofs_comment = LittleLong(header.ofs_comment);
+ header.num_extensions = LittleLong(header.num_extensions);
+ header.ofs_extensions = LittleLong(header.ofs_extensions);
+
+ if (header.version == 1)
+ {
+ if (pbase + header.ofs_joints + header.num_joints*sizeof(iqmjoint1_t) > pend ||
+ pbase + header.ofs_poses + header.num_poses*sizeof(iqmpose1_t) > pend)
+ {
+ Con_Printf("%s has invalid size or offset information\n", loadmodel->name);
+ return;
+ }
+ }
+ else
+ {
+ if (pbase + header.ofs_joints + header.num_joints*sizeof(iqmjoint_t) > pend ||
+ pbase + header.ofs_poses + header.num_poses*sizeof(iqmpose_t) > pend)
+ {
+ Con_Printf("%s has invalid size or offset information\n", loadmodel->name);
+ return;
+ }
+ }
+ if (pbase + header.ofs_text + header.num_text > pend ||
+ pbase + header.ofs_meshes + header.num_meshes*sizeof(iqmmesh_t) > pend ||
+ pbase + header.ofs_vertexarrays + header.num_vertexarrays*sizeof(iqmvertexarray_t) > pend ||
+ pbase + header.ofs_triangles + header.num_triangles*sizeof(int[3]) > pend ||
+ (header.ofs_neighbors && pbase + header.ofs_neighbors + header.num_triangles*sizeof(int[3]) > pend) ||
+ pbase + header.ofs_anims + header.num_anims*sizeof(iqmanim_t) > pend ||
+ pbase + header.ofs_frames + header.num_frames*header.num_framechannels*sizeof(unsigned short) > pend ||
+ (header.ofs_bounds && pbase + header.ofs_bounds + header.num_frames*sizeof(iqmbounds_t) > pend) ||
+ pbase + header.ofs_comment + header.num_comment > pend)
+ {
+ Con_Printf("%s has invalid size or offset information\n", loadmodel->name);
+ return;
+ }
+
+ // copy structs to make them aligned in memory (otherwise we crash on Sparc and PowerPC and others)
+ if (header.num_vertexarrays)
+ vas = (iqmvertexarray_t *)(pbase + header.ofs_vertexarrays);
+ if (header.num_anims)
+ anims = (iqmanim_t *)(pbase + header.ofs_anims);
+ if (header.ofs_bounds)
+ bounds = (iqmbounds_t *)(pbase + header.ofs_bounds);
+ if (header.num_meshes)
+ meshes = (iqmmesh_t *)(pbase + header.ofs_meshes);
+
+ for (i = 0;i < (int)header.num_vertexarrays;i++)
+ {
+ iqmvertexarray_t va;
+ size_t vsize;
+ va.type = LittleLong(vas[i].type);
+ va.flags = LittleLong(vas[i].flags);
+ va.format = LittleLong(vas[i].format);
+ va.size = LittleLong(vas[i].size);
+ va.offset = LittleLong(vas[i].offset);
+ vsize = header.num_vertexes*va.size;
+ switch (va.format)
+ {
+ case IQM_FLOAT: vsize *= sizeof(float); break;
+ case IQM_UBYTE: vsize *= sizeof(unsigned char); break;
+ default: continue;
+ }
+ if (pbase + va.offset + vsize > pend)
+ continue;
+ // no need to copy the vertex data for alignment because LittleLong/LittleShort will be invoked on reading them, and the destination is aligned
+ switch (va.type)