]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
more work on mod_generatelightmaps command
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 24 Nov 2009 06:09:22 +0000 (06:09 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 24 Nov 2009 06:09:22 +0000 (06:09 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9515 d7cf8633-e32d-0410-b094-e92efae38249

model_brush.c
model_shared.c
model_shared.h

index b20992d6e12c5f09d7b4746c101eb06cdea73594..60dd1bab38a3a9fce11da2752a9d35cd623ad978 100644 (file)
@@ -2228,70 +2228,6 @@ static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surface)
 }
 #endif
 
-/* Maximum size of a single LM */
-#define MAX_SINGLE_LM_SIZE    256
-
-struct alloc_lm_row
-{
-       int rowY;
-       int currentX;
-};
-
-struct alloc_lm_state
-{
-       int currentY;
-       struct alloc_lm_row rows[MAX_SINGLE_LM_SIZE];
-};
-
-static void init_alloc_lm_state (struct alloc_lm_state* state)
-{
-       int r;
-
-       state->currentY = 0;
-       for (r = 0; r < MAX_SINGLE_LM_SIZE; r++)
-       {
-         state->rows[r].currentX = 0;
-         state->rows[r].rowY = -1;
-       }
-}
-
-static qboolean Mod_Q1BSP_AllocLightmapBlock(struct alloc_lm_state* state, int totalwidth, int totalheight, int blockwidth, int blockheight, int *outx, int *outy)
-{
-       struct alloc_lm_row* row;
-       int r;
-
-       row = &(state->rows[blockheight]);
-       if ((row->rowY < 0) || (row->currentX + blockwidth > totalwidth))
-       {
-               if (state->currentY + blockheight <= totalheight)
-               {
-                       row->rowY = state->currentY;
-                       row->currentX = 0;
-                       state->currentY += blockheight;
-               }
-               else
-               {
-                       /* See if we can stuff the block into a higher row */
-                       row = NULL;
-                       for (r = blockheight; r < MAX_SINGLE_LM_SIZE; r++)
-                       {
-                               if ((state->rows[r].rowY >= 0)
-                                 && (state->rows[r].currentX + blockwidth <= totalwidth))
-                               {
-                                       row = &(state->rows[r]);
-                                       break;
-                               }
-                       }
-                       if (row == NULL) return false;
-               }
-       }
-       *outy = row->rowY;
-       *outx = row->currentX;
-       row->currentX += blockwidth;
-
-       return true;
-}
-
 extern cvar_t gl_max_size;
 static void Mod_Q1BSP_LoadFaces(lump_t *l)
 {
@@ -2481,8 +2417,9 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
        if (cls.state != ca_dedicated)
        {
                int stainmapsize = 0;
-               struct alloc_lm_state allocState;
+               mod_alloclightmap_state_t allocState;
 
+               Mod_AllocLightmap_Init(&allocState, lightmapsize, lightmapsize);
                for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
                {
                        int i, iu, iv, lightmapx = 0, lightmapy = 0;
@@ -2497,7 +2434,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                        tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
                        stainmapsize += ssize * tsize * 3;
 
-                       if (!lightmaptexture || !Mod_Q1BSP_AllocLightmapBlock(&allocState, lightmapsize, lightmapsize, ssize, tsize, &lightmapx, &lightmapy))
+                       if (!lightmaptexture || !Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy))
                        {
                                // allocate a texture pool if we need it
                                if (loadmodel->texturepool == NULL)
@@ -2507,8 +2444,8 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                                if (loadmodel->brushq1.nmaplightdata)
                                        deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
                                lightmapnumber++;
-                               init_alloc_lm_state (&allocState);
-                               Mod_Q1BSP_AllocLightmapBlock(&allocState, lightmapsize, lightmapsize, ssize, tsize, &lightmapx, &lightmapy);
+                               Mod_AllocLightmap_Reset(&allocState);
+                               Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy);
                        }
                        surface->lightmaptexture = lightmaptexture;
                        surface->deluxemaptexture = deluxemaptexture;
@@ -6216,6 +6153,9 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->brush.num_leafs = 0;
        Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
 
+       if (loadmodel->brush.numsubmodels)
+               loadmodel->brush.submodels = (dp_model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
+
        mod = loadmodel;
        for (i = 0;i < loadmodel->brush.numsubmodels;i++)
        {
@@ -6243,6 +6183,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        mod->brush.AmbientSoundLevelsForPoint = NULL;
                }
                mod->brush.submodel = i;
+               if (loadmodel->brush.submodels)
+                       loadmodel->brush.submodels[i] = mod;
 
                // make the model surface list (used by shadowing/lighting)
                mod->firstmodelsurface = mod->brushq3.data_models[i].firstface;
index 81c4c3f277255940ba73611ba17240cf8d56493a..721e92e64fc591964f0d44b8c87b2ae58def5f41 100644 (file)
@@ -25,8 +25,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #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;
 
@@ -150,6 +154,9 @@ void Mod_Init (void)
        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");
@@ -2915,10 +2922,79 @@ static void Mod_Decompile_f(void)
        }
 }
 
