redesigned skeletal model loading and rendering to use matrix-palette animation ...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 10 May 2006 11:52:21 +0000 (11:52 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 10 May 2006 11:52:21 +0000 (11:52 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6362 d7cf8633-e32d-0410-b094-e92efae38249

gl_rmain.c
gl_rsurf.c
model_alias.c
model_shared.h
r_shadow.c
render.h
todo

index 7d8ec4f..7e19215 100644 (file)
@@ -2485,7 +2485,7 @@ rsurfmode_t rsurface_mode;
 texture_t *rsurface_glsl_texture;
 qboolean rsurface_glsl_uselightmap;
 
-void RSurf_ActiveEntity(const entity_render_t *ent)
+void RSurf_ActiveEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents)
 {
        Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, rsurface_modelorg);
        rsurface_entity = ent;
@@ -2494,13 +2494,32 @@ void RSurf_ActiveEntity(const entity_render_t *ent)
                R_Mesh_ResizeArrays(rsurface_model->surfmesh.num_vertices);
        R_Mesh_Matrix(&ent->matrix);
        Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, rsurface_modelorg);
-       if ((rsurface_entity->frameblend[0].lerp != 1 || rsurface_entity->frameblend[0].frame != 0) && (rsurface_model->surfmesh.data_morphvertex3f || rsurface_model->surfmesh.data_vertexboneweights))
+       if ((rsurface_entity->frameblend[0].lerp != 1 || rsurface_entity->frameblend[0].frame != 0) && (rsurface_model->surfmesh.data_morphvertex3f || rsurface_model->surfmesh.data_vertexweightindex4i))
        {
-               rsurface_modelvertex3f = rsurface_array_modelvertex3f;
-               rsurface_modelsvector3f = NULL;
-               rsurface_modeltvector3f = NULL;
-               rsurface_modelnormal3f = NULL;
-               Mod_Alias_GetMesh_Vertex3f(rsurface_model, rsurface_entity->frameblend, rsurface_array_modelvertex3f);
+               if (wanttangents)
+               {
+                       rsurface_modelvertex3f = rsurface_array_modelvertex3f;
+                       rsurface_modelsvector3f = rsurface_array_modelsvector3f;
+                       rsurface_modeltvector3f = rsurface_array_modeltvector3f;
+                       rsurface_modelnormal3f = rsurface_array_modelnormal3f;
+                       Mod_Alias_GetMesh_Vertices(rsurface_model, rsurface_entity->frameblend, rsurface_array_modelvertex3f, rsurface_array_modelnormal3f, rsurface_array_modelsvector3f, rsurface_array_modeltvector3f);
+               }
+               else if (wantnormals)
+               {
+                       rsurface_modelvertex3f = rsurface_array_modelvertex3f;
+                       rsurface_modelsvector3f = NULL;
+                       rsurface_modeltvector3f = NULL;
+                       rsurface_modelnormal3f = rsurface_array_modelnormal3f;
+                       Mod_Alias_GetMesh_Vertices(rsurface_model, rsurface_entity->frameblend, rsurface_array_modelvertex3f, rsurface_array_modelnormal3f, NULL, NULL);
+               }
+               else
+               {
+                       rsurface_modelvertex3f = rsurface_array_modelvertex3f;
+                       rsurface_modelsvector3f = NULL;
+                       rsurface_modeltvector3f = NULL;
+                       rsurface_modelnormal3f = NULL;
+                       Mod_Alias_GetMesh_Vertices(rsurface_model, rsurface_entity->frameblend, rsurface_array_modelvertex3f, NULL, NULL, NULL);
+               }
                rsurface_generatedvertex = true;
        }
        else
@@ -3272,7 +3291,13 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        int batchcount;
        texture_t *t;
        msurface_t *texturesurfacelist[BATCHSIZE];
