totalmaxs[0] = maxs[0];
totalmaxs[1] = maxs[1];
totalmaxs[2] = maxs[2];
- // if there is only one child this is a leaf
- if (numchildren < 2)
- return -1-leaflist[0];
// if we run out of nodes it's the caller's fault, but don't crash
if (bih->numnodes == bih->maxnodes)
{
if (!bih->error)
bih->error = BIHERROR_OUT_OF_NODES;
- return -1-leaflist[0];
+ return 0;
}
nodenum = bih->numnodes++;
node = bih->nodes + nodenum;
node->frontmin = 0;
node->backmax = 0;
memset(node->children, -1, sizeof(node->children));
- // check if this should be an unordered node to reduce recursion depth
- if (numchildren <= bih->maxchildrenunordered)
+ // check if there are few enough children to store an unordered node
+ if (numchildren <= BIH_MAXUNORDEREDCHILDREN)
{
node->type = BIH_UNORDERED;
for (j = 0;j < numchildren;j++)
return nodenum;
}
-int BIH_Build(bih_t *bih, int numleafs, bih_leaf_t *leafs, int maxnodes, bih_node_t *nodes, int *temp_leafsort, int *temp_leafsortscratch, int maxchildrenunordered)
+int BIH_Build(bih_t *bih, int numleafs, bih_leaf_t *leafs, int maxnodes, bih_node_t *nodes, int *temp_leafsort, int *temp_leafsortscratch)
{
int i;
bih->numnodes = 0;
bih->maxnodes = maxnodes;
bih->nodes = nodes;
- bih->maxchildrenunordered = maxchildrenunordered;
- if (bih->maxchildrenunordered > BIH_MAXUNORDEREDCHILDREN)
- bih->maxchildrenunordered = BIH_MAXUNORDEREDCHILDREN;
// clear things we intend to rebuild
memset(bih->nodes, 0, sizeof(bih->nodes[0]) * bih->maxnodes);
int axis;
bih_node_t *node;
bih_leaf_t *leaf;
- while (nodenum >= 0)
+ for(;;)
{
node = bih->nodes + nodenum;
// check if this is an unordered node (which holds an array of leaf numbers)
// fell between the child groups, nothing here
return;
}
- leaf = bih->leafs + (-1-nodenum);
- if (mins[0] > leaf->maxs[0] || maxs[0] < leaf->mins[0]
- || mins[1] > leaf->maxs[1] || maxs[1] < leaf->mins[1]
- || mins[2] > leaf->maxs[2] || maxs[2] < leaf->mins[2])
- return;
- switch(leaf->type)
- {
- case BIH_RENDERTRIANGLE:
- if (*numtrianglespointer >= maxtriangles)
- {
- ++*numtrianglespointer; // so the caller can detect overflow
- break;
- }
- if(trianglelist_surf)
- trianglelist_surf[*numtrianglespointer] = leaf->surfaceindex;
- trianglelist_idx[*numtrianglespointer] = leaf->itemindex;
- ++*numtrianglespointer;
- break;
- default:
- break;
- }
}
int BIH_GetTriangleListForBox(const bih_t *bih, int maxtriangles, int *trianglelist_idx, int *trianglelist_surf, const float *mins, const float *maxs)
{
int numtriangles = 0;
- BIH_GetTriangleListForBox_Node(bih, 0, maxtriangles, trianglelist_idx, trianglelist_surf, &numtriangles, mins, maxs);
+ BIH_GetTriangleListForBox_Node(bih, bih->rootnode, maxtriangles, trianglelist_idx, trianglelist_surf, &numtriangles, mins, maxs);
return numtriangles;
}
#ifndef BIH_H
#define BIH_H
-#define BIH_MAXUNORDEREDCHILDREN 16
+#define BIH_MAXUNORDEREDCHILDREN 8
typedef enum biherror_e
{
// TODO: move bounds data to parent node and remove it from leafs?
float mins[3];
float maxs[3];
- // < 0 is a leaf index (-1-leafindex), >= 0 is another node index (always >= this node's index)
+ // node indexes of children (always > this node's index)
int front;
int back;
// interval of children
float frontmin; // children[0]
float backmax; // children[1]
- // BIH_UNORDERED uses this for a list of leafindex (positive), -1 = end of list
+ // BIH_UNORDERED uses this for a list of leafindex (all >= 0), -1 = end of list
int children[BIH_MAXUNORDEREDCHILDREN];
}
bih_node_t;
// fields used only during BIH_Build:
int maxnodes;
- int maxchildrenunordered;
int error; // set to a value if an error occurs in building (such as numnodes == maxnodes)
int *leafsort;
int *leafsortscratch;
}
bih_t;
-int BIH_Build(bih_t *bih, int numleafs, bih_leaf_t *leafs, int maxnodes, bih_node_t *nodes, int *temp_leafsort, int *temp_leafsortscratch, int maxchildrenunordered);
+int BIH_Build(bih_t *bih, int numleafs, bih_leaf_t *leafs, int maxnodes, bih_node_t *nodes, int *temp_leafsort, int *temp_leafsortscratch);
int BIH_GetTriangleListForBox(const bih_t *bih, int maxtriangles, int *trianglelist_idx, int *trianglelist_surf, const float *mins, const float *maxs);
int surfaceindex;
int t;
int nodeleafindex;
- int nodenumleafs;
const int *nodeleaflist;
int currentmaterialflags;
qboolean castshadow;
// note: because the BSP leafs are not in the BIH tree, the _BSP function
// must be called to mark leafs visible for entity culling...
// we start at the root node
- nodestack[nodestackpos++] = 0;
+ nodestack[nodestackpos++] = bih->rootnode;
// we'll be done when the stack is empty
while (nodestackpos)
{
// pop one off the stack to process
nodenum = nodestack[--nodestackpos];
- if (nodenum >= 0)
+ // node
+ node = bih->nodes + nodenum;
+ if (node->type == BIH_UNORDERED)
{
- // node
- node = bih->nodes + nodenum;
- if (node->type == BIH_UNORDERED)
+ nodeleaflist = node->children;
+ for (nodeleafindex = 0;nodeleafindex < BIH_MAXUNORDEREDCHILDREN && node->children[nodeleafindex] >= 0;nodeleafindex++)
{
- nodeleaflist = node->children;
- for (nodenumleafs = 0;nodenumleafs < BIH_MAXUNORDEREDCHILDREN;nodenumleafs++)
- if (node->children[nodenumleafs] < 0)
- break;
- }
- else
- {
- axis = node->type - BIH_SPLITX;
-#if 0
- if (!BoxesOverlap(info->lightmins, info->lightmaxs, node->mins, node->maxs))
+ leaf = bih->leafs + node->children[nodeleafindex];
+ if (leaf->type != BIH_RENDERTRIANGLE)
+ continue;
+#if 1
+ if (!BoxesOverlap(info->lightmins, info->lightmaxs, leaf->mins, leaf->maxs))
continue;
#endif
-#if 0
- if (!r_shadow_compilingrtlight && R_CullBoxCustomPlanes(node->mins, node->maxs, rtlight->cached_numfrustumplanes, rtlight->cached_frustumplanes))
+#if 1
+ if (!r_shadow_compilingrtlight && R_CullBoxCustomPlanes(leaf->mins, leaf->maxs, info->numfrustumplanes, info->frustumplanes))
continue;
#endif
- if (info->lightmins[axis] <= node->backmax)
+ surfaceindex = leaf->surfaceindex;
+ surface = info->model->data_surfaces + surfaceindex;
+ currentmaterialflags = R_GetCurrentTexture(surface->texture)->currentmaterialflags;
+ castshadow = !(currentmaterialflags & MATERIALFLAG_NOSHADOW);
+ t = leaf->itemindex + surface->num_firstshadowmeshtriangle - surface->num_firsttriangle;
+ e = info->model->brush.shadowmesh->element3i + t * 3;
+ v[0] = info->model->brush.shadowmesh->vertex3f + e[0] * 3;
+ v[1] = info->model->brush.shadowmesh->vertex3f + e[1] * 3;
+ v[2] = info->model->brush.shadowmesh->vertex3f + e[2] * 3;
+ VectorCopy(v[0], v2[0]);
+ VectorCopy(v[1], v2[1]);
+ VectorCopy(v[2], v2[2]);
+ if (info->svbsp_insertoccluder)
{
- if (info->lightmaxs[axis] >= node->frontmin && nodestackpos < GETLIGHTINFO_MAXNODESTACK)
- nodestack[nodestackpos++] = node->front;
- nodestack[nodestackpos++] = node->back;
+ if (castshadow)
+ SVBSP_AddPolygon(&r_svbsp, 3, v2[0], true, NULL, NULL, 0);
continue;
}
- else if (info->lightmaxs[axis] >= node->frontmin)
- {
- nodestack[nodestackpos++] = node->front;
+ if (info->svbsp_active && !(SVBSP_AddPolygon(&r_svbsp, 3, v2[0], false, NULL, NULL, 0) & 2))
continue;
+ // we don't occlude triangles from lighting even
+ // if they are backfacing, because when using
+ // shadowmapping they are often not fully occluded
+ // on the horizon of an edge
+ SETPVSBIT(info->outlighttrispvs, t);
+ if (castshadow)
+ {
+ if (currentmaterialflags & MATERIALFLAG_NOCULLFACE)
+ {
+ // if the material is double sided we
+ // can't cull by direction
+ SETPVSBIT(info->outshadowtrispvs, t);
+ }
+ else if (r_shadow_frontsidecasting.integer)
+ {
+ // front side casting occludes backfaces,
+ // so they are completely useless as both
+ // casters and lit polygons
+ if (PointInfrontOfTriangle(info->relativelightorigin, v2[0], v2[1], v2[2]))
+ SETPVSBIT(info->outshadowtrispvs, t);
+ }
+ else
+ {
+ // back side casting does not occlude
+ // anything so we can't cull lit polygons
+ if (!PointInfrontOfTriangle(info->relativelightorigin, v2[0], v2[1], v2[2]))
+ SETPVSBIT(info->outshadowtrispvs, t);
+ }
+ }
+ if (!CHECKPVSBIT(info->outsurfacepvs, surfaceindex))
+ {
+ SETPVSBIT(info->outsurfacepvs, surfaceindex);
+ info->outsurfacelist[info->outnumsurfaces++] = surfaceindex;
}
- else
- continue; // light falls between children, nothing here
}
}
else
{
- // leaf
- nodenum = -1-nodenum;
- nodenumleafs = 1;
- nodeleaflist = &nodenum;
- }
-
- for (nodeleafindex = 0;nodeleafindex < nodenumleafs;nodeleafindex++)
- {
- leaf = bih->leafs + nodeleaflist[nodeleafindex];
- if (leaf->type != BIH_RENDERTRIANGLE)
- continue;
-#if 1
- if (!BoxesOverlap(info->lightmins, info->lightmaxs, leaf->mins, leaf->maxs))
+ axis = node->type - BIH_SPLITX;
+#if 0
+ if (!BoxesOverlap(info->lightmins, info->lightmaxs, node->mins, node->maxs))
continue;
#endif
-#if 1
- if (!r_shadow_compilingrtlight && R_CullBoxCustomPlanes(leaf->mins, leaf->maxs, info->numfrustumplanes, info->frustumplanes))
+#if 0
+ if (!r_shadow_compilingrtlight && R_CullBoxCustomPlanes(node->mins, node->maxs, rtlight->cached_numfrustumplanes, rtlight->cached_frustumplanes))
continue;
#endif
- surfaceindex = leaf->surfaceindex;
- surface = info->model->data_surfaces + surfaceindex;
- currentmaterialflags = R_GetCurrentTexture(surface->texture)->currentmaterialflags;
- castshadow = !(currentmaterialflags & MATERIALFLAG_NOSHADOW);
- t = leaf->itemindex + surface->num_firstshadowmeshtriangle - surface->num_firsttriangle;
- e = info->model->brush.shadowmesh->element3i + t * 3;
- v[0] = info->model->brush.shadowmesh->vertex3f + e[0] * 3;
- v[1] = info->model->brush.shadowmesh->vertex3f + e[1] * 3;
- v[2] = info->model->brush.shadowmesh->vertex3f + e[2] * 3;
- VectorCopy(v[0], v2[0]);
- VectorCopy(v[1], v2[1]);
- VectorCopy(v[2], v2[2]);
- if (info->svbsp_insertoccluder)
+ if (info->lightmins[axis] <= node->backmax)
{
- if (castshadow)
- SVBSP_AddPolygon(&r_svbsp, 3, v2[0], true, NULL, NULL, 0);
- continue;
- }
- if (info->svbsp_active && !(SVBSP_AddPolygon(&r_svbsp, 3, v2[0], false, NULL, NULL, 0) & 2))
+ if (info->lightmaxs[axis] >= node->frontmin && nodestackpos < GETLIGHTINFO_MAXNODESTACK)
+ nodestack[nodestackpos++] = node->front;
+ nodestack[nodestackpos++] = node->back;
continue;
- // we don't occlude triangles from lighting even
- // if they are backfacing, because when using
- // shadowmapping they are often not fully occluded
- // on the horizon of an edge
- SETPVSBIT(info->outlighttrispvs, t);
- if (castshadow)
- {
- if (currentmaterialflags & MATERIALFLAG_NOCULLFACE)
- {
- // if the material is double sided we
- // can't cull by direction
- SETPVSBIT(info->outshadowtrispvs, t);
- }
- else if (r_shadow_frontsidecasting.integer)
- {
- // front side casting occludes backfaces,
- // so they are completely useless as both
- // casters and lit polygons
- if (PointInfrontOfTriangle(info->relativelightorigin, v2[0], v2[1], v2[2]))
- SETPVSBIT(info->outshadowtrispvs, t);
- }
- else
- {
- // back side casting does not occlude
- // anything so we can't cull lit polygons
- if (!PointInfrontOfTriangle(info->relativelightorigin, v2[0], v2[1], v2[2]))
- SETPVSBIT(info->outshadowtrispvs, t);
- }
}
- if (!CHECKPVSBIT(info->outsurfacepvs, surfaceindex))
+ else if (info->lightmaxs[axis] >= node->frontmin)
{
- SETPVSBIT(info->outsurfacepvs, surfaceindex);
- info->outsurfacelist[info->outnumsurfaces++] = surfaceindex;
+ nodestack[nodestackpos++] = node->front;
+ continue;
}
+ else
+ continue; // light falls between children, nothing here
}
}
}
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_t mod_collision_bih_childrengrouping = {0, "mod_collision_bih_childrengrouping", "16", "this setting prevents excessive recursion depth in the BIH tree by building a shallow tree"};
cvar_t mod_recalculatenodeboxes = {0, "mod_recalculatenodeboxes", "1", "enables use of generated node bounding boxes based on BSP tree portal reconstruction, rather than the node boxes supplied by the map compiler"};
static texture_t mod_q1bsp_texture_solid;
Cvar_RegisterVariable(&mod_q3shader_default_polygonoffset);
Cvar_RegisterVariable(&mod_q1bsp_polygoncollisions);
Cvar_RegisterVariable(&mod_collision_bih);
- Cvar_RegisterVariable(&mod_collision_bih_childrengrouping);
Cvar_RegisterVariable(&mod_recalculatenodeboxes);
memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid));
const bih_node_t *node;
const colbrushf_t *brush;
int axis;
- while (nodenum >= 0)
+ for(;;)
{
node = model->collision_bih.nodes + nodenum;
if (node->type == BIH_UNORDERED)
else // no overlap with either child? just return
return;
}
- if (!model->collision_bih.leafs)
- return;
- leaf = model->collision_bih.leafs + (-1-nodenum);
-#if 1
- if (!BoxesOverlap(point, point, leaf->mins, leaf->maxs))
- return;
-#endif
- switch(leaf->type)
- {
- case BIH_BRUSH:
- brush = model->brush.data_brushes[leaf->itemindex].colbrushf;
- Collision_TracePointBrushFloat(trace, point, brush);
- break;
- case BIH_COLLISIONTRIANGLE:
- // collision triangle - skipped because they have no volume
- break;
- case BIH_RENDERTRIANGLE:
- // render triangle - skipped because they have no volume
- break;
- }
}
static void Mod_CollisionBIH_TraceLine_RecursiveBIHNode(trace_t *trace, dp_model_t *model, bih_t *bih, int nodenum, const vec3_t start, const vec3_t end, const vec3_t linestart, const vec3_t lineend)
segmentmaxs[0] = max(start[0], end[0]);
segmentmaxs[1] = max(start[1], end[1]);
segmentmaxs[2] = max(start[2], end[2]);
- while (nodenum >= 0)
+ for (;;)
{
node = bih->nodes + nodenum;
#if 0
#endif
#endif
}
- if (!bih->leafs)
- return;
- leaf = bih->leafs + (-1-nodenum);
-#if 1
- if (!BoxesOverlap(segmentmins, segmentmaxs, leaf->mins, leaf->maxs))
- return;
-#endif
- switch(leaf->type)
- {
- case BIH_BRUSH:
- brush = model->brush.data_brushes[leaf->itemindex].colbrushf;
- Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
- break;
- case BIH_COLLISIONTRIANGLE:
- if (!mod_q3bsp_curves_collisions.integer)
- return;
- e = model->brush.data_collisionelement3i + 3*leaf->itemindex;
- texture = model->data_textures + leaf->textureindex;
- Collision_TraceLineTriangleFloat(trace, linestart, lineend, model->brush.data_collisionvertex3f + e[0] * 3, model->brush.data_collisionvertex3f + e[1] * 3, model->brush.data_collisionvertex3f + e[2] * 3, texture->supercontents, texture->surfaceflags, texture);
- break;
- case BIH_RENDERTRIANGLE:
- e = model->surfmesh.data_element3i + 3*leaf->itemindex;
- texture = model->data_textures + leaf->textureindex;
- Collision_TraceLineTriangleFloat(trace, linestart, lineend, model->surfmesh.data_vertex3f + e[0] * 3, model->surfmesh.data_vertex3f + e[1] * 3, model->surfmesh.data_vertex3f + e[2] * 3, texture->supercontents, texture->surfaceflags, texture);
- break;
- }
}
static void Mod_CollisionBIH_TraceBrush_RecursiveBIHNode(trace_t *trace, dp_model_t *model, int nodenum, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, const vec3_t segmentmins, const vec3_t segmentmaxs)
const int *e;
const texture_t *texture;
int axis;
- while (nodenum >= 0)
+ for(;;)
{
node = model->collision_bih.nodes + nodenum;
if (node->type == BIH_UNORDERED)
else
return; // trace falls between children
}
- if (!model->collision_bih.leafs)
- return;
- leaf = model->collision_bih.leafs + (-1-nodenum);
-#if 1
- if (!BoxesOverlap(segmentmins, segmentmaxs, leaf->mins, leaf->maxs))
- return;
-#endif
- switch(leaf->type)
- {
- case BIH_BRUSH:
- brush = model->brush.data_brushes[leaf->itemindex].colbrushf;
- Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
- break;
- case BIH_COLLISIONTRIANGLE:
- if (!mod_q3bsp_curves_collisions.integer)
- return;
- e = model->brush.data_collisionelement3i + 3*leaf->itemindex;
- texture = model->data_textures + leaf->textureindex;
- Collision_TraceBrushTriangleFloat(trace, thisbrush_start, thisbrush_end, model->brush.data_collisionvertex3f + e[0] * 3, model->brush.data_collisionvertex3f + e[1] * 3, model->brush.data_collisionvertex3f + e[2] * 3, texture->supercontents, texture->surfaceflags, texture);
- break;
- case BIH_RENDERTRIANGLE:
- e = model->surfmesh.data_element3i + 3*leaf->itemindex;
- texture = model->data_textures + leaf->textureindex;
- Collision_TraceBrushTriangleFloat(trace, thisbrush_start, thisbrush_end, model->surfmesh.data_vertex3f + e[0] * 3, model->surfmesh.data_vertex3f + e[1] * 3, model->surfmesh.data_vertex3f + e[2] * 3, texture->supercontents, texture->surfaceflags, texture);
- break;
- }
}
void Mod_CollisionBIH_TracePoint(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
temp_leafsortscratch = temp_leafsort + bihnumleafs;
// now build it
- BIH_Build(out, bihnumleafs, bihleafs, bihmaxnodes, bihnodes, temp_leafsort, temp_leafsortscratch, mod_collision_bih_childrengrouping.integer);
+ BIH_Build(out, bihnumleafs, bihleafs, bihmaxnodes, bihnodes, temp_leafsort, temp_leafsortscratch);
// we're done with the temporary data
Mem_Free(temp_leafsort);