+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 normal[3];
        float sh1[4][3];
        float *vertex_color;
        unsigned char *lm_bgr;
@@ -2928,24 +3004,58 @@ lightmapsample_t;
 
 typedef struct lightmapvertex_s
 {
+       int index;
        float pos[3];
        float normal[3];
        float texcoordbase[2];
        float texcoordlightmap[2];
-       lightmapsample_t sample;
+       float lightcolor[4];
 }
 lightmapvertex_t;
 
 typedef struct lightmaptriangle_s
 {
+       int triangleindex;
        int surfaceindex;
-       lightmapvertex_t vertex[3];
+       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];
 }
 lightmaptriangle_t;
 
-//static void Mod_GenerateLightmaps_AddSample(const float *pos, const float *normal, float *vertex_color, unsigned char *lm_bgr, unsigned char *lm_dir)
-//{
-//}
+lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
+
+static void Mod_GenerateLightmaps_Sample(const float *pos, const float *normal, float *vertex_color, unsigned char *lm_bgr, unsigned char *lm_dir)
+{
+       
+/*
+       if (mod_generatelightmaps_maxsamples <= mod_generatelightmaps_numsamples)
+       {
+               lightmapsample_t *oldsamples = mod_generatelightmaps_samples;
+               mod_generatelightmaps_maxsamples = max(65536, mod_generatelightmaps_maxsamples * 2);
+               
+               
+       }
+       sample = &mod_generatelightmaps_samples[mod_generatelightmaps_numsamples++];
+       memset(sample, 0, sizeof(*sample));
+       sample->pos[0] = pos[0];
+       sample->pos[1] = pos[1];
+       sample->pos[2] = pos[2];
+       sample->normal[0] = normal[0];
+       sample->normal[1] = normal[1];
+       sample->normal[2] = normal[2];
+       sample->vertex_color = vertex_color;
+       sample->lm_bgr = lm_bgr;
+       sample->lm_dir = lm_dir;
+       */
+}
 
 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
 {
@@ -2974,6 +3084,413 @@ static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
        }
 }
 