-       RSurf_ActiveEntity(ent);
+       // if the model is static it doesn't matter what value we give for
+       // wantnormals and wanttangents, so this logic uses only rules applicable
+       // to a model, knowing that they are meaningless otherwise
+       if ((ent->effects & EF_FULLBRIGHT) || r_showsurfaces.integer || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
+               RSurf_ActiveEntity(ent, false, false);
+       else
+               RSurf_ActiveEntity(ent, true, r_glsl.integer && gl_support_fragment_shader);
        batchcount = 0;
        t = NULL;
        for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
@@ -3336,7 +3361,13 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
        if (model == NULL)
                return;
 
-       RSurf_ActiveEntity(ent);
+       // if the model is static it doesn't matter what value we give for
+       // wantnormals and wanttangents, so this logic uses only rules applicable
+       // to a model, knowing that they are meaningless otherwise
+       if ((ent->effects & EF_FULLBRIGHT) || r_showsurfaces.integer || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
+               RSurf_ActiveEntity(ent, false, false);
+       else
+               RSurf_ActiveEntity(ent, true, r_glsl.integer && gl_support_fragment_shader);
 
        // update light styles
        if (!skysurfaces && model->brushq1.light_styleupdatechains)
index 6db1aa0..a8448d6 100644 (file)
@@ -753,7 +753,7 @@ void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin,
        else
        {
                projectdistance = lightradius + model->radius*2;
-               RSurf_ActiveEntity(ent);
+               RSurf_ActiveEntity(ent, false, false);
                R_Shadow_PrepareShadowMark(model->surfmesh.num_triangles);
                // identify lit faces within the bounding box
                for (modelsurfacelistindex = 0;modelsurfacelistindex < modelnumsurfaces;modelsurfacelistindex++)
@@ -839,7 +839,7 @@ void R_Q1BSP_DrawLight(entity_render_t *ent, int numsurfaces, const int *surface
        texture_t *tex;
        qboolean skip;
        CHECKGLERROR
-       RSurf_ActiveEntity(ent);
+       RSurf_ActiveEntity(ent, true, true);
        R_UpdateAllTextureInfo(ent);
        tex = NULL;
        rsurface_texture = NULL;
index 4a9cf05..52b3390 100644 (file)
@@ -39,14 +39,27 @@ void Mod_AliasInit (void)
        Cvar_RegisterVariable(&r_skeletal_debugtranslatez);
 }
 
-void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameblend, float *out3f)
+void Mod_Alias_GetMesh_Vertices(const model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
 {
-       if (model->surfmesh.num_vertexboneweights)
+       if (model->surfmesh.data_vertexweightindex4i)
        {
                int i, k, blends;
-               surfmeshvertexboneweight_t *v;
-               float *out, *matrix, m[12], bonepose[256][12];
+               const float *v = model->surfmesh.data_vertex3f;
+               const float *n = model->surfmesh.data_normal3f;
+               const float *sv = model->surfmesh.data_svector3f;
+               const float *tv = model->surfmesh.data_tvector3f;
+               const int *wi = model->surfmesh.data_vertexweightindex4i;
+               const float *wf = model->surfmesh.data_vertexweightinfluence4f;
+               float *matrix, m[12], bonepose[256][12], boneposerelative[256][12];
                // vertex weighted skeletal
+               memset(vertex3f, 0, model->surfmesh.num_vertices * sizeof(float[3]));
+               if (normal3f)
+                       memset(normal3f, 0, model->surfmesh.num_vertices * sizeof(float[3]));
+               if (svector3f)
+               {
+                       memset(svector3f, 0, model->surfmesh.num_vertices * sizeof(float[3]));
+                       memset(tvector3f, 0, model->surfmesh.num_vertices * sizeof(float[3]));
+               }
                // interpolate matrices and concatenate them to their parents
                for (i = 0;i < model->num_bones;i++)
                {
@@ -68,23 +81,67 @@ void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameb
                        else
                                for (k = 0;k < 12;k++)
                                        bonepose[i][k] = m[k];
+                       // create a relative deformation matrix to describe displacement
+                       // from the base mesh, which is used by the actual weighting
+                       R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative[i]);
                }
                // blend the vertex bone weights
-               memset(out3f, 0, model->surfmesh.num_vertices * sizeof(float[3]));
-               v = model->surfmesh.data_vertexboneweights;
-               for (i = 0;i < model->surfmesh.num_vertexboneweights;i++, v++)
+               if (svector3f)
+               {
+                       for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, n += 3, sv += 3, tv += 3, wi += 4, wf += 4, vertex3f += 3, normal3f += 3, svector3f += 3, tvector3f += 3)
+                       {
+                               for (k = 0;k < 4 && wf[k];k++)
+                               {
+                                       const float *m = boneposerelative[wi[k]];
+                                       float f = wf[k];
+                                       vertex3f[0] += f * (v[0] * m[0] + v[1] * m[1] + v[2] * m[ 2] + m[ 3]);
+                                       vertex3f[1] += f * (v[0] * m[4] + v[1] * m[5] + v[2] * m[ 6] + m[ 7]);
+                                       vertex3f[2] += f * (v[0] * m[8] + v[1] * m[9] + v[2] * m[10] + m[11]);
+                                       normal3f[0] += f * (n[0] * m[0] + n[1] * m[1] + n[2] * m[ 2]);
+                                       normal3f[1] += f * (n[0] * m[4] + n[1] * m[5] + n[2] * m[ 6]);
+                                       normal3f[2] += f * (n[0] * m[8] + n[1] * m[9] + n[2] * m[10]);
+                                       svector3f[0] += f * (sv[0] * m[0] + sv[1] * m[1] + sv[2] * m[ 2]);
+                                       svector3f[1] += f * (sv[0] * m[4] + sv[1] * m[5] + sv[2] * m[ 6]);
+                                       svector3f[2] += f * (sv[0] * m[8] + sv[1] * m[9] + sv[2] * m[10]);
+                                       tvector3f[0] += f * (tv[0] * m[0] + tv[1] * m[1] + tv[2] * m[ 2]);
+                                       tvector3f[1] += f * (tv[0] * m[4] + tv[1] * m[5] + tv[2] * m[ 6]);
+                                       tvector3f[2] += f * (tv[0] * m[8] + tv[1] * m[9] + tv[2] * m[10]);
+                               }
+                       }
+               }
+               else if (normal3f)
+               {
+                       for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, n += 3, wi += 4, wf += 4, vertex3f += 3, normal3f += 3)
+                       {
+                               for (k = 0;k < 4 && wf[k];k++)
+                               {
+                                       const float *m = boneposerelative[wi[k]];
+                                       float f = wf[k];
+                                       vertex3f[0] += f * (v[0] * m[0] + v[1] * m[1] + v[2] * m[ 2] + m[ 3]);
+                                       vertex3f[1] += f * (v[0] * m[4] + v[1] * m[5] + v[2] * m[ 6] + m[ 7]);
+                                       vertex3f[2] += f * (v[0] * m[8] + v[1] * m[9] + v[2] * m[10] + m[11]);
+                                       normal3f[0] += f * (n[0] * m[0] + n[1] * m[1] + n[2] * m[ 2]);
+                                       normal3f[1] += f * (n[0] * m[4] + n[1] * m[5] + n[2] * m[ 6]);
+                                       normal3f[2] += f * (n[0] * m[8] + n[1] * m[9] + n[2] * m[10]);
+                               }
+                       }
+               }
+               else
                {
-                       out = out3f + v->vertexindex * 3;
-                       matrix = bonepose[v->boneindex];
-                       // FIXME: this can very easily be optimized with SSE or 3DNow
-                       out[0] += v->origin[0] * matrix[0] + v->origin[1] * matrix[1] + v->origin[2] * matrix[ 2] + v->origin[3] * matrix[ 3];
-                       out[1] += v->origin[0] * matrix[4] + v->origin[1] * matrix[5] + v->origin[2] * matrix[ 6] + v->origin[3] * matrix[ 7];
-                       out[2] += v->origin[0] * matrix[8] + v->origin[1] * matrix[9] + v->origin[2] * matrix[10] + v->origin[3] * matrix[11];
+                       for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, wi += 4, wf += 4, vertex3f += 3)
+                       {
+                               for (k = 0;k < 4 && wf[k];k++)
+                               {
+                                       const float *m = boneposerelative[wi[k]];
+                                       float f = wf[k];
+                                       vertex3f[0] += f * (v[0] * m[0] + v[1] * m[1] + v[2] * m[ 2] + m[ 3]);
+                                       vertex3f[1] += f * (v[0] * m[4] + v[1] * m[5] + v[2] * m[ 6] + m[ 7]);
+                                       vertex3f[2] += f * (v[0] * m[8] + v[1] * m[9] + v[2] * m[10] + m[11]);
+                               }
+                       }
                }
        }
