]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_alias.c
slight readability improvement to PointInfrontOfTriangle
[xonotic/darkplaces.git] / model_alias.c
index 5d24ba6b829ad5845935b7a905e4ff02f0e9fa46..d36e737ad6f4d23d7467fc9f310aff0379820e62 100644 (file)
@@ -22,12 +22,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "image.h"
 #include "r_shadow.h"
 
-cvar_t r_skeletal_debugbone = {0, "r_skeletal_debugbone", "-1"};
-cvar_t r_skeletal_debugbonecomponent = {0, "r_skeletal_debugbonecomponent", "3"};
-cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100"};
-cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1"};
-cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1"};
-cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1"};
+cvar_t r_skeletal_debugbone = {0, "r_skeletal_debugbone", "-1", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugbonecomponent = {0, "r_skeletal_debugbonecomponent", "3", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1", "development cvar for testing skeletal model code"};
 
 void Mod_AliasInit (void)
 {
@@ -89,7 +89,7 @@ void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameb
                const float *vertsbase, *verts1, *verts2, *verts3, *verts4;
                // vertex morph
                if (!mesh->data_morphvertex3f)
-                       Host_Error("model %s has no skeletal or vertex morph animation data\n", model->name);
+                       Host_Error("model %s has no skeletal or vertex morph animation data", model->name);
                vertsbase = mesh->data_morphvertex3f;
                vertcount = mesh->num_vertices;
                verts1 = vertsbase + frameblend[0].frame * vertcount * 3;
@@ -126,7 +126,7 @@ int Mod_Alias_GetTagMatrix(const model_t *model, int poseframe, int tagindex, ma
 {
        const float *boneframe;
        float tempbonematrix[12], bonematrix[12];
-       Matrix4x4_CreateIdentity(outmatrix);
+       *outmatrix = identitymatrix;
        if (model->num_bones)
        {
                if (tagindex < 0 || tagindex >= model->num_bones)
@@ -198,45 +198,75 @@ static void Mod_Alias_Mesh_CompileFrameZero(surfmesh_t *mesh)
        Mod_BuildTextureVectorsAndNormals(0, mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f, true);
 }
 
-static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
+static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
 {
-       int i, linetrace;
+       int i;
        float segmentmins[3], segmentmaxs[3];
        frameblend_t frameblend[4];
        msurface_t *surface;
        surfmesh_t *mesh;
-       colbrushf_t *thisbrush_start = NULL, *thisbrush_end = NULL;
-       matrix4x4_t startmatrix, endmatrix;
+       static int maxvertices = 0;
+       static float *vertex3f = NULL;
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
-       segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
-       segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
-       segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
-       segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
-       segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
-       linetrace = VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs);
-       if (!linetrace)
-       {
-               // box trace, performed as brush trace
-               Matrix4x4_CreateIdentity(&startmatrix);
-               Matrix4x4_CreateIdentity(&endmatrix);
-               thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
-               thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
-       }
        memset(frameblend, 0, sizeof(frameblend));
        frameblend[0].frame = frame;
        frameblend[0].lerp = 1;
-       for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+       if (VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
+       {
+               // line trace
+               segmentmins[0] = min(start[0], end[0]) - 1;
+               segmentmins[1] = min(start[1], end[1]) - 1;
+               segmentmins[2] = min(start[2], end[2]) - 1;
+               segmentmaxs[0] = max(start[0], end[0]) + 1;
+               segmentmaxs[1] = max(start[1], end[1]) + 1;
+               segmentmaxs[2] = max(start[2], end[2]) + 1;
+               for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+               {
+                       mesh = surface->groupmesh;
+                       if (maxvertices < mesh->num_vertices)
+                       {
+                               if (vertex3f)
+                                       Z_Free(vertex3f);
+                               maxvertices = (mesh->num_vertices + 255) & ~255;
+                               vertex3f = Z_Malloc(maxvertices * sizeof(float[3]));
+                       }
+                       Mod_Alias_GetMesh_Vertex3f(model, frameblend, mesh, vertex3f);
+                       Collision_TraceLineTriangleMeshFloat(trace, start, end, mesh->num_triangles, mesh->data_element3i, vertex3f, SUPERCONTENTS_SOLID, segmentmins, segmentmaxs);
+               }
+       }
+       else
        {
-               mesh = surface->groupmesh;
-               Mod_Alias_GetMesh_Vertex3f(model, frameblend, mesh, varray_vertex3f);
-               if (linetrace)
-                       Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, mesh->num_triangles, mesh->data_element3i, varray_vertex3f, SUPERCONTENTS_SOLID, segmentmins, segmentmaxs);
-               else
-                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, mesh->num_triangles, mesh->data_element3i, varray_vertex3f, SUPERCONTENTS_SOLID, segmentmins, segmentmaxs);
+               // box trace, performed as brush trace
+               colbrushf_t *thisbrush_start, *thisbrush_end;
+               vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
+               segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
+               segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
+               segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
+               segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
+               segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
+               segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
+               VectorAdd(start, boxmins, boxstartmins);
+               VectorAdd(start, boxmaxs, boxstartmaxs);
+               VectorAdd(end, boxmins, boxendmins);
+               VectorAdd(end, boxmaxs, boxendmaxs);
+               thisbrush_start = Collision_BrushForBox(&identitymatrix, boxstartmins, boxstartmaxs);
+               thisbrush_end = Collision_BrushForBox(&identitymatrix, boxendmins, boxendmaxs);
+               for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+               {
+                       mesh = surface->groupmesh;
+                       if (maxvertices < mesh->num_vertices)
+                       {
+                               if (vertex3f)
+                                       Z_Free(vertex3f);
+                               maxvertices = (mesh->num_vertices + 255) & ~255;
+                               vertex3f = Z_Malloc(maxvertices * sizeof(float[3]));
+                       }
+                       Mod_Alias_GetMesh_Vertex3f(model, frameblend, mesh, vertex3f);
+                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, mesh->num_triangles, mesh->data_element3i, vertex3f, SUPERCONTENTS_SOLID, segmentmins, segmentmaxs);
+               }
        }
 }
 
@@ -432,8 +462,8 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
        }
 }
 
