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++)
{
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;
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)
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}};
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);
}
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);
}
}
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);
}
}
{
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;
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);
//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;
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)
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;
meshvertices = 0;
meshtriangles = 0;
- numvertexboneweights = 0;
// gather combined statistics from the meshes
dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
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++;
}
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);
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;
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
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)
#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;
}
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
}
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();
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);
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];
}
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
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);
}