-       else if (!model->surfmesh.data_morphvertex3f)
-               Host_Error("model %s has no skeletal or vertex morph animation data", model->name);
-       else
+       else if (model->surfmesh.data_morphvertex3f)
        {
                // vertex morph
                int numverts = model->surfmesh.num_vertices;
@@ -105,19 +162,27 @@ void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameb
                                        const float *verts4 = vertsbase + numverts * 3 * frameblend[3].frame;
                                        float lerp4 = frameblend[3].lerp;
                                        for (i = 0;i < numverts * 3;i++)
-                                               out3f[i] = lerp1 * verts1[i] + lerp2 * verts2[i] + lerp3 * verts3[i] + lerp4 * verts4[i];
+                                               vertex3f[i] = lerp1 * verts1[i] + lerp2 * verts2[i] + lerp3 * verts3[i] + lerp4 * verts4[i];
                                }
                                else
                                        for (i = 0;i < numverts * 3;i++)
-                                               out3f[i] = lerp1 * verts1[i] + lerp2 * verts2[i] + lerp3 * verts3[i];
+                                               vertex3f[i] = lerp1 * verts1[i] + lerp2 * verts2[i] + lerp3 * verts3[i];
                        }
                        else
                                for (i = 0;i < numverts * 3;i++)
-                                       out3f[i] = lerp1 * verts1[i] + lerp2 * verts2[i];
+                                       vertex3f[i] = lerp1 * verts1[i] + lerp2 * verts2[i];
                }
                else
-                       memcpy(out3f, verts1, numverts * 3 * sizeof(float));
+                       memcpy(vertex3f, verts1, numverts * 3 * sizeof(float));
+               if (normal3f)
+               {
+                       Mod_BuildNormals(0, model->surfmesh.num_vertices, model->surfmesh.num_triangles, vertex3f, model->surfmesh.data_element3i, normal3f, r_smoothnormals_areaweighting.integer);
+                       if (svector3f)
+                               Mod_BuildTextureVectorsFromNormals(0, model->surfmesh.num_vertices, model->surfmesh.num_triangles, vertex3f, model->surfmesh.data_texcoordtexture2f, normal3f, model->surfmesh.data_element3i, svector3f, tvector3f, r_smoothnormals_areaweighting.integer);
+               }
        }
+       else
+               Host_Error("model %s has no skeletal or vertex morph animation data", model->name);
 }
 
 int Mod_Alias_GetTagMatrix(const model_t *model, int poseframe, int tagindex, matrix4x4_t *outmatrix)
@@ -185,6 +250,48 @@ int Mod_Alias_GetTagIndexForName(const model_t *model, unsigned int skin, const
        return 0;
 }
 
+static void Mod_BuildBaseBonePoses(void)
+{
+       int i, k;
+       double scale;
+       float *in12f = loadmodel->data_poses;
+       float *out12f = loadmodel->data_basebonepose;
+       float *outinv12f = loadmodel->data_baseboneposeinverse;
+       for (i = 0;i < loadmodel->num_bones;i++, in12f += 12, out12f += 12, outinv12f += 12)
+       {
+               if (loadmodel->data_bones[i].parent >= 0)
+                       R_ConcatTransforms(loadmodel->data_basebonepose + 12 * loadmodel->data_bones[i].parent, in12f, out12f);
+               else
+                       for (k = 0;k < 12;k++)
+                               out12f[k] = in12f[k];
+
+               // invert The Matrix
+
+               // we only support uniform scaling, so assume the first row is enough
+               // (note the lack of sqrt here, because we're trying to undo the scaling,
+               // this means multiplying by the inverse scale twice - squaring it, which
+               // makes the sqrt a waste of time)
+               scale = 1.0 / (out12f[ 0] * out12f[ 0] + out12f[ 1] * out12f[ 1] + out12f[ 2] * out12f[ 2]);
+
+               // invert the rotation by transposing and multiplying by the squared
+               // recipricol of the input matrix scale as described above
+               outinv12f[ 0] = (float)(out12f[ 0] * scale);
+               outinv12f[ 1] = (float)(out12f[ 4] * scale);
+               outinv12f[ 2] = (float)(out12f[ 8] * scale);
+               outinv12f[ 4] = (float)(out12f[ 1] * scale);
+               outinv12f[ 5] = (float)(out12f[ 5] * scale);
+               outinv12f[ 6] = (float)(out12f[ 9] * scale);
+               outinv12f[ 8] = (float)(out12f[ 2] * scale);
+               outinv12f[ 9] = (float)(out12f[ 6] * scale);
+               outinv12f[10] = (float)(out12f[10] * scale);
+
+               // invert the translate
+               outinv12f[ 3] = -(out12f[ 3] * outinv12f[ 0] + out12f[ 7] * outinv12f[ 1] + out12f[11] * outinv12f[ 2]);
+               outinv12f[ 7] = -(out12f[ 3] * outinv12f[ 4] + out12f[ 7] * outinv12f[ 5] + out12f[11] * outinv12f[ 6]);
+               outinv12f[11] = -(out12f[ 3] * outinv12f[ 8] + out12f[ 7] * outinv12f[ 9] + out12f[11] * outinv12f[10]);
+       }
+}
+
 static void Mod_Alias_Mesh_CompileFrameZero(void)
 {
        frameblend_t frameblend[4] = {{0, 1}, {0, 0}, {0, 0}, {0, 0}};
@@ -192,7 +299,7 @@ static void Mod_Alias_Mesh_CompileFrameZero(void)
        loadmodel->surfmesh.data_svector3f = loadmodel->surfmesh.data_vertex3f + loadmodel->surfmesh.num_vertices * 3;
        loadmodel->surfmesh.data_tvector3f = loadmodel->surfmesh.data_vertex3f + loadmodel->surfmesh.num_vertices * 6;
        loadmodel->surfmesh.data_normal3f = loadmodel->surfmesh.data_vertex3f + loadmodel->surfmesh.num_vertices * 9;
-       Mod_Alias_GetMesh_Vertex3f(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f);
+       Mod_Alias_GetMesh_Vertices(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL);
        Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
        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, true);
 }
