+void Mod_GenerateWarpMesh (msurface_t *surf)
+{
+ int i, j;
+ surfvertex_t *v;
+ surfmesh_t *mesh;
+
+ subdivpolytriangles = 0;
+ subdivpolyverts = 0;
+ SubdividePolygon (surf->poly_numverts, surf->poly_verts);
+ if (subdivpolytriangles < 1)
+ Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
+
+ surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
+ mesh->numverts = subdivpolyverts;
+ mesh->numtriangles = subdivpolytriangles;
+ mesh->vertex = (surfvertex_t *)(mesh + 1);
+ mesh->index = (int *)(mesh->vertex + mesh->numverts);
+ memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
+
+ for (i = 0;i < mesh->numtriangles;i++)
+ for (j = 0;j < 3;j++)
+ mesh->index[i*3+j] = subdivpolyindex[i][j];
+
+ for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
+ {
+ VectorCopy(subdivpolyvert[i], v->v);
+ v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
+ v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
+ }
+}
+#endif
+
+void Mod_GenerateVertexLitMesh (msurface_t *surf)
+{
+ int i, is, it, *index, smax, tmax;
+ float *in, s, t;
+ surfvertex_t *out;
+ surfmesh_t *mesh;
+
+ smax = surf->extents[0] >> 4;
+ tmax = surf->extents[1] >> 4;
+ surf->lightmaptexturestride = 0;
+ surf->lightmaptexture = NULL;
+
+ surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
+ mesh->numverts = surf->poly_numverts;
+ mesh->numtriangles = surf->poly_numverts - 2;
+ mesh->vertex = (surfvertex_t *)(mesh + 1);
+ mesh->index = (int *)(mesh->vertex + mesh->numverts);
+ memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
+
+ index = mesh->index;
+ for (i = 0;i < mesh->numtriangles;i++)
+ {
+ *index++ = 0;
+ *index++ = i + 1;
+ *index++ = i + 2;
+ }
+
+ for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
+ {
+ VectorCopy (in, out->v);
+
+ s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
+ t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
+
+ out->st[0] = s / surf->texinfo->texture->width;
+ out->st[1] = t / surf->texinfo->texture->height;
+
+ s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
+ t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
+
+ // lightmap coordinates
+ out->uv[0] = 0;
+ out->uv[1] = 0;
+
+ // LordHavoc: calc lightmap data offset for vertex lighting to use
+ is = (int) s;
+ it = (int) t;
+ is = bound(0, is, smax);
+ it = bound(0, it, tmax);
+ out->lightmapoffset = ((it * (smax+1) + is) * 3);
+ }
+}
+
+void Mod_GenerateLightmappedMesh (msurface_t *surf)
+{
+ int i, is, it, *index, smax, tmax;
+ float *in, s, t, xbase, ybase, xscale, yscale;
+ surfvertex_t *out;
+ surfmesh_t *mesh;
+
+ surf->flags |= SURF_LIGHTMAP;
+ smax = surf->extents[0] >> 4;
+ tmax = surf->extents[1] >> 4;
+ if (r_miplightmaps.integer)
+ {
+ surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
+ surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE);
+ }
+ else
+ {
+ surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
+ surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE);
+ }
+ R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
+ xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
+ yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
+
+ surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
+ mesh->numverts = surf->poly_numverts;
+ mesh->numtriangles = surf->poly_numverts - 2;
+ mesh->vertex = (surfvertex_t *)(mesh + 1);
+ mesh->index = (int *)(mesh->vertex + mesh->numverts);
+ memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
+
+ index = mesh->index;
+ for (i = 0;i < mesh->numtriangles;i++)
+ {
+ *index++ = 0;
+ *index++ = i + 1;
+ *index++ = i + 2;
+ }
+
+ for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
+ {
+ VectorCopy (in, out->v);
+
+ s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
+ t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
+
+ out->st[0] = s / surf->texinfo->texture->width;
+ out->st[1] = t / surf->texinfo->texture->height;
+
+ s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
+ t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
+
+ // lightmap coordinates
+ out->uv[0] = s * xscale + xbase;
+ out->uv[1] = t * yscale + ybase;
+
+ // LordHavoc: calc lightmap data offset for vertex lighting to use
+ is = (int) s;
+ it = (int) t;
+ is = bound(0, is, smax);
+ it = bound(0, it, tmax);
+ out->lightmapoffset = ((it * (smax+1) + is) * 3);
+ }
+}
+
+void Mod_GenerateVertexMesh (msurface_t *surf)
+{
+ int i, *index;
+ float *in;
+ surfvertex_t *out;
+ surfmesh_t *mesh;
+
+ surf->lightmaptexturestride = 0;
+ surf->lightmaptexture = NULL;
+
+ surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
+ mesh->numverts = surf->poly_numverts;
+ mesh->numtriangles = surf->poly_numverts - 2;
+ mesh->vertex = (surfvertex_t *)(mesh + 1);
+ mesh->index = (int *)(mesh->vertex + mesh->numverts);
+ memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
+
+ index = mesh->index;
+ for (i = 0;i < mesh->numtriangles;i++)
+ {
+ *index++ = 0;
+ *index++ = i + 1;
+ *index++ = i + 2;
+ }
+
+ for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
+ {
+ VectorCopy (in, out->v);
+ out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
+ out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
+ }
+}
+
+void Mod_GenerateSurfacePolygon (msurface_t *surf)
+{
+ float *vert;
+ int i;
+ int lindex;
+ float *vec;
+
+ // convert edges back to a normal polygon
+ surf->poly_numverts = surf->numedges;
+ vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
+ for (i = 0;i < surf->numedges;i++)
+ {
+ lindex = loadmodel->surfedges[surf->firstedge + i];
+ if (lindex > 0)
+ vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
+ else
+ vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
+ VectorCopy (vec, vert);
+ vert += 3;
+ }
+}
+
+static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
+{
+ int j, base, tricount, newvertexcount, *index, *vertexremap;
+ surfmesh_t *newmesh, *oldmesh, *firstmesh;
+ if (s->mesh->numtriangles > 1000)
+ {
+ vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
+ base = 0;
+ oldmesh = NULL;
+ firstmesh = NULL;
+ newmesh = NULL;
+ while (base < s->mesh->numtriangles)
+ {
+ tricount = s->mesh->numtriangles - base;
+ if (tricount > 1000)
+ tricount = 1000;
+ index = s->mesh->index + base * 3;
+ base += tricount;
+
+ newvertexcount = 0;
+ memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
+ for (j = 0;j < tricount * 3;j++)
+ if (vertexremap[index[j]] < 0)
+ vertexremap[index[j]] = newvertexcount++;
+
+ newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
+ newmesh->chain = NULL;
+ newmesh->numverts = newvertexcount;
+ newmesh->numtriangles = tricount;
+ newmesh->vertex = (surfvertex_t *)(newmesh + 1);
+ newmesh->index = (int *)(newmesh->vertex + newvertexcount);
+ for (j = 0;j < tricount * 3;j++)
+ {
+ newmesh->index[j] = vertexremap[index[j]];
+ // yes this copies the same vertex multiple times in many cases... but that's ok...
+ memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
+ }
+ if (oldmesh)
+ oldmesh->chain = newmesh;
+ else
+ firstmesh = newmesh;
+ oldmesh = newmesh;
+ }
+ Mem_Free(vertexremap);
+ Mem_Free(s->mesh);
+ s->mesh = firstmesh;
+ }
+}
+
+/*
+=================
+Mod_LoadFaces
+=================
+*/
+static void Mod_LoadFaces (lump_t *l)
+{
+ dface_t *in;
+ msurface_t *out;
+ int i, count, surfnum, planenum, ssize, tsize;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+
+ loadmodel->surfaces = out;
+ loadmodel->numsurfaces = count;
+
+ for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
+ {
+ // FIXME: validate edges, texinfo, etc?
+ out->firstedge = LittleLong(in->firstedge);
+ out->numedges = LittleShort(in->numedges);
+ if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
+ Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
+
+ i = LittleShort (in->texinfo);
+ if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
+ Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
+ out->texinfo = loadmodel->texinfo + i;
+ out->flags = out->texinfo->texture->flags;
+
+ planenum = LittleShort(in->planenum);
+ if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
+ Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
+
+ if (LittleShort(in->side))
+ out->flags |= SURF_PLANEBACK;
+
+ out->plane = loadmodel->planes + planenum;
+
+ // clear lightmap (filled in later)
+ out->lightmaptexture = NULL;
+
+ // force lightmap upload on first time seeing the surface
+ out->cached_dlight = true;
+ out->cached_ambient = -1000;
+ out->cached_lightscalebit = -1000;
+
+ CalcSurfaceExtents (out);
+
+ ssize = (out->extents[0] >> 4) + 1;
+ tsize = (out->extents[1] >> 4) + 1;
+
+ // lighting info
+ for (i = 0;i < MAXLIGHTMAPS;i++)
+ out->styles[i] = in->styles[i];
+ i = LittleLong(in->lightofs);
+ if (i == -1)
+ out->samples = NULL;
+ else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
+ out->samples = loadmodel->lightdata + i;
+ else // LordHavoc: white lighting (bsp version 29)
+ out->samples = loadmodel->lightdata + (i * 3);
+
+ Mod_GenerateSurfacePolygon(out);
+
+ if (out->texinfo->texture->flags & SURF_DRAWSKY)
+ {
+ out->shader = &Cshader_sky;
+ out->samples = NULL;
+ Mod_GenerateVertexMesh (out);
+ }
+ else if (out->texinfo->texture->flags & SURF_DRAWTURB)
+ {
+ out->shader = &Cshader_water;
+ out->samples = NULL;
+ Mod_GenerateVertexMesh (out);
+ }
+ else
+ {
+ if (!R_TextureHasAlpha(out->texinfo->texture->texture))
+ out->flags |= SURF_CLIPSOLID;
+ if (out->texinfo->flags & TEX_SPECIAL)
+ {
+ // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
+ out->shader = &Cshader_water;
+ out->shader = &Cshader_water;
+ out->samples = NULL;
+ Mod_GenerateVertexMesh (out);
+ }
+ else if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
+ {
+ Con_Printf ("Bad surface extents, converting to fullbright polygon");
+ out->shader = &Cshader_wall_fullbright;
+ out->samples = NULL;
+ Mod_GenerateVertexMesh(out);
+ }
+ else
+ {
+ // stainmap for permanent marks on walls
+ out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
+ // clear to white
+ memset(out->stainsamples, 255, ssize * tsize * 3);
+ if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
+ {
+ out->shader = &Cshader_wall_vertex;
+ Mod_GenerateVertexLitMesh(out);
+ }
+ else
+ {
+ out->shader = &Cshader_wall_lightmap;
+ Mod_GenerateLightmappedMesh(out);
+ }
+ }
+ }
+ Mod_SplitSurfMeshIfTooBig(out);
+ }
+}
+
+static model_t *sortmodel;
+
+static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
+{
+ const msurface_t *a, *b;
+ a = *((const msurface_t **)voida);
+ b = *((const msurface_t **)voidb);
+ if (a->shader != b->shader)
+ return (qbyte *) a->shader - (qbyte *) b->shader;
+ if (a->texinfo->texture != b->texinfo->texture);
+ return a->texinfo->texture - b->texinfo->texture;
+ return 0;
+}
+
+static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
+{
+ int surfnum;
+ sortmodel = model;
+ sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
+ for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
+ sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
+
+ if (r_sortsurfaces.integer)
+ qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
+}
+
+
+/*
+=================
+Mod_SetParent
+=================
+*/
+static void Mod_SetParent (mnode_t *node, mnode_t *parent)
+{
+ node->parent = parent;
+ if (node->contents < 0)
+ return;
+ Mod_SetParent (node->children[0], node);
+ Mod_SetParent (node->children[1], node);
+}
+
+/*
+=================
+Mod_LoadNodes
+=================
+*/
+static void Mod_LoadNodes (lump_t *l)
+{
+ int i, j, count, p;
+ dnode_t *in;
+ mnode_t *out;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+
+ loadmodel->nodes = out;
+ loadmodel->numnodes = count;
+
+ for ( i=0 ; i<count ; i++, in++, out++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ out->mins[j] = LittleShort (in->mins[j]);
+ out->maxs[j] = LittleShort (in->maxs[j]);
+ }
+
+ p = LittleLong(in->planenum);
+ out->plane = loadmodel->planes + p;
+
+ out->firstsurface = LittleShort (in->firstface);
+ out->numsurfaces = LittleShort (in->numfaces);
+
+ for (j=0 ; j<2 ; j++)
+ {
+ p = LittleShort (in->children[j]);
+ if (p >= 0)
+ out->children[j] = loadmodel->nodes + p;
+ else
+ out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
+ }
+ }
+
+ Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
+}
+
+/*
+=================
+Mod_LoadLeafs
+=================
+*/
+static void Mod_LoadLeafs (lump_t *l)