cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4"};
cvar_t r_subdivisions_minlevel = {0, "r_subdivisions_minlevel", "0"};
-cvar_t r_subdivisions_maxlevel = {0, "r_subdivisions_maxlevel", "4"};
+cvar_t r_subdivisions_maxlevel = {0, "r_subdivisions_maxlevel", "10"};
cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536"};
+cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15"};
+cvar_t r_subdivisions_collision_minlevel = {0, "r_subdivisions_collision_minlevel", "0"};
+cvar_t r_subdivisions_collision_maxlevel = {0, "r_subdivisions_collision_maxlevel", "10"};
+cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225"};
cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0"};
Cvar_RegisterVariable(&r_subdivisions_minlevel);
Cvar_RegisterVariable(&r_subdivisions_maxlevel);
Cvar_RegisterVariable(&r_subdivisions_maxvertices);
+ Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
+ Cvar_RegisterVariable(&r_subdivisions_collision_minlevel);
+ Cvar_RegisterVariable(&r_subdivisions_collision_maxlevel);
+ Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
if (t->trace->allsolid)
t->trace->startsolid = true;
#if COLLISIONPARANOID >= 3
- Con_Printf("S");
+ Con_Print("S");
#endif
return HULLCHECKSTATE_SOLID;
}
{
t->trace->allsolid = false;
#if COLLISIONPARANOID >= 3
- Con_Printf("E");
+ Con_Print("E");
#endif
return HULLCHECKSTATE_EMPTY;
}
if (t2 < 0)
{
#if COLLISIONPARANOID >= 3
- Con_Printf("<");
+ Con_Print("<");
#endif
num = node->children[1];
goto loc0;
if (t2 >= 0)
{
#if COLLISIONPARANOID >= 3
- Con_Printf(">");
+ Con_Print(">");
#endif
num = node->children[0];
goto loc0;
// the line intersects, find intersection point
// LordHavoc: this uses the original trace for maximum accuracy
#if COLLISIONPARANOID >= 3
- Con_Printf("M");
+ Con_Print("M");
#endif
if (plane->type < 3)
{
t->trace->fraction = bound(0, midf, 1);
#if COLLISIONPARANOID >= 3
- Con_Printf("D");
+ Con_Print("D");
#endif
return HULLCHECKSTATE_DONE;
}
#if COLLISIONPARANOID >= 2
Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
- Con_Printf("\n");
+ Con_Print("\n");
#else
if (DotProduct(rhc.dist, rhc.dist))
Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
}
if ((mtwidth & 15) || (mtheight & 15))
- Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
+ Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned\n", dmiptex->name, loadmodel->name);
// LordHavoc: force all names to lowercase
for (j = 0;name[j];j++)
else
{
if (fs_filesize == 8)
- Con_Printf("Empty .lit file, ignoring\n");
+ Con_Print("Empty .lit file, ignoring\n");
else
- Con_Printf("Corrupt .lit file (old version?), ignoring\n");
+ Con_Print("Corrupt .lit file (old version?), ignoring\n");
Mem_Free(data);
}
}
{
if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
{
- Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
+ Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
return;
}
if (p >= 0 && out->clusterindex >= 0)
{
if (p >= loadmodel->brushq1.num_compressedpvs)
- Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
+ Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
else
Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, loadmodel->brush.data_pvsclusters + out->clusterindex * loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.data_pvsclusters + (out->clusterindex + 1) * loadmodel->brush.num_pvsclusterbytes);
}
nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
if (!nodeportalwinding)
{
- Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
+ Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
break;
}
}
}
}
+//Returns PVS data for a given point
+//(note: can return NULL)
+static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
+{
+ mnode_t *node;
+ Mod_CheckLoaded(model);
+ node = model->brushq1.nodes;
+ while (node->plane)
+ node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
+ if (((mleaf_t *)node)->clusterindex >= 0)
+ return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
+ else
+ return NULL;
+}
+
static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
{
while (node->plane)
{
int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3;
bytes = min(bytes, pvsbufferlength);
- if (r_novis.integer)
+ if (r_novis.integer || !Mod_Q1BSP_GetPVS(model, org))
{
memset(pvsbuffer, 0xFF, bytes);
return bytes;
return bytes;
}
-//Returns PVS data for a given point
-//(note: can return NULL)
-static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
-{
- mnode_t *node;
- Mod_CheckLoaded(model);
- node = model->brushq1.nodes;
- while (node->plane)
- node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
- if (((mleaf_t *)node)->clusterindex >= 0)
- return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
- else
- return NULL;
-}
-
static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
{
vec3_t size;
const char *text;
int flags;
char shadername[Q3PATHLENGTH];
+ char sky[Q3PATHLENGTH];
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
text = f;
while (COM_ParseToken(&text, false))
{
- snprintf(shadername, sizeof(shadername), "%s", com_token);
+ strncpy(shadername, com_token, sizeof(shadername));
flags = 0;
+ sky[0] = 0;
if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
{
while (COM_ParseToken(&text, false))
goto parseerror;
}
}
+ else if (!strcasecmp(com_token, "sky"))
+ {
+ if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
+ if (strlen(com_token) < sizeof(sky))
+ strcpy(sky, com_token);
+ }
+ else if (!strcasecmp(com_token, "skyparms"))
+ {
+ if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
+ {
+ if (strlen(com_token) < sizeof(sky) && !atoi(com_token) && strcasecmp(com_token, "-"))
+ strcpy(sky, com_token);
+ if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
+ COM_ParseToken(&text, true);
+ }
+ }
else
{
// look for linebreak or }
// add shader to list (shadername and flags)
// actually here we just poke into the texture settings
for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
+ {
if (!strcasecmp(out->name, shadername))
+ {
out->surfaceparms = flags;
+ if ((flags & Q3SURFACEPARM_SKY) && sky[0])
+ strcpy(loadmodel->brush.skybox, sky);
+ }
+ }
}
else
{
Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
}
// q3map does not put in collision brushes for curves... ugh
+ // build the lower quality collision geometry
out->collisions = true;
+ xlevel = QuadraticSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
+ ylevel = QuadraticSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
+ // bound to user settings
+ xlevel = bound(r_subdivisions_collision_minlevel.integer, xlevel, r_subdivisions_collision_maxlevel.integer);
+ ylevel = bound(r_subdivisions_collision_minlevel.integer, ylevel, r_subdivisions_collision_maxlevel.integer);
+ // bound to sanity settings
+ xlevel = bound(0, xlevel, 10);
+ ylevel = bound(0, ylevel, 10);
+ // bound to user limit on vertices
+ while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
+ {
+ if (xlevel > ylevel)
+ xlevel--;
+ else
+ ylevel--;
+ }
+ finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
+ finalheight = ((patchsize[1] - 1) << ylevel) + 1;
+ finalvertices = finalwidth * finalheight;
+ finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+ out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
+ out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
+ out->num_collisionvertices = finalvertices;
+ out->num_collisiontriangles = finaltriangles;
+ QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_collisionvertex3f);
+ // generate elements
+ e = out->data_collisionelement3i;
+ for (y = 0;y < finalheight - 1;y++)
+ {
+ row0 = (y + 0) * finalwidth;
+ row1 = (y + 1) * finalwidth;
+ for (x = 0;x < finalwidth - 1;x++)
+ {
+ *e++ = row0;
+ *e++ = row1;
+ *e++ = row0 + 1;
+ *e++ = row1;
+ *e++ = row1 + 1;
+ *e++ = row0 + 1;
+ row0++;
+ row1++;
+ }
+ }
+ out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
+ if (developer.integer)
+ {
+ if (out->num_collisiontriangles < finaltriangles)
+ Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_collisionvertices, finaltriangles, finaltriangles - out->num_collisiontriangles, out->num_collisiontriangles);
+ else
+ Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_collisionvertices, out->num_collisiontriangles);
+ }
break;
case Q3FACETYPE_FLARE:
Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
out->data_element3i[j] = 0;
}
- Con_Printf("\n");
+ Con_Print("\n");
}
// for shadow volumes
Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
{
face->collisionmarkframe = markframe;
- Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+ Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
if (startfrac > trace->realfraction)
return;
}
if (face->collisions && face->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
{
face->markframe = markframe;
- Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+ Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
}
}
}
{
face = model->brushq3.data_thismodel->firstface + i;
if (face->collisions)
- Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+ Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
}
}
}
{
face = model->brushq3.data_thismodel->firstface + i;
if (face->collisions)
- Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+ Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
}
}
}
{
int bytes = model->brush.num_pvsclusterbytes;
bytes = min(bytes, pvsbufferlength);
- if (r_novis.integer || !loadmodel->brush.num_pvsclusters)
+ if (r_novis.integer || !loadmodel->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org))
{
memset(pvsbuffer, 0xFF, bytes);
return bytes;