@@ -230,7 +337,7 @@ static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, co
                segmentmaxs[2] = max(start[2], end[2]) + 1;
                for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
                {
-                       Mod_Alias_GetMesh_Vertex3f(model, frameblend, vertex3f);
+                       Mod_Alias_GetMesh_Vertices(model, frameblend, vertex3f, NULL, NULL, NULL);
                        Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
                }
        }
@@ -260,7 +367,7 @@ static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, co
                                maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
                                vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
                        }
-                       Mod_Alias_GetMesh_Vertex3f(model, frameblend, vertex3f);
+                       Mod_Alias_GetMesh_Vertices(model, frameblend, vertex3f, NULL, NULL, NULL);
                        Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
                }
        }
@@ -1165,8 +1272,8 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 {
        zymtype1header_t *pinmodel, *pheader;
        unsigned char *pbase;
-       int i, j, k, l, numposes, meshvertices, meshtriangles, numvertexboneweights, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements;
-       float modelradius, corner[2], *poses, *intexcoord2f, *outtexcoord2f;
+       int i, j, k, numposes, meshvertices, meshtriangles, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements;
+       float modelradius, corner[2], *poses, *intexcoord2f, *outtexcoord2f, *bonepose;
        zymvertex_t *verts, *vertdata;
        zymscene_t *scene;
        zymbone_t *bone;
@@ -1317,30 +1424,35 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        for (i = 0;i < pheader->numverts;i++)
        {
                vertbonecounts[i] = BigLong(bonecount[i]);
-               if (vertbonecounts[i] < 1)
-                       Host_Error("%s bonecount[%i] < 1", loadmodel->name, i);
+               if (vertbonecounts[i] != 1)
+                       Host_Error("%s bonecount[%i] != 1 (vertex weight support is impossible in this format)", loadmodel->name, i);
        }
 
        loadmodel->num_poses = pheader->lump_poses.length / sizeof(float[3][4]);
 
        meshvertices = pheader->numverts;
        meshtriangles = pheader->numtris;
-       numvertexboneweights = pheader->lump_verts.length / sizeof(zymvertex_t);
 
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces;
-       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]));
+       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[14]) + meshvertices * sizeof(int[4]) + meshvertices * sizeof(float[4]) + loadmodel->num_poses * sizeof(float[36]));
        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_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[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->surfmesh.data_vertexweightindex4i = (int *)data;data += meshvertices * sizeof(int[4]);
+       loadmodel->surfmesh.data_vertexweightinfluence4f = (float *)data;data += meshvertices * sizeof(float[4]);
+       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
+       loadmodel->data_basebonepose = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
+       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
 
        //zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
        poses = (float *) (pheader->lump_poses.start + pbase);
@@ -1350,21 +1462,40 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        //zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
        verts = (zymvertex_t *)Mem_Alloc(loadmodel->mempool, pheader->lump_verts.length);
        vertdata = (zymvertex_t *) (pheader->lump_verts.start + pbase);
-       l = 0;
-       for (j = 0;j < pheader->numverts;j++)
+       // reconstruct frame 0 matrices to allow reconstruction of the base mesh
+       // (converting from weight-blending skeletal animation to
+       //  deformation-based skeletal animation)
+       bonepose = Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
+       for (i = 0;i < loadmodel->num_bones;i++)
        {
-               for (k = 0;k < vertbonecounts[j];k++, l++)
-               {
-                       // this format really should have had a per vertexweight weight value...
-                       float influence = 1.0f / vertbonecounts[j];
-                       loadmodel->surfmesh.data_vertexboneweights[l].vertexindex = j;
-                       loadmodel->surfmesh.data_vertexboneweights[l].boneindex = BigLong(vertdata[l].bonenum);
-                       loadmodel->surfmesh.data_vertexboneweights[l].origin[0] = BigFloat(vertdata[l].origin[0]) * influence;
-                       loadmodel->surfmesh.data_vertexboneweights[l].origin[1] = BigFloat(vertdata[l].origin[1]) * influence;
-                       loadmodel->surfmesh.data_vertexboneweights[l].origin[2] = BigFloat(vertdata[l].origin[2]) * influence;
-                       loadmodel->surfmesh.data_vertexboneweights[l].origin[3] = influence;
-               }
+               const float *m = loadmodel->data_poses + i * 12;
+               if (loadmodel->data_bones[i].parent >= 0)
+                       R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
+               else
+                       for (k = 0;k < 12;k++)
+                               bonepose[12*i+k] = m[k];
        }
+       for (j = 0;j < pheader->numverts;j++)
+       {
+               // this format really should have had a per vertexweight weight value...
+               // but since it does not, the weighting is completely ignored and
+               // only one weight is allowed per vertex
+               int boneindex = BigLong(vertdata[j].bonenum);
+               const float *m = bonepose + 12 * boneindex;
+               float relativeorigin[3];
+               relativeorigin[0] = BigFloat(vertdata[j].origin[0]);
+               relativeorigin[1] = BigFloat(vertdata[j].origin[1]);
+               relativeorigin[2] = BigFloat(vertdata[j].origin[2]);
+               // transform the vertex bone weight into the base mesh
+               loadmodel->surfmesh.data_vertex3f[j*3+0] = relativeorigin[0] * m[0] + relativeorigin[1] * m[1] + relativeorigin[2] * m[ 2] + m[ 3];
+               loadmodel->surfmesh.data_vertex3f[j*3+1] = relativeorigin[0] * m[4] + relativeorigin[1] * m[5] + relativeorigin[2] * m[ 6] + m[ 7];
+               loadmodel->surfmesh.data_vertex3f[j*3+2] = relativeorigin[0] * m[8] + relativeorigin[1] * m[9] + relativeorigin[2] * m[10] + m[11];
+               // store the weight as the primary weight on this vertex
+               loadmodel->surfmesh.data_vertexweightindex4i[j*4+0] = boneindex;
+               loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+0] = 1;
+       }
+       Z_Free(bonepose);
+       // normals and tangents are calculated after elements are loaded
 
        //zymlump_t lump_texcoords; // float texcoords[numvertices][2];
        outtexcoord2f = loadmodel->surfmesh.data_texcoordtexture2f;
