+shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts)
+{
+ shadowmesh_t *mesh;
+ mesh = Mem_Alloc(mempool, sizeof(shadowmesh_t) + maxverts * sizeof(float[3]) + maxverts * sizeof(int[3]) + maxverts * sizeof(int[3]) + SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *) + maxverts * sizeof(shadowmeshvertexhash_t));
+ mesh->maxverts = maxverts;
+ mesh->maxtriangles = maxverts;
+ mesh->numverts = 0;
+ mesh->numtriangles = 0;
+ mesh->vertex3f = (float *)(mesh + 1);
+ mesh->element3i = (int *)(mesh->vertex3f + mesh->maxverts * 3);
+ mesh->neighbor3i = (int *)(mesh->element3i + mesh->maxtriangles * 3);
+ mesh->vertexhashtable = (shadowmeshvertexhash_t **)(mesh->neighbor3i + mesh->maxtriangles * 3);
+ mesh->vertexhashentries = (shadowmeshvertexhash_t *)(mesh->vertexhashtable + SHADOWMESHVERTEXHASH);
+ return mesh;
+}
+
+shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh)
+{
+ shadowmesh_t *newmesh;
+ newmesh = Mem_Alloc(mempool, sizeof(shadowmesh_t) + oldmesh->numverts * sizeof(float[3]) + oldmesh->numtriangles * sizeof(int[3]) + oldmesh->numtriangles * sizeof(int[3]));
+ newmesh->maxverts = newmesh->numverts = oldmesh->numverts;
+ newmesh->maxtriangles = newmesh->numtriangles = oldmesh->numtriangles;
+ newmesh->vertex3f = (float *)(newmesh + 1);
+ newmesh->element3i = (int *)(newmesh->vertex3f + newmesh->maxverts * 3);
+ newmesh->neighbor3i = (int *)(newmesh->element3i + newmesh->maxtriangles * 3);
+ memcpy(newmesh->vertex3f, oldmesh->vertex3f, newmesh->numverts * sizeof(float[3]));
+ memcpy(newmesh->element3i, oldmesh->element3i, newmesh->numtriangles * sizeof(int[3]));
+ memcpy(newmesh->neighbor3i, oldmesh->neighbor3i, newmesh->numtriangles * sizeof(int[3]));
+ return newmesh;
+}
+
+int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, float *v)
+{
+ int hashindex;
+ float *m;
+ shadowmeshvertexhash_t *hash;
+ // this uses prime numbers intentionally
+ hashindex = (int) (v[0] * 3 + v[1] * 5 + v[2] * 7) % SHADOWMESHVERTEXHASH;
+ for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
+ {
+ m = mesh->vertex3f + (hash - mesh->vertexhashentries) * 3;
+ if (m[0] == v[0] && m[1] == v[1] && m[2] == v[2])
+ return hash - mesh->vertexhashentries;
+ }
+ hash = mesh->vertexhashentries + mesh->numverts;
+ hash->next = mesh->vertexhashtable[hashindex];
+ mesh->vertexhashtable[hashindex] = hash;
+ m = mesh->vertex3f + (hash - mesh->vertexhashentries) * 3;
+ VectorCopy(v, m);
+ mesh->numverts++;
+ return mesh->numverts - 1;
+}
+
+void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, float *vert0, float *vert1, float *vert2)
+{
+ while (mesh->numverts + 3 > mesh->maxverts || mesh->numtriangles + 1 > mesh->maxtriangles)
+ {
+ if (mesh->next == NULL)
+ mesh->next = Mod_ShadowMesh_Alloc(mempool, max(mesh->maxtriangles, 1));
+ mesh = mesh->next;
+ }
+ mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vert0);
+ mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vert1);
+ mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vert2);
+ mesh->numtriangles++;
+}
+
+void Mod_ShadowMesh_AddPolygon(mempool_t *mempool, shadowmesh_t *mesh, int numverts, float *verts)
+{
+ int i;
+ float *v;
+ for (i = 0, v = verts + 3;i < numverts - 2;i++, v += 3)
+ Mod_ShadowMesh_AddTriangle(mempool, mesh, verts, v, v + 3);
+ /*
+ int i, i1, i2, i3;
+ float *v;
+ while (mesh->numverts + numverts > mesh->maxverts || mesh->numtriangles + (numverts - 2) > mesh->maxtriangles)
+ {
+ if (mesh->next == NULL)
+ mesh->next = Mod_ShadowMesh_Alloc(mempool, max(mesh->maxtriangles, numverts));
+ mesh = mesh->next;
+ }
+ i1 = Mod_ShadowMesh_AddVertex(mesh, verts);
+ i2 = 0;
+ i3 = Mod_ShadowMesh_AddVertex(mesh, verts + 3);
+ for (i = 0, v = verts + 6;i < numverts - 2;i++, v += 3)
+ {
+ i2 = i3;
+ i3 = Mod_ShadowMesh_AddVertex(mesh, v);
+ mesh->elements[mesh->numtriangles * 3 + 0] = i1;
+ mesh->elements[mesh->numtriangles * 3 + 1] = i2;
+ mesh->elements[mesh->numtriangles * 3 + 2] = i3;
+ mesh->numtriangles++;
+ }
+ */
+}
+
+void Mod_ShadowMesh_AddMesh(mempool_t *mempool, shadowmesh_t *mesh, int numverts, float *verts, int numtris, int *elements)
+{
+ int i;
+ for (i = 0;i < numtris;i++, elements += 3)
+ Mod_ShadowMesh_AddTriangle(mempool, mesh, verts + elements[0] * 3, verts + elements[1] * 3, verts + elements[2] * 3);
+}
+
+shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int initialnumtriangles)
+{
+ return Mod_ShadowMesh_Alloc(mempool, initialnumtriangles);
+}
+
+shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh)
+{
+#if 1
+ //int i;
+ 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);
+ newmesh->next = firstmesh;
+ firstmesh = newmesh;
+ //Con_Printf("mesh\n");
+ //for (i = 0;i < newmesh->numtriangles;i++)
+ // Con_Printf("tri %d %d %d\n", newmesh->elements[i * 3 + 0], newmesh->elements[i * 3 + 1], newmesh->elements[i * 3 + 2]);
+ Mod_ValidateElements(newmesh->element3i, newmesh->numtriangles, newmesh->numverts, __FILE__, __LINE__);
+ Mod_BuildTriangleNeighbors(newmesh->neighbor3i, newmesh->element3i, newmesh->numtriangles);
+ }
+ Mem_Free(mesh);
+ }
+#else
+ shadowmesh_t *mesh;
+ for (mesh = firstmesh;mesh;mesh = mesh->next)
+ {
+ Mod_ValidateElements(mesh->elements, mesh->numtriangles, mesh->numverts, __FILE__, __LINE__);
+ Mod_BuildTriangleNeighbors(mesh->neighbors, mesh->elements, mesh->numtriangles);
+ }
+#endif
+ 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);
+ }
+}
+
+static rtexture_t *GL_TextureForSkinLayer(const qbyte *in, int width, int height, const char *name, const unsigned int *palette, int textureflags)
+{
+ int i;
+ for (i = 0;i < width*height;i++)
+ if (((qbyte *)&palette[in[i]])[3] > 0)
+ return R_LoadTexture2D (loadmodel->texturepool, name, width, height, in, TEXTYPE_PALETTE, textureflags, palette);
+ return NULL;
+}
+
+static int detailtexturecycle = 0;
+int Mod_LoadSkinFrame (skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int usedetailtexture, int loadglowtexture)
+{
+ imageskin_t s;
+ memset(skinframe, 0, sizeof(*skinframe));
+ if (!image_loadskin(&s, basename))
+ return false;
+ if (usedetailtexture)
+ skinframe->detail = mod_shared_detailtextures[(detailtexturecycle++) % NUM_DETAILTEXTURES];
+ skinframe->base = R_LoadTexture2D (loadmodel->texturepool, basename, s.basepixels_width, s.basepixels_height, s.basepixels, TEXTYPE_RGBA, textureflags, NULL);
+ if (s.nmappixels != NULL)
+ skinframe->nmap = R_LoadTexture2D (loadmodel->texturepool, va("%s_nmap", basename), s.basepixels_width, s.basepixels_height, s.nmappixels, TEXTYPE_RGBA, textureflags, NULL);
+ if (s.glosspixels != NULL)
+ skinframe->gloss = R_LoadTexture2D (loadmodel->texturepool, va("%s_gloss", basename), s.glosspixels_width, s.glosspixels_height, s.glosspixels, TEXTYPE_RGBA, textureflags, NULL);
+ if (s.glowpixels != NULL && loadglowtexture)
+ skinframe->glow = R_LoadTexture2D (loadmodel->texturepool, va("%s_glow", basename), s.glowpixels_width, s.glowpixels_height, s.glowpixels, TEXTYPE_RGBA, textureflags, NULL);
+ if (s.maskpixels != NULL)
+ skinframe->fog = R_LoadTexture2D (loadmodel->texturepool, va("%s_mask", basename), s.maskpixels_width, s.maskpixels_height, s.maskpixels, TEXTYPE_RGBA, textureflags, NULL);
+ if (loadpantsandshirt)
+ {
+ if (s.pantspixels != NULL)
+ skinframe->pants = R_LoadTexture2D (loadmodel->texturepool, va("%s_pants", basename), s.pantspixels_width, s.pantspixels_height, s.pantspixels, TEXTYPE_RGBA, textureflags, NULL);
+ if (s.shirtpixels != NULL)
+ skinframe->shirt = R_LoadTexture2D (loadmodel->texturepool, va("%s_shirt", basename), s.shirtpixels_width, s.shirtpixels_height, s.shirtpixels, TEXTYPE_RGBA, textureflags, NULL);
+ }
+ image_freeskin(&s);
+ return true;
+}
+
+int Mod_LoadSkinFrame_Internal (skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int usedetailtexture, int loadglowtexture, qbyte *skindata, int width, int height)
+{
+ qbyte *temp1, *temp2;
+ memset(skinframe, 0, sizeof(*skinframe));
+ if (!skindata)
+ return false;
+ if (usedetailtexture)
+ skinframe->detail = mod_shared_detailtextures[(detailtexturecycle++) % NUM_DETAILTEXTURES];
+ if (r_shadow_bumpscale_basetexture.value > 0)
+ {
+ temp1 = Mem_Alloc(loadmodel->mempool, width * height * 8);
+ temp2 = temp1 + width * height * 4;
+ Image_Copy8bitRGBA(skindata, temp1, width * height, palette_nofullbrights);
+ Image_HeightmapToNormalmap(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
+ skinframe->nmap = R_LoadTexture2D(loadmodel->texturepool, va("%s_nmap", basename), width, height, temp2, TEXTYPE_RGBA, textureflags, NULL);
+ Mem_Free(temp1);
+ }
+ if (loadglowtexture)
+ {
+ skinframe->glow = GL_TextureForSkinLayer(skindata, width, height, va("%s_glow", basename), palette_onlyfullbrights, textureflags); // glow
+ skinframe->base = skinframe->merged = GL_TextureForSkinLayer(skindata, width, height, va("%s_merged", basename), palette_nofullbrights, textureflags); // all but fullbrights
+ if (loadpantsandshirt)
+ {
+ skinframe->pants = GL_TextureForSkinLayer(skindata, width, height, va("%s_pants", basename), palette_pantsaswhite, textureflags); // pants
+ skinframe->shirt = GL_TextureForSkinLayer(skindata, width, height, va("%s_shirt", basename), palette_shirtaswhite, textureflags); // shirt
+ if (skinframe->pants || skinframe->shirt)
+ skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename), palette_nocolormapnofullbrights, textureflags); // no special colors
+ }
+ }
+ else
+ {
+ skinframe->base = skinframe->merged = GL_TextureForSkinLayer(skindata, width, height, va("%s_merged", basename), palette_complete, textureflags); // all but fullbrights
+ if (loadpantsandshirt)
+ {
+ skinframe->pants = GL_TextureForSkinLayer(skindata, width, height, va("%s_pants", basename), palette_pantsaswhite, textureflags); // pants
+ skinframe->shirt = GL_TextureForSkinLayer(skindata, width, height, va("%s_shirt", basename), palette_shirtaswhite, textureflags); // shirt
+ if (skinframe->pants || skinframe->shirt)
+ skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename), palette_nocolormap, textureflags); // no pants or shirt
+ }
+ }
+ return true;
+}