-#define BOUNDI(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%d exceeds %d - %d)\n", loadmodel->name, VALUE, MIN, MAX);
-#define BOUNDF(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%f exceeds %f - %f)\n", loadmodel->name, VALUE, MIN, MAX);
+#define BOUNDI(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%d exceeds %d - %d)", loadmodel->name, VALUE, MIN, MAX);
+#define BOUNDF(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%f exceeds %f - %f)", loadmodel->name, VALUE, MIN, MAX);
 void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
 {
        int i, j, version, totalskins, skinwidth, skinheight, groupframes, groupskins, numverts;
@@ -700,7 +730,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                                else
                                        sprintf (name, "%s_%i", loadmodel->name, i);
                                if (!Mod_LoadSkinFrame(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP, true, true))
-                                       Mod_LoadSkinFrame_Internal(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP, true, r_fullbrights.integer, (unsigned char *)datapointer, skinwidth, skinheight);
+                                       Mod_LoadSkinFrame_Internal(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP, true, r_fullbrights.integer, (unsigned char *)datapointer, skinwidth, skinheight, 8, NULL, NULL);
                                Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, &tempskinframe);
                                datapointer += skinwidth * skinheight;
                                totalskins++;
@@ -1017,7 +1047,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        pinmodel = (md3modelheader_t *)buffer;
 
        if (memcmp(pinmodel->identifier, "IDP3", 4))
-               Host_Error ("%s is not a MD3 (IDP3) file\n", loadmodel->name);
+               Host_Error ("%s is not a MD3 (IDP3) file", loadmodel->name);
        version = LittleLong (pinmodel->version);
        if (version != MD3VERSION)
                Host_Error ("%s has wrong version number (%i should be %i)",
@@ -1069,7 +1099,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        for (i = 0, pintag = (md3tag_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_tags));i < loadmodel->num_tagframes * loadmodel->num_tags;i++, pintag++)
        {
                strcpy(loadmodel->data_tags[i].name, pintag->name);
-               Matrix4x4_CreateIdentity(&loadmodel->data_tags[i].matrix);
+               loadmodel->data_tags[i].matrix = identitymatrix;
                for (j = 0;j < 3;j++)
                {
                        for (k = 0;k < 3;k++)
@@ -1096,7 +1126,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        for (i = 0, pinmesh = (md3mesh_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_meshes));i < loadmodel->num_surfaces;i++, pinmesh = (md3mesh_t *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_end)))
        {
                if (memcmp(pinmesh->identifier, "IDP3", 4))
-                       Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)\n");
+                       Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
                mesh = loadmodel->meshlist[i];
                mesh->num_morphframes = LittleLong(pinmesh->num_frames);
                mesh->num_vertices = LittleLong(pinmesh->num_vertices);