@@ -1434,14 +1565,17 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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);
+
+       // compute all the mesh information that was not loaded from the file
+       Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
+       Mod_BuildBaseBonePoses();
+       Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
+       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, true);
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
 }
 
 void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
@@ -1451,9 +1585,10 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        dpmbone_t *bone;
        dpmmesh_t *dpmmesh;
        unsigned char *pbase;
-       int i, j, k, meshvertices, meshtriangles, numvertexboneweights;
+       int i, j, k, meshvertices, meshtriangles;
        skinfile_t *skinfiles;
        unsigned char *data;
+       float *bonepose;
 
        pheader = (dpmheader_t *)buffer;
        pbase = (unsigned char *)buffer;
@@ -1522,7 +1657,6 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
        meshvertices = 0;
        meshtriangles = 0;
-       numvertexboneweights = 0;
 
        // gather combined statistics from the meshes
        dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
@@ -1531,15 +1665,6 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                int numverts = BigLong(dpmmesh->num_verts);
                meshvertices += numverts;;
                meshtriangles += BigLong(dpmmesh->num_tris);
-
-               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++;
        }
 
@@ -1548,18 +1673,24 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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));
+       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[14]) + sizeof(int[4]) + sizeof(float[4])) + loadmodel->num_poses * sizeof(float[36]) + 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_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[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->surfmesh.data_vertexweightindex4i = (int *)data;data += meshvertices * sizeof(int[4]);
+       loadmodel->surfmesh.data_vertexweightinfluence4f = (float *)data;data += meshvertices * sizeof(float[4]);
+       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
+       loadmodel->data_basebonepose = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
+       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
        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);
@@ -1605,7 +1736,19 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
        meshvertices = 0;
        meshtriangles = 0;
