#include "quakedef.h"
#include "image.h"
#include "r_shadow.h"
+#include "polygon.h"
cvar_t r_mipskins = {CVAR_SAVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
+cvar_t mod_generatelightmaps_unitspersample = {CVAR_SAVE, "mod_generatelightmaps_unitspersample", "16", "lightmap resolution"};
+cvar_t mod_generatelightmaps_borderpixels = {CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
+cvar_t mod_generatelightmaps_texturesize = {CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
dp_model_t *loadmodel;
int nummodels = Mem_ExpandableArray_IndexRange(&models);
dp_model_t *mod;
- R_SkinFrame_PrepareForPurge();
for (i = 0;i < nummodels;i++)
{
- if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_textures)
+ if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
{
- for (j = 0;j < mod->num_textures;j++)
+ for (j = 0;j < mod->num_textures && mod->data_textures;j++)
{
for (k = 0;k < mod->data_textures[j].numskinframes;k++)
R_SkinFrame_MarkUsed(mod->data_textures[j].skinframes[k]);
for (k = 0;k < mod->data_textures[j].backgroundnumskinframes;k++)
R_SkinFrame_MarkUsed(mod->data_textures[j].backgroundskinframes[k]);
}
+ if (mod->brush.solidskyskinframe)
+ R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
+ if (mod->brush.alphaskyskinframe)
+ R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
}
}
- R_SkinFrame_Purge();
if (!cl_stainmaps_clearonload.integer)
return;
static void Mod_Precache (void);
static void Mod_Decompile_f(void);
static void Mod_BuildVBOs(void);
+static void Mod_GenerateLightmaps_f(void);
void Mod_Init (void)
{
mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
Mod_SpriteInit();
Cvar_RegisterVariable(&r_mipskins);
+ Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
+ Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
+ Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
Cmd_AddCommand ("modellist", Mod_Print, "prints a list of loaded models");
Cmd_AddCommand ("modelprecache", Mod_Precache, "load a model");
Cmd_AddCommand ("modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
+ Cmd_AddCommand ("mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
}
void Mod_RenderInit(void)
}
}
+void Mod_CreateCollisionMesh(dp_model_t *mod)
+{
+ int k;
+ int numcollisionmeshtriangles;
+ const msurface_t *surface;
+ mempool_t *mempool = mod->mempool;
+ if (!mempool && mod->brush.parentmodel)
+ mempool = mod->brush.parentmodel->mempool;
+ // make a single combined collision mesh for physics engine use
+ // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
+ numcollisionmeshtriangles = 0;
+ for (k = 0;k < mod->nummodelsurfaces;k++)
+ {
+ surface = mod->data_surfaces + mod->firstmodelsurface + k;
+ if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
+ continue;
+ numcollisionmeshtriangles += surface->num_triangles;
+ }
+ mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles, NULL, NULL, NULL, false, false, true);
+ for (k = 0;k < mod->nummodelsurfaces;k++)
+ {
+ surface = mod->data_surfaces + mod->firstmodelsurface + k;
+ if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
+ continue;
+ Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
+ }
+ mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mempool, mod->brush.collisionmesh, false, true, false);
+}
+
void Mod_GetTerrainVertex3fTexCoord2fFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
{
float v[3], tc[3];
Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
}
+#if 0
+void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
+{
+ float mins[3];
+ float maxs[3];
+ float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
+ float chunkheight = min(stepsize, model->terrain.height - 1 - y);
+ float viewvector[3];
+ unsigned int firstvertex;
+ unsigned int *e;
+ float *v;
+ if (chunkwidth < 2 || chunkheight < 2)
+ return;
+ VectorSet(mins, model->terrain.mins[0] + x * stepsize * model->terrain.scale[0], model->terrain.mins[1] + y * stepsize * model->terrain.scale[1], model->terrain.mins[2]);
+ VectorSet(maxs, model->terrain.mins[0] + (x+1) * stepsize * model->terrain.scale[0], model->terrain.mins[1] + (y+1) * stepsize * model->terrain.scale[1], model->terrain.maxs[2]);
+ viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
+ viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
+ viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
+ if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
+ {
+ // too close for this stepsize, emit as 4 chunks instead
+ stepsize /= 2;
+ Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
+ Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
+ Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
+ Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
+ return;
+ }
+ // emit the geometry at stepsize into our vertex buffer / index buffer
+ // we add two columns and two rows for skirt
+ outwidth = chunkwidth+2;
+ outheight = chunkheight+2;
+ outwidth2 = outwidth-1;
+ outheight2 = outheight-1;
+ outwidth3 = outwidth+1;
+ outheight3 = outheight+1;
+ firstvertex = numvertices;
+ e = model->terrain.element3i + numtriangles;
+ numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
+ v = model->terrain.vertex3f + numvertices;
+ numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
+ // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
+ for (ty = 0;ty < outheight;ty++)
+ {
+ for (tx = 0;tx < outwidth;tx++)
+ {
+ *e++ = firstvertex + (ty )*outwidth3+(tx );
+ *e++ = firstvertex + (ty )*outwidth3+(tx+1);
+ *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
+ *e++ = firstvertex + (ty )*outwidth3+(tx );
+ *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
+ *e++ = firstvertex + (ty+1)*outwidth3+(tx );
+ }
+ }
+ // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
+ for (ty = 0;ty <= outheight;ty++)
+ {
+ skirtrow = ty == 0 || ty == outheight;
+ ry = y+bound(1, ty, outheight)*stepsize;
+ for (tx = 0;tx <= outwidth;tx++)
+ {
+ skirt = skirtrow || tx == 0 || tx == outwidth;
+ rx = x+bound(1, tx, outwidth)*stepsize;
+ v[0] = rx*scale[0];
+ v[1] = ry*scale[1];
+ v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
+ v += 3;
+ }
+ }
+ // TODO: emit skirt vertices
+}
+
+void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
+{
+ for (y = 0;y < model->terrain.size[1];y += model->terrain.
+ Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
+ Mod_Terrain_BuildChunk(model,
+}
+#endif
+
q3wavefunc_t Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
{
if (!strcasecmp(s, "sin")) return Q3WAVEFUNC_SIN;
shader.reflectfactor = 1;
Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
shader.r_water_wateralpha = 1;
+ shader.specularscalemod = 1;
+ shader.specularpowermod = 1;
strlcpy(shader.name, com_token, sizeof(shader.name));
if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
if (!COM_ParseToken_QuakeC(&text, true))
break;
}
- for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
- parameter[j][0] = 0;
+ //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
+ // parameter[j][0] = 0;
if (developer.integer >= 100)
{
Con_Printf("%s %i: ", shader.name, shader.numlayers - 1);
if (!COM_ParseToken_QuakeC(&text, true))
break;
}
- for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
- parameter[j][0] = 0;
+ //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
+ // parameter[j][0] = 0;
if (fileindex == 0 && !strcasecmp(com_token, "}"))
break;
if (developer.integer >= 100)
Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
shader.r_water_wateralpha = atof(parameter[11]);
}
+ else if (!strcasecmp(parameter[0], "dp_glossintensitymod") && numparameters >= 2)
+ {
+ shader.specularscalemod = atof(parameter[1]);
+ }
+ else if (!strcasecmp(parameter[0], "dp_glossexponentmod") && numparameters >= 2)
+ {
+ shader.specularpowermod = atof(parameter[1]);
+ }
else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
{
int i, deformindex;
texflagsmask &= ~TEXF_PICMIP;
if(!(defaulttexflags & TEXF_COMPRESS))
texflagsmask &= ~TEXF_COMPRESS;
+ texture->specularscalemod = 1; // unless later loaded from the shader
+ texture->specularpowermod = 1; // unless later loaded from the shader
if (shader)
{
texture->reflectfactor = shader->reflectfactor;
Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
texture->r_water_wateralpha = shader->r_water_wateralpha;
+ texture->specularscalemod = shader->specularscalemod;
+ texture->specularpowermod = shader->specularpowermod;
}
else if (!strcmp(texture->name, "noshader") || !texture->name[0])
{
tag_torso,
*/
memset(word, 0, sizeof(word));
- for (i = 0;i < MAX_SKINS && (data = text = (char *)FS_LoadFile(va("%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
+ for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va("%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
{
// If it's the first file we parse
if (skinfile == NULL)
static void Mod_BuildVBOs(void)
{
+ if (developer.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
+ {
+ int i;
+ for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+ {
+ if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
+ {
+ Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
+ loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+ }
+ }
+ }
+
if (!gl_support_arb_vertex_buffer_object)
return;
if (loadmodel->surfmesh.num_triangles)
{
if (loadmodel->surfmesh.data_element3s)
- {
- int i;
- for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
- loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
loadmodel->surfmesh.ebo3s = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]), loadmodel->name);
- }
else
loadmodel->surfmesh.ebo3i = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(unsigned int[3]), loadmodel->name);
}
countvertices += surface->num_vertices;
countfaces += surface->num_triangles;
texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
- for (textureindex = 0;textureindex < maxtextures && texturenames[textureindex*MAX_QPATH];textureindex++)
+ for (textureindex = 0;textureindex < counttextures;textureindex++)
if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
break;
+ if (textureindex < counttextures)
+ continue; // already wrote this material entry
if (textureindex >= maxtextures)
continue; // just a precaution
- if (counttextures < textureindex + 1)
- counttextures = textureindex + 1;
+ textureindex = counttextures++;
strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
if (outbufferpos >= outbuffermax >> 1)
{
}
}
+void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, int width, int height)
+{
+ int y;
+ memset(state, 0, sizeof(*state));
+ state->width = width;
+ state->height = height;
+ state->currentY = 0;
+ state->rows = Mem_Alloc(tempmempool, state->height * sizeof(*state->rows));
+ for (y = 0;y < state->height;y++)
+ {
+ state->rows[y].currentX = 0;
+ state->rows[y].rowY = -1;
+ }
+}
+
+void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
+{
+ int y;
+ state->currentY = 0;
+ for (y = 0;y < state->height;y++)
+ {
+ state->rows[y].currentX = 0;
+ state->rows[y].rowY = -1;
+ }
+}
+
+void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
+{
+ if (state->rows)
+ Mem_Free(state->rows);
+ memset(state, 0, sizeof(*state));
+}
+
+qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
+{
+ mod_alloclightmap_row_t *row;
+ int y;
+
+ row = state->rows + blockheight;
+ if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
+ {
+ if (state->currentY + blockheight <= state->height)
+ {
+ // use the current allocation position
+ row->rowY = state->currentY;
+ row->currentX = 0;
+ state->currentY += blockheight;
+ }
+ else
+ {
+ // find another position
+ for (y = blockheight;y < state->height;y++)
+ {
+ if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
+ {
+ row = state->rows + y;
+ break;
+ }
+ }
+ if (y == state->height)
+ return false;
+ }
+ }
+ *outy = row->rowY;
+ *outx = row->currentX;
+ row->currentX += blockwidth;
+
+ return true;
+}
+
+typedef struct lightmapsample_s
+{
+ float pos[3];
+ float sh1[4][3];
+ float *vertex_color;
+ unsigned char *lm_bgr;
+ unsigned char *lm_dir;
+}
+lightmapsample_t;
+
+typedef struct lightmapvertex_s
+{
+ int index;
+ float pos[3];
+ float normal[3];
+ float texcoordbase[2];
+ float texcoordlightmap[2];
+ float lightcolor[4];
+}
+lightmapvertex_t;
+
+typedef struct lightmaptriangle_s
+{
+ int triangleindex;
+ int surfaceindex;
+ int lightmapindex;
+ int axis;
+ int lmoffset[2];
+ int lmsize[2];
+ // 2D modelspace coordinates of min corner
+ // snapped to lightmap grid but not in grid coordinates
+ float lmbase[2];
+ // 2D modelspace to lightmap coordinate scale
+ float lmscale[2];
+ float vertex[3][3];
+ float mins[3];
+ float maxs[3];
+}
+lightmaptriangle_t;
+
+lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
+
+extern void R_SampleRTLights(const float *pos, float *sh1);
+
+static void Mod_GenerateLightmaps_SamplePoint(const float *pos, float *sh1)
+{
+ int i;
+ for (i = 0;i < 4*3;i++)
+ sh1[i] = 0.0f;
+ R_SampleRTLights(pos, sh1);
+}
+
+static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
+{
+ float sh1[4*3];
+ float color[3];
+ float dir[3];
+ Mod_GenerateLightmaps_SamplePoint(pos, sh1);
+ VectorSet(dir, VectorLength(sh1 + 3), VectorLength(sh1 + 6), VectorLength(sh1 + 9));
+ VectorNormalize(dir);
+ VectorScale(sh1, 127.5f, color);
+ //VectorCopy(normal, dir);
+ if (r_test.integer & 1) dir[0] *= -1.0f;
+ if (r_test.integer & 2) dir[1] *= -1.0f;
+ if (r_test.integer & 4) dir[2] *= -1.0f;
+ VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
+ lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
+ lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
+ lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
+ lm_bgr[3] = 255;
+ lm_dir[0] = (unsigned char)dir[0];
+ lm_dir[1] = (unsigned char)dir[1];
+ lm_dir[2] = (unsigned char)dir[2];
+ lm_dir[3] = 255;
+}
+
+static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
+{
+ float sh1[4*3];
+ Mod_GenerateLightmaps_SamplePoint(pos, sh1);
+ VectorCopy(sh1, vertex_color);
+}
+
+static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
+{
+ float sh1[4*3];
+ float ambient[3];
+ float diffuse[3];
+ float dir[3];
+ Mod_GenerateLightmaps_SamplePoint(pos, sh1);
+ // calculate the direction we'll use to reduce the sh1 to a directional light source
+ VectorSet(dir, VectorLength(sh1 + 3), VectorLength(sh1 + 6), VectorLength(sh1 + 9));
+ VectorNormalize(dir);
+ // scale the ambient from 0-2 to 0-255
+ VectorScale(sh1, 127.5f, ambient);
+ // extract the diffuse color along the chosen direction and scale it
+ diffuse[0] = (dir[0]*sh1[3] + dir[1]*sh1[6] + dir[2]*sh1[ 9] + sh1[0]) * 127.5f;
+ diffuse[1] = (dir[0]*sh1[4] + dir[1]*sh1[7] + dir[2]*sh1[10] + sh1[1]) * 127.5f;
+ diffuse[2] = (dir[0]*sh1[5] + dir[1]*sh1[8] + dir[2]*sh1[11] + sh1[2]) * 127.5f;
+ // encode to the grid format
+ s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
+ s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
+ s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
+ s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
+ s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
+ s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
+ if (dir[2] >= 0.99f) {s->diffuseyaw = 0;s->diffusepitch = 0;}
+ else if (dir[2] <= -0.99f) {s->diffuseyaw = 0;s->diffusepitch = 128;}
+ else {s->diffuseyaw = (unsigned char)(acos(dir[2]) * (127.5f/M_PI));s->diffusepitch = (unsigned char)(atan2(dir[1], dir[0]) * (127.5f/M_PI));}
+}
+
+static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
+{
+ msurface_t *surface;
+ int surfaceindex;
+ int i;
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ surface->lightmaptexture = NULL;
+ surface->deluxemaptexture = NULL;
+ }
+ if (model->brushq3.data_lightmaps)
+ {
+ for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
+ R_FreeTexture(model->brushq3.data_lightmaps[i]);
+ Mem_Free(model->brushq3.data_lightmaps);
+ model->brushq3.data_lightmaps = NULL;
+ }
+ if (model->brushq3.data_deluxemaps)
+ {
+ for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
+ R_FreeTexture(model->brushq3.data_deluxemaps[i]);
+ Mem_Free(model->brushq3.data_deluxemaps);
+ model->brushq3.data_deluxemaps = NULL;
+ }
+}
+
+static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
+{
+ msurface_t *surface;
+ int surfaceindex;
+ int vertexindex;
+ int outvertexindex;
+ int i;
+ const int *e;
+ surfmesh_t oldsurfmesh;
+ size_t size;
+ unsigned char *data;
+ oldsurfmesh = model->surfmesh;
+ model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
+ model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
+ size = 0;
+ size += model->surfmesh.num_vertices * sizeof(float[3]);
+ size += model->surfmesh.num_vertices * sizeof(float[3]);
+ size += model->surfmesh.num_vertices * sizeof(float[3]);
+ size += model->surfmesh.num_vertices * sizeof(float[3]);
+ size += model->surfmesh.num_vertices * sizeof(float[2]);
+ size += model->surfmesh.num_vertices * sizeof(float[2]);
+ size += model->surfmesh.num_vertices * sizeof(float[4]);
+ data = (unsigned char *)Mem_Alloc(model->mempool, size);
+ model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
+ model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
+ model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
+ model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
+ model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
+ model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
+ model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
+ if (model->surfmesh.num_vertices > 65536)
+ model->surfmesh.data_element3s = NULL;
+
+ if (model->surfmesh.vbo)
+ R_Mesh_DestroyBufferObject(model->surfmesh.vbo);
+ model->surfmesh.vbo = 0;
+ if (model->surfmesh.ebo3i)
+ R_Mesh_DestroyBufferObject(model->surfmesh.ebo3i);
+ model->surfmesh.ebo3i = 0;
+ if (model->surfmesh.ebo3s)
+ R_Mesh_DestroyBufferObject(model->surfmesh.ebo3s);
+ model->surfmesh.ebo3s = 0;
+
+ // convert all triangles to unique vertex data
+ outvertexindex = 0;
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ surface->num_firstvertex = outvertexindex;
+ surface->num_vertices = surface->num_triangles*3;
+ e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
+ for (i = 0;i < surface->num_triangles*3;i++)
+ {
+ vertexindex = e[i];
+ model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
+ model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
+ model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
+ model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
+ model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
+ model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
+ model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
+ model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
+ model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
+ model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
+ model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
+ model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
+ model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
+ model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
+ model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
+ model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
+ model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
+ model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
+ model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
+ model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
+ model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
+ outvertexindex++;
+ }
+ }
+ if (model->surfmesh.data_element3s)
+ for (i = 0;i < model->surfmesh.num_triangles*3;i++)
+ model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
+
+ // find and update all submodels to use this new surfmesh data
+ for (i = 0;i < model->brush.numsubmodels;i++)
+ model->brush.submodels[i]->surfmesh = model->surfmesh;
+}
+
+static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
+{
+ msurface_t *surface;
+ int surfaceindex;
+ int i;
+ const int *e;
+ lightmaptriangle_t *triangle;
+ // generate lightmap triangle structs
+ mod_generatelightmaps_lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
+ for (i = 0;i < surface->num_triangles;i++)
+ {
+ triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
+ triangle->triangleindex = surface->num_firsttriangle+i;
+ triangle->surfaceindex = surfaceindex;
+ VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
+ VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
+ VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
+ }
+ }
+}
+
+float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
+
+static void Mod_GenerateLightmaps_ClassifyTriangles(dp_model_t *model)
+{
+ msurface_t *surface;
+ int surfaceindex;
+ int i;
+ int axis;
+ float normal[3];
+ const int *e;
+ lightmaptriangle_t *triangle;
+
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
+ for (i = 0;i < surface->num_triangles;i++)
+ {
+ triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
+ // calculate bounds of triangle
+ triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
+ triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
+ triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
+ triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
+ triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
+ triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
+ // pick an axial projection based on the triangle normal
+ TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
+ axis = 0;
+ if (fabs(normal[1]) > fabs(normal[axis]))
+ axis = 1;
+ if (fabs(normal[2]) > fabs(normal[axis]))
+ axis = 2;
+ triangle->axis = axis;
+ }
+ }
+}
+
+static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
+{
+ msurface_t *surface;
+ int surfaceindex;
+ int lightmapindex;
+ int lightmapnumber;
+ int i;
+ int j;
+ int k;
+ int x;
+ int y;
+ int axis;
+ int axis1;
+ int axis2;
+ int retry;
+ int pixeloffset;
+ float trianglenormal[3];
+ float samplecenter[3];
+ float samplenormal[3];
+ float temp[3];
+ float lmiscale[2];
+ float slopex;
+ float slopey;
+ float slopebase;
+ float lmscalepixels;
+ float lmmins;
+ float lmmaxs;
+ float lm_basescalepixels;
+ int lm_borderpixels;
+ int lm_texturesize;
+ int lm_maxpixels;
+ const int *e;
+ lightmaptriangle_t *triangle;
+ unsigned char *lightmappixels;
+ unsigned char *deluxemappixels;
+ mod_alloclightmap_state_t lmstate;
+
+ // generate lightmap projection information for all triangles
+ if (model->texturepool == NULL)
+ model->texturepool = R_AllocTexturePool();
+ lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
+ lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
+ lm_texturesize = bound(lm_borderpixels*2+1, 64, gl_max_texture_size);
+ lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
+ Mod_AllocLightmap_Init(&lmstate, lm_texturesize, lm_texturesize);
+ lightmapnumber = 0;
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
+ lmscalepixels = lm_basescalepixels;
+ for (retry = 0;retry < 30;retry++)
+ {
+ // after a couple failed attempts, degrade quality to make it fit
+ if (retry > 1)
+ lmscalepixels *= 0.5f;
+ for (i = 0;i < surface->num_triangles;i++)
+ {
+ triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
+ triangle->lightmapindex = lightmapnumber;
+ // calculate lightmap bounds in 3D pixel coordinates, limit size,
+ // pick two planar axes for projection
+ // lightmap coordinates here are in pixels
+ // lightmap projections are snapped to pixel grid explicitly, such
+ // that two neighboring triangles sharing an edge and projection
+ // axis will have identical sampl espacing along their shared edge
+ k = 0;
+ for (j = 0;j < 3;j++)
+ {
+ if (j == triangle->axis)
+ continue;
+ lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
+ lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
+ triangle->lmsize[k] = (int)(lmmaxs-lmmins);
+ triangle->lmbase[k] = lmmins/lmscalepixels;
+ triangle->lmscale[k] = lmscalepixels;
+ k++;
+ }
+ if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
+ break;
+ }
+ // if all fit in this texture, we're done with this surface
+ if (i == surface->num_triangles)
+ break;
+ // if we haven't maxed out the lightmap size yet, we retry the
+ // entire surface batch...
+ if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, gl_max_texture_size))
+ {
+ lm_texturesize *= 2;
+ surfaceindex = -1;
+ lightmapnumber = 0;
+ Mod_AllocLightmap_Free(&lmstate);
+ Mod_AllocLightmap_Init(&lmstate, lm_texturesize, lm_texturesize);
+ break;
+ }
+ // if we have maxed out the lightmap size, and this triangle does
+ // not fit in the same texture as the rest of the surface, we have
+ // to retry the entire surface in a new texture (can only use one)
+ // with multiple retries, the lightmap quality degrades until it
+ // fits (or gives up)
+ if (surfaceindex > 0)
+ lightmapnumber++;
+ Mod_AllocLightmap_Reset(&lmstate);
+ }
+ }
+ lightmapnumber++;
+ Mod_AllocLightmap_Free(&lmstate);
+
+ // now together lightmap textures
+ model->brushq3.deluxemapping_modelspace = true;
+ model->brushq3.deluxemapping = true;
+ model->brushq3.num_mergedlightmaps = lightmapnumber;
+ model->brushq3.data_lightmaps = Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
+ model->brushq3.data_deluxemaps = Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
+ lightmappixels = Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
+ deluxemappixels = Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
+ for (i = 0;i < surface->num_triangles;i++)
+ {
+ triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
+ TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
+ VectorNormalize(trianglenormal);
+ VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
+ axis = triangle->axis;
+ axis1 = axis == 0 ? 1 : 0;
+ axis2 = axis == 2 ? 1 : 2;
+ lmiscale[0] = 1.0f / triangle->lmscale[0];
+ lmiscale[1] = 1.0f / triangle->lmscale[1];
+ if (trianglenormal[axis] < 0)
+ VectorNegate(trianglenormal, trianglenormal);
+ CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
+ CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
+ slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
+ for (j = 0;j < 3;j++)
+ {
+ float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
+ t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
+ t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
+#if 0
+ samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
+ samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
+ samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
+ Con_Printf("%f:%f %f:%f %f:%f = %f %f\n", triangle->vertex[j][axis1], samplecenter[axis1], triangle->vertex[j][axis2], samplecenter[axis2], triangle->vertex[j][axis], samplecenter[axis], t2f[0], t2f[1]);
+#endif
+ }
+
+#if 0
+ switch (axis)
+ {
+ default:
+ case 0:
+ forward[0] = 0;
+ forward[1] = 1.0f / triangle->lmscale[0];
+ forward[2] = 0;
+ left[0] = 0;
+ left[1] = 0;
+ left[2] = 1.0f / triangle->lmscale[1];
+ up[0] = 1.0f;
+ up[1] = 0;
+ up[2] = 0;
+ origin[0] = 0;
+ origin[1] = triangle->lmbase[0];
+ origin[2] = triangle->lmbase[1];
+ break;
+ case 1:
+ forward[0] = 1.0f / triangle->lmscale[0];
+ forward[1] = 0;
+ forward[2] = 0;
+ left[0] = 0;
+ left[1] = 0;
+ left[2] = 1.0f / triangle->lmscale[1];
+ up[0] = 0;
+ up[1] = 1.0f;
+ up[2] = 0;
+ origin[0] = triangle->lmbase[0];
+ origin[1] = 0;
+ origin[2] = triangle->lmbase[1];
+ break;
+ case 2:
+ forward[0] = 1.0f / triangle->lmscale[0];
+ forward[1] = 0;
+ forward[2] = 0;
+ left[0] = 0;
+ left[1] = 1.0f / triangle->lmscale[1];
+ left[2] = 0;
+ up[0] = 0;
+ up[1] = 0;
+ up[2] = 1.0f;
+ origin[0] = triangle->lmbase[0];
+ origin[1] = triangle->lmbase[1];
+ origin[2] = 0;
+ break;
+ }
+ Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
+#endif
+#define LM_DIST_EPSILON (1.0f / 32.0f)
+ for (y = 0;y < triangle->lmsize[1];y++)
+ {
+ pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
+ for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
+ {
+ samplecenter[axis1] = x*lmiscale[0] + triangle->lmbase[0];
+ samplecenter[axis2] = y*lmiscale[1] + triangle->lmbase[1];
+ samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
+ VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
+ Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
+ }
+ }
+ }
+ }
+
+ for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
+ {
+ model->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(model->texturepool, va("lightmap%i", lightmapindex), lm_texturesize, lm_texturesize, lightmappixels + lightmapindex * lm_texturesize * lm_texturesize * 4, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+ model->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(model->texturepool, va("deluxemap%i", lightmapindex), lm_texturesize, lm_texturesize, deluxemappixels + lightmapindex * lm_texturesize * lm_texturesize * 4, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+ }
+
+ if (lightmappixels)
+ Mem_Free(lightmappixels);
+ if (deluxemappixels)
+ Mem_Free(deluxemappixels);
+
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
+ if (!surface->num_triangles)
+ continue;
+ lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
+ surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
+ surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
+ }
+}
+
+static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
+{
+ int i;
+ for (i = 0;i < model->surfmesh.num_vertices;i++)
+ Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
+}
+
+static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
+{
+ int x;
+ int y;
+ int z;
+ int index = 0;
+ float pos[3];
+ for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
+ {
+ pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
+ for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
+ {
+ pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
+ for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
+ {
+ pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
+ Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
+ }
+ }
+ }
+}
+
+extern cvar_t mod_q3bsp_nolightmaps;
+static void Mod_GenerateLightmaps(dp_model_t *model)
+{
+ //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
+ dp_model_t *oldloadmodel = loadmodel;
+ loadmodel = model;
+
+ Mod_GenerateLightmaps_DestroyLightmaps(model);
+ Mod_GenerateLightmaps_UnweldTriangles(model);
+ Mod_GenerateLightmaps_CreateTriangleInformation(model);
+ Mod_GenerateLightmaps_ClassifyTriangles(model);
+ if(!mod_q3bsp_nolightmaps.integer)
+ Mod_GenerateLightmaps_CreateLightmaps(model);
+ Mod_GenerateLightmaps_UpdateVertexColors(model);
+ Mod_GenerateLightmaps_UpdateLightGrid(model);
+#if 0
+ // stage 1:
+ // first step is deleting the lightmaps
+ for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
+ {
+ surface = model->data_surfaces + surfaceindex;
+ surface->lightmap = NULL;
+ surface->deluxemap = NULL;
+ // add a sample for each vertex of surface
+ for (i = 0, vertexindex = surface->num_firstvertex;i < surface->num_vertices;i++, vertexindex++)
+ Mod_GenerateLightmaps_AddSample(model->surfmesh.data_vertex3f + 3*vertexindex, model->surfmesh.data_normal3f + 3*vertexindex, model->surfmesh.data_lightmapcolor4f + 4*vertexindex, NULL, NULL);
+ // generate lightmaptriangle_t for each triangle of surface
+ for (i = 0, triangleindex = surface->num_firstvertex;i < surface->num_triangles;i++, triangleindex++)
+ {
+ }
+#endif
+
+ if (mod_generatelightmaps_lightmaptriangles)
+ Mem_Free(mod_generatelightmaps_lightmaptriangles);
+ mod_generatelightmaps_lightmaptriangles = NULL;
+
+ loadmodel = oldloadmodel;
+}
+
+static void Mod_GenerateLightmaps_f(void)
+{
+ if (Cmd_Argc() != 1)
+ {
+ Con_Printf("usage: mod_generatelightmaps\n");
+ return;
+ }
+ if (!cl.worldmodel)
+ {
+ Con_Printf("no worldmodel loaded\n");
+ return;
+ }
+ Mod_GenerateLightmaps(cl.worldmodel);
+}