+int Mod_Alias_GetTagMatrix(const model_t *model, int poseframe, int tagindex, matrix4x4_t *outmatrix)
+{
+ const float *boneframe;
+ float tempbonematrix[12], bonematrix[12];
+ *outmatrix = identitymatrix;
+ if (model->num_bones)
+ {
+ if (tagindex < 0 || tagindex >= model->num_bones)
+ return 4;
+ if (poseframe >= model->num_poses)
+ return 6;
+ boneframe = model->data_poses + poseframe * model->num_bones * 12;
+ memcpy(bonematrix, boneframe + tagindex * 12, sizeof(float[12]));
+ while (model->data_bones[tagindex].parent >= 0)
+ {
+ memcpy(tempbonematrix, bonematrix, sizeof(float[12]));
+ R_ConcatTransforms(boneframe + model->data_bones[tagindex].parent * 12, tempbonematrix, bonematrix);
+ tagindex = model->data_bones[tagindex].parent;
+ }
+ outmatrix->m[0][0] = bonematrix[0];
+ outmatrix->m[0][1] = bonematrix[1];
+ outmatrix->m[0][2] = bonematrix[2];
+ outmatrix->m[0][3] = bonematrix[3];
+ outmatrix->m[1][0] = bonematrix[4];
+ outmatrix->m[1][1] = bonematrix[5];
+ outmatrix->m[1][2] = bonematrix[6];
+ outmatrix->m[1][3] = bonematrix[7];
+ outmatrix->m[2][0] = bonematrix[8];
+ outmatrix->m[2][1] = bonematrix[9];
+ outmatrix->m[2][2] = bonematrix[10];
+ outmatrix->m[2][3] = bonematrix[11];
+ outmatrix->m[3][0] = 0;
+ outmatrix->m[3][1] = 0;
+ outmatrix->m[3][2] = 0;
+ outmatrix->m[3][3] = 1;
+ }
+ else if (model->num_tags)
+ {
+ if (tagindex < 0 || tagindex >= model->num_tags)
+ return 4;
+ if (poseframe >= model->num_tagframes)
+ return 6;
+ *outmatrix = model->data_tags[poseframe * model->num_tags + tagindex].matrix;
+ }
+ return 0;
+}
+
+int Mod_Alias_GetTagIndexForName(const model_t *model, unsigned int skin, const char *tagname)
+{
+ int i;
+ if (model->data_overridetagnamesforskin && skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)skin].num_overridetagnames)
+ for (i = 0;i < model->data_overridetagnamesforskin[skin].num_overridetagnames;i++)
+ if (!strcasecmp(tagname, model->data_overridetagnamesforskin[skin].data_overridetagnames[i].name))
+ return i + 1;
+ if (model->num_bones)
+ for (i = 0;i < model->num_bones;i++)
+ if (!strcasecmp(tagname, model->data_bones[i].name))
+ return i + 1;
+ if (model->num_tags)
+ for (i = 0;i < model->num_tags;i++)
+ if (!strcasecmp(tagname, model->data_tags[i].name))
+ return i + 1;
+ return 0;
+}
+
+static void Mod_Alias_Mesh_CompileFrameZero(void)
+{
+ frameblend_t frameblend[4] = {{0, 1}, {0, 0}, {0, 0}, {0, 0}};
+ loadmodel->surfmesh.data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3][4]));
+ 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_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);
+}
+
+static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+{
+ int i;
+ float segmentmins[3], segmentmaxs[3];
+ frameblend_t frameblend[4];
+ msurface_t *surface;
+ static int maxvertices = 0;
+ static float *vertex3f = NULL;
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = 1;
+ trace->realfraction = 1;
+ trace->hitsupercontentsmask = hitsupercontentsmask;
+ memset(frameblend, 0, sizeof(frameblend));
+ frameblend[0].frame = frame;
+ frameblend[0].lerp = 1;
+ if (maxvertices < model->surfmesh.num_vertices)
+ {
+ if (vertex3f)
+ Z_Free(vertex3f);
+ maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
+ vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
+ }
+ if (VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
+ {
+ // line trace
+ segmentmins[0] = min(start[0], end[0]) - 1;
+ segmentmins[1] = min(start[1], end[1]) - 1;
+ segmentmins[2] = min(start[2], end[2]) - 1;
+ segmentmaxs[0] = max(start[0], end[0]) + 1;
+ segmentmaxs[1] = max(start[1], end[1]) + 1;
+ segmentmaxs[2] = max(start[2], end[2]) + 1;
+ for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+ {
+ Mod_Alias_GetMesh_Vertex3f(model, frameblend, vertex3f);
+ Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
+ }
+ }
+ else
+ {
+ // box trace, performed as brush trace
+ colbrushf_t *thisbrush_start, *thisbrush_end;
+ vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
+ segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
+ segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
+ segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
+ segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
+ segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
+ segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
+ VectorAdd(start, boxmins, boxstartmins);
+ VectorAdd(start, boxmaxs, boxstartmaxs);
+ VectorAdd(end, boxmins, boxendmins);
+ VectorAdd(end, boxmaxs, boxendmaxs);
+ thisbrush_start = Collision_BrushForBox(&identitymatrix, boxstartmins, boxstartmaxs, 0, 0, NULL);
+ thisbrush_end = Collision_BrushForBox(&identitymatrix, boxendmins, boxendmaxs, 0, 0, NULL);
+ for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+ {
+ if (maxvertices < model->surfmesh.num_vertices)
+ {
+ if (vertex3f)
+ Z_Free(vertex3f);
+ maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
+ vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
+ }
+ Mod_Alias_GetMesh_Vertex3f(model, frameblend, vertex3f);
+ Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
+ }
+ }
+}
+
+static void Mod_CalcAliasModelBBoxes (void)
+{
+ int vnum;
+ float dist, yawradius, radius;
+ float *v;
+ VectorClear(loadmodel->normalmins);
+ VectorClear(loadmodel->normalmaxs);
+ yawradius = 0;
+ radius = 0;
+ for (vnum = 0, v = loadmodel->surfmesh.data_morphvertex3f;vnum < loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes;vnum++, v += 3)
+ {
+ if (loadmodel->normalmins[0] > v[0]) loadmodel->normalmins[0] = v[0];
+ if (loadmodel->normalmins[1] > v[1]) loadmodel->normalmins[1] = v[1];
+ if (loadmodel->normalmins[2] > v[2]) loadmodel->normalmins[2] = v[2];
+ if (loadmodel->normalmaxs[0] < v[0]) loadmodel->normalmaxs[0] = v[0];
+ if (loadmodel->normalmaxs[1] < v[1]) loadmodel->normalmaxs[1] = v[1];
+ if (loadmodel->normalmaxs[2] < v[2]) loadmodel->normalmaxs[2] = v[2];
+ dist = v[0] * v[0] + v[1] * v[1];
+ if (yawradius < dist)
+ yawradius = dist;
+ dist += v[2] * v[2];
+ if (radius < dist)
+ radius = dist;
+ }
+ radius = sqrt(radius);
+ yawradius = sqrt(yawradius);
+ loadmodel->yawmins[0] = loadmodel->yawmins[1] = -yawradius;
+ loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = yawradius;
+ loadmodel->yawmins[2] = loadmodel->normalmins[2];
+ loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2];
+ loadmodel->rotatedmins[0] = loadmodel->rotatedmins[1] = loadmodel->rotatedmins[2] = -radius;
+ loadmodel->rotatedmaxs[0] = loadmodel->rotatedmaxs[1] = loadmodel->rotatedmaxs[2] = radius;
+ loadmodel->radius = radius;
+ loadmodel->radius2 = radius * radius;
+}