+ n[0] = Mod_FindTriangleWithEdge(elements, numtriangles, e[1], e[0], i);
+ n[1] = Mod_FindTriangleWithEdge(elements, numtriangles, e[2], e[1], i);
+ n[2] = Mod_FindTriangleWithEdge(elements, numtriangles, e[0], e[2], i);
+ }
+}
+#endif
+
+void Mod_ValidateElements(const int *elements, int numtriangles, int numverts, const char *filename, int fileline)
+{
+ int i;
+ for (i = 0;i < numtriangles * 3;i++)
+ if ((unsigned int)elements[i] >= (unsigned int)numverts)
+ Con_Printf("Mod_ValidateElements: out of bounds element detected at %s:%d\n", filename, fileline);
+}
+
+// warning: this is an expensive function!
+void Mod_BuildNormals(int numverts, int numtriangles, const float *vertex3f, const int *elements, float *normal3f)
+{
+ int i, tnum;
+ float normal[3], *v;
+ const int *e;
+ // clear the vectors
+ memset(normal3f, 0, numverts * sizeof(float[3]));
+ // process each vertex of each triangle and accumulate the results
+ for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
+ {
+ TriangleNormal(vertex3f + e[0] * 3, vertex3f + e[1] * 3, vertex3f + e[2] * 3, normal);
+ VectorNormalize(normal);
+ v = normal3f + e[0] * 3;
+ v[0] += normal[0];
+ v[1] += normal[1];
+ v[2] += normal[2];
+ v = normal3f + e[1] * 3;
+ v[0] += normal[0];
+ v[1] += normal[1];
+ v[2] += normal[2];
+ v = normal3f + e[2] * 3;
+ v[0] += normal[0];
+ v[1] += normal[1];
+ v[2] += normal[2];
+ }
+ // now we could divide the vectors by the number of averaged values on
+ // each vertex... but instead normalize them
+ for (i = 0, v = normal3f;i < numverts;i++, v += 3)
+ VectorNormalize(v);
+}
+
+void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *svector3f, float *tvector3f, float *normal3f)
+{
+ float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
+ // 103 add/sub/negate/multiply (1 cycle), 3 divide (20 cycle), 3 sqrt (22 cycle), 4 compare (3 cycle?), total cycles not counting load/store/exchange roughly 241 cycles
+ // 12 add, 28 subtract, 57 multiply, 3 divide, 3 sqrt, 4 compare, 50% chance of 6 negates
+
+ // 6 multiply, 9 subtract
+ VectorSubtract(v1, v0, v10);
+ VectorSubtract(v2, v0, v20);
+ normal3f[0] = v10[1] * v20[2] - v10[2] * v20[1];
+ normal3f[1] = v10[2] * v20[0] - v10[0] * v20[2];
+ normal3f[2] = v10[0] * v20[1] - v10[1] * v20[0];
+ // 1 sqrt, 1 divide, 6 multiply, 2 add, 1 compare
+ VectorNormalize(normal3f);
+ // 12 multiply, 10 subtract
+ tc10[1] = tc1[1] - tc0[1];
+ tc20[1] = tc2[1] - tc0[1];
+ svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
+ svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
+ svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
+ tc10[0] = tc1[0] - tc0[0];
+ tc20[0] = tc2[0] - tc0[0];
+ tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
+ tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
+ tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
+ // 12 multiply, 4 add, 6 subtract
+ f = DotProduct(svector3f, normal3f);
+ svector3f[0] -= f * normal3f[0];
+ svector3f[1] -= f * normal3f[1];
+ svector3f[2] -= f * normal3f[2];
+ f = DotProduct(tvector3f, normal3f);
+ tvector3f[0] -= f * normal3f[0];
+ tvector3f[1] -= f * normal3f[1];
+ tvector3f[2] -= f * normal3f[2];
+ // 2 sqrt, 2 divide, 12 multiply, 4 add, 2 compare
+ VectorNormalize(svector3f);
+ VectorNormalize(tvector3f);
+ // if texture is mapped the wrong way (counterclockwise), the tangents
+ // have to be flipped, this is detected by calculating a normal from the
+ // two tangents, and seeing if it is opposite the surface normal
+ // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
+ CrossProduct(tvector3f, svector3f, tangentcross);
+ if (DotProduct(tangentcross, normal3f) < 0)
+ {
+ VectorNegate(svector3f, svector3f);
+ VectorNegate(tvector3f, tvector3f);
+ }
+}
+
+// warning: this is a very expensive function!
+void Mod_BuildTextureVectorsAndNormals(int numverts, int numtriangles, const float *vertex3f, const float *texcoord2f, const int *elements, float *svector3f, float *tvector3f, float *normal3f)
+{
+ int i, tnum;
+ float sdir[3], tdir[3], normal[3], *v;
+ const int *e;
+ // clear the vectors
+ if (svector3f)
+ memset(svector3f, 0, numverts * sizeof(float[3]));
+ if (tvector3f)
+ memset(tvector3f, 0, numverts * sizeof(float[3]));
+ if (normal3f)
+ memset(normal3f, 0, numverts * sizeof(float[3]));
+ // process each vertex of each triangle and accumulate the results
+ for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
+ {
+ Mod_BuildBumpVectors(vertex3f + e[0] * 3, vertex3f + e[1] * 3, vertex3f + e[2] * 3, texcoord2f + e[0] * 2, texcoord2f + e[1] * 2, texcoord2f + e[2] * 2, sdir, tdir, normal);
+ if (svector3f)
+ {
+ for (i = 0;i < 3;i++)
+ {
+ svector3f[e[i]*3 ] += sdir[0];
+ svector3f[e[i]*3+1] += sdir[1];
+ svector3f[e[i]*3+2] += sdir[2];
+ }
+ }
+ if (tvector3f)
+ {
+ for (i = 0;i < 3;i++)
+ {
+ tvector3f[e[i]*3 ] += tdir[0];
+ tvector3f[e[i]*3+1] += tdir[1];
+ tvector3f[e[i]*3+2] += tdir[2];
+ }
+ }
+ if (normal3f)
+ {
+ for (i = 0;i < 3;i++)
+ {
+ normal3f[e[i]*3 ] += normal[0];
+ normal3f[e[i]*3+1] += normal[1];
+ normal3f[e[i]*3+2] += normal[2];
+ }
+ }
+ }
+ // now we could divide the vectors by the number of averaged values on
+ // each vertex... but instead normalize them
+ // 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
+ if (svector3f)
+ for (i = 0, v = svector3f;i < numverts;i++, v += 3)
+ VectorNormalize(v);
+ // 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
+ if (tvector3f)
+ for (i = 0, v = tvector3f;i < numverts;i++, v += 3)
+ VectorNormalize(v);
+ // 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
+ if (normal3f)
+ for (i = 0, v = normal3f;i < numverts;i++, v += 3)
+ VectorNormalize(v);
+}
+
+surfmesh_t *Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean detailtexcoords, qboolean lightmapoffsets, qboolean vertexcolors, qboolean neighbors)
+{
+ surfmesh_t *mesh;
+ qbyte *data;
+ mesh = Mem_Alloc(mempool, sizeof(surfmesh_t) + numvertices * (3 + 3 + 3 + 3 + 2 + 2 + (detailtexcoords ? 2 : 0) + (vertexcolors ? 4 : 0)) * sizeof(float) + numvertices * (lightmapoffsets ? 1 : 0) * sizeof(int) + numtriangles * (3 + (neighbors ? 3 : 0)) * sizeof(int));
+ mesh->num_vertices = numvertices;
+ mesh->num_triangles = numtriangles;
+ data = (qbyte *)(mesh + 1);
+ if (mesh->num_vertices)
+ {
+ mesh->data_vertex3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+ mesh->data_svector3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+ mesh->data_tvector3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+ mesh->data_normal3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+ mesh->data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * mesh->num_vertices;
+ mesh->data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * mesh->num_vertices;
+ if (detailtexcoords)
+ mesh->data_texcoorddetail2f = (float *)data, data += sizeof(float[2]) * mesh->num_vertices;
+ if (vertexcolors)
+ mesh->data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * mesh->num_vertices;
+ if (lightmapoffsets)
+ mesh->data_lightmapoffsets = (int *)data, data += sizeof(int) * mesh->num_vertices;
+ }
+ if (mesh->num_triangles)
+ {
+ mesh->data_element3i = (int *)data, data += sizeof(int[3]) * mesh->num_triangles;
+ if (neighbors)
+ mesh->data_neighbor3i = (int *)data, data += sizeof(int[3]) * mesh->num_triangles;
+ }
+ return mesh;
+}
+
+shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable)
+{
+ shadowmesh_t *newmesh;
+ qbyte *data;
+ int size;
+ size = sizeof(shadowmesh_t);
+ size += maxverts * sizeof(float[3]);
+ if (light)
+ size += maxverts * sizeof(float[11]);
+ size += maxtriangles * sizeof(int[3]);
+ if (neighbors)
+ size += maxtriangles * sizeof(int[3]);
+ if (expandable)
+ size += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *) + maxverts * sizeof(shadowmeshvertexhash_t);
+ data = Mem_Alloc(mempool, size);
+ newmesh = (void *)data;data += sizeof(*newmesh);
+ newmesh->map_diffuse = map_diffuse;
+ newmesh->map_specular = map_specular;
+ newmesh->map_normal = map_normal;
+ newmesh->maxverts = maxverts;
+ newmesh->maxtriangles = maxtriangles;
+ newmesh->numverts = 0;
+ newmesh->numtriangles = 0;
+
+ newmesh->vertex3f = (void *)data;data += maxverts * sizeof(float[3]);
+ if (light)
+ {
+ newmesh->svector3f = (void *)data;data += maxverts * sizeof(float[3]);
+ newmesh->tvector3f = (void *)data;data += maxverts * sizeof(float[3]);
+ newmesh->normal3f = (void *)data;data += maxverts * sizeof(float[3]);
+ newmesh->texcoord2f = (void *)data;data += maxverts * sizeof(float[2]);
+ }
+ newmesh->element3i = (void *)data;data += maxtriangles * sizeof(int[3]);
+ if (neighbors)
+ {
+ newmesh->neighbor3i = (void *)data;data += maxtriangles * sizeof(int[3]);
+ }
+ if (expandable)
+ {
+ newmesh->vertexhashtable = (void *)data;data += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *);
+ newmesh->vertexhashentries = (void *)data;data += maxverts * sizeof(shadowmeshvertexhash_t);
+ }
+ return newmesh;
+}
+
+shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh, int light, int neighbors)
+{
+ shadowmesh_t *newmesh;
+ newmesh = Mod_ShadowMesh_Alloc(mempool, oldmesh->numverts, oldmesh->numtriangles, oldmesh->map_diffuse, oldmesh->map_specular, oldmesh->map_normal, light, neighbors, false);
+ newmesh->numverts = oldmesh->numverts;
+ newmesh->numtriangles = oldmesh->numtriangles;
+
+ memcpy(newmesh->vertex3f, oldmesh->vertex3f, oldmesh->numverts * sizeof(float[3]));
+ if (newmesh->svector3f && oldmesh->svector3f)
+ {
+ memcpy(newmesh->svector3f, oldmesh->svector3f, oldmesh->numverts * sizeof(float[3]));
+ memcpy(newmesh->tvector3f, oldmesh->tvector3f, oldmesh->numverts * sizeof(float[3]));
+ memcpy(newmesh->normal3f, oldmesh->normal3f, oldmesh->numverts * sizeof(float[3]));
+ memcpy(newmesh->texcoord2f, oldmesh->texcoord2f, oldmesh->numverts * sizeof(float[2]));
+ }
+ memcpy(newmesh->element3i, oldmesh->element3i, oldmesh->numtriangles * sizeof(int[3]));
+ if (newmesh->neighbor3i && oldmesh->neighbor3i)
+ memcpy(newmesh->neighbor3i, oldmesh->neighbor3i, oldmesh->numtriangles * sizeof(int[3]));
+ return newmesh;
+}
+
+int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, float *vertex14f)
+{
+ int hashindex, vnum;
+ shadowmeshvertexhash_t *hash;
+ // this uses prime numbers intentionally
+ hashindex = (unsigned int) (vertex14f[0] * 3 + vertex14f[1] * 5 + vertex14f[2] * 7) % SHADOWMESHVERTEXHASH;
+ for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
+ {
+ vnum = (hash - mesh->vertexhashentries);
+ if ((mesh->vertex3f == NULL || (mesh->vertex3f[vnum * 3 + 0] == vertex14f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex14f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex14f[2]))
+ && (mesh->svector3f == NULL || (mesh->svector3f[vnum * 3 + 0] == vertex14f[3] && mesh->svector3f[vnum * 3 + 1] == vertex14f[4] && mesh->svector3f[vnum * 3 + 2] == vertex14f[5]))
+ && (mesh->tvector3f == NULL || (mesh->tvector3f[vnum * 3 + 0] == vertex14f[6] && mesh->tvector3f[vnum * 3 + 1] == vertex14f[7] && mesh->tvector3f[vnum * 3 + 2] == vertex14f[8]))
+ && (mesh->normal3f == NULL || (mesh->normal3f[vnum * 3 + 0] == vertex14f[9] && mesh->normal3f[vnum * 3 + 1] == vertex14f[10] && mesh->normal3f[vnum * 3 + 2] == vertex14f[11]))
+ && (mesh->texcoord2f == NULL || (mesh->texcoord2f[vnum * 2 + 0] == vertex14f[12] && mesh->texcoord2f[vnum * 2 + 1] == vertex14f[13])))
+ return hash - mesh->vertexhashentries;
+ }
+ vnum = mesh->numverts++;
+ hash = mesh->vertexhashentries + vnum;
+ hash->next = mesh->vertexhashtable[hashindex];
+ mesh->vertexhashtable[hashindex] = hash;
+ if (mesh->vertex3f) {mesh->vertex3f[vnum * 3 + 0] = vertex14f[0];mesh->vertex3f[vnum * 3 + 1] = vertex14f[1];mesh->vertex3f[vnum * 3 + 2] = vertex14f[2];}
+ if (mesh->svector3f) {mesh->svector3f[vnum * 3 + 0] = vertex14f[3];mesh->svector3f[vnum * 3 + 1] = vertex14f[4];mesh->svector3f[vnum * 3 + 2] = vertex14f[5];}
+ if (mesh->tvector3f) {mesh->tvector3f[vnum * 3 + 0] = vertex14f[6];mesh->tvector3f[vnum * 3 + 1] = vertex14f[7];mesh->tvector3f[vnum * 3 + 2] = vertex14f[8];}
+ if (mesh->normal3f) {mesh->normal3f[vnum * 3 + 0] = vertex14f[9];mesh->normal3f[vnum * 3 + 1] = vertex14f[10];mesh->normal3f[vnum * 3 + 2] = vertex14f[11];}
+ if (mesh->texcoord2f) {mesh->texcoord2f[vnum * 2 + 0] = vertex14f[12];mesh->texcoord2f[vnum * 2 + 1] = vertex14f[13];}
+ return vnum;
+}
+
+void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, float *vertex14f)
+{
+ if (mesh->numtriangles == 0)
+ {
+ // set the properties on this empty mesh to be more favorable...
+ // (note: this case only occurs for the first triangle added to a new mesh chain)
+ mesh->map_diffuse = map_diffuse;
+ mesh->map_specular = map_specular;
+ mesh->map_normal = map_normal;
+ }
+ while (mesh->map_diffuse != map_diffuse || mesh->map_specular != map_specular || mesh->map_normal != map_normal || mesh->numverts + 3 > mesh->maxverts || mesh->numtriangles + 1 > mesh->maxtriangles)
+ {
+ if (mesh->next == NULL)
+ mesh->next = Mod_ShadowMesh_Alloc(mempool, max(mesh->maxverts, 300), max(mesh->maxtriangles, 100), map_diffuse, map_specular, map_normal, mesh->svector3f != NULL, mesh->neighbor3i != NULL, true);
+ mesh = mesh->next;
+ }
+ mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 0);
+ mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 1);
+ mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 2);
+ mesh->numtriangles++;
+}
+
+void Mod_ShadowMesh_AddMesh(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, int numtris, const int *element3i)
+{
+ int i, j, e;
+ float vbuf[3*14], *v;
+ memset(vbuf, 0, sizeof(vbuf));
+ for (i = 0;i < numtris;i++)
+ {
+ for (j = 0, v = vbuf;j < 3;j++, v += 14)
+ {
+ e = *element3i++;
+ if (vertex3f)
+ {
+ v[0] = vertex3f[e * 3 + 0];
+ v[1] = vertex3f[e * 3 + 1];
+ v[2] = vertex3f[e * 3 + 2];
+ }
+ if (svector3f)
+ {
+ v[3] = svector3f[e * 3 + 0];
+ v[4] = svector3f[e * 3 + 1];
+ v[5] = svector3f[e * 3 + 2];
+ }
+ if (tvector3f)
+ {
+ v[6] = tvector3f[e * 3 + 0];
+ v[7] = tvector3f[e * 3 + 1];
+ v[8] = tvector3f[e * 3 + 2];
+ }
+ if (normal3f)
+ {
+ v[9] = normal3f[e * 3 + 0];
+ v[10] = normal3f[e * 3 + 1];
+ v[11] = normal3f[e * 3 + 2];
+ }
+ if (texcoord2f)
+ {
+ v[12] = texcoord2f[e * 2 + 0];
+ v[13] = texcoord2f[e * 2 + 1];
+ }
+ }
+ Mod_ShadowMesh_AddTriangle(mempool, mesh, map_diffuse, map_specular, map_normal, vbuf);
+ }
+}
+
+shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable)
+{
+ return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles, map_diffuse, map_specular, map_normal, light, neighbors, expandable);
+}
+
+shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, int light, int neighbors)
+{
+ shadowmesh_t *mesh, *newmesh, *nextmesh;
+ // reallocate meshs to conserve space
+ for (mesh = firstmesh, firstmesh = NULL;mesh;mesh = nextmesh)
+ {
+ nextmesh = mesh->next;
+ if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
+ {
+ newmesh = Mod_ShadowMesh_ReAlloc(mempool, mesh, light, neighbors);
+ newmesh->next = firstmesh;
+ firstmesh = newmesh;
+ }
+ Mem_Free(mesh);
+ }
+ return firstmesh;
+}
+
+void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
+{
+ int i;
+ shadowmesh_t *mesh;
+ vec3_t nmins, nmaxs, ncenter, temp;
+ float nradius2, dist2, *v;
+ // calculate bbox
+ for (mesh = firstmesh;mesh;mesh = mesh->next)
+ {
+ if (mesh == firstmesh)
+ {
+ VectorCopy(mesh->vertex3f, nmins);
+ VectorCopy(mesh->vertex3f, nmaxs);
+ }
+ for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
+ {
+ if (nmins[0] > v[0]) nmins[0] = v[0];if (nmaxs[0] < v[0]) nmaxs[0] = v[0];
+ if (nmins[1] > v[1]) nmins[1] = v[1];if (nmaxs[1] < v[1]) nmaxs[1] = v[1];
+ if (nmins[2] > v[2]) nmins[2] = v[2];if (nmaxs[2] < v[2]) nmaxs[2] = v[2];
+ }
+ }
+ // calculate center and radius
+ ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
+ ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
+ ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
+ nradius2 = 0;
+ for (mesh = firstmesh;mesh;mesh = mesh->next)
+ {
+ for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
+ {
+ VectorSubtract(v, ncenter, temp);
+ dist2 = DotProduct(temp, temp);
+ if (nradius2 < dist2)
+ nradius2 = dist2;
+ }
+ }
+ // return data
+ if (mins)
+ VectorCopy(nmins, mins);
+ if (maxs)
+ VectorCopy(nmaxs, maxs);
+ if (center)
+ VectorCopy(ncenter, center);
+ if (radius)
+ *radius = sqrt(nradius2);
+}
+
+void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
+{
+ shadowmesh_t *nextmesh;
+ for (;mesh;mesh = nextmesh)
+ {
+ nextmesh = mesh->next;
+ Mem_Free(mesh);