]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_alias.c
Add an in_releaseall command for debugging/working around stuck keys.
[xonotic/darkplaces.git] / model_alias.c
index 5407c86d5041bd2569a40b3fee2ca3ce4ae28d5a..e319582c5e3ee77c89d238adf2e650eeeba69810 100644 (file)
@@ -62,6 +62,104 @@ void *Mod_Skeletal_AnimateVertices_AllocBuffers(size_t nbytes)
        return Mod_Skeletal_AnimateVertices_bonepose;
 }
 
+void Mod_Skeletal_BuildTransforms(const dp_model_t * RESTRICT model, const frameblend_t * RESTRICT frameblend, const skeleton_t *skeleton, float * RESTRICT bonepose, float * RESTRICT boneposerelative)
+{
+       int i, blends;
+       float m[12];
+
+       if (!bonepose)
+               bonepose = (float * RESTRICT) Mod_Skeletal_AnimateVertices_AllocBuffers(sizeof(float[12]) * model->num_bones);
+               
+       if (skeleton && !skeleton->relativetransforms)
+               skeleton = NULL;
+
+       // interpolate matrices
+       if (skeleton)
+       {
+               for (i = 0;i < model->num_bones;i++)
+               {
+                       Matrix4x4_ToArray12FloatD3D(&skeleton->relativetransforms[i], m);
+                       if (model->data_bones[i].parent >= 0)
+                               R_ConcatTransforms(bonepose + model->data_bones[i].parent * 12, m, bonepose + i * 12);
+                       else
+                               memcpy(bonepose + i * 12, m, sizeof(m));
+
+                       // create a relative deformation matrix to describe displacement
+                       // from the base mesh, which is used by the actual weighting
+                       R_ConcatTransforms(bonepose + i * 12, model->data_baseboneposeinverse + i * 12, boneposerelative + i * 12);
+               }
+       }
+       else
+       {
+               for (i = 0;i < model->num_bones;i++)
+               {
+                       // blend by transform each quaternion/translation into a dual-quaternion first, then blending
+                       const short * RESTRICT firstpose7s = model->data_poses7s + 7 * (frameblend[0].subframe * model->num_bones + i);
+                       float firstlerp = frameblend[0].lerp,
+                               firsttx = firstpose7s[0], firstty = firstpose7s[1], firsttz = firstpose7s[2],
+                               rx = firstpose7s[3] * firstlerp,
+                               ry = firstpose7s[4] * firstlerp,
+                               rz = firstpose7s[5] * firstlerp,
+                               rw = firstpose7s[6] * firstlerp,
+                               dx = firsttx*rw + firstty*rz - firsttz*ry,
+                               dy = -firsttx*rz + firstty*rw + firsttz*rx,
+                               dz = firsttx*ry - firstty*rx + firsttz*rw,
+                               dw = -firsttx*rx - firstty*ry - firsttz*rz,
+                               scale, sx, sy, sz, sw;
+                       for (blends = 1;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
+                       {
+                               const short * RESTRICT blendpose7s = model->data_poses7s + 7 * (frameblend[blends].subframe * model->num_bones + i);
+                               float blendlerp = frameblend[blends].lerp,
+                                       blendtx = blendpose7s[0], blendty = blendpose7s[1], blendtz = blendpose7s[2],
+                                       qx = blendpose7s[3], qy = blendpose7s[4], qz = blendpose7s[5], qw = blendpose7s[6];
+                               if(rx*qx + ry*qy + rz*qz + rw*qw < 0) blendlerp = -blendlerp;
+                               qx *= blendlerp;
+                               qy *= blendlerp;
+                               qz *= blendlerp;
+                               qw *= blendlerp;
+                               rx += qx;
+                               ry += qy;
+                               rz += qz;
+                               rw += qw;
+                               dx += blendtx*qw + blendty*qz - blendtz*qy;
+                               dy += -blendtx*qz + blendty*qw + blendtz*qx;
+                               dz += blendtx*qy - blendty*qx + blendtz*qw;
+                               dw += -blendtx*qx - blendty*qy - blendtz*qz;
+                       }
+                       // generate a matrix from the dual-quaternion, implicitly normalizing it in the process
+                       scale = 1.0f / (rx*rx + ry*ry + rz*rz + rw*rw);
+                       sx = rx * scale;
+                       sy = ry * scale;
+                       sz = rz * scale;
+                       sw = rw * scale;
+                       m[0] = sw*rw + sx*rx - sy*ry - sz*rz;
+                       m[1] = 2*(sx*ry - sw*rz);
+                       m[2] = 2*(sx*rz + sw*ry);
+                       m[3] = model->num_posescale*(dx*sw - dy*sz + dz*sy - dw*sx);
+                       m[4] = 2*(sx*ry + sw*rz);
+                       m[5] = sw*rw + sy*ry - sx*rx - sz*rz;
+                       m[6] = 2*(sy*rz - sw*rx);
+                       m[7] = model->num_posescale*(dx*sz + dy*sw - dz*sx - dw*sy);
+                       m[8] = 2*(sx*rz - sw*ry);
+                       m[9] = 2*(sy*rz + sw*rx);
+                       m[10] = sw*rw + sz*rz - sx*rx - sy*ry;
+                       m[11] = model->num_posescale*(dy*sx + dz*sw - dx*sy - dw*sz);
+                       if (i == r_skeletal_debugbone.integer)
+                               m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
+                       m[3] *= r_skeletal_debugtranslatex.value;
+                       m[7] *= r_skeletal_debugtranslatey.value;
+                       m[11] *= r_skeletal_debugtranslatez.value;
+                       if (model->data_bones[i].parent >= 0)
+                               R_ConcatTransforms(bonepose + model->data_bones[i].parent * 12, m, bonepose + i * 12);
+                       else
+                               memcpy(bonepose + i * 12, m, sizeof(m));
+                       // create a relative deformation matrix to describe displacement
+                       // from the base mesh, which is used by the actual weighting
+                       R_ConcatTransforms(bonepose + i * 12, model->data_baseboneposeinverse + i * 12, boneposerelative + i * 12);
+               }
+       }
+}
+
 static void Mod_Skeletal_AnimateVertices(const dp_model_t * RESTRICT model, const frameblend_t * RESTRICT frameblend, const skeleton_t *skeleton, float * RESTRICT vertex3f, float * RESTRICT normal3f, float * RESTRICT svector3f, float * RESTRICT tvector3f)
 {
 
@@ -525,26 +623,39 @@ static void Mod_BuildBaseBonePoses(void)
        Mem_Free(basebonepose);
 }
 
-static void Mod_Alias_CalculateBoundingBox(void)
+static qboolean Mod_Alias_CalculateBoundingBox(void)
 {
        int vnum;
        qboolean firstvertex = true;
        float dist, yawradius, radius;
        float *v;
+       qboolean isanimated = false;
        VectorClear(loadmodel->normalmins);
        VectorClear(loadmodel->normalmaxs);
        yawradius = 0;
        radius = 0;
        if (loadmodel->AnimateVertices)
        {
-               float *vertex3f;
+               float *vertex3f, *refvertex3f;
                frameblend_t frameblend[MAX_FRAMEBLENDS];
                memset(frameblend, 0, sizeof(frameblend));
                frameblend[0].lerp = 1;
-               vertex3f = (float *) Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+               vertex3f = (float *) Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]) * 2);
+               refvertex3f = NULL;
                for (frameblend[0].subframe = 0;frameblend[0].subframe < loadmodel->num_poses;frameblend[0].subframe++)
                {
                        loadmodel->AnimateVertices(loadmodel, frameblend, NULL, vertex3f, NULL, NULL, NULL);
+                       if (!refvertex3f)
+                       {
+                               // make a copy of the first frame for comparing all others
+                               refvertex3f = vertex3f + loadmodel->surfmesh.num_vertices * 3;
+                               memcpy(refvertex3f, vertex3f, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+                       }
+                       else
+                       {
+                               if (!isanimated && memcmp(refvertex3f, vertex3f, loadmodel->surfmesh.num_vertices * sizeof(float[3])))
+                                       isanimated = true;
+                       }
                        for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
                        {
                                if (firstvertex)
@@ -610,6 +721,7 @@ static void Mod_Alias_CalculateBoundingBox(void)
        loadmodel->rotatedmaxs[0] = loadmodel->rotatedmaxs[1] = loadmodel->rotatedmaxs[2] = radius;
        loadmodel->radius = radius;
        loadmodel->radius2 = radius * radius;
+       return isanimated;
 }
 
 static void Mod_Alias_MorphMesh_CompileFrames(void)
@@ -640,33 +752,46 @@ static void Mod_Alias_MorphMesh_CompileFrames(void)
        }
 }
 
