//cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"};
-cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
cvar_t r_novis = {0, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
cvar_t r_picmipworld = {CVAR_SAVE, "r_picmipworld", "1", "whether gl_picmip shall apply to world textures too"};
cvar_t r_nosurftextures = {0, "r_nosurftextures", "0", "pretends there was no texture lump found in the q1bsp/hlbsp loading (useful for debugging this rare case)"};
cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4", "maximum error tolerance on curve subdivision for rendering purposes (in other words, the curves will be given as many polygons as necessary to represent curves at this quality)"};
-cvar_t r_subdivisions_mintess = {0, "r_subdivisions_mintess", "1", "minimum number of subdivisions (values above 1 will smooth curves that don't need it)"};
+cvar_t r_subdivisions_mintess = {0, "r_subdivisions_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
cvar_t r_subdivisions_maxtess = {0, "r_subdivisions_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536", "maximum vertices allowed per subdivided curve"};
cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"};
-cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess", "1", "minimum number of subdivisions (values above 1 will smooth curves that don't need it)"};
+cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
cvar_t r_subdivisions_collision_maxtess = {0, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"};
cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
+cvar_t mod_q3bsp_curves_collisions_stride = {0, "mod_q3bsp_curves_collisions_stride", "16", "collisions against curves: optimize performance by doing a combined collision check for this triangle amount first"};
+cvar_t mod_q3bsp_curves_stride = {0, "mod_q3bsp_curves_stride", "16", "particle effect collisions against curves: optimize performance by doing a combined collision check for this triangle amount first"};
cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
cvar_t mod_q3bsp_lightmapmergepower = {CVAR_SAVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."};
void Mod_BrushInit(void)
{
// Cvar_RegisterVariable(&r_subdivide_size);
- Cvar_RegisterVariable(&halflifebsp);
Cvar_RegisterVariable(&r_novis);
Cvar_RegisterVariable(&r_picmipworld);
Cvar_RegisterVariable(&r_nosurftextures);
Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
+ Cvar_RegisterVariable(&mod_q3bsp_curves_collisions_stride);
+ Cvar_RegisterVariable(&mod_q3bsp_curves_stride);
Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower);
typedef struct findnonsolidlocationinfo_s
{
vec3_t center;
+ vec3_t absmin, absmax;
vec_t radius;
vec3_t nudge;
vec_t bestdist;
}
findnonsolidlocationinfo_t;
-static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
+static void Mod_Q1BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t *info, msurface_t *surface, int k)
{
- int i, surfacenum, k, *tri, *mark;
+ int i, *tri;
float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
+
+ tri = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle) + k * 3;
+ VectorCopy((info->model->surfmesh.data_vertex3f + tri[0] * 3), vert[0]);
+ VectorCopy((info->model->surfmesh.data_vertex3f + tri[1] * 3), vert[1]);
+ VectorCopy((info->model->surfmesh.data_vertex3f + tri[2] * 3), vert[2]);
+ VectorSubtract(vert[1], vert[0], edge[0]);
+ VectorSubtract(vert[2], vert[1], edge[1]);
+ CrossProduct(edge[1], edge[0], facenormal);
+ if (facenormal[0] || facenormal[1] || facenormal[2])
+ {
+ VectorNormalize(facenormal);
+ f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
+ if (f <= info->bestdist && f >= -info->bestdist)
+ {
+ VectorSubtract(vert[0], vert[2], edge[2]);
+ VectorNormalize(edge[0]);
+ VectorNormalize(edge[1]);
+ VectorNormalize(edge[2]);
+ CrossProduct(facenormal, edge[0], edgenormal[0]);
+ CrossProduct(facenormal, edge[1], edgenormal[1]);
+ CrossProduct(facenormal, edge[2], edgenormal[2]);
+ // face distance
+ if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
+ && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
+ && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
+ {
+ // we got lucky, the center is within the face
+ dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
+ if (dist < 0)
+ {
+ dist = -dist;
+ if (info->bestdist > dist)
+ {
+ info->bestdist = dist;
+ VectorScale(facenormal, (info->radius - -dist), info->nudge);
+ }
+ }
+ else
+ {
+ if (info->bestdist > dist)
+ {
+ info->bestdist = dist;
+ VectorScale(facenormal, (info->radius - dist), info->nudge);
+ }
+ }
+ }
+ else
+ {
+ // check which edge or vertex the center is nearest
+ for (i = 0;i < 3;i++)
+ {
+ f = DotProduct(info->center, edge[i]);
+ if (f >= DotProduct(vert[0], edge[i])
+ && f <= DotProduct(vert[1], edge[i]))
+ {
+ // on edge
+ VectorMA(info->center, -f, edge[i], point);
+ dist = sqrt(DotProduct(point, point));
+ if (info->bestdist > dist)
+ {
+ info->bestdist = dist;
+ VectorScale(point, (info->radius / dist), info->nudge);
+ }
+ // skip both vertex checks
+ // (both are further away than this edge)
+ i++;
+ }
+ else
+ {
+ // not on edge, check first vertex of edge
+ VectorSubtract(info->center, vert[i], point);
+ dist = sqrt(DotProduct(point, point));
+ if (info->bestdist > dist)
+ {
+ info->bestdist = dist;
+ VectorScale(point, (info->radius / dist), info->nudge);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
+{
+ int surfacenum, k, *mark;
msurface_t *surface;
for (surfacenum = 0, mark = leaf->firstleafsurface;surfacenum < leaf->numleafsurfaces;surfacenum++, mark++)
{
surface = info->model->data_surfaces + *mark;
if (surface->texture->supercontents & SUPERCONTENTS_SOLID)
{
- for (k = 0;k < surface->num_triangles;k++)
+ if(surface->num_bboxstride)
{
- tri = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle) + k * 3;
- VectorCopy((info->model->surfmesh.data_vertex3f + tri[0] * 3), vert[0]);
- VectorCopy((info->model->surfmesh.data_vertex3f + tri[1] * 3), vert[1]);
- VectorCopy((info->model->surfmesh.data_vertex3f + tri[2] * 3), vert[2]);
- VectorSubtract(vert[1], vert[0], edge[0]);
- VectorSubtract(vert[2], vert[1], edge[1]);
- CrossProduct(edge[1], edge[0], facenormal);
- if (facenormal[0] || facenormal[1] || facenormal[2])
+ int i, cnt, tri;
+ cnt = (surface->num_triangles + surface->num_bboxstride - 1) / surface->num_bboxstride;
+ for(i = 0; i < cnt; ++i)
{
- VectorNormalize(facenormal);
- f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
- if (f <= info->bestdist && f >= -info->bestdist)
+ if(BoxesOverlap(surface->data_bbox6f + i * 6, surface->data_bbox6f + i * 6 + 3, info->absmin, info->absmax))
{
- VectorSubtract(vert[0], vert[2], edge[2]);
- VectorNormalize(edge[0]);
- VectorNormalize(edge[1]);
- VectorNormalize(edge[2]);
- CrossProduct(facenormal, edge[0], edgenormal[0]);
- CrossProduct(facenormal, edge[1], edgenormal[1]);
- CrossProduct(facenormal, edge[2], edgenormal[2]);
- // face distance
- if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
- && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
- && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
- {
- // we got lucky, the center is within the face
- dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
- if (dist < 0)
- {
- dist = -dist;
- if (info->bestdist > dist)
- {
- info->bestdist = dist;
- VectorScale(facenormal, (info->radius - -dist), info->nudge);
- }
- }
- else
- {
- if (info->bestdist > dist)
- {
- info->bestdist = dist;
- VectorScale(facenormal, (info->radius - dist), info->nudge);
- }
- }
- }
- else
+ for(k = 0; k < surface->num_bboxstride; ++k)
{
- // check which edge or vertex the center is nearest
- for (i = 0;i < 3;i++)
- {
- f = DotProduct(info->center, edge[i]);
- if (f >= DotProduct(vert[0], edge[i])
- && f <= DotProduct(vert[1], edge[i]))
- {
- // on edge
- VectorMA(info->center, -f, edge[i], point);
- dist = sqrt(DotProduct(point, point));
- if (info->bestdist > dist)
- {
- info->bestdist = dist;
- VectorScale(point, (info->radius / dist), info->nudge);
- }
- // skip both vertex checks
- // (both are further away than this edge)
- i++;
- }
- else
- {
- // not on edge, check first vertex of edge
- VectorSubtract(info->center, vert[i], point);
- dist = sqrt(DotProduct(point, point));
- if (info->bestdist > dist)
- {
- info->bestdist = dist;
- VectorScale(point, (info->radius / dist), info->nudge);
- }
- }
- }
+ tri = i * surface->num_bboxstride + k;
+ if(tri >= surface->num_triangles)
+ break;
+ Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, tri);
}
}
}
}
+ else
+ {
+ for (k = 0;k < surface->num_triangles;k++)
+ {
+ Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, k);
+ }
+ }
}
}
}
{
VectorClear(info.nudge);
info.bestdist = radius;
+ VectorCopy(info.center, info.absmin);
+ VectorCopy(info.center, info.absmax);
+ info.absmin[0] -= info.radius + 1;
+ info.absmin[1] -= info.radius + 1;
+ info.absmin[2] -= info.radius + 1;
+ info.absmax[0] += info.radius + 1;
+ info.absmax[1] += info.radius + 1;
+ info.absmax[2] += info.radius + 1;
Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
VectorAdd(info.center, info.nudge, info.center);
}
case CONTENTS_EMPTY:
return 0;
case CONTENTS_SOLID:
- return SUPERCONTENTS_SOLID;
+ return SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
case CONTENTS_WATER:
return SUPERCONTENTS_WATER;
case CONTENTS_SLIME:
case CONTENTS_LAVA:
return SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
case CONTENTS_SKY:
- return SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP;
+ return SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP | SUPERCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
}
return 0;
}
tx->skinframerate = 1;
tx->skinframes[0] = skinframe;
tx->currentskinframe = tx->skinframes[0];
- tx->basematerialflags = 0;
}
tx->basematerialflags = MATERIALFLAG_WALL;
if (i == loadmodel->num_textures - 1)
// LordHavoc: HL sky textures are entirely different than quake
if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
{
- if (loadmodel->isworldmodel)
+ data = loadimagepixelsbgra(tx->name, false, false);
+ if (data && image_width == 256 && image_height == 128)
{
- data = loadimagepixelsbgra(tx->name, false, false);
- if (data && image_width == 256 && image_height == 128)
- {
- R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
- Mem_Free(data);
- }
- else if (mtdata != NULL)
- R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
+ R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
+ Mem_Free(data);
}
+ else if (mtdata != NULL)
+ R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
}
else
{
else if (!strncmp(tx->name, "sky", 3))
tx->basematerialflags = MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW;
else if (!strcmp(tx->name, "caulk"))
- tx->basematerialflags = MATERIALFLAG_NODRAW;
+ tx->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
else if (tx->skinframes[0] && tx->skinframes[0]->fog)
tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
{
char key[128], value[4096];
- char wadname[128];
int i, j, k;
if (!data)
return;
{
k = value[i];
value[i] = 0;
- strlcpy(wadname, "textures/", sizeof(wadname));
- strlcat(wadname, &value[j], sizeof(wadname));
- W_LoadTextureWadFile(wadname, false);
+ W_LoadTextureWadFile(&value[j], false);
j = i+1;
if (!k)
break;
if (out->v[0] >= loadmodel->brushq1.numvertexes || out->v[1] >= loadmodel->brushq1.numvertexes)
{
Con_Printf("Mod_Q1BSP_LoadEdges: %s has invalid vertex indices in edge %i (vertices %i %i >= numvertices %i)\n", loadmodel->name, i, out->v[0], out->v[1], loadmodel->brushq1.numvertexes);
+ if(!loadmodel->brushq1.numvertexes)
+ Host_Error("Mod_Q1BSP_LoadEdges: %s has edges but no vertexes, cannot fix\n", loadmodel->name);
+
out->v[0] = 0;
out->v[1] = 0;
}
{
int lindex = loadmodel->brushq1.surfedges[firstedge + i];
float s, t;
- if (lindex > 0)
+ // note: the q1bsp format does not allow a 0 surfedge (it would have no negative counterpart)
+ if (lindex >= 0)
VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
else
VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
if (i == -1)
{
surface->lightmapinfo->samples = NULL;
+#if 0
// give non-lightmapped water a 1x white lightmap
if (surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256)
{
surface->lightmapinfo->styles[0] = 0;
memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3);
}
+#endif
}
else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + i;
// now that we've decided the lightmap texture size, we can do the rest
if (cls.state != ca_dedicated)
{
+ int stainmapsize = 0;
struct alloc_lm_state allocState;
for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
int i, iu, iv, lightmapx = 0, lightmapy = 0;
float u, v, ubase, vbase, uscale, vscale;
+ if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+ continue;
+
smax = surface->lightmapinfo->extents[0] >> 4;
tmax = surface->lightmapinfo->extents[1] >> 4;
ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
+ stainmapsize += ssize * tsize * 3;
if (!lightmaptexture || !Mod_Q1BSP_AllocLightmapBlock(&allocState, lightmapsize, lightmapsize, ssize, tsize, &lightmapx, &lightmapy))
{
iv = (int) v;
(loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
}
-
}
if (cl_stainmaps.integer)
{
- // allocate stainmaps for permanent marks on walls
- int stainmapsize = 0;
+ // allocate stainmaps for permanent marks on walls and clear white
unsigned char *stainsamples = NULL;
- for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
- {
- ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
- tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
- stainmapsize += ssize * tsize * 3;
- }
- // allocate and clear to white
stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, stainmapsize);
memset(stainsamples, 255, stainmapsize);
// assign pointers
for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
{
+ if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+ continue;
ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
surface->lightmapinfo->stainsamples = stainsamples;
out->firstleafsurface = loadmodel->brush.data_leafsurfaces + (unsigned short)LittleShort(in->firstmarksurface);
out->numleafsurfaces = (unsigned short)LittleShort(in->nummarksurfaces);
- if (out->firstleafsurface < 0 || (unsigned short)LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
+ if ((unsigned short)LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
{
Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", (int)(out->firstleafsurface - loadmodel->brush.data_leafsurfaces), (int)(out->firstleafsurface + out->numleafsurfaces - loadmodel->brush.data_leafsurfaces), 0, loadmodel->brush.num_leafsurfaces);
out->firstleafsurface = NULL;
mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
-
- if (loadmodel->isworldmodel)
- Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
+ mod->Draw = R_Q1BSP_Draw;
+ mod->DrawDepth = R_Q1BSP_DrawDepth;
+ mod->DrawDebug = R_Q1BSP_DrawDebug;
+ mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+ mod->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+ mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
+ mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+ mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+ mod->DrawLight = R_Q1BSP_DrawLight;
// load into heap
if (i > 0)
{
char name[10];
- // LordHavoc: only register submodels if it is the world
- // (prevents external bsp models from replacing world submodels with
- // their own)
- if (!loadmodel->isworldmodel)
- continue;
// duplicate the basic information
dpsnprintf(name, sizeof(name), "*%i", i);
- mod = Mod_FindName(name);
+ 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.TraceLineOfSight = 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;
mod->nummodelsurfaces = bm->numfaces;
// make the model surface list (used by shadowing/lighting)
- mod->surfacelist = (int *)datapointer;datapointer += mod->nummodelsurfaces * sizeof(int);
- for (j = 0;j < mod->nummodelsurfaces;j++)
- mod->surfacelist[j] = mod->firstmodelsurface + j;
-
- // this gets altered below if sky or water is used
- mod->DrawSky = NULL;
- mod->DrawAddWaterPlanes = NULL;
- mod->Draw = R_Q1BSP_Draw;
- mod->DrawDepth = R_Q1BSP_DrawDepth;
- mod->DrawDebug = R_Q1BSP_DrawDebug;
- mod->GetLightInfo = R_Q1BSP_GetLightInfo;
- mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
- mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
- mod->DrawLight = R_Q1BSP_DrawLight;
- if (i != 0)
- {
- mod->brush.TraceLineOfSight = 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->sortedmodelsurfaces = (int *)datapointer;datapointer += mod->nummodelsurfaces * sizeof(int);
+ Mod_MakeSortedSurfaces(mod);
// copy the submodel bounds, then enlarge the yaw and rotated bounds according to radius
// (previously this code measured the radius of the vertices of surfaces in the submodel, but that broke submodels that contain only CLIP brushes, which do not produce surfaces)
mod->radius = modelradius;
mod->radius2 = modelradius * modelradius;
+ // this gets altered below if sky or water is used
+ mod->DrawSky = NULL;
+ mod->DrawAddWaterPlanes = NULL;
+
// scan surfaces for sky and water and flag the submodel as possessing these features or not
// build lightstyle lists for quick marking of dirty lightmaps when lightstyles flicker
if (mod->nummodelsurfaces)
{
for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
- {
- // we only need to have a drawsky function if it is used(usually only on world model)
if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
- mod->DrawSky = R_Q1BSP_DrawSky;
+ break;
+ if (j < mod->nummodelsurfaces)
+ mod->DrawSky = R_Q1BSP_DrawSky;
+
+ for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
- mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
- }
+ break;
+ if (j < mod->nummodelsurfaces)
+ mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
// build lightstyle update chains
// (used to rapidly mark lightmapupdateflags on many surfaces
i = LittleLong(header->version);
if (i != Q2BSPVERSION)
Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
- mod->brush.ishlbsp = false;
- if (loadmodel->isworldmodel)
- Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
mod_base = (unsigned char *)header;
}
// make the colbrush from the planes
out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
+
+ // this whole loop can take a while (e.g. on redstarrepublic4)
+ CL_KeepaliveMessage(false);
}
if (planes)
Mem_Free(planes);
if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
{
Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
+ if(!loadmodel->brushq3.num_vertices)
+ Host_Error("Mod_Q1BSP_LoadTrianglles: %s has triangles but no vertexes, cannot fix\n", loadmodel->name);
*out = 0;
}
}
}
}
- convertedpixels = Mem_Alloc(tempmempool, size*size*4);
+ convertedpixels = (unsigned char *) Mem_Alloc(tempmempool, size*size*4);
loadmodel->brushq3.lightmapsize = size;
loadmodel->brushq3.num_originallightmaps = count;
Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
// figure out what the most reasonable merge power is within limits
+
loadmodel->brushq3.num_lightmapmergepower = 0;
- for (power = 1;power <= mod_q3bsp_lightmapmergepower.integer && (128 << power) <= gl_max_texture_size && (1 << (power * 2)) < 4 * (count >> loadmodel->brushq3.deluxemapping);power++)
- loadmodel->brushq3.num_lightmapmergepower = power;
- // as the lightmap size may actually be another power of 2, adjust for this
- // (and interpret it as the power for 128x128 lightmaps above)
for(i = 0; (128 << i) < size; ++i)
- loadmodel->brushq3.num_lightmapmergepower -= 1;
- if(loadmodel->brushq3.num_lightmapmergepower < 0)
- loadmodel->brushq3.num_lightmapmergepower = 0;
+ ;
+ // i is now 0 for 128, 1 for 256, etc
+
+ for (power = 1;power + i <= mod_q3bsp_lightmapmergepower.integer && (size << power) <= gl_max_texture_size && (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 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
+ loadmodel->brushq3.num_mergedlightmaps = ((count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) + (1 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
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 *));
// 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) - (lightmapindex << power2);
+ 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)
}
mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
- j = (i >> loadmodel->brushq3.deluxemapping) & ((1 << power2) - 1);
+ 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
}
}
+static void Mod_Q3BSP_BuildBBoxes(const int *element3i, int num_triangles, const float *vertex3f, float **collisionbbox6f, int *collisionstride, int stride)
+{
+ int j, k, cnt, tri;
+ float *mins, *maxs;
+ const float *vert;
+ if(stride > 0)
+ {
+ *collisionstride = stride;
+ cnt = (num_triangles + stride - 1) / stride;
+ *collisionbbox6f = (float *) Mem_Alloc(loadmodel->mempool, sizeof(float[6]) * cnt);
+ for(j = 0; j < cnt; ++j)
+ {
+ mins = &((*collisionbbox6f)[6 * j + 0]);
+ maxs = &((*collisionbbox6f)[6 * j + 3]);
+ for(k = 0; k < stride; ++k)
+ {
+ tri = j * stride + k;
+ if(tri >= num_triangles)
+ break;
+ vert = &(vertex3f[element3i[3 * tri + 0] * 3]);
+ if(!k || vert[0] < mins[0]) mins[0] = vert[0];
+ if(!k || vert[1] < mins[1]) mins[1] = vert[1];
+ if(!k || vert[2] < mins[2]) mins[2] = vert[2];
+ if(!k || vert[0] > maxs[0]) maxs[0] = vert[0];
+ if(!k || vert[1] > maxs[1]) maxs[1] = vert[1];
+ if(!k || vert[2] > maxs[2]) maxs[2] = vert[2];
+ vert = &(vertex3f[element3i[3 * tri + 1] * 3]);
+ if(vert[0] < mins[0]) mins[0] = vert[0];
+ if(vert[1] < mins[1]) mins[1] = vert[1];
+ if(vert[2] < mins[2]) mins[2] = vert[2];
+ if(vert[0] > maxs[0]) maxs[0] = vert[0];
+ if(vert[1] > maxs[1]) maxs[1] = vert[1];
+ if(vert[2] > maxs[2]) maxs[2] = vert[2];
+ vert = &(vertex3f[element3i[3 * tri + 2] * 3]);
+ if(vert[0] < mins[0]) mins[0] = vert[0];
+ if(vert[1] < mins[1]) mins[1] = vert[1];
+ if(vert[2] < mins[2]) mins[2] = vert[2];
+ if(vert[0] > maxs[0]) maxs[0] = vert[0];
+ if(vert[1] > maxs[1]) maxs[1] = vert[1];
+ if(vert[2] > maxs[2]) maxs[2] = vert[2];
+ }
+ }
+ }
+ else
+ {
+ *collisionstride = 0;
+ *collisionbbox6f = NULL;
+ }
+}
+
typedef struct patchtess_s
{
- qboolean grouped;
+ patchinfo_t info;
+
+ // Auxiliary data used only by patch loading code in Mod_Q3BSP_LoadFaces
int surface_id;
- int xtess, ytess;
- int xsize, ysize;
- int cxtess, cytess;
- int cxsize, cysize;
float lodgroup[6];
-}
-patchtess_t;
+ float *originalvertex3f;
+} patchtess_t;
#define PATCHTESS_SAME_LODGROUP(a,b) \
( \
{
q3dface_t *in, *oldin;
msurface_t *out, *oldout;
- int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshvertices, meshtriangles, numvertices, numtriangles, groupsize, cxtess, cytess;
+ int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshvertices, meshtriangles, numvertices, numtriangles, cxtess, cytess;
float lightmaptcbase[2], lightmaptcscale[2];
//int *originalelement3i;
//int *originalneighbor3i;
float *v;
patchtess_t *patchtess = NULL;
int patchtesscount = 0;
+ qboolean again;
in = (q3dface_t *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
loadmodel->num_surfaces = count;
if(count > 0)
- patchtess = Mem_Alloc(tempmempool, count * sizeof(*patchtess));
+ patchtess = (patchtess_t*) Mem_Alloc(tempmempool, count * sizeof(*patchtess));
i = 0;
oldi = i;
xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
// bound to sanity settings
- xtess = bound(1, xtess, 1024);
- ytess = bound(1, ytess, 1024);
+ xtess = bound(0, xtess, 1024);
+ ytess = bound(0, ytess, 1024);
// lower quality collision patches! Same procedure as before, but different cvars
// convert patch to Q3FACETYPE_MESH
cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
// bound to user settings
- cxtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
- cytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
+ cxtess = bound(r_subdivisions_collision_mintess.integer, cxtess, r_subdivisions_collision_maxtess.integer);
+ cytess = bound(r_subdivisions_collision_mintess.integer, cytess, r_subdivisions_collision_maxtess.integer);
// bound to sanity settings
- cxtess = bound(1, xtess, 1024);
- cytess = bound(1, ytess, 1024);
+ cxtess = bound(0, cxtess, 1024);
+ cytess = bound(0, cytess, 1024);
// store it for the LOD grouping step
+ patchtess[patchtesscount].info.xsize = patchsize[0];
+ patchtess[patchtesscount].info.ysize = patchsize[1];
+ patchtess[patchtesscount].info.lods[PATCH_LOD_VISUAL].xtess = xtess;
+ patchtess[patchtesscount].info.lods[PATCH_LOD_VISUAL].ytess = ytess;
+ patchtess[patchtesscount].info.lods[PATCH_LOD_COLLISION].xtess = cxtess;
+ patchtess[patchtesscount].info.lods[PATCH_LOD_COLLISION].ytess = cytess;
+
patchtess[patchtesscount].surface_id = i;
- patchtess[patchtesscount].grouped = false;
- patchtess[patchtesscount].xtess = xtess;
- patchtess[patchtesscount].ytess = ytess;
- patchtess[patchtesscount].xsize = patchsize[0];
- patchtess[patchtesscount].ysize = patchsize[1];
patchtess[patchtesscount].lodgroup[0] = in->specific.patch.mins[0];
patchtess[patchtesscount].lodgroup[1] = in->specific.patch.mins[1];
patchtess[patchtesscount].lodgroup[2] = in->specific.patch.mins[2];
patchtess[patchtesscount].lodgroup[3] = in->specific.patch.maxs[0];
patchtess[patchtesscount].lodgroup[4] = in->specific.patch.maxs[1];
patchtess[patchtesscount].lodgroup[5] = in->specific.patch.maxs[2];
-
- patchtess[patchtesscount].cxtess = xtess;
- patchtess[patchtesscount].cytess = ytess;
+ patchtess[patchtesscount].originalvertex3f = originalvertex3f;
++patchtesscount;
break;
case Q3FACETYPE_FLARE:
meshtriangles += out->num_triangles;
}
- for(i = 0; i < patchtesscount; ++i)
+ // Fix patches tesselations so that they make no seams
+ do
{
- if(patchtess[i].grouped) // already grouped
- continue;
-
- // get the highest required tess parameters for this group
- groupsize = 0;
- xtess = ytess = 0;
- cxtess = cytess = 0;
- for(j = 0; j < patchtesscount; ++j)
- {
- if(patchtess[j].grouped) // already grouped
- continue;
- if(!PATCHTESS_SAME_LODGROUP(patchtess[i], patchtess[j]))
- continue;
- if(patchtess[j].xtess > xtess)
- xtess = patchtess[j].xtess;
- if(patchtess[j].ytess > ytess)
- ytess = patchtess[j].ytess;
- if(patchtess[j].cxtess > cxtess)
- cxtess = patchtess[j].cxtess;
- if(patchtess[j].cytess > cytess)
- cytess = patchtess[j].cytess;
- ++groupsize;
- }
-
- if(groupsize == 0)
- {
- Con_Printf("ERROR: patch %d isn't in any LOD group (1)?!?\n", patchtess[i].surface_id);
- continue;
- }
-
- // bound to user limit on vertices
- for(;;)
+ again = false;
+ for(i = 0; i < patchtesscount; ++i)
{
- numvertices = 0;
- numtriangles = 0;
-
- for(j = 0; j < patchtesscount; ++j)
+ for(j = i+1; j < patchtesscount; ++j)
{
- if(patchtess[j].grouped) // already grouped
- continue;
- if(!PATCHTESS_SAME_LODGROUP(patchtess[i], patchtess[j]))
+ if (!PATCHTESS_SAME_LODGROUP(patchtess[i], patchtess[j]))
continue;
- finalwidth = (patchtess[j].xsize - 1) * xtess + 1;
- finalheight = (patchtess[j].ysize - 1) * ytess + 1;
- numvertices += finalwidth * finalheight;
- numtriangles += (finalwidth - 1) * (finalheight - 1) * 2;
+ if (Q3PatchAdjustTesselation(3, &patchtess[i].info, patchtess[i].originalvertex3f, &patchtess[j].info, patchtess[j].originalvertex3f) )
+ again = true;
}
-
- if(xtess <= 1 && ytess <= 1)
- break;
-
- if(numvertices > min(r_subdivisions_maxvertices.integer, 262144) * groupsize)
- {
- if (xtess > ytess)
- xtess--;
- else
- ytess--;
- continue; // try again
- }
-
- break;
- }
-
- // bound to user limit on vertices (collision)
- for(;;)
- {
- numvertices = 0;
- numtriangles = 0;
-
- for(j = 0; j < patchtesscount; ++j)
- {
- if(patchtess[j].grouped) // already grouped
- continue;
- if(!PATCHTESS_SAME_LODGROUP(patchtess[i], patchtess[j]))
- continue;
-
- finalwidth = (patchtess[j].xsize - 1) * cxtess + 1;
- finalheight = (patchtess[j].ysize - 1) * cytess + 1;
- numvertices += finalwidth * finalheight;
- numtriangles += (finalwidth - 1) * (finalheight - 1) * 2;
- }
-
- if(cxtess <= 1 && cytess <= 1)
- break;
-
- if(numvertices > min(r_subdivisions_collision_maxvertices.integer, 262144) * groupsize)
- {
- if (cxtess > cytess)
- cxtess--;
- else
- cytess--;
- continue; // try again
- }
-
- break;
}
+ }
+ while (again);
- for(j = 0; j < patchtesscount; ++j)
- {
- if(patchtess[j].grouped) // already grouped
- continue;
- if(!PATCHTESS_SAME_LODGROUP(patchtess[i], patchtess[j]))
- continue;
-
- finalwidth = (patchtess[j].xsize - 1) * xtess + 1;
- finalheight = (patchtess[j].ysize - 1) * ytess + 1;
- numvertices = finalwidth * finalheight;
- numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
-
- oldout[patchtess[j].surface_id].num_vertices = numvertices;
- oldout[patchtess[j].surface_id].num_triangles = numtriangles;
- meshvertices += oldout[patchtess[j].surface_id].num_vertices;
- meshtriangles += oldout[patchtess[j].surface_id].num_triangles;
+ // Calculate resulting number of triangles
+ for(i = 0; i < patchtesscount; ++i)
+ {
+ finalwidth = Q3PatchDimForTess(patchtess[i].info.xsize, patchtess[i].info.lods[PATCH_LOD_VISUAL].xtess);
+ finalheight = Q3PatchDimForTess(patchtess[i].info.ysize,patchtess[i].info.lods[PATCH_LOD_VISUAL].ytess);
+ numvertices = finalwidth * finalheight;
+ numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
- patchtess[j].grouped = true;
- patchtess[j].xtess = xtess;
- patchtess[j].ytess = ytess;
- patchtess[j].cxtess = cxtess;
- patchtess[j].cytess = cytess;
- }
+ oldout[patchtess[i].surface_id].num_vertices = numvertices;
+ oldout[patchtess[i].surface_id].num_triangles = numtriangles;
+ meshvertices += oldout[patchtess[i].surface_id].num_vertices;
+ meshtriangles += oldout[patchtess[i].surface_id].num_triangles;
}
i = oldi;
originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
- xtess = ytess = cxtess = cytess = 0;
+ xtess = ytess = cxtess = cytess = -1;
for(j = 0; j < patchtesscount; ++j)
- if(patchtess[j].grouped && patchtess[j].surface_id == i)
+ if(patchtess[j].surface_id == i)
{
- xtess = patchtess[j].xtess;
- ytess = patchtess[j].ytess;
- cxtess = patchtess[j].cxtess;
- cytess = patchtess[j].cytess;
+ xtess = patchtess[j].info.lods[PATCH_LOD_VISUAL].xtess;
+ ytess = patchtess[j].info.lods[PATCH_LOD_VISUAL].ytess;
+ cxtess = patchtess[j].info.lods[PATCH_LOD_COLLISION].xtess;
+ cytess = patchtess[j].info.lods[PATCH_LOD_COLLISION].ytess;
break;
}
- if(xtess == 0)
+ if(xtess == -1)
{
- Con_Printf("ERROR: patch %d isn't in any LOD group (2)?!?\n", i);
- xtess = ytess = cxtess = cytess = 1;
+ Con_Printf("ERROR: patch %d isn't preprocessed?!?\n", i);
+ xtess = ytess = cxtess = cytess = 0;
}
- finalwidth = ((patchsize[0] - 1) * xtess) + 1;
- finalheight = ((patchsize[1] - 1) * ytess) + 1;
+ finalwidth = Q3PatchDimForTess(patchsize[0],xtess); //((patchsize[0] - 1) * xtess) + 1;
+ finalheight = Q3PatchDimForTess(patchsize[1],ytess); //((patchsize[1] - 1) * ytess) + 1;
finalvertices = finalwidth * finalheight;
finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
type = Q3FACETYPE_MESH;
Q3PatchTesselateFloat(2, sizeof(float[2]), (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
Q3PatchTesselateFloat(4, sizeof(float[4]), (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
Q3PatchTriangleElements((loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), finalwidth, finalheight, out->num_firstvertex);
+
out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), loadmodel->surfmesh.data_vertex3f);
+
if (developer.integer >= 100)
{
if (out->num_triangles < finaltriangles)
}
// q3map does not put in collision brushes for curves... ugh
// build the lower quality collision geometry
- finalwidth = ((patchsize[0] - 1) * cxtess) + 1;
- finalheight = ((patchsize[1] - 1) * cytess) + 1;
+ finalwidth = Q3PatchDimForTess(patchsize[0],cxtess); //((patchsize[0] - 1) * cxtess) + 1;
+ finalheight = Q3PatchDimForTess(patchsize[1],cytess); //((patchsize[1] - 1) * cytess) + 1;
finalvertices = finalwidth * finalheight;
finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
oldnumtriangles = out->num_triangles;
oldnumtriangles2 = out->num_collisiontriangles;
out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
+
+ // now optimize the collision mesh by finding triangle bboxes...
+ Mod_Q3BSP_BuildBBoxes(out->data_collisionelement3i, out->num_collisiontriangles, out->data_collisionvertex3f, &out->data_collisionbbox6f, &out->num_collisionbboxstride, mod_q3bsp_curves_collisions_stride.integer);
+ Mod_Q3BSP_BuildBBoxes(loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle, out->num_triangles, loadmodel->surfmesh.data_vertex3f, &out->data_bbox6f, &out->num_bboxstride, mod_q3bsp_curves_stride.integer);
+
if (developer.integer >= 100)
Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve became %i:%i vertices / %i:%i triangles (%i:%i degenerate)\n", patchsize[0], patchsize[1], out->num_vertices, out->num_collisionvertices, oldnumtriangles, oldnumtriangles2, oldnumtriangles - out->num_triangles, oldnumtriangles2 - out->num_collisiontriangles);
break;
if (cls.state != ca_dedicated && out->lightmaptexture)
{
// figure out which part of the merged lightmap this fits into
- int lightmapindex = LittleLong(in->lightmapindex) >> loadmodel->brushq3.deluxemapping;
+ int lightmapindex = LittleLong(in->lightmapindex) >> (loadmodel->brushq3.deluxemapping ? 1 : 0);
int mergewidth = R_TextureWidth(out->lightmaptexture) / loadmodel->brushq3.lightmapsize;
int mergeheight = R_TextureHeight(out->lightmaptexture) / loadmodel->brushq3.lightmapsize;
lightmapindex &= mergewidth * mergeheight - 1;
if (l->filelen)
{
if (l->filelen < count * (int)sizeof(*in))
- Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)", l->filelen, (int)(count * sizeof(*in)), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
+ Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)", l->filelen, (int)(count * sizeof(*in)), loadmodel->brushq3.num_lightgrid_isize[0], loadmodel->brushq3.num_lightgrid_isize[1], loadmodel->brushq3.num_lightgrid_isize[2]);
if (l->filelen != count * (int)sizeof(*in))
Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", (int)(count * sizeof(*in)), l->filelen);
out = (q3dlightgrid_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
{
surface->collisionmarkframe = markframe;
- Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+ Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
}
}
}
if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
{
surface->collisionmarkframe = markframe;
- Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+ Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
}
}
}
static void Mod_Q3BSP_TraceBox(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
{
int i;
+ vec3_t shiftstart, shiftend;
float segmentmins[3], segmentmaxs[3];
static int markframe = 0;
msurface_t *surface;
trace->fraction = 1;
trace->realfraction = 1;
trace->hitsupercontentsmask = hitsupercontentsmask;
- if (mod_q3bsp_optimizedtraceline.integer && VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
+ if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxmins, boxmaxs))
{
- if (VectorCompare(start, end))
+ VectorAdd(start, boxmins, shiftstart);
+ VectorAdd(end, boxmins, shiftend);
+ if (VectorCompare(shiftstart, shiftend))
{
// point trace
if (model->brush.submodel)
{
for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
if (brush->colbrushf)
- Collision_TracePointBrushFloat(trace, start, brush->colbrushf);
+ Collision_TracePointBrushFloat(trace, shiftstart, brush->colbrushf);
}
else
- Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, ++markframe);
+ Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, shiftstart, ++markframe);
}
else
{
// line trace
- segmentmins[0] = min(start[0], end[0]) - 1;
- segmentmins[1] = min(start[1], end[1]) - 1;
- segmentmins[2] = min(start[2], end[2]) - 1;
- segmentmaxs[0] = max(start[0], end[0]) + 1;
- segmentmaxs[1] = max(start[1], end[1]) + 1;
- segmentmaxs[2] = max(start[2], end[2]) + 1;
+ segmentmins[0] = min(shiftstart[0], shiftend[0]) - 1;
+ segmentmins[1] = min(shiftstart[1], shiftend[1]) - 1;
+ segmentmins[2] = min(shiftstart[2], shiftend[2]) - 1;
+ segmentmaxs[0] = max(shiftstart[0], shiftend[0]) + 1;
+ segmentmaxs[1] = max(shiftstart[1], shiftend[1]) + 1;
+ segmentmaxs[2] = max(shiftstart[2], shiftend[2]) + 1;
if (model->brush.submodel)
{
for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
if (brush->colbrushf)
- Collision_TraceLineBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
+ Collision_TraceLineBrushFloat(trace, shiftstart, shiftend, 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)
- Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+ Collision_TraceLineTriangleMeshFloat(trace, shiftstart, shiftend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
}
else
- Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
+ Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, shiftstart, shiftend, 0, 1, shiftstart, shiftend, ++markframe, segmentmins, segmentmaxs);
}
}
else
if (mod_q3bsp_curves_collisions.integer)
for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
if (surface->num_collisiontriangles)
- Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+ Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
}
else
Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
supercontents |= SUPERCONTENTS_MONSTERCLIP;
if (nativecontents & CONTENTSQ3_DONOTENTER)
supercontents |= SUPERCONTENTS_DONOTENTER;
+ if (nativecontents & CONTENTSQ3_BOTCLIP)
+ supercontents |= SUPERCONTENTS_BOTCLIP;
+ if (!(nativecontents & CONTENTSQ3_TRANSLUCENT))
+ supercontents |= SUPERCONTENTS_OPAQUE;
return supercontents;
}
nativecontents |= CONTENTSQ3_MONSTERCLIP;
if (supercontents & SUPERCONTENTS_DONOTENTER)
nativecontents |= CONTENTSQ3_DONOTENTER;
+ if (supercontents & SUPERCONTENTS_BOTCLIP)
+ nativecontents |= CONTENTSQ3_BOTCLIP;
+ if (!(supercontents & SUPERCONTENTS_OPAQUE))
+ nativecontents |= CONTENTSQ3_TRANSLUCENT;
return nativecontents;
}
i = LittleLong(header->version);
if (i != Q3BSPVERSION && i != Q3BSPVERSION_IG && i != Q3BSPVERSION_LIVE)
Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
- mod->brush.ishlbsp = false;
- if (loadmodel->isworldmodel)
- Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
mod->soundfromcenter = true;
mod->TraceBox = Mod_Q3BSP_TraceBox;
mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
+ mod->brush.AmbientSoundLevelsForPoint = NULL;
+ mod->brush.RoundUpToHullSize = NULL;
mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
mod->Draw = R_Q1BSP_Draw;
mod->DrawDepth = R_Q1BSP_DrawDepth;
mod->DrawDebug = R_Q1BSP_DrawDebug;
mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+ mod->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+ mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
mod->DrawLight = R_Q1BSP_DrawLight;
- mod->DrawAddWaterPlanes = NULL;
mod_base = (unsigned char *)header;
if (i == Q3LUMP_PVS || i == Q3LUMP_LEAFS || i == Q3LUMP_NODES)
continue;
mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
+
+ // all this checksumming can take a while, so let's send keepalives here too
+ CL_KeepaliveMessage(false);
}
Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
if (i > 0)
{
char name[10];
- // LordHavoc: only register submodels if it is the world
- // (prevents external bsp models from replacing world submodels with
- // their own)
- if (!loadmodel->isworldmodel)
- continue;
// duplicate the basic information
dpsnprintf(name, sizeof(name), "*%i", i);
- mod = Mod_FindName(name);
+ 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.BoxTouchingVisibleLeafs = NULL;
mod->brush.FindBoxClusters = NULL;
mod->brush.LightPoint = NULL;
- mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
+ mod->brush.AmbientSoundLevelsForPoint = NULL;
}
mod->brush.submodel = i;
mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush;
mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes;
- mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
- for (j = 0;j < mod->nummodelsurfaces;j++)
- mod->surfacelist[j] = mod->firstmodelsurface + j;
+ mod->sortedmodelsurfaces = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
+ Mod_MakeSortedSurfaces(mod);
VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
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;
- else
- mod->DrawSky = NULL;
for (j = 0;j < mod->nummodelsurfaces;j++)
if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
break;
if (j < mod->nummodelsurfaces)
mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
- else
- mod->DrawAddWaterPlanes = NULL;
}
}