-       numvertexboneweights = 0;
+       // reconstruct frame 0 matrices to allow reconstruction of the base mesh
+       // (converting from weight-blending skeletal animation to
+       //  deformation-based skeletal animation)
+       bonepose = Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
+       for (i = 0;i < loadmodel->num_bones;i++)
+       {
+               const float *m = loadmodel->data_poses + i * 12;
+               if (loadmodel->data_bones[i].parent >= 0)
+                       R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
+               else
+                       for (k = 0;k < 12;k++)
+                               bonepose[12*i+k] = m[k];
+       }
        for (i = 0;i < loadmodel->num_surfaces;i++, dpmmesh++)
        {
                const int *inelements;
@@ -1640,23 +1783,71 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        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++)
+               for (j = surface->num_firstvertex;j < surface->num_firstvertex + surface->num_vertices;j++)
                {
+                       float sum;
+                       int l;
                        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++;
+                               int boneindex = BigLong(vert->bonenum);
+                               const float *m = bonepose + 12 * boneindex;
+                               float influence = BigFloat(vert->influence);
+                               float relativeorigin[3], relativenormal[3];
+                               relativeorigin[0] = BigFloat(vert->origin[0]);
+                               relativeorigin[1] = BigFloat(vert->origin[1]);
+                               relativeorigin[2] = BigFloat(vert->origin[2]);
+                               relativenormal[0] = BigFloat(vert->normal[0]);
+                               relativenormal[1] = BigFloat(vert->normal[1]);
+                               relativenormal[2] = BigFloat(vert->normal[2]);
+                               // blend the vertex bone weights into the base mesh
+                               loadmodel->surfmesh.data_vertex3f[j*3+0] += relativeorigin[0] * m[0] + relativeorigin[1] * m[1] + relativeorigin[2] * m[ 2] + influence * m[ 3];
+                               loadmodel->surfmesh.data_vertex3f[j*3+1] += relativeorigin[0] * m[4] + relativeorigin[1] * m[5] + relativeorigin[2] * m[ 6] + influence * m[ 7];
+                               loadmodel->surfmesh.data_vertex3f[j*3+2] += relativeorigin[0] * m[8] + relativeorigin[1] * m[9] + relativeorigin[2] * m[10] + influence * m[11];
+                               loadmodel->surfmesh.data_normal3f[j*3+0] += relativenormal[0] * m[0] + relativenormal[1] * m[1] + relativenormal[2] * m[ 2];
+                               loadmodel->surfmesh.data_normal3f[j*3+1] += relativenormal[0] * m[4] + relativenormal[1] * m[5] + relativenormal[2] * m[ 6];
+                               loadmodel->surfmesh.data_normal3f[j*3+2] += relativenormal[0] * m[8] + relativenormal[1] * m[9] + relativenormal[2] * m[10];
+                               if (!k)
+                               {
+                                       // store the first (and often only) weight
+                                       loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+0] = influence;
+                                       loadmodel->surfmesh.data_vertexweightindex4i[j*4+0] = boneindex;
+                               }
+                               else
+                               {
+                                       // sort the new weight into this vertex's weight table
+                                       // (which only accepts up to 4 bones per vertex)
+                                       for (l = 0;l < 4;l++)
+                                       {
+                                               if (loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l] < influence)
+                                               {
+                                                       // move weaker influence weights out of the way first
+                                                       int l2;
+                                                       for (l2 = 3;l2 > l;l2--)
+                                                       {
+                                                               loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l2] = loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l2-1];
+                                                               loadmodel->surfmesh.data_vertexweightindex4i[j*4+l2] = loadmodel->surfmesh.data_vertexweightindex4i[j*4+l2-1];
+                                                       }
+                                                       // store the new weight
+                                                       loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l] = influence;
+                                                       loadmodel->surfmesh.data_vertexweightindex4i[j*4+l] = boneindex;
+                                                       break;
+                                               }
+                                       }
+                               }
                                data += sizeof(dpmbonevert_t);
                        }
+                       sum = 0;
+                       for (l = 0;l < 4;l++)
+                               sum += loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l];
+                       if (sum && fabs(sum - 1) > (1.0f / 256.0f))
+                       {
+                               float f = 1.0f / sum;
+                               for (l = 0;l < 4;l++)
+                                       loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l] *= f;
+                       }
                }
 
                // since dpm models do not have named sections, reuse their shader name as the section name
@@ -1668,9 +1859,13 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
                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();
+       Z_Free(bonepose);
        Mod_FreeSkinFiles(skinfiles);
+
+       // compute all the mesh information that was not loaded from the file
+       Mod_BuildBaseBonePoses();
+       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, true);
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
 }
 
 static void Mod_PSKMODEL_AnimKeyToMatrix(float *origin, float *quat, matrix4x4_t *m)
@@ -1686,7 +1881,7 @@ static void Mod_PSKMODEL_AnimKeyToMatrix(float *origin, float *quat, matrix4x4_t
 #define PSKQUATNEGATIONS
 void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 {
-       int i, j, index, version, recordsize, numrecords, meshvertices, meshtriangles, numvertexboneweights;
+       int i, j, index, version, recordsize, numrecords, meshvertices, meshtriangles;
        int numpnts, numvtxw, numfaces, nummatts, numbones, numrawweights, numanimbones, numanims, numanimkeys;
        fs_offset_t filesize;
        pskpnts_t *pnts;
@@ -2029,9 +2224,13 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                }
                                else
                                {
-                                       p->quat[0] *=  1;
-                                       p->quat[1] *= -1;
-                                       p->quat[2] *=  1;
+                                       //p->quat[0] *=  1;
+                                       //p->quat[1] *= -1;
+                                       //p->quat[2] *=  1;
+                                       // clear root bone to defaults to recenter all frames of an animation
+                                       // (root bone is often tilted, or worse);
+                                       VectorSet(p->origin, 0, 0, 0);
+                                       Vector4Set(p->quat, 0, 0, 0.707106781187, 0.707106781187);
                                }
 #endif
                        }
@@ -2068,11 +2267,6 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
        meshvertices = numvtxw;
        meshtriangles = numfaces;
-       numvertexboneweights = 0;
-       for (index = 0;index < numvtxw;index++)
-               for (j = 0;j < numrawweights;j++)
-                       if (rawweights[j].pntsindex == vtxw[index].pntsindex)
-                               numvertexboneweights++;
 
        // load external .skin files if present
        skinfiles = Mod_LoadSkinFiles();
@@ -2082,18 +2276,24 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->num_poses = loadmodel->num_bones * loadmodel->numframes;
        loadmodel->num_textures = loadmodel->nummodelsurfaces = loadmodel->num_surfaces = nummatts;
        // 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));
+       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[14]) + sizeof(int[4]) + sizeof(float[4])) + loadmodel->num_poses * sizeof(float[36]) + 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_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[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->surfmesh.data_vertexweightindex4i = (int *)data;data += meshvertices * sizeof(int[4]);
+       loadmodel->surfmesh.data_vertexweightinfluence4f = (float *)data;data += meshvertices * sizeof(float[4]);
+       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
+       loadmodel->data_basebonepose = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
+       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
        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);
@@ -2121,9 +2321,12 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->data_surfaces[index].num_vertices = loadmodel->surfmesh.num_vertices;
        }
 