-static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
+static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
        msurface_t *surface;
-       float vertex3fbuf[1024*3];
+       float vertex3fbuf[1024 * 3];
        float *vertex3f = vertex3fbuf;
+       float *freevertex3f = NULL;
+       // for static cases we can just call CollisionBIH which is much faster
+       if ((frameblend == NULL || (frameblend[0].subframe == 0 && frameblend[1].lerp == 0)) && (skeleton == NULL || skeleton->relativetransforms == NULL))
+       {
+               Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
+               return;
+       }
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
-       trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       if (model->surfmesh.num_vertices > 1024)
-               vertex3f = (float *)Mem_Alloc(tempmempool, model->surfmesh.num_vertices * sizeof(float[3]));
+       trace->skipsupercontentsmask = skipsupercontentsmask;
+       trace->skipmaterialflagsmask = skipmaterialflagsmask;
        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;
-       model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
+       if (frameblend == NULL || frameblend[0].subframe != 0 || frameblend[0].lerp != 0 || skeleton != NULL)
+       {
+               if (model->surfmesh.num_vertices > 1024)
+                       vertex3f = freevertex3f = (float *)Mem_Alloc(tempmempool, model->surfmesh.num_vertices * sizeof(float[3]));
+               model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
+       }
+       else
+               vertex3f = model->surfmesh.data_vertex3f;
        for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
                Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, 0, NULL, SUPERCONTENTS_SOLID | (surface->texture->basematerialflags & MATERIALFLAGMASK_TRANSLUCENT ? 0 : SUPERCONTENTS_OPAQUE), 0, surface->texture, segmentmins, segmentmaxs);
