+ surface->num_vertices = lastvertex + 1 - surface->num_firstvertex;
+
+ // since zym models do not have named sections, reuse their shader
+ // name as the section name
+ shadername = (char *) (pheader->lump_shaders.start + pbase) + i * 32;
+ if (shadername[0])
+ Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, shadername, shadername);
+ else
+ for (j = 0;j < loadmodel->numskins;j++)
+ Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + i + j * loadmodel->num_surfaces, NULL);
+
+ Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
+ }
+ Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+ Mod_Alias_Mesh_CompileFrameZero();
+ Mod_FreeSkinFiles(skinfiles);
+ Mem_Free(vertbonecounts);
+ Mem_Free(verts);
+}
+
+void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
+{
+ dpmheader_t *pheader;
+ dpmframe_t *frame;
+ dpmbone_t *bone;
+ dpmmesh_t *dpmmesh;
+ unsigned char *pbase;
+ int i, j, k, meshvertices, meshtriangles, numvertexboneweights;
+ skinfile_t *skinfiles;
+ unsigned char *data;
+
+ pheader = (dpmheader_t *)buffer;
+ pbase = (unsigned char *)buffer;
+ if (memcmp(pheader->id, "DARKPLACESMODEL\0", 16))
+ Host_Error ("Mod_DARKPLACESMODEL_Load: %s is not a darkplaces model");
+ if (BigLong(pheader->type) != 2)
+ Host_Error ("Mod_DARKPLACESMODEL_Load: only type 2 (hierarchical skeletal pose) models are currently supported (name = %s)", loadmodel->name);
+
+ loadmodel->type = mod_alias;
+ loadmodel->flags = 0; // there are no flags on zym models
+ loadmodel->synctype = ST_RAND;
+
+ // byteswap header
+ pheader->type = BigLong(pheader->type);
+ pheader->filesize = BigLong(pheader->filesize);
+ pheader->mins[0] = BigFloat(pheader->mins[0]);
+ pheader->mins[1] = BigFloat(pheader->mins[1]);
+ pheader->mins[2] = BigFloat(pheader->mins[2]);
+ pheader->maxs[0] = BigFloat(pheader->maxs[0]);
+ pheader->maxs[1] = BigFloat(pheader->maxs[1]);
+ pheader->maxs[2] = BigFloat(pheader->maxs[2]);
+ pheader->yawradius = BigFloat(pheader->yawradius);
+ pheader->allradius = BigFloat(pheader->allradius);
+ pheader->num_bones = BigLong(pheader->num_bones);
+ pheader->num_meshs = BigLong(pheader->num_meshs);
+ pheader->num_frames = BigLong(pheader->num_frames);
+ pheader->ofs_bones = BigLong(pheader->ofs_bones);
+ pheader->ofs_meshs = BigLong(pheader->ofs_meshs);
+ pheader->ofs_frames = BigLong(pheader->ofs_frames);
+
+ if (pheader->num_bones < 1 || pheader->num_meshs < 1)
+ {
+ Con_Printf("%s has no geometry\n");
+ return;
+ }
+ if (pheader->num_frames < 1)
+ {
+ Con_Printf("%s has no frames\n");
+ return;
+ }
+
+ loadmodel->DrawSky = NULL;
+ loadmodel->Draw = R_Q1BSP_Draw;
+ loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+ loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+ loadmodel->DrawLight = R_Q1BSP_DrawLight;
+ loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+
+ // model bbox
+ for (i = 0;i < 3;i++)
+ {
+ loadmodel->normalmins[i] = pheader->mins[i];
+ loadmodel->normalmaxs[i] = pheader->maxs[i];
+ loadmodel->yawmins[i] = i != 2 ? -pheader->yawradius : pheader->mins[i];
+ loadmodel->yawmaxs[i] = i != 2 ? pheader->yawradius : pheader->maxs[i];
+ loadmodel->rotatedmins[i] = -pheader->allradius;
+ loadmodel->rotatedmaxs[i] = pheader->allradius;
+ }
+ loadmodel->radius = pheader->allradius;
+ loadmodel->radius2 = pheader->allradius * pheader->allradius;
+
+ // load external .skin files if present
+ skinfiles = Mod_LoadSkinFiles();
+ if (loadmodel->numskins < 1)
+ loadmodel->numskins = 1;
+
+ meshvertices = 0;
+ meshtriangles = 0;
+ numvertexboneweights = 0;
+
+ // load the meshes now
+ dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
+ for (i = 0;i < loadmodel->num_surfaces;i++)
+ {
+ int numverts = BigLong(dpmmesh->num_verts);
+ meshvertices += numverts;;
+ meshtriangles += BigLong(dpmmesh->num_tris);
+
+ // to find out how many weights exist we two a two-stage load...
+ data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
+ for (j = 0;j < numverts;j++)
+ {
+ int numweights = BigLong(((dpmvertex_t *)data)->numbones);
+ numvertexboneweights += numweights;
+ data += sizeof(dpmvertex_t);
+ data += numweights * sizeof(dpmbonevert_t);
+ }
+ dpmmesh++;
+ }
+
+ loadmodel->numframes = pheader->num_frames;
+ loadmodel->num_bones = pheader->num_bones;
+ loadmodel->num_poses = loadmodel->num_bones * loadmodel->numframes;
+ loadmodel->num_textures = loadmodel->nummodelsurfaces = loadmodel->num_surfaces = pheader->num_meshs;
+ // do most allocations as one merged chunk
+ data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + meshtriangles * sizeof(int[3]) + meshvertices * sizeof(float[2]) + numvertexboneweights * sizeof(surfmeshvertexboneweight_t) + loadmodel->num_poses * sizeof(float[3][4]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
+ loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
+ loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+ loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
+ loadmodel->surfmesh.num_vertices = meshvertices;
+ loadmodel->surfmesh.num_triangles = meshtriangles;
+ loadmodel->surfmesh.num_vertexboneweights = numvertexboneweights;
+ loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+ loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+ loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
+ loadmodel->surfmesh.data_vertexboneweights = (surfmeshvertexboneweight_t *)data;data += numvertexboneweights * sizeof(surfmeshvertexboneweight_t);
+ loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[3][4]);
+ loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
+ loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
+ loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+
+ for (i = 0;i < loadmodel->numskins;i++)
+ {
+ loadmodel->skinscenes[i].firstframe = i;
+ loadmodel->skinscenes[i].framecount = 1;
+ loadmodel->skinscenes[i].loop = true;
+ loadmodel->skinscenes[i].framerate = 10;
+ }
+
+ // load the bone info
+ bone = (dpmbone_t *) (pbase + pheader->ofs_bones);
+ for (i = 0;i < loadmodel->num_bones;i++)
+ {
+ memcpy(loadmodel->data_bones[i].name, bone[i].name, sizeof(bone[i].name));
+ loadmodel->data_bones[i].flags = BigLong(bone[i].flags);
+ loadmodel->data_bones[i].parent = BigLong(bone[i].parent);
+ if (loadmodel->data_bones[i].parent >= i)
+ Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
+ }
+
+ // load the frames
+ frame = (dpmframe_t *) (pbase + pheader->ofs_frames);
+ for (i = 0;i < loadmodel->numframes;i++)
+ {
+ const float *poses;
+ memcpy(loadmodel->animscenes[i].name, frame->name, sizeof(frame->name));
+ loadmodel->animscenes[i].firstframe = i;
+ loadmodel->animscenes[i].framecount = 1;
+ loadmodel->animscenes[i].loop = true;
+ loadmodel->animscenes[i].framerate = 10;
+ // load the bone poses for this frame
+ poses = (float *) (pbase + BigLong(frame->ofs_bonepositions));
+ for (j = 0;j < loadmodel->num_bones*12;j++)
+ loadmodel->data_poses[i * loadmodel->num_bones*12 + j] = BigFloat(poses[j]);
+ // stuff not processed here: mins, maxs, yawradius, allradius
+ frame++;
+ }
+
+ // load the meshes now
+ dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
+ meshvertices = 0;
+ meshtriangles = 0;
+ numvertexboneweights = 0;
+ for (i = 0;i < loadmodel->num_surfaces;i++, dpmmesh++)
+ {
+ const int *inelements;
+ int *outelements;
+ const float *intexcoord;
+ msurface_t *surface;
+
+ loadmodel->surfacelist[i] = i;
+ surface = loadmodel->data_surfaces + i;
+ surface->texture = loadmodel->data_textures + i;
+ surface->num_firsttriangle = meshtriangles;
+ surface->num_triangles = BigLong(dpmmesh->num_tris);
+ surface->num_firstvertex = meshvertices;
+ surface->num_vertices = BigLong(dpmmesh->num_verts);
+ meshvertices += surface->num_vertices;
+ meshtriangles += surface->num_triangles;
+
+ inelements = (int *) (pbase + BigLong(dpmmesh->ofs_indices));
+ outelements = loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+ for (j = 0;j < surface->num_triangles;j++)
+ {
+ // swap element order to flip triangles, because Quake uses clockwise (rare) and dpm uses counterclockwise (standard)
+ outelements[0] = surface->num_firstvertex + BigLong(inelements[2]);
+ outelements[1] = surface->num_firstvertex + BigLong(inelements[1]);
+ outelements[2] = surface->num_firstvertex + BigLong(inelements[0]);
+ inelements += 3;
+ outelements += 3;
+ }
+
+ intexcoord = (float *) (pbase + BigLong(dpmmesh->ofs_texcoords));
+ for (j = 0;j < surface->num_vertices*2;j++)
+ loadmodel->surfmesh.data_texcoordtexture2f[j + surface->num_firstvertex * 2] = BigFloat(intexcoord[j]);
+
+ data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
+ for (j = 0;j < surface->num_vertices;j++)
+ {
+ int numweights = BigLong(((dpmvertex_t *)data)->numbones);
+ data += sizeof(dpmvertex_t);
+ for (k = 0;k < numweights;k++)
+ {
+ const dpmbonevert_t *vert = (dpmbonevert_t *) data;
+ // stuff not processed here: normal
+ loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].vertexindex = j;
+ loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].boneindex = BigLong(vert->bonenum);
+ loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[0] = BigFloat(vert->origin[0]);
+ loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[1] = BigFloat(vert->origin[1]);
+ loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[2] = BigFloat(vert->origin[2]);
+ loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[3] = BigFloat(vert->influence);
+ numvertexboneweights++;
+ data += sizeof(dpmbonevert_t);
+ }
+ }
+
+ // since dpm models do not have named sections, reuse their shader name as the section name
+ if (dpmmesh->shadername[0])
+ Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, dpmmesh->shadername, dpmmesh->shadername);
+ else
+ for (j = 0;j < loadmodel->numskins;j++)
+ Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + i + j * loadmodel->num_surfaces, NULL);
+
+ Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
+ }
+ Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+ Mod_Alias_Mesh_CompileFrameZero();
+ Mod_FreeSkinFiles(skinfiles);
+}
+
+static void Mod_PSKMODEL_AnimKeyToMatrix(float *origin, float *quat, matrix4x4_t *m)
+{
+ float x = quat[0], y = quat[1], z = quat[2], w = quat[3];
+ m->m[0][0]=1-2*(y*y+z*z);m->m[0][1]= 2*(x*y-z*w);m->m[0][2]= 2*(x*z+y*w);m->m[0][3]=origin[0];
+ m->m[1][0]= 2*(x*y+z*w);m->m[1][1]=1-2*(x*x+z*z);m->m[1][2]= 2*(y*z-x*w);m->m[1][3]=origin[1];
+ m->m[2][0]= 2*(x*z-y*w);m->m[2][1]= 2*(y*z+x*w);m->m[2][2]=1-2*(x*x+y*y);m->m[2][3]=origin[2];
+ m->m[3][0]= 0 ;m->m[3][1]= 0 ;m->m[3][2]= 0 ;m->m[3][3]=1;
+}
+
+// no idea why PSK/PSA files contain weird quaternions but they do...
+#define PSKQUATNEGATIONS
+void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
+{
+ int i, j, index, version, recordsize, numrecords, meshvertices, meshtriangles, numvertexboneweights;
+ int numpnts, numvtxw, numfaces, nummatts, numbones, numrawweights, numanimbones, numanims, numanimkeys;
+ fs_offset_t filesize;
+ pskpnts_t *pnts;
+ pskvtxw_t *vtxw;
+ pskface_t *faces;
+ pskmatt_t *matts;
+ pskboneinfo_t *bones;
+ pskrawweights_t *rawweights;
+ pskboneinfo_t *animbones;
+ pskaniminfo_t *anims;
+ pskanimkeys_t *animkeys;
+ void *animfilebuffer, *animbuffer, *animbufferend;
+ unsigned char *data;
+ pskchunk_t *pchunk;
+ skinfile_t *skinfiles;
+ char animname[MAX_QPATH];
+
+ pchunk = (pskchunk_t *)buffer;
+ if (strcmp(pchunk->id, "ACTRHEAD"))
+ Host_Error ("Mod_PSKMODEL_Load: %s is not an Unreal Engine ActorX (.psk + .psa) model");
+
+ loadmodel->type = mod_alias;
+ loadmodel->DrawSky = NULL;
+ loadmodel->Draw = R_Q1BSP_Draw;
+ loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+ loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+ loadmodel->DrawLight = R_Q1BSP_DrawLight;
+ loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+ loadmodel->flags = 0; // there are no flags on zym models
+ loadmodel->synctype = ST_RAND;
+
+ FS_StripExtension(loadmodel->name, animname, sizeof(animname));
+ strlcat(animname, ".psa", sizeof(animname));
+ animbuffer = animfilebuffer = FS_LoadFile(animname, loadmodel->mempool, false, &filesize);
+ animbufferend = (void *)((unsigned char*)animbuffer + (int)filesize);
+ if (animbuffer == NULL)
+ Host_Error("%s: can't find .psa file (%s)", loadmodel->name, animname);
+
+ numpnts = 0;
+ pnts = NULL;
+ numvtxw = 0;
+ vtxw = NULL;
+ numfaces = 0;
+ faces = NULL;
+ nummatts = 0;
+ matts = NULL;
+ numbones = 0;
+ bones = NULL;
+ numrawweights = 0;
+ rawweights = NULL;
+ numanims = 0;
+ anims = NULL;
+ numanimkeys = 0;
+ animkeys = NULL;
+
+ while (buffer < bufferend)
+ {
+ pchunk = (pskchunk_t *)buffer;
+ buffer = (void *)((unsigned char *)buffer + sizeof(pskchunk_t));
+ version = LittleLong(pchunk->version);
+ recordsize = LittleLong(pchunk->recordsize);
+ numrecords = LittleLong(pchunk->numrecords);
+ if (developer.integer >= 100)
+ Con_Printf("%s: %s %x: %i * %i = %i\n", loadmodel->name, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
+ if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e && version != 0x12f2bc && version != 0x12f2f0)
+ Con_Printf ("%s: chunk %s has unknown version %x (0x1e83b9, 0x1e9179, 0x2e, 0x12f2bc, 0x12f2f0 are currently supported), trying to load anyway!\n", loadmodel->name, pchunk->id, version);
+ if (!strcmp(pchunk->id, "ACTRHEAD"))
+ {
+ // nothing to do
+ }
+ else if (!strcmp(pchunk->id, "PNTS0000"))