-       // copy over the texcoords
+       // copy over the vertex locations and texcoords
        for (index = 0;index < numvtxw;index++)
        {
+               loadmodel->surfmesh.data_vertex3f[index*3+0] = pnts[vtxw[index].pntsindex].origin[0];
+               loadmodel->surfmesh.data_vertex3f[index*3+1] = pnts[vtxw[index].pntsindex].origin[1];
+               loadmodel->surfmesh.data_vertex3f[index*3+2] = pnts[vtxw[index].pntsindex].origin[2];
                loadmodel->surfmesh.data_texcoordtexture2f[index*2+0] = vtxw[index].texcoord[0];
                loadmodel->surfmesh.data_texcoordtexture2f[index*2+1] = vtxw[index].texcoord[1];
        }
@@ -2154,32 +2357,46 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        Host_Error("%s bone[%i].parent >= %i", loadmodel->name, index, index);
        }
 
-       // build bone-relative vertex weights from the psk point weights
-       numvertexboneweights = 0;
+       // sort the psk point weights into the vertex weight tables
+       // (which only accept up to 4 bones per vertex)
        for (index = 0;index < numvtxw;index++)
        {
+               int l;
+               float sum;
                for (j = 0;j < numrawweights;j++)
                {
                        if (rawweights[j].pntsindex == vtxw[index].pntsindex)
                        {
-                               matrix4x4_t matrix, inversematrix;
-                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].vertexindex = index;
-                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].boneindex = rawweights[j].boneindex;
-                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].weight = rawweights[j].weight;
-                               matrix = identitymatrix;
-                               for (i = rawweights[j].boneindex;i >= 0;i = loadmodel->data_bones[i].parent)
+                               int boneindex = rawweights[j].boneindex;
+                               float influence = rawweights[j].weight;
+                               for (l = 0;l < 4;l++)
                                {
-                                       matrix4x4_t childmatrix, tempmatrix;
-                                       Mod_PSKMODEL_AnimKeyToMatrix(bones[i].basepose.origin, bones[i].basepose.quat, &tempmatrix);
-                                       childmatrix = matrix;
-                                       Matrix4x4_Concat(&matrix, &tempmatrix, &childmatrix);
+                                       if (loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l] < influence)
+                                       {
+                                               // move lower influence weights out of the way first
+                                               int l2;
+                                               for (l2 = 3;l2 > l;l2--)
+                                               {
+                                                       loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l2] = loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l2-1];
+                                                       loadmodel->surfmesh.data_vertexweightindex4i[index*4+l2] = loadmodel->surfmesh.data_vertexweightindex4i[index*4+l2-1];
+                                               }
+                                               // store the new weight
+                                               loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l] = influence;
+                                               loadmodel->surfmesh.data_vertexweightindex4i[index*4+l] = boneindex;
+                                               break;
+                                       }
                                }
-                               Matrix4x4_Invert_Simple(&inversematrix, &matrix);
-                               Matrix4x4_Transform(&inversematrix, pnts[rawweights[j].pntsindex].origin, loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin);
-                               VectorScale(loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin, loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].weight, loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin);
-                               numvertexboneweights++;
                        }
                }
+               sum = 0;
+               for (l = 0;l < 4;l++)
+                       sum += loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l];
+               if (sum && fabs(sum - 1) > (1.0f / 256.0f))
+               {
+                       float f = 1.0f / sum;
+                       for (l = 0;l < 4;l++)
+                               loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l] *= f;
+               }
        }
 
        // set up the animscenes based on the anims
@@ -2213,13 +2430,15 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->data_poses[index*12+10] = matrix.m[2][2];
                loadmodel->data_poses[index*12+11] = matrix.m[2][3];
        }
+       Mod_FreeSkinFiles(skinfiles);
+       Mem_Free(animfilebuffer);
 
-       // compile extra data we want
+       // compute all the mesh information that was not loaded from the file
+       // TODO: honor smoothing groups somehow?
        Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
+       Mod_BuildBaseBonePoses();
+       Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
+       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, true);
        Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
-       Mod_Alias_Mesh_CompileFrameZero();
-       Mod_FreeSkinFiles(skinfiles);
-
-       Mem_Free(animfilebuffer);
 }
 
index 7f1aaa2..f7b2445 100644 (file)
@@ -72,15 +72,6 @@ typedef struct overridetagnameset_s
 }
 overridetagnameset_t;
 
-typedef struct surfmeshvertexboneweight_s
-{
-       unsigned int vertexindex;
-       unsigned int boneindex;
-       float origin[3];
-       float weight;
-}
-surfmeshvertexboneweight_t;
-
 // used for mesh lists in q1bsp/q3bsp map models
 // (the surfaces reference portions of these meshes)
 typedef struct surfmesh_s
@@ -100,9 +91,9 @@ typedef struct surfmesh_s
        // morph blending, these are zero if model is skeletal or static
        int num_morphframes;
        float *data_morphvertex3f;
-       // skeletal blending, these are zero if model is morph or static
-       int num_vertexboneweights;
-       surfmeshvertexboneweight_t *data_vertexboneweights;
+       // skeletal blending, these are NULL if model is morph or static
+       int *data_vertexweightindex4i;
+       float *data_vertexweightinfluence4f;
 }
 surfmesh_t;
 
@@ -551,6 +542,8 @@ typedef struct model_s
        aliasbone_t             *data_bones;
        int                             num_poses;
        float                   *data_poses;
+       float                   *data_basebonepose;
+       float                   *data_baseboneposeinverse;
        // textures of this model
        int                             num_textures;
        texture_t               *data_textures;
@@ -676,7 +669,7 @@ void R_Q1BSP_DrawLight(struct entity_render_s *ent, int numsurfaces, const int *
 // alias models
 struct frameblend_s;
 void Mod_AliasInit(void);
-void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const struct frameblend_s *frameblend, float *out3f);
+void Mod_Alias_GetMesh_Vertices(const model_t *model, const struct frameblend_s *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f);
 int Mod_Alias_GetTagMatrix(const model_t *model, int poseframe, int tagindex, matrix4x4_t *outmatrix);
 int Mod_Alias_GetTagIndexForName(const model_t *model, unsigned int skin, const char *tagname);
 