-       if (vertex3f != vertex3fbuf)
-               Mem_Free(vertex3f);
+       if (freevertex3f)
+               Mem_Free(freevertex3f);
 }
 
-static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
 {
        int i;
        vec3_t shiftstart, shiftend;
@@ -681,16 +806,24 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, const frameblend_t *frameb
        {
                VectorAdd(start, boxmins, shiftstart);
                VectorAdd(end, boxmins, shiftend);
-               Mod_MDLMD2MD3_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
+               Mod_MDLMD2MD3_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
                VectorSubtract(trace->endpos, boxmins, trace->endpos);
                return;
        }
 
+       // for static cases we can just call CollisionBIH which is much faster
+       if ((frameblend == NULL || (frameblend[0].subframe == 0 && frameblend[1].lerp == 0)) && (skeleton == NULL || skeleton->relativetransforms == NULL))
+       {
+               Mod_CollisionBIH_TraceBox(model, frameblend, skeleton, trace, start, boxmins, boxmaxs, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
+               return;
+       }
+
        // box trace, performed as brush trace
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
-       trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
+       trace->skipsupercontentsmask = skipsupercontentsmask;
+       trace->skipmaterialflagsmask = skipmaterialflagsmask;
        if (model->surfmesh.num_vertices > 1024)
                vertex3f = (float *)Mem_Alloc(tempmempool, model->surfmesh.num_vertices * sizeof(float[3]));
        segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
@@ -799,9 +932,7 @@ static void Mod_BuildAliasSkinFromSkinFrame(texture_t *texture, skinframe_t *ski
        memset(texture, 0, sizeof(*texture));
        texture->currentframe = texture;
        //texture->animated = false;
-       texture->numskinframes = 1;
-       texture->skinframerate = 1;
-       texture->skinframes[0] = skinframe;
+       texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(skinframe);
        texture->currentskinframe = skinframe;
        //texture->backgroundnumskinframes = 0;
        //texture->customblendfunc[0] = 0;
@@ -812,6 +943,7 @@ static void Mod_BuildAliasSkinFromSkinFrame(texture_t *texture, skinframe_t *ski
        //texture->textureflags = 0;
 
        texture->basematerialflags = MATERIALFLAG_WALL;
+       texture->basealpha = 1.0f;
        if (texture->currentskinframe->hasalpha)
                texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
        texture->currentmaterialflags = texture->basematerialflags;
@@ -927,6 +1059,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        // FIXME add TraceBrush!
        loadmodel->PointSuperContents = NULL;
+       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices;
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
@@ -1092,10 +1225,9 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
                loadmodel->surfmesh.data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_triangles * sizeof(int[3]));
        }
        Mod_MDL_LoadFrames (startframes, numverts, vertremap);
-       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices; // needed during loading, may be cleared by code later in this function
        if (loadmodel->surfmesh.data_neighbor3i)
                Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
-       Mod_Alias_CalculateBoundingBox();
+       loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
        Mod_Alias_MorphMesh_CompileFrames();
 
        Mem_Free(vertst);
@@ -1198,6 +1330,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        loadmodel->skinscenes[loadmodel->numskins].loop = true;
 
                        //increase skin counts
+                       loadmodel->num_textures++;
                        loadmodel->numskins++;
                        totalskins++;
 
@@ -1215,14 +1348,13 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        surface->num_firstvertex = 0;
        surface->num_vertices = loadmodel->surfmesh.num_vertices;
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
        if(mod_alias_force_animated.string[0])
                loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
-       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_MDL_AnimateVertices : NULL;
 
+       // 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)
        {
-               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
                loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
                loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
                loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
@@ -1286,6 +1418,7 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
+       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices;
 
        if (LittleLong(pinmodel->num_tris) < 1 || LittleLong(pinmodel->num_tris) > 65536)
                Host_Error ("%s has invalid number of triangles: %i", loadmodel->name, LittleLong(pinmodel->num_tris));
@@ -1476,15 +1609,12 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        Mem_Free(vertremap);
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
-       if(mod_alias_force_animated.string[0])
-               loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
-       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices; // needed during loading, may be cleared by code later in this function
        if (loadmodel->surfmesh.data_neighbor3i)
                Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
-       Mod_Alias_CalculateBoundingBox();
+       loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
        Mod_Alias_MorphMesh_CompileFrames();
-       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_MDL_AnimateVertices : NULL;
+       if(mod_alias_force_animated.string[0])
+               loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
 
        surface = loadmodel->data_surfaces;
        surface->texture = loadmodel->data_textures;
@@ -1493,9 +1623,10 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
        surface->num_firstvertex = 0;
        surface->num_vertices = loadmodel->surfmesh.num_vertices;
 
+       // 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)
        {
-               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
                loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
                loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
                loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
@@ -1554,6 +1685,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
+       loadmodel->AnimateVertices = Mod_MD3_AnimateVertices;
        loadmodel->synctype = ST_RAND;
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
        i = LittleLong (pinmodel->flags);
@@ -1678,21 +1810,19 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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];
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
-       if(mod_alias_force_animated.string[0])
-               loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
-       loadmodel->AnimateVertices = Mod_MD3_AnimateVertices; // needed during loading, may be cleared by code later in this function
        if (loadmodel->surfmesh.data_neighbor3i)
                Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_Alias_MorphMesh_CompileFrames();
-       Mod_Alias_CalculateBoundingBox();
+       loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
        Mod_FreeSkinFiles(skinfiles);
        Mod_MakeSortedSurfaces(loadmodel);
-       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_MD3_AnimateVertices : NULL;
+       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)
        {
-               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
                loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
                loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
                loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
@@ -1796,6 +1926,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
+       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
 
        loadmodel->numframes = pheader->numscenes;
        loadmodel->num_surfaces = pheader->numshaders;
@@ -1815,6 +1946,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        }
 
        // model bbox
+       // LordHavoc: actually we blow this away later with Mod_Alias_CalculateBoundingBox()
        modelradius = pheader->radius;
        for (i = 0;i < 3;i++)
        {
@@ -1884,10 +2016,6 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        meshvertices = pheader->numverts;
        meshtriangles = pheader->numtris;
 
-       loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
-       if(mod_alias_force_animated.string[0])
-               loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
-       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
@@ -2088,10 +2216,14 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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)
        {
-               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
                loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
                loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
                loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
@@ -2179,8 +2311,10 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
+       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
 
        // model bbox
+       // LordHavoc: actually we blow this away later with Mod_Alias_CalculateBoundingBox()
        for (i = 0;i < 3;i++)
        {
                loadmodel->normalmins[i] = pheader->mins[i];
@@ -2217,10 +2351,6 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces = pheader->num_meshs;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
-       if(mod_alias_force_animated.string[0])
-               loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
-       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
        // 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]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + (r_enableshadowvolumes.integer ? meshtriangles * sizeof(int[3]) : 0) + meshvertices * (sizeof(float[14]) + sizeof(unsigned short) + sizeof(unsigned char[2][4])) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]) + 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);
@@ -2465,10 +2595,14 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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)
        {
-               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
                loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
                loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
                loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
@@ -2531,6 +2665,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
+       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
        loadmodel->synctype = ST_RAND;
 
        FS_StripExtension(loadmodel->name, animname, sizeof(animname));
@@ -2884,10 +3019,6 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
-       loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
-       if(mod_alias_force_animated.string[0])
-               loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
-       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
        // do most allocations as one merged chunk
        size = loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + (r_enableshadowvolumes.integer ? loadmodel->surfmesh.num_triangles * sizeof(int[3]) : 0)  + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]) + loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]) + loadmodel->surfmesh.num_vertices * sizeof(unsigned short) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t) + ((loadmodel->surfmesh.num_vertices <= 65536) ? (loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3])) : 0);
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, size);
@@ -3143,11 +3274,14 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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);
-       Mod_Alias_CalculateBoundingBox();
+       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)
        {
-               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
                loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
                loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
                loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
@@ -3365,6 +3499,7 @@ void Mod_INTERQUAKEMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
+       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
 
        // load external .skin files if present
        skinfiles = Mod_LoadSkinFiles();
@@ -3377,7 +3512,6 @@ void Mod_INTERQUAKEMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces = header.num_meshes;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices; // updated later
 
        meshvertices = header.num_vertexes;
        meshtriangles = header.num_triangles;
@@ -3528,7 +3662,6 @@ void Mod_INTERQUAKEMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
        if(mod_alias_force_animated.string[0])
                loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
-       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
 
        biggestorigin = 0;
        if (header.version == 1)
@@ -3912,9 +4045,10 @@ void Mod_INTERQUAKEMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        if (!header.ofs_bounds)
                Mod_Alias_CalculateBoundingBox();
 
-       if (!loadmodel->surfmesh.isanimated && loadmodel->surfmesh.num_triangles >= 1)
+       // 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)
        {
-               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
                loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
                loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
                loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;