+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]);
+               }
+       }
+}
+
+#if 0
+float lmprojection[3][3][3] =
+{
+       {{0, 1, 0}, {0, 0, 1}, {1, 0, 0}},
+       {{1, 0, 0}, {0, 0, 1}, {0, 1, 0}},
+       {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}},
+};
+#endif
+
+static void Mod_GenerateLightmaps_ClassifyTriangles(dp_model_t *model)
+{
+       msurface_t *surface;
+       int surfaceindex;
+       int i;
+       int axis;
+       float mins[3];
+       float maxs[3];
+       float aabbsize[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
+                       mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
+                       mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
+                       mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
+                       maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
+                       maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
+                       maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
+                       // pick an axial projection based on the shortest dimension of the
+                       // axially aligned bounding box, this is almost equivalent to
+                       // calculating a triangle normal and classifying its primary axis.
+                       //
+                       // one difference is that this method can pick a workable
+                       // projection axis even for a degenerate triangle whose only shape
+                       // is a line (where the chosen projection will always map the line
+                       // to non-zero coordinates in texture space)
+                       VectorSubtract(maxs, mins, aabbsize);
+                       axis = 0;
+                       if (aabbsize[1] < aabbsize[axis])
+                               axis = 1;
+                       if (aabbsize[2] < aabbsize[axis])
+                               axis = 2;
+                       triangle->axis = axis;
+               }
+       }
+}
+
+static void Mod_GenerateLightmaps_AllocateLightmaps(dp_model_t *model)
+{
+       msurface_t *surface;
+       int surfaceindex;
+       int lightmapindex;
+       int lightmapnumber;
+       int i;
+       int j;
+       int k;
+       int x;
+       int y;
+       int retry;
+       int pixeloffset;
+       int row_numpoints;
+       int column_numpoints;
+       float f;
+       float row_plane[3];
+       float column_plane[3];
+       float trianglenormal[3];
+       float clipbuf[5][8][3];
+       float samplecenter[3];
+       float samplenormal[3];
+       float mins[3];
+       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(mins[j]*lmscalepixels)-lm_borderpixels;
+                                       lmmaxs = floor(mins[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);
+               }
+       }
+       Mod_AllocLightmap_Free(&lmstate);
+
+       // now together lightmap textures
+       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);
+       lightmapnumber = 0;
+       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];
+                       VectorClear(row_plane);
+                       VectorClear(column_plane);
+                       row_plane[triangle->axis] = 1.0f;
+                       column_plane[!triangle->axis] = 1.0f;
+#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)
+                       TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
+                       VectorNormalize(trianglenormal);
+                       PolygonF_QuadForPlane(clipbuf[0][0], trianglenormal[0], trianglenormal[1], trianglenormal[2], DotProduct(trianglenormal, triangle->vertex[0]), model->radius * 2);
+                       VectorCopy(trianglenormal, samplenormal); // FIXME!
+                       for (y = 0;y < triangle->lmsize[1];y++)
+                       {
+                               row_numpoints = PolygonF_Clip(4            , clipbuf[0][0],  row_plane[0],  row_plane[1],  row_plane[2],   triangle->lmbase[1] +  y    / triangle->lmscale[1] , LM_DIST_EPSILON, 8, clipbuf[1][0]);
+                               if (!row_numpoints)
+                                       continue;
+                               row_numpoints = PolygonF_Clip(row_numpoints, clipbuf[1][0], -row_plane[0], -row_plane[1], -row_plane[2], -(triangle->lmbase[1] + (y+1) / triangle->lmscale[1]), LM_DIST_EPSILON, 8, clipbuf[2][0]);
+                               if (!row_numpoints)
+                                       continue;
+                               pixeloffset = triangle->lightmapindex * lm_texturesize * lm_texturesize * 4 + (y+triangle->lmoffset[1]) * lm_texturesize * 4 + triangle->lmoffset[0] * 4;
+                               for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
+                               {
+                                       column_numpoints = PolygonF_Clip(row_numpoints   , clipbuf[2][0],  column_plane[0],  column_plane[1],  column_plane[2],   triangle->lmbase[0] +  x    / triangle->lmscale[0] , LM_DIST_EPSILON, 8, clipbuf[3][0]);
+                                       if (!column_numpoints)
+                                               continue;
+                                       column_numpoints = PolygonF_Clip(column_numpoints, clipbuf[3][0], -column_plane[0], -column_plane[1], -column_plane[2], -(triangle->lmbase[0] + (x+1) / triangle->lmscale[0]), LM_DIST_EPSILON, 8, clipbuf[4][0]);
+                                       if (!column_numpoints)
+                                               continue;
+                                       // we now have a polygon fragment in clipbuf[4], we can
+                                       // sample its center or subdivide it further for
+                                       // antialiasing
+                                       VectorClear(samplecenter);
+                                       for (j = 0;j < column_numpoints;j++)
+                                               VectorAdd(samplecenter, clipbuf[4][j], samplecenter);
+                                       f = 1.0f / column_numpoints;
+                                       VectorScale(samplecenter, f, samplecenter);
+                                       VectorMA(samplecenter, 0.125f, trianglenormal, samplecenter);
+                                       Mod_GenerateLightmaps_Sample(samplecenter, samplenormal, NULL, 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", lightmapnumber), lm_texturesize, lm_texturesize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+               model->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(model->texturepool, va("deluxemap%i", lightmapnumber), lm_texturesize, lm_texturesize, NULL, 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;
+               if (model->brushq3.num_mergedlightmaps)
+               {
+                       lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
+                       surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
+                       surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
+               }
+               else
+               {
+                       surface->lightmaptexture = NULL;
+                       surface->deluxemaptexture = NULL;
+               }
+       }
+}
+
 static void Mod_GenerateLightmaps(dp_model_t *model)
 {
        //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
@@ -2981,6 +3498,10 @@ static void Mod_GenerateLightmaps(dp_model_t *model)
        loadmodel = model;
 
        Mod_GenerateLightmaps_DestroyLightmaps(model);
+       Mod_GenerateLightmaps_UnweldTriangles(model);
+       Mod_GenerateLightmaps_CreateTriangleInformation(model);
+       Mod_GenerateLightmaps_ClassifyTriangles(model);
+       Mod_GenerateLightmaps_AllocateLightmaps(model);
 #if 0
        // stage 1:
        // first step is deleting the lightmaps
@@ -2998,6 +3519,10 @@ static void Mod_GenerateLightmaps(dp_model_t *model)
        }
 #endif
 
+       if (mod_generatelightmaps_lightmaptriangles)
+               Mem_Free(mod_generatelightmaps_lightmaptriangles);
+       mod_generatelightmaps_lightmaptriangles = NULL;
+
        loadmodel = oldloadmodel;
 }
 
index fda4957823b80114427cde2b0321bdcfe2dcf89f..3d14ec913210464e033bf44fb555c9227775a929 100644 (file)
@@ -1005,6 +1005,27 @@ void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float
 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f);
 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer);
 
+typedef struct mod_alloclightmap_row_s
+{
+       int rowY;
+       int currentX;
+}
+mod_alloclightmap_row_t;
+
+typedef struct mod_alloclightmap_state_s
+{
+       int width;
+       int height;
+       int currentY;
+       mod_alloclightmap_row_t *rows;
+}
+mod_alloclightmap_state_t;
+
+void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, int width, int height);
+void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state);
+void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state);
+qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy);
+
 // bsp models
 void Mod_BrushInit(void);
 // used for talking to the QuakeC mainly