cvar_t mod_q3bsp_nolightmaps = {CVAR_SAVE, "mod_q3bsp_nolightmaps", "0", "do not load lightmaps in Q3BSP maps (to save video RAM, but be warned: it looks ugly)"};
cvar_t mod_q3bsp_tracelineofsight_brushes = {0, "mod_q3bsp_tracelineofsight_brushes", "0", "enables culling of entities behind detail brushes, curves, etc"};
cvar_t mod_q3shader_default_offsetmapping = {CVAR_SAVE, "mod_q3shader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces"};
+cvar_t mod_q3shader_default_polygonfactor = {0, "mod_q3shader_default_polygonfactor", "0", "biases depth values of 'polygonoffset' shaders to prevent z-fighting artifacts"};
+cvar_t mod_q3shader_default_polygonoffset = {0, "mod_q3shader_default_polygonoffset", "-2", "biases depth values of 'polygonoffset' shaders to prevent z-fighting artifacts"};
cvar_t mod_q1bsp_polygoncollisions = {0, "mod_q1bsp_polygoncollisions", "0", "disables use of precomputed cliphulls and instead collides with polygons (uses Bounding Interval Hierarchy optimizations)"};
cvar_t mod_collision_bih = {0, "mod_collision_bih", "1", "enables use of generated Bounding Interval Hierarchy tree instead of compiled bsp tree in collision code"};
Cvar_RegisterVariable(&mod_q3bsp_nolightmaps);
Cvar_RegisterVariable(&mod_q3bsp_tracelineofsight_brushes);
Cvar_RegisterVariable(&mod_q3shader_default_offsetmapping);
+ Cvar_RegisterVariable(&mod_q3shader_default_polygonfactor);
+ Cvar_RegisterVariable(&mod_q3shader_default_polygonoffset);
Cvar_RegisterVariable(&mod_q1bsp_polygoncollisions);
Cvar_RegisterVariable(&mod_collision_bih);
Cvar_RegisterVariable(&mod_recalculatenodeboxes);
int x, y;
int w = width/2;
int h = height;
- unsigned *solidpixels = Mem_Alloc(tempmempool, w*h*sizeof(unsigned char[4]));
- unsigned *alphapixels = Mem_Alloc(tempmempool, w*h*sizeof(unsigned char[4]));
+ unsigned int *solidpixels = (unsigned int *)Mem_Alloc(tempmempool, w*h*sizeof(unsigned char[4]));
+ unsigned int *alphapixels = (unsigned int *)Mem_Alloc(tempmempool, w*h*sizeof(unsigned char[4]));
// allocate a texture pool if we need it
if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
// LordHavoc: HL sky textures are entirely different than quake
if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == mtheight * 2)
{
- data = loadimagepixelsbgra(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), false, false, r_texture_convertsRGB_skin.integer, NULL);
+ data = loadimagepixelsbgra(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), false, false, r_texture_convertsRGB_skin.integer != 0, NULL);
if (!data)
- data = loadimagepixelsbgra(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), false, false, r_texture_convertsRGB_skin.integer, NULL);
+ data = loadimagepixelsbgra(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), false, false, r_texture_convertsRGB_skin.integer != 0, NULL);
if (data && image_width == image_height * 2)
{
R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
}
// compile additional data about the surface geometry
- Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_normal3f, true);
- Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, true);
+ Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0);
+ Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex));
// generate surface extents information
// lightmap is needed on this surface (rather than duplicating the
// logic above)
loadmodel->brushq1.lightmapupdateflags[surfacenum] = true;
+ loadmodel->lit = true;
}
}
loadmodel->texturepool = R_AllocTexturePool();
// could not find room, make a new lightmap
loadmodel->brushq3.num_mergedlightmaps = lightmapnumber + 1;
- loadmodel->brushq3.data_lightmaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0]));
- loadmodel->brushq3.data_deluxemaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0]));
+ loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0]));
+ loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0]));
loadmodel->brushq3.data_lightmaps[lightmapnumber] = lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL);
if (loadmodel->brushq1.nmaplightdata)
loadmodel->brushq3.data_deluxemaps[lightmapnumber] = deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL);
if (portalpointsbuffersize < portalpointsbufferoffset + 6*MAX_PORTALPOINTS)
{
portalpointsbuffersize = portalpointsbufferoffset * 2;
- portalpointsbuffer = Mem_Realloc(loadmodel->mempool, portalpointsbuffer, portalpointsbuffersize * sizeof(*portalpointsbuffer));
+ portalpointsbuffer = (double *)Mem_Realloc(loadmodel->mempool, portalpointsbuffer, portalpointsbuffersize * sizeof(*portalpointsbuffer));
}
frontpoints = portalpointsbuffer + portalpointsbufferoffset;
portalpointsbufferoffset += 3*MAX_PORTALPOINTS;
Mem_ExpandableArray_NewArray(&portalarray, loadmodel->mempool, sizeof(portal_t), 1020*1024/sizeof(portal_t));
portalpointsbufferoffset = 0;
portalpointsbuffersize = 6*MAX_PORTALPOINTS*128;
- portalpointsbuffer = Mem_Alloc(loadmodel->mempool, portalpointsbuffersize * sizeof(*portalpointsbuffer));
+ portalpointsbuffer = (double *)Mem_Alloc(loadmodel->mempool, portalpointsbuffersize * sizeof(*portalpointsbuffer));
Mod_Q1BSP_RecursiveNodePortals(loadmodel->brush.data_nodes + loadmodel->brushq1.hulls[0].firstclipnode);
Mem_Free(portalpointsbuffer);
portalpointsbuffer = NULL;
VectorAdd(inmins, hull->clip_size, outmaxs);
}
+static int Mod_Q1BSP_CreateShadowMesh(dp_model_t *mod)
+{
+ int j;
+ int numshadowmeshtriangles = 0;
+ msurface_t *surface;
+ if (cls.state == ca_dedicated)
+ return 0;
+ // make a single combined shadow mesh to allow optimized shadow volume creation
+
+ for (j = 0, surface = mod->data_surfaces;j < mod->num_surfaces;j++, surface++)
+ {
+ surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
+ numshadowmeshtriangles += surface->num_triangles;
+ }
+ mod->brush.shadowmesh = Mod_ShadowMesh_Begin(mod->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
+ for (j = 0, surface = mod->data_surfaces;j < mod->num_surfaces;j++, surface++)
+ if (surface->num_triangles > 0)
+ Mod_ShadowMesh_AddMesh(mod->mempool, mod->brush.shadowmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
+ mod->brush.shadowmesh = Mod_ShadowMesh_Finish(mod->mempool, mod->brush.shadowmesh, false, true, false);
+ if (mod->brush.shadowmesh)
+ Mod_BuildTriangleNeighbors(mod->brush.shadowmesh->neighbor3i, mod->brush.shadowmesh->element3i, mod->brush.shadowmesh->numtriangles);
+
+ return numshadowmeshtriangles;
+}
+
void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
{
int i, j, k;
dmodel_t *bm;
float dist, modelyawradius, modelradius;
msurface_t *surface;
- int numshadowmeshtriangles;
hullinfo_t hullinfo;
int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256];
model_brush_lightstyleinfo_t styleinfo[256];
mod->numskins = 1;
// make a single combined shadow mesh to allow optimized shadow volume creation
- numshadowmeshtriangles = 0;
- for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
- {
- surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
- numshadowmeshtriangles += surface->num_triangles;
- }
- loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
- for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
- Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
- loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
- Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
+ Mod_Q1BSP_CreateShadowMesh(loadmodel);
if (loadmodel->brush.numsubmodels)
loadmodel->brush.submodels = (dp_model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
}
//mod->brushq1.num_visleafs = bm->visleafs;
+ // build a Bounding Interval Hierarchy for culling triangles in light rendering
+ Mod_MakeCollisionBIH(mod, true, &mod->render_bih);
+
if (mod_q1bsp_polygoncollisions.integer)
{
- Mod_MakeCollisionBIH(mod, true, &mod->collision_bih);
+ mod->collision_bih = mod->render_bih;
// point traces and contents checks still use the bsp tree
mod->TraceLine = Mod_CollisionBIH_TraceLine;
mod->TraceBox = Mod_CollisionBIH_TraceBox;
+ mod->TraceBrush = Mod_CollisionBIH_TraceBrush;
}
- else
- Mod_MakeCollisionBIH(mod, true, &mod->render_bih);
// generate VBOs and other shared data before cloning submodels
if (i == 0)
loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
+ if(in->color4ub[0] != 255 || in->color4ub[1] != 255 || in->color4ub[2] != 255)
+ loadmodel->lit = true;
}
}
static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
{
q3dlightmap_t *input_pointer;
- int i, j, k, count, power, power2, endlightmap, mergewidth, mergeheight;
+ int i;
+ int j;
+ int k;
+ int count;
+ int powerx;
+ int powery;
+ int powerxy;
+ int powerdxy;
+ int endlightmap;
+ int mergegoal;
+ int lightmapindex;
+ int realcount;
+ int realindex;
+ int mergedwidth;
+ int mergedheight;
+ int mergedcolumns;
+ int mergedrows;
+ int mergedrowsxcolumns;
+ int size;
+ int bytesperpixel;
+ int rgbmap[3];
unsigned char *c;
-
- unsigned char *convertedpixels;
+ unsigned char *mergedpixels;
+ unsigned char *mergeddeluxepixels;
+ unsigned char *mergebuf;
char mapname[MAX_QPATH];
- int size, bytesperpixel, rgbmap[3];
qboolean external;
unsigned char *inpixels[10000]; // max count q3map2 can output (it uses 4 digits)
}
}
- convertedpixels = (unsigned char *) Mem_Alloc(tempmempool, size*size*4);
loadmodel->brushq3.lightmapsize = size;
loadmodel->brushq3.num_originallightmaps = count;
// figure out what the most reasonable merge power is within limits
- loadmodel->brushq3.num_lightmapmergepower = 0;
-
- for(i = 0; (128 << i) < size; ++i)
- ;
- // i is now 0 for 128, 1 for 256, etc
-
- for (power = 1;power + i <= mod_q3bsp_lightmapmergepower.integer && (size << power) <= (int)vid.maxtexturesize_2d && (1 << (power * 2)) < 4 * (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)); power++)
- loadmodel->brushq3.num_lightmapmergepower = power;
-
- loadmodel->brushq3.num_lightmapmerge = 1 << loadmodel->brushq3.num_lightmapmergepower;
-
- loadmodel->brushq3.num_mergedlightmaps = ((count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) + (1 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
+ // find the appropriate NxN dimensions to merge to, to avoid wasted space
+ realcount = count >> (int)loadmodel->brushq3.deluxemapping;
+
+ // figure out how big the merged texture has to be
+ mergegoal = 128<<bound(0, mod_q3bsp_lightmapmergepower.integer, 6);
+ mergegoal = bound(size, mergegoal, (int)vid.maxtexturesize_2d);
+ while (mergegoal > size && mergegoal * mergegoal / 4 >= size * size * realcount)
+ mergegoal /= 2;
+ mergedwidth = mergegoal;
+ mergedheight = mergegoal;
+ // choose non-square size (2x1 aspect) if only half the space is used;
+ // this really only happens when the entire set fits in one texture, if
+ // there are multiple textures, we don't worry about shrinking the last
+ // one to fit, because the driver prefers the same texture size on
+ // consecutive draw calls...
+ if (mergedwidth * mergedheight / 2 >= size*size*realcount)
+ mergedheight /= 2;
+
+ loadmodel->brushq3.num_lightmapmergedwidthpower = 0;
+ loadmodel->brushq3.num_lightmapmergedheightpower = 0;
+ while (mergedwidth > size<<loadmodel->brushq3.num_lightmapmergedwidthpower)
+ loadmodel->brushq3.num_lightmapmergedwidthpower++;
+ while (mergedheight > size<<loadmodel->brushq3.num_lightmapmergedheightpower)
+ loadmodel->brushq3.num_lightmapmergedheightpower++;
+ loadmodel->brushq3.num_lightmapmergedwidthheightdeluxepower = loadmodel->brushq3.num_lightmapmergedwidthpower + loadmodel->brushq3.num_lightmapmergedheightpower + (loadmodel->brushq3.deluxemapping ? 1 : 0);
+
+ powerx = loadmodel->brushq3.num_lightmapmergedwidthpower;
+ powery = loadmodel->brushq3.num_lightmapmergedheightpower;
+ powerxy = powerx+powery;
+ powerdxy = loadmodel->brushq3.deluxemapping + powerxy;
+
+ mergedcolumns = 1 << powerx;
+ mergedrows = 1 << powery;
+ mergedrowsxcolumns = 1 << powerxy;
+
+ loadmodel->brushq3.num_mergedlightmaps = (realcount + (1 << powerxy) - 1) >> powerxy;
loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
if (loadmodel->brushq3.deluxemapping)
loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
loadmodel->texturepool = R_AllocTexturePool();
- power = loadmodel->brushq3.num_lightmapmergepower;
- power2 = power * 2;
+ mergedpixels = (unsigned char *) Mem_Alloc(tempmempool, mergedwidth * mergedheight * 4);
+ mergeddeluxepixels = loadmodel->brushq3.deluxemapping ? (unsigned char *) Mem_Alloc(tempmempool, mergedwidth * mergedheight * 4) : NULL;
for (i = 0;i < count;i++)
{
// figure out which merged lightmap texture this fits into
- int lightmapindex = i >> (loadmodel->brushq3.deluxemapping + power2);
- for (k = 0;k < size*size;k++)
- {
- convertedpixels[k*4+0] = inpixels[i][k*bytesperpixel+rgbmap[0]];
- convertedpixels[k*4+1] = inpixels[i][k*bytesperpixel+rgbmap[1]];
- convertedpixels[k*4+2] = inpixels[i][k*bytesperpixel+rgbmap[2]];
- convertedpixels[k*4+3] = 255;
- }
- if (loadmodel->brushq3.num_lightmapmergepower > 0)
+ realindex = i >> (int)loadmodel->brushq3.deluxemapping;
+ lightmapindex = i >> powerdxy;
+
+ // choose the destination address
+ mergebuf = (loadmodel->brushq3.deluxemapping && (i & 1)) ? mergeddeluxepixels : mergedpixels;
+ mergebuf += 4 * (realindex & (mergedcolumns-1))*size + 4 * ((realindex >> powerx) & (mergedrows-1))*mergedwidth*size;
+ if ((i & 1) == 0 || !loadmodel->brushq3.deluxemapping)
+ Con_Printf("copying original lightmap %i (%ix%i) to %i (at %i,%i)\n", i, size, size, lightmapindex, (realindex & (mergedcolumns-1))*size, ((realindex >> powerx) & (mergedrows-1))*size);
+
+ // convert pixels from RGB or BGRA while copying them into the destination rectangle
+ for (j = 0;j < size;j++)
+ for (k = 0;k < size;k++)
{
- // if the lightmap has not been allocated yet, create it
- if (!loadmodel->brushq3.data_lightmaps[lightmapindex])
- {
- // create a lightmap only as large as necessary to hold the
- // remaining size*size blocks
- // if there are multiple merged lightmap textures then they will
- // all be full size except the last one which may be smaller
- // because it only needs to the remaining blocks, and it will often
- // be odd sizes like 2048x512 due to only being 25% full or so.
- j = (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) - (lightmapindex << power2);
- for (mergewidth = 1;mergewidth < j && mergewidth < (1 << power);mergewidth *= 2)
- ;
- for (mergeheight = 1;mergewidth*mergeheight < j && mergeheight < (1 << power);mergeheight *= 2)
- ;
- if (developer_loading.integer)
- Con_Printf("lightmap merge texture #%i is %ix%i (%i of %i used)\n", lightmapindex, mergewidth*size, mergeheight*size, min(j, mergewidth*mergeheight), mergewidth*mergeheight);
- loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES), -1, NULL);
- if (loadmodel->brushq3.data_deluxemaps)
- loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES), -1, NULL);
- }
- mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
- mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
- j = (i >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) & ((1 << power2) - 1);
- if (loadmodel->brushq3.deluxemapping && (i & 1))
- R_UpdateTexture(loadmodel->brushq3.data_deluxemaps[lightmapindex], convertedpixels, (j % mergewidth) * size, (j / mergewidth) * size, size, size);
- else
- R_UpdateTexture(loadmodel->brushq3.data_lightmaps [lightmapindex], convertedpixels, (j % mergewidth) * size, (j / mergewidth) * size, size, size);
+ mergebuf[(j*mergedwidth+k)*4+0] = inpixels[i][(j*size+k)*bytesperpixel+rgbmap[0]];
+ mergebuf[(j*mergedwidth+k)*4+1] = inpixels[i][(j*size+k)*bytesperpixel+rgbmap[1]];
+ mergebuf[(j*mergedwidth+k)*4+2] = inpixels[i][(j*size+k)*bytesperpixel+rgbmap[2]];
+ mergebuf[(j*mergedwidth+k)*4+3] = 255;
}
- else
+
+ // upload texture if this was the last tile being written to the texture
+ if (((realindex + 1) & (mergedrowsxcolumns - 1)) == 0 || (realindex + 1) == realcount)
{
- // figure out which merged lightmap texture this fits into
if (loadmodel->brushq3.deluxemapping && (i & 1))
- loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), size, size, convertedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
+ loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergedwidth, mergedheight, mergeddeluxepixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
else
- loadmodel->brushq3.data_lightmaps [lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), size, size, convertedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
+ loadmodel->brushq3.data_lightmaps [lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergedwidth, mergedheight, mergedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
}
}
- Mem_Free(convertedpixels);
+ if (mergeddeluxepixels)
+ Mem_Free(mergeddeluxepixels);
+ Mem_Free(mergedpixels);
if(external)
{
for(i = 0; i < count; ++i)
}
else
{
- out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
+ out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n >> loadmodel->brushq3.num_lightmapmergedwidthheightdeluxepower];
if (loadmodel->brushq3.deluxemapping)
- out->deluxemaptexture = loadmodel->brushq3.data_deluxemaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
+ out->deluxemaptexture = loadmodel->brushq3.data_deluxemaps[n >> loadmodel->brushq3.num_lightmapmergedwidthheightdeluxepower];
+ loadmodel->lit = true;
}
}
Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, true, false);
if (collisiontriangles)
{
- loadmodel->brush.data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, collisionvertices * sizeof(float[3]));
- loadmodel->brush.data_collisionelement3i = Mem_Alloc(loadmodel->mempool, collisiontriangles * sizeof(int[3]));
+ loadmodel->brush.data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, collisionvertices * sizeof(float[3]));
+ loadmodel->brush.data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, collisiontriangles * sizeof(int[3]));
}
meshvertices = 0;
meshtriangles = 0;
if(out->num_vertices && out->num_triangles)
continue;
if(out->num_vertices == 0)
- Con_Printf("Mod_Q3BSP_LoadFaces: surface %d has no vertices, ignoring\n", i);
- if(out->num_triangles == 0)
- Con_Printf("Mod_Q3BSP_LoadFaces: surface %d has no triangles, ignoring\n", i);
+ {
+ Con_Printf("Mod_Q3BSP_LoadFaces: surface %d (texture %s) has no vertices, ignoring\n", i, out->texture ? out->texture->name : "(none)");
+ if(out->num_triangles == 0)
+ Con_Printf("Mod_Q3BSP_LoadFaces: surface %d (texture %s) has no triangles, ignoring\n", i, out->texture ? out->texture->name : "(none)");
+ }
+ else if(out->num_triangles == 0)
+ Con_Printf("Mod_Q3BSP_LoadFaces: surface %d (texture %s, near %f %f %f) has no triangles, ignoring\n", i, out->texture ? out->texture->name : "(none)",
+ (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[0 * 3 + 0],
+ (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[1 * 3 + 0],
+ (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[2 * 3 + 0]);
}
// for per pixel lighting
- Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, true);
+ Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
// generate ushort elements array if possible
if (loadmodel->surfmesh.data_element3s)
q3dlightgrid_t *a, *s;
// scale lighting by lightstyle[0] so that darkmode in dpmod works properly
- stylescale = r_refdef.scene.rtlightstylevalue[0];
+ if (vid.renderpath == RENDERPATH_GL20)
+ stylescale = 1; // added while render
+ else
+ stylescale = r_refdef.scene.rtlightstylevalue[0];
if (!model->brushq3.num_lightgrid)
{
if (VectorCompare(start, end))
Mod_CollisionBIH_TracePoint(model, frameblend, skeleton, trace, shiftstart, hitsupercontentsmask);
else
- {
Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
- VectorSubtract(trace->endpos, boxmins, trace->endpos);
- }
return;
}
Mod_CollisionBIH_TraceBrush_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, &thisbrush_start.brush, &thisbrush_end.brush, segmentmins, segmentmaxs);
}
+void Mod_CollisionBIH_TraceBrush(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, colbrushf_t *start, colbrushf_t *end, int hitsupercontentsmask)
+{
+ float segmentmins[3], segmentmaxs[3];
+
+ if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(start->mins, start->maxs) && VectorCompare(end->mins, end->maxs))
+ {
+ if (VectorCompare(start->mins, end->mins))
+ Mod_CollisionBIH_TracePoint(model, frameblend, skeleton, trace, start->mins, hitsupercontentsmask);
+ else
+ Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, start->mins, end->mins, hitsupercontentsmask);
+ return;
+ }
+
+ // box trace, performed as brush trace
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = 1;
+ trace->realfraction = 1;
+ trace->hitsupercontentsmask = hitsupercontentsmask;
+ segmentmins[0] = min(start->mins[0], end->mins[0]);
+ segmentmins[1] = min(start->mins[1], end->mins[1]);
+ segmentmins[2] = min(start->mins[2], end->mins[2]);
+ segmentmaxs[0] = max(start->maxs[0], end->maxs[0]);
+ segmentmaxs[1] = max(start->maxs[1], end->maxs[1]);
+ segmentmaxs[2] = max(start->maxs[2], end->maxs[2]);
+ Mod_CollisionBIH_TraceBrush_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, start, end, segmentmins, segmentmaxs);
+}
+
int Mod_CollisionBIH_PointSuperContents(struct model_s *model, int frame, const vec3_t point)
{
trace_t trace;
if (VectorCompare(start, end))
Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, shiftstart, hitsupercontentsmask);
else
- {
Mod_Q3BSP_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
- VectorSubtract(trace->endpos, boxmins, trace->endpos);
- }
return;
}
Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, &thisbrush_start.brush, &thisbrush_end.brush, ++markframe, segmentmins, segmentmaxs);
}
+void Mod_Q3BSP_TraceBrush(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, colbrushf_t *start, colbrushf_t *end, int hitsupercontentsmask)
+{
+ float segmentmins[3], segmentmaxs[3];
+ int i;
+ msurface_t *surface;
+ q3mbrush_t *brush;
+
+ if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(start->mins, start->maxs) && VectorCompare(end->mins, end->maxs))
+ {
+ if (VectorCompare(start->mins, end->mins))
+ Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, start->mins, hitsupercontentsmask);
+ else
+ Mod_Q3BSP_TraceLine(model, frameblend, skeleton, trace, start->mins, end->mins, hitsupercontentsmask);
+ return;
+ }
+
+ // box trace, performed as brush trace
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = 1;
+ trace->realfraction = 1;
+ trace->hitsupercontentsmask = hitsupercontentsmask;
+ segmentmins[0] = min(start->mins[0], end->mins[0]);
+ segmentmins[1] = min(start->mins[1], end->mins[1]);
+ segmentmins[2] = min(start->mins[2], end->mins[2]);
+ segmentmaxs[0] = max(start->maxs[0], end->maxs[0]);
+ segmentmaxs[1] = max(start->maxs[1], end->maxs[1]);
+ segmentmaxs[2] = max(start->maxs[2], end->maxs[2]);
+ if (mod_collision_bih.integer)
+ Mod_CollisionBIH_TraceBrush_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, start, end, segmentmins, segmentmaxs);
+ else if (model->brush.submodel)
+ {
+ for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+ if (brush->colbrushf && BoxesOverlap(segmentmins, segmentmaxs, brush->colbrushf->mins, brush->colbrushf->maxs))
+ Collision_TraceBrushBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
+ if (mod_q3bsp_curves_collisions.integer)
+ for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+ if (surface->num_collisiontriangles && BoxesOverlap(segmentmins, segmentmaxs, surface->mins, surface->maxs))
+ Collision_TraceBrushTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+ }
+ else
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, ++markframe, segmentmins, segmentmaxs);
+}
+
static int Mod_Q3BSP_PointSuperContents(struct model_s *model, int frame, const vec3_t point)
{
int i;
return NULL;
// allocate the memory for the BIH leaf nodes
- bihleafs = Mem_Alloc(loadmodel->mempool, sizeof(bih_leaf_t) * bihnumleafs);
+ bihleafs = (bih_leaf_t *)Mem_Alloc(loadmodel->mempool, sizeof(bih_leaf_t) * bihnumleafs);
// now populate the BIH leaf nodes
bihleafindex = 0;
// allocate buffers for the produced and temporary data
bihmaxnodes = bihnumleafs - 1;
- bihnodes = Mem_Alloc(loadmodel->mempool, sizeof(bih_node_t) * bihmaxnodes);
- temp_leafsort = Mem_Alloc(loadmodel->mempool, sizeof(int) * bihnumleafs * 2);
+ bihnodes = (bih_node_t *)Mem_Alloc(loadmodel->mempool, sizeof(bih_node_t) * bihmaxnodes);
+ temp_leafsort = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int) * bihnumleafs * 2);
temp_leafsortscratch = temp_leafsort + bihnumleafs;
// now build it
if (out->maxnodes > out->numnodes)
{
out->maxnodes = out->numnodes;
- out->nodes = Mem_Realloc(loadmodel->mempool, out->nodes, out->numnodes * sizeof(bih_node_t));
+ out->nodes = (bih_node_t *)Mem_Realloc(loadmodel->mempool, out->nodes, out->numnodes * sizeof(bih_node_t));
}
return out;
void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
{
- int i, j, numshadowmeshtriangles, lumps;
+ int i, j, lumps;
q3dheader_t *header;
float corner[3], yawradius, modelradius;
- msurface_t *surface;
mod->modeldatatypestring = "Q3BSP";
mod->soundfromcenter = true;
mod->TraceBox = Mod_Q3BSP_TraceBox;
+ mod->TraceBrush = Mod_Q3BSP_TraceBrush;
mod->TraceLine = Mod_Q3BSP_TraceLine;
mod->TracePoint = Mod_Q3BSP_TracePoint;
mod->PointSuperContents = Mod_Q3BSP_PointSuperContents;
loadmodel->brush.supportwateralpha = true;
// make a single combined shadow mesh to allow optimized shadow volume creation
- numshadowmeshtriangles = 0;
- if (cls.state != ca_dedicated)
- {
- for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
- {
- surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
- numshadowmeshtriangles += surface->num_triangles;
- }
- loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
- for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
- if (surface->num_triangles > 0)
- Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
- loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
- if (loadmodel->brush.shadowmesh)
- Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
- }
+ Mod_Q1BSP_CreateShadowMesh(loadmodel);
loadmodel->brush.num_leafs = 0;
Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
typedef struct objvertex_s
{
int nextindex;
+ int submodelindex;
int textureindex;
float v[3];
float vt[2];
}
objvertex_t;
+static unsigned char nobsp_pvs[1] = {1};
+
void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
{
const char *textbase = (char *)buffer, *text = textbase;
char *argv[512];
char line[1024];
char materialname[MAX_QPATH];
- int i, j, numvertices, firstvertex, firsttriangle, elementindex, vertexindex, numsurfaces, surfacevertices, surfacetriangles, surfaceelements;
+ int i, j, l, numvertices, firstvertex, firsttriangle, elementindex, vertexindex, surfacevertices, surfacetriangles, surfaceelements, submodelindex = 0;
int index1, index2, index3;
objvertex_t vfirst, vprev, vcurrent;
int argc;
int maxvt = 0, numvt = 1;
int maxvn = 0, numvn = 1;
char *texturenames = NULL;
- float dist, modelradius, modelyawradius;
+ float dist, modelradius, modelyawradius, yawradius;
float *v = NULL;
float *vt = NULL;
float *vn = NULL;
float mins[3];
float maxs[3];
+ float corner[3];
objvertex_t *thisvertex = NULL;
int vertexhashindex;
int *vertexhashtable = NULL;
int vertexhashcount = 0;
skinfile_t *skinfiles = NULL;
unsigned char *data = NULL;
+ int *submodelfirstsurface;
+ msurface_t *surface;
+ msurface_t *tempsurfaces;
memset(&vfirst, 0, sizeof(vfirst));
memset(&vprev, 0, sizeof(vprev));
loadmodel->type = mod_obj;
loadmodel->soundfromcenter = true;
loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+ loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
// parse the OBJ text now
for(;;)
{
+ static char emptyarg[1] = "";
if (!*text)
break;
linenumber++;
line[linelen] = text[linelen];
line[linelen] = 0;
for (argc = 0;argc < 4;argc++)
- argv[argc] = "";
+ argv[argc] = emptyarg;
argc = 0;
s = line;
while (*s == ' ' || *s == '\t')
index3 = numvn - index3;
vcurrent.nextindex = -1;
vcurrent.textureindex = textureindex;
- VectorCopy(v + 3*index1, vcurrent.v);
- Vector2Copy(vt + 2*index2, vcurrent.vt);
- VectorCopy(vn + 3*index3, vcurrent.vn);
+ vcurrent.submodelindex = submodelindex;
+ if (v && index1 >= 0 && index1 < numv)
+ VectorCopy(v + 3*index1, vcurrent.v);
+ if (vt && index2 >= 0 && index2 < numvt)
+ Vector2Copy(vt + 2*index2, vcurrent.vt);
+ if (vn && index3 >= 0 && index3 < numvn)
+ VectorCopy(vn + 3*index3, vcurrent.vn);
if (numtriangles == 0)
{
VectorCopy(vcurrent.v, mins);
}
}
else if (!strcmp(argv[0], "o") || !strcmp(argv[0], "g"))
- ;
+ {
+ submodelindex = atof(argv[1]);
+ loadmodel->brush.numsubmodels = max(submodelindex + 1, loadmodel->brush.numsubmodels);
+ }
else if (!strcmp(argv[0], "usemtl"))
{
for (i = 0;i < numtextures;i++)
loadmodel->radius2 = modelradius * modelradius;
// allocate storage for triangles
- loadmodel->num_surfaces = loadmodel->nummodelsurfaces = numsurfaces = numtextures;
- loadmodel->surfmesh.data_element3i = Mem_Alloc(loadmodel->mempool, numtriangles * sizeof(int[3]));
- loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t));
+ loadmodel->surfmesh.data_element3i = (int *)Mem_Alloc(loadmodel->mempool, numtriangles * sizeof(int[3]));
// allocate vertex hash structures to build an optimal vertex subset
vertexhashsize = numtriangles*2;
- vertexhashtable = Mem_Alloc(loadmodel->mempool, sizeof(int) * vertexhashsize);
+ vertexhashtable = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int) * vertexhashsize);
memset(vertexhashtable, 0xFF, sizeof(int) * vertexhashsize);
- vertexhashdata = Mem_Alloc(loadmodel->mempool, sizeof(*vertexhashdata) * numtriangles*3);
+ vertexhashdata = (objvertex_t *)Mem_Alloc(loadmodel->mempool, sizeof(*vertexhashdata) * numtriangles*3);
vertexhashcount = 0;
// gather surface stats for assigning vertex/triangle ranges
firstvertex = 0;
firsttriangle = 0;
elementindex = 0;
- for (textureindex = 0;textureindex < numtextures;textureindex++)
+ loadmodel->num_surfaces = 0;
+ // allocate storage for the worst case number of surfaces, later we resize
+ tempsurfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, numtextures * loadmodel->brush.numsubmodels * sizeof(msurface_t));
+ submodelfirstsurface = (int *)Mem_Alloc(loadmodel->mempool, (loadmodel->brush.numsubmodels+1) * sizeof(int));
+ surface = tempsurfaces;
+ for (submodelindex = 0;submodelindex < loadmodel->brush.numsubmodels;submodelindex++)
{
- msurface_t *surface = loadmodel->data_surfaces + textureindex;
- // copy the mins/maxs of the model backwards so that the first vertex
- // added will set the surface bounds to a point
- VectorCopy(loadmodel->normalmaxs, surface->mins);
- VectorCopy(loadmodel->normalmins, surface->maxs);
- surfacevertices = 0;
- surfaceelements = 0;
- for (vertexindex = 0;vertexindex < numtriangles*3;vertexindex++)
- {
- thisvertex = vertices + vertexindex;
- if (thisvertex->textureindex != textureindex)
- continue;
- surface->mins[0] = min(surface->mins[0], thisvertex->v[0]);
- surface->mins[1] = min(surface->mins[1], thisvertex->v[1]);
- surface->mins[2] = min(surface->mins[2], thisvertex->v[2]);
- surface->maxs[0] = max(surface->maxs[0], thisvertex->v[0]);
- surface->maxs[1] = max(surface->maxs[1], thisvertex->v[1]);
- surface->maxs[2] = max(surface->maxs[2], thisvertex->v[2]);
- vertexhashindex = (unsigned int)(thisvertex->v[0] * 3571 + thisvertex->v[0] * 1777 + thisvertex->v[0] * 457) % (unsigned int)vertexhashsize;
- for (i = vertexhashtable[vertexhashindex];i >= 0;i = vertexhashdata[i].nextindex)
+ submodelfirstsurface[submodelindex] = loadmodel->num_surfaces;
+ for (textureindex = 0;textureindex < numtextures;textureindex++)
+ {
+ for (vertexindex = 0;vertexindex < numtriangles*3;vertexindex++)
{
- vdata = vertexhashdata + i;
- if (vdata->textureindex == thisvertex->textureindex && VectorCompare(thisvertex->v, vdata->v) && VectorCompare(thisvertex->vn, vdata->vn) && Vector2Compare(thisvertex->vt, vdata->vt))
+ thisvertex = vertices + vertexindex;
+ if (thisvertex->submodelindex == submodelindex && thisvertex->textureindex == textureindex)
break;
}
- if (i < 0)
+ // skip the surface creation if there are no triangles for it
+ if (vertexindex == numtriangles*3)
+ continue;
+ // create a surface for these vertices
+ surfacevertices = 0;
+ surfaceelements = 0;
+ // we hack in a texture index in the surface to be fixed up later...
+ surface->texture = (texture_t *)((size_t)textureindex);
+ // calculate bounds as we go
+ VectorCopy(thisvertex->v, surface->mins);
+ VectorCopy(thisvertex->v, surface->maxs);
+ for (;vertexindex < numtriangles*3;vertexindex++)
{
- i = vertexhashcount++;
- vdata = vertexhashdata + i;
- *vdata = *thisvertex;
- vdata->nextindex = vertexhashtable[vertexhashindex];
- vertexhashtable[vertexhashindex] = i;
- surfacevertices++;
+ thisvertex = vertices + vertexindex;
+ if (thisvertex->submodelindex != submodelindex)
+ continue;
+ if (thisvertex->textureindex != textureindex)
+ continue;
+ // add vertex to surface bounds
+ surface->mins[0] = min(surface->mins[0], thisvertex->v[0]);
+ surface->mins[1] = min(surface->mins[1], thisvertex->v[1]);
+ surface->mins[2] = min(surface->mins[2], thisvertex->v[2]);
+ surface->maxs[0] = max(surface->maxs[0], thisvertex->v[0]);
+ surface->maxs[1] = max(surface->maxs[1], thisvertex->v[1]);
+ surface->maxs[2] = max(surface->maxs[2], thisvertex->v[2]);
+ // add the vertex if it is not found in the merged set, and
+ // get its index (triangle element) for the surface
+ vertexhashindex = (unsigned int)(thisvertex->v[0] * 3571 + thisvertex->v[0] * 1777 + thisvertex->v[0] * 457) % (unsigned int)vertexhashsize;
+ for (i = vertexhashtable[vertexhashindex];i >= 0;i = vertexhashdata[i].nextindex)
+ {
+ vdata = vertexhashdata + i;
+ if (vdata->submodelindex == thisvertex->submodelindex && vdata->textureindex == thisvertex->textureindex && VectorCompare(thisvertex->v, vdata->v) && VectorCompare(thisvertex->vn, vdata->vn) && Vector2Compare(thisvertex->vt, vdata->vt))
+ break;
+ }
+ if (i < 0)
+ {
+ i = vertexhashcount++;
+ vdata = vertexhashdata + i;
+ *vdata = *thisvertex;
+ vdata->nextindex = vertexhashtable[vertexhashindex];
+ vertexhashtable[vertexhashindex] = i;
+ surfacevertices++;
+ }
+ loadmodel->surfmesh.data_element3i[elementindex++] = i;
+ surfaceelements++;
}
- loadmodel->surfmesh.data_element3i[elementindex++] = i;
- surfaceelements++;
- }
- surfacetriangles = surfaceelements / 3;
- surface->num_vertices = surfacevertices;
- surface->num_triangles = surfacetriangles;
- surface->num_firstvertex = firstvertex;
- surface->num_firsttriangle = firsttriangle;
- firstvertex += surface->num_vertices;
- firsttriangle += surface->num_triangles;
- }
+ surfacetriangles = surfaceelements / 3;
+ surface->num_vertices = surfacevertices;
+ surface->num_triangles = surfacetriangles;
+ surface->num_firstvertex = firstvertex;
+ surface->num_firsttriangle = firsttriangle;
+ firstvertex += surface->num_vertices;
+ firsttriangle += surface->num_triangles;
+ surface++;
+ loadmodel->num_surfaces++;
+ }
+ }
+ submodelfirstsurface[submodelindex] = loadmodel->num_surfaces;
numvertices = firstvertex;
+ loadmodel->data_surfaces = (msurface_t *)Mem_Realloc(loadmodel->mempool, tempsurfaces, loadmodel->num_surfaces * sizeof(msurface_t));
+ tempsurfaces = NULL;
// allocate storage for final mesh data
loadmodel->num_textures = numtextures * loadmodel->numskins;
loadmodel->num_texturesperskin = numtextures;
- data = (unsigned char *)Mem_Alloc(loadmodel->mempool, numsurfaces * sizeof(int) + numsurfaces * loadmodel->numskins * sizeof(texture_t) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0) + numvertices * sizeof(float[14]));
- loadmodel->sortedmodelsurfaces = (int *)data;data += numsurfaces * sizeof(int);
- loadmodel->data_textures = (texture_t *)data;data += numsurfaces * loadmodel->numskins * sizeof(texture_t);
+ data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0) + numvertices * sizeof(float[14]) + loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
+ loadmodel->brush.submodels = (dp_model_t **)data;data += loadmodel->brush.numsubmodels * sizeof(dp_model_t *);
+ loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+ loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
loadmodel->surfmesh.num_vertices = numvertices;
loadmodel->surfmesh.num_triangles = numtriangles;
loadmodel->surfmesh.data_neighbor3i = (int *)data;data += numtriangles * sizeof(int[3]);
Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + textureindex, skinfiles, texturenames + textureindex*MAX_QPATH, texturenames + textureindex*MAX_QPATH);
Mod_FreeSkinFiles(skinfiles);
- // set the surface textures
- for (textureindex = 0;textureindex < numtextures;textureindex++)
- {
- msurface_t *surface = loadmodel->data_surfaces + textureindex;
- surface->texture = loadmodel->data_textures + textureindex;
- }
+ // set the surface textures to their real values now that we loaded them...
+ for (i = 0;i < loadmodel->num_surfaces;i++)
+ loadmodel->data_surfaces[i].texture = loadmodel->data_textures + (size_t)loadmodel->data_surfaces[i].texture;
// free data
Mem_Free(vertices);
Mem_Free(vertexhashtable);
Mem_Free(vertexhashdata);
+ // make a single combined shadow mesh to allow optimized shadow volume creation
+ Mod_Q1BSP_CreateShadowMesh(loadmodel);
+
// compute all the mesh information that was not loaded from the file
- Mod_MakeSortedSurfaces(loadmodel);
if (loadmodel->surfmesh.data_element3s)
for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
// generate normals if the file did not have them
if (!VectorLength2(loadmodel->surfmesh.data_normal3f))
- Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
- Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, true);
+ Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0);
+ Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
- Mod_MakeCollisionBIH(loadmodel, true, &mod->collision_bih);
- mod->render_bih = mod->collision_bih;
+ // if this is a worldmodel and has no BSP tree, create a fake one for the purpose
+ loadmodel->brush.num_visleafs = 1;
+ loadmodel->brush.num_leafs = 1;
+ loadmodel->brush.num_nodes = 0;
+ loadmodel->brush.num_leafsurfaces = loadmodel->num_surfaces;
+ loadmodel->brush.data_leafs = (mleaf_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafs * sizeof(mleaf_t));
+ loadmodel->brush.data_nodes = (mnode_t *)loadmodel->brush.data_leafs;
+ loadmodel->brush.num_pvsclusters = 1;
+ loadmodel->brush.num_pvsclusterbytes = 1;
+ loadmodel->brush.data_pvsclusters = nobsp_pvs;
+ //if (loadmodel->num_nodes) loadmodel->data_nodes = (mnode_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_nodes * sizeof(mnode_t));
+ //loadmodel->data_leafsurfaces = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->num_leafsurfaces * sizeof(int));
+ loadmodel->brush.data_leafsurfaces = loadmodel->sortedmodelsurfaces;
+ VectorCopy(loadmodel->normalmins, loadmodel->brush.data_leafs->mins);
+ VectorCopy(loadmodel->normalmaxs, loadmodel->brush.data_leafs->maxs);
+ loadmodel->brush.data_leafs->combinedsupercontents = 0; // FIXME?
+ loadmodel->brush.data_leafs->clusterindex = 0;
+ loadmodel->brush.data_leafs->areaindex = 0;
+ loadmodel->brush.data_leafs->numleafsurfaces = loadmodel->brush.num_leafsurfaces;
+ loadmodel->brush.data_leafs->firstleafsurface = loadmodel->brush.data_leafsurfaces;
+ loadmodel->brush.data_leafs->numleafbrushes = 0;
+ loadmodel->brush.data_leafs->firstleafbrush = NULL;
+ loadmodel->brush.supportwateralpha = true;
+
+ 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++)
+ {
+ if (i > 0)
+ {
+ char name[10];
+ // duplicate the basic information
+ dpsnprintf(name, sizeof(name), "*%i", i);
+ mod = Mod_FindName(name, loadmodel->name);
+ // copy the base model to this one
+ *mod = *loadmodel;
+ // rename the clone back to its proper name
+ strlcpy(mod->name, name, sizeof(mod->name));
+ mod->brush.parentmodel = loadmodel;
+ // textures and memory belong to the main model
+ mod->texturepool = NULL;
+ mod->mempool = NULL;
+ mod->brush.GetPVS = NULL;
+ mod->brush.FatPVS = NULL;
+ mod->brush.BoxTouchingPVS = NULL;
+ mod->brush.BoxTouchingLeafPVS = NULL;
+ mod->brush.BoxTouchingVisibleLeafs = NULL;
+ mod->brush.FindBoxClusters = NULL;
+ mod->brush.LightPoint = NULL;
+ 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 = submodelfirstsurface[i];
+ mod->nummodelsurfaces = submodelfirstsurface[i+1] - submodelfirstsurface[i];
+ mod->firstmodelbrush = 0;
+ mod->nummodelbrushes = 0;
+ mod->sortedmodelsurfaces = loadmodel->sortedmodelsurfaces + mod->firstmodelsurface;
+ Mod_MakeSortedSurfaces(mod);
+
+ VectorClear(mod->normalmins);
+ VectorClear(mod->normalmaxs);
+ l = false;
+ for (j = 0;j < mod->nummodelsurfaces;j++)
+ {
+ const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
+ const float *v = mod->surfmesh.data_vertex3f + 3 * surface->num_firstvertex;
+ int k;
+ if (!surface->num_vertices)
+ continue;
+ if (!l)
+ {
+ l = true;
+ VectorCopy(v, mod->normalmins);
+ VectorCopy(v, mod->normalmaxs);
+ }
+ for (k = 0;k < surface->num_vertices;k++, v += 3)
+ {
+ mod->normalmins[0] = min(mod->normalmins[0], v[0]);
+ mod->normalmins[1] = min(mod->normalmins[1], v[1]);
+ mod->normalmins[2] = min(mod->normalmins[2], v[2]);
+ mod->normalmaxs[0] = max(mod->normalmaxs[0], v[0]);
+ mod->normalmaxs[1] = max(mod->normalmaxs[1], v[1]);
+ mod->normalmaxs[2] = max(mod->normalmaxs[2], v[2]);
+ }
+ }
+ corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
+ corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
+ corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
+ modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
+ yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
+ mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
+ mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
+ mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
+ mod->yawmins[0] = mod->yawmins[1] = -yawradius;
+ mod->yawmins[2] = mod->normalmins[2];
+ mod->yawmaxs[2] = mod->normalmaxs[2];
+ mod->radius = modelradius;
+ mod->radius2 = modelradius * modelradius;
+
+ // this gets altered below if sky or water is used
+ mod->DrawSky = NULL;
+ mod->DrawAddWaterPlanes = NULL;
+
+ for (j = 0;j < mod->nummodelsurfaces;j++)
+ if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & MATERIALFLAG_SKY)
+ break;
+ if (j < mod->nummodelsurfaces)
+ mod->DrawSky = R_Q1BSP_DrawSky;
+
+ for (j = 0;j < mod->nummodelsurfaces;j++)
+ if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+ break;
+ if (j < mod->nummodelsurfaces)
+ mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+
+ Mod_MakeCollisionBIH(mod, true, &mod->collision_bih);
+ mod->render_bih = mod->collision_bih;
+
+ // generate VBOs and other shared data before cloning submodels
+ if (i == 0)
+ Mod_BuildVBOs();
+ }
+ mod = loadmodel;
+ Mem_Free(submodelfirstsurface);
+
+ Con_DPrintf("Stats for obj model \"%s\": %i faces, %i nodes, %i leafs, %i clusters, %i clusterportals, mesh: %i vertices, %i triangles, %i surfaces\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->num_surfaces);
}