index 74feb38..e1a736e 100644 (file)
@@ -2246,7 +2246,7 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfa
 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
 {
        // set up properties for rendering light onto this entity
-       RSurf_ActiveEntity(ent);
+       RSurf_ActiveEntity(ent, true, true);
        Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_rtlight->matrix_worldtolight, &ent->matrix);
        Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
        Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
index 40d40ae..0a092a0 100644 (file)
--- a/render.h
+++ b/render.h
@@ -297,7 +297,7 @@ extern texture_t *rsurface_texture;
 extern rtexture_t *rsurface_lightmaptexture;
 extern rsurfmode_t rsurface_mode;
 
-void RSurf_ActiveEntity(const entity_render_t *ent);
+void RSurf_ActiveEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents);
 void RSurf_CleanUp(void);
 
 void R_Mesh_ResizeArrays(int newvertices);
diff --git a/todo b/todo
index c0ddf7c..3340f4e 100644 (file)
--- a/todo
+++ b/todo
@@ -15,6 +15,7 @@
 -d (QuakeMatt) change darkplaces menu: remove gl_combine from menu as it's not saved to config and really shouldn't be changed except when debugging drivers (QuakeMatt)
 -d (Speedy) feature darkplaces init: add -demolooponly option which makes escape key quit, and disables all other keys (Speedy)
 -d (Spike) change darkplaces prvm: disable the unknown opcode error in progs loading so that fteqcc fastarrays progs will load (Spike)
+-d (Spirit) bug darkplaces console: first character is missing on quake brown-text lines, but not consistently, resolved: stripping off the chat prefix character on prints was stripping other characters sometimes due to signed comparison (Spirit)
 -d (Supa, Wazat, Spike) feature darkplaces server: make a DP_SV_CUSTOMIZEENTITYFORCLIENT extension which calls a .float customizeentityforclient() function for each client that may see the entity, the function returns TRUE if it should send, FALSE if it should not, and is fully capable of editing the entity's fields, this allows cloaked players to appear less transparent to their teammates, navigation markers to only show to their team, etc (Urre, Supa, Wazat, SavageX, Vermeulen, Spike)
 -d (Toddd) bug darkplaces client: fix gl_flashblend, it's still drawing rtdlights even when gl_flashblend is on (Toddd)
 -d (Wazat) bug darkplaces renderer: make sure that unlit maps show up fullbright (Wazat)
 2 optimization darkplaces renderer: move skybox/skysphere render to after sky polygons, so that they can do a depth comparison to draw only on the sky polygons, using DepthRange(1,1) and DepthFunc(GL_GREATER), note though that portal renders will need half depth range (Mercury)
 3 bug darkplaces collision: add edge bevels in collision code, by trying all crossproduct combinations of an edge with planes from the other brush, and choosing only the ones which are valid
 3 bug darkplaces compatibility: quakerally does not work, figure out why (Spike)
-3 bug darkplaces compatibility: targetquake does not work, figure out why
 3 bug darkplaces menu: find a way to prevent display of savegames from id1 in mods (WodahsEht)
 3 bug darkplaces renderer: add stainmaps to realtime lighting mode
 3 bug darkplaces renderer: crash when using rtworld mode and reloading a map that has been modified and now has less surfaces, this means the map name is the same, but the surface indices/pointers are no longer valid
@@ -486,10 +486,13 @@ d bug darkplaces client: make "wait" command wait fornext network frame somehow
 d bug darkplaces client: make envmap command work with the corrected layout
 d bug darkplaces client: make server queries use a queue to avoid flooding out queries too fast (Willis)
 d bug darkplaces client: missing bolt/beam models should not produce warnings
+d bug darkplaces client: quakeworld servers often stuffcmd the cvars topcolor, bottomcolor, pants, team, skin, noaim, so commands for these need to be added (topcolor/bottomcolor will modify _cl_color, the others can be real cvars)
 d bug darkplaces client: te_customflash isn't working? (Wazat)
+d bug darkplaces client: userinfo strings are not being updated by name/color commands
 d bug darkplaces collision: check Urre's sltest.bsp and slopestuck.dem and fix the sticking bug, which only happens with sv_newflymove 1 (Urre)
 d bug darkplaces collision: frikbots are falling through the map (Sajt)
 d bug darkplaces commands: say command is not posting to server console (Vermeulen)
+d bug darkplaces compatibility: targetquake does not work, figure out why
 d bug darkplaces console: $variable expansion is not working on forwarded commands like "say I'm $_cl_name", it does work on local commands like set (esteel, Black)
 d bug darkplaces console: alias test "echo 1";test;echo 2 should print 1 then 2, not 2 then 1 or an error (div0, FrikaC)
 d bug darkplaces console: chat messages are showing up in brown quake characters and having ^7 and such printed literally
@@ -585,6 +588,7 @@ d bug darkplaces sound: spatialization bug occurs in The Ascension of Vigil, mak
 d bug dpmod: air control doesn't seem to be working (Kedhrin)
 d bug dpmod: fix the 'shell casing spawning at wrong player' bug somehow
 d bug dpmod: items aren't respawning in coop, they should
+d bug dpmod: nailgun mine launching doesn't trigger a player animation (sng one does)
 d bug dpmod: shouldn't get double kill for killing something and its corpse (Sajt)
 d bug dpmodel: scale parameter isn't affecting animations (Ghostface)
 d bug hmap2: make sure seconds reports in all tools don't print secondssss when they're printing shorter and shorter updates (FrikaC)