@@ -1159,17 +1189,11 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pinmodel = (zymtype1header_t *)buffer;
        pbase = (unsigned char *)buffer;
        if (memcmp(pinmodel->id, "ZYMOTICMODEL", 12))
-               Host_Error ("Mod_ZYMOTICMODEL_Load: %s is not a zymotic model\n");
+               Host_Error ("Mod_ZYMOTICMODEL_Load: %s is not a zymotic model");
        if (BigLong(pinmodel->type) != 1)
-               Host_Error ("Mod_ZYMOTICMODEL_Load: only type 1 (skeletal pose) models are currently supported (name = %s)\n", loadmodel->name);
+               Host_Error ("Mod_ZYMOTICMODEL_Load: only type 1 (skeletal pose) models are currently supported (name = %s)", loadmodel->name);
 
        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;
 
@@ -1208,6 +1232,24 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pheader->lump_trizone.start = BigLong(pinmodel->lump_trizone.start);
        pheader->lump_trizone.length = BigLong(pinmodel->lump_trizone.length);
 
+       if (pheader->numtris < 1 || pheader->numverts < 3 || pheader->numshaders < 1)
+       {
+               Con_Printf("%s has no geometry\n");
+               return;
+       }
+       if (pheader->numscenes < 1 || pheader->lump_poses.length < (int)sizeof(float[3][4]))
+       {
+               Con_Printf("%s has no animations\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;
+
        loadmodel->numframes = pheader->numscenes;
        loadmodel->num_surfaces = pheader->numshaders;
 
@@ -1259,11 +1301,11 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->animscenes[i].framerate = BigFloat(scene->framerate);
                loadmodel->animscenes[i].loop = (BigLong(scene->flags) & ZYMSCENEFLAG_NOLOOP) == 0;
                if ((unsigned int) loadmodel->animscenes[i].firstframe >= (unsigned int) numposes)
-                       Host_Error("%s scene->firstframe (%i) >= numposes (%i)\n", loadmodel->name, loadmodel->animscenes[i].firstframe, numposes);
+                       Host_Error("%s scene->firstframe (%i) >= numposes (%i)", loadmodel->name, loadmodel->animscenes[i].firstframe, numposes);
                if ((unsigned int) loadmodel->animscenes[i].firstframe + (unsigned int) loadmodel->animscenes[i].framecount > (unsigned int) numposes)
-                       Host_Error("%s scene->firstframe (%i) + framecount (%i) >= numposes (%i)\n", loadmodel->name, loadmodel->animscenes[i].firstframe, loadmodel->animscenes[i].framecount, numposes);
+                       Host_Error("%s scene->firstframe (%i) + framecount (%i) >= numposes (%i)", loadmodel->name, loadmodel->animscenes[i].firstframe, loadmodel->animscenes[i].framecount, numposes);
                if (loadmodel->animscenes[i].framerate < 0)
-                       Host_Error("%s scene->framerate (%f) < 0\n", loadmodel->name, loadmodel->animscenes[i].framerate);
+                       Host_Error("%s scene->framerate (%f) < 0", loadmodel->name, loadmodel->animscenes[i].framerate);
                scene++;
        }
 
@@ -1284,7 +1326,7 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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\n", loadmodel->name, i, i);
+                       Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
        }
 
        //zymlump_t lump_vertbonecounts; // int vertbonecounts[numvertices]; // how many bones influence each vertex (separate mainly to make this compress better)
@@ -1294,7 +1336,7 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        {
                vertbonecounts[i] = BigLong(bonecount[i]);
                if (vertbonecounts[i] < 1)
-                       Host_Error("%s bonecount[%i] < 1\n", loadmodel->name, i);
+                       Host_Error("%s bonecount[%i] < 1", loadmodel->name, i);
        }
 
        //zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
@@ -1341,16 +1383,16 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        // byteswap, validate, and swap winding order of tris
        count = pheader->numshaders * sizeof(int) + pheader->numtris * sizeof(int[3]);
        if (pheader->lump_render.length != count)
-               Host_Error("%s renderlist is wrong size (%i bytes, should be %i bytes)\n", loadmodel->name, pheader->lump_render.length, count);
+               Host_Error("%s renderlist is wrong size (%i bytes, should be %i bytes)", loadmodel->name, pheader->lump_render.length, count);
        renderlist = (int *) (pheader->lump_render.start + pbase);
        renderlistend = (int *) ((unsigned char *) renderlist + pheader->lump_render.length);
        for (i = 0;i < loadmodel->num_surfaces;i++)
        {
                if (renderlist >= renderlistend)
-                       Host_Error("%s corrupt renderlist (wrong size)\n", loadmodel->name);
+                       Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
                count = BigLong(*renderlist);renderlist++;
                if (renderlist + count * 3 > renderlistend || (i == pheader->numshaders - 1 && renderlist + count * 3 != renderlistend))
-                       Host_Error("%s corrupt renderlist (wrong size)\n", loadmodel->name);
+                       Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
                mesh = loadmodel->meshlist[i];
                mesh->num_triangles = count;
                mesh->data_element3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
@@ -1364,9 +1406,9 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        if ((unsigned int)outelements[0] >= (unsigned int)pheader->numverts
                         || (unsigned int)outelements[1] >= (unsigned int)pheader->numverts
                         || (unsigned int)outelements[2] >= (unsigned int)pheader->numverts)
-                               Host_Error("%s corrupt renderlist (out of bounds index)\n", loadmodel->name);
+                               Host_Error("%s corrupt renderlist (out of bounds index)", loadmodel->name);
                        if (vertbonecounts[outelements[0]] == 0 || vertbonecounts[outelements[1]] == 0 || vertbonecounts[outelements[2]] == 0)
-                               Host_Error("%s corrupt renderlist (references vertex with no bone weights\n", loadmodel->name);
+                               Host_Error("%s corrupt renderlist (references vertex with no bone weights", loadmodel->name);
                        renderlist += 3;
                        outelements += 3;
                }
@@ -1436,6 +1478,7 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        Mem_Free(vertbonecounts);
        Mem_Free(verts);
        Mem_Free(outtexcoord2f);
+       Mod_FreeSkinFiles(skinfiles);
 }
 
 void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
@@ -1452,17 +1495,11 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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\n");
+               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)\n", loadmodel->name);
+               Host_Error ("Mod_DARKPLACESMODEL_Load: only type 2 (hierarchical skeletal pose) models are currently supported (name = %s)", loadmodel->name);
 
        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;
 
@@ -1484,6 +1521,24 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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++)
        {
@@ -1538,7 +1593,7 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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\n", loadmodel->name, i, i);
+                       Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
        }
 
        // load the frames
@@ -1649,6 +1704,7 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
                dpmmesh++;
        }
+       Mod_FreeSkinFiles(skinfiles);
 }
 
 static void Mod_PSKMODEL_AnimKeyToMatrix(float *origin, float *quat, matrix4x4_t *m)
@@ -1684,7 +1740,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
        pchunk = (pskchunk_t *)buffer;
        if (strcmp(pchunk->id, "ACTRHEAD"))
-               Host_Error ("Mod_PSKMODEL_Load: %s is not a ActorX model\n");
+               Host_Error ("Mod_PSKMODEL_Load: %s is not an Unreal Engine ActorX (.psk + .psa) model");
 
        loadmodel->type = mod_alias;
        loadmodel->DrawSky = NULL;
@@ -1714,7 +1770,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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)\n", loadmodel->name, animname);
+               Host_Error("%s: can't find .psa file (%s)", loadmodel->name, animname);
 
        numpnts = 0;
        pnts = NULL;
@@ -1752,7 +1808,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskpnts_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numpnts = numrecords;
                        pnts = (pskpnts_t *)buffer;
@@ -1768,7 +1824,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskvtxw_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numvtxw = numrecords;
                        vtxw = (pskvtxw_t *)buffer;
@@ -1789,7 +1845,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskface_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numfaces = numrecords;
                        faces = (pskface_t *)buffer;
@@ -1821,7 +1877,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskmatt_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        nummatts = numrecords;
                        matts = (pskmatt_t *)buffer;
@@ -1834,7 +1890,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskboneinfo_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numbones = numrecords;
                        bones = (pskboneinfo_t *)buffer;
@@ -1879,7 +1935,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskrawweights_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numrawweights = numrecords;
                        rawweights = (pskrawweights_t *)buffer;
@@ -1922,7 +1978,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskboneinfo_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", animname, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        // byteswap in place and keep the pointer
                        numanimbones = numrecords;
                        animbones = (pskboneinfo_t *)animbuffer;
@@ -1931,7 +1987,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        // positions from the psk, but this is hard for me to implement
                        // and people can easily make animations that match.
                        if (numanimbones != numbones)
-                               Host_Error("%s: this loader only supports animations with the same bones as the mesh\n");
+                               Host_Error("%s: this loader only supports animations with the same bones as the mesh");
                        for (index = 0, p = (pskboneinfo_t *)animbuffer;index < numrecords;index++, p++)
                        {
                                p->numchildren = LittleLong(p->numchildren);
@@ -1968,7 +2024,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                }
                                // check that bones are the same as in the base
                                if (strcmp(p->name, bones[index].name) || p->parent != bones[index].parent)
-                                       Host_Error("%s: this loader only supports animations with the same bones as the mesh\n", animname);
+                                       Host_Error("%s: this loader only supports animations with the same bones as the mesh", animname);
                        }
                        animbuffer = p;
                }
@@ -1976,7 +2032,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskaniminfo_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", animname, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        // byteswap in place and keep the pointer
                        numanims = numrecords;
                        anims = (pskaniminfo_t *)animbuffer;
@@ -1998,7 +2054,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskanimkeys_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", animname, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        numanimkeys = numrecords;
                        animkeys = (pskanimkeys_t *)animbuffer;
                        for (index = 0, p = (pskanimkeys_t *)animbuffer;index < numrecords;index++, p++)
@@ -2034,7 +2090,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
 
        if (!numpnts || !pnts || !numvtxw || !vtxw || !numfaces || !faces || !nummatts || !matts || !numbones || !bones || !numrawweights || !rawweights || !numanims || !anims || !numanimkeys || !animkeys)
-               Host_Error("%s: missing required chunks\n", loadmodel->name);
+               Host_Error("%s: missing required chunks", loadmodel->name);
 
        // FIXME: model bbox
        // model bbox
@@ -2059,7 +2115,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->num_textures = loadmodel->nummeshes = loadmodel->nummodelsurfaces = loadmodel->num_surfaces = nummatts;
 
        if (numanimkeys != loadmodel->num_bones * loadmodel->numframes)
-               Host_Error("%s: %s has incorrect number of animation keys\n", animname, pchunk->id);
+               Host_Error("%s: %s has incorrect number of animation keys", animname, pchunk->id);
 
        loadmodel->data_poses = (float *)Mem_Alloc(loadmodel->mempool, loadmodel->num_poses * sizeof(float[12]));
        loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(animscene_t));
@@ -2122,7 +2178,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                strlcpy(loadmodel->data_bones[index].name, bones[index].name, sizeof(loadmodel->data_bones[index].name));
                loadmodel->data_bones[index].parent = (index || bones[index].parent > 0) ? bones[index].parent : -1;
                if (loadmodel->data_bones[index].parent >= index)
-                       Host_Error("%s bone[%i].parent >= %i\n", loadmodel->name, index, index);
+                       Host_Error("%s bone[%i].parent >= %i", loadmodel->name, index, index);
        }
 
        // build bone-relative vertex weights from the psk point weights
@@ -2143,7 +2199,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                mesh->data_vertexboneweights[mesh->num_vertexboneweights].vertexindex = index;
                                mesh->data_vertexboneweights[mesh->num_vertexboneweights].boneindex = rawweights[j].boneindex;
                                mesh->data_vertexboneweights[mesh->num_vertexboneweights].weight = rawweights[j].weight;
-                               Matrix4x4_CreateIdentity(&matrix);
+                               matrix = identitymatrix;
                                for (i = rawweights[j].boneindex;i >= 0;i = loadmodel->data_bones[i].parent)
                                {
                                        matrix4x4_t childmatrix, tempmatrix;
@@ -2197,5 +2253,6 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        Mod_Alias_Mesh_CompileFrameZero(mesh);
 
        Mem_Free(animfilebuffer);
+       Mod_FreeSkinFiles(skinfiles);
 }