/** * @brief Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH */ static int32_t Map_BoxOnPlaneSide(vec3_t mins, vec3_t maxs, map_plane_t *plane) { int32_t side; int32_t i; vec3_t corners[2]; vec_t dist1, dist2; // axial planes are easy if (AXIAL(plane)) { side = 0; if (maxs[plane->type] > plane->dist + SIDE_EPSILON) { side |= SIDE_FRONT; } if (mins[plane->type] < plane->dist - SIDE_EPSILON) { side |= SIDE_BACK; } return side; } // create the proper leading and trailing verts for the box for (i = 0; i < 3; i++) { if (plane->normal[i] < 0) { corners[0][i] = mins[i]; corners[1][i] = maxs[i]; } else { corners[1][i] = mins[i]; corners[0][i] = maxs[i]; } } dist1 = DotProduct(plane->normal, corners[0]) - plane->dist; dist2 = DotProduct(plane->normal, corners[1]) - plane->dist; side = 0; if (dist1 >= SIDE_EPSILON) { side = SIDE_FRONT; } if (dist2 < SIDE_EPSILON) { side |= SIDE_BACK; } return side; }
static uint16_t CreateNewFloatPlane (vec3_t normal, vec_t dist) { plane_t* p; if (VectorLength(normal) < 0.5) Sys_Error("FloatPlane: bad normal (%.3f:%.3f:%.3f)", normal[0], normal[1], normal[2]); /* create a new plane */ if (nummapplanes + 2 > MAX_MAP_PLANES) Sys_Error("MAX_MAP_PLANES (%i)", nummapplanes + 2); p = &mapplanes[nummapplanes]; VectorCopy(normal, p->normal); p->dist = dist; p->type = (p + 1)->type = PlaneTypeForNormal(p->normal); VectorSubtract(vec3_origin, normal, (p + 1)->normal); (p + 1)->dist = -dist; nummapplanes += 2; /* always put axial planes facing positive first */ if (AXIAL(p)) { if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) { /* flip order by swapping the planes */ const plane_t temp = *p; *p = *(p + 1); *(p + 1) = temp; AddPlaneToHash(p); AddPlaneToHash(p + 1); return nummapplanes - 1; } } AddPlaneToHash(p); AddPlaneToHash(p + 1); return nummapplanes - 2; }
/* * @brief */ static int32_t CreateNewFloatPlane(vec3_t normal, vec_t dist) { map_plane_t *p, temp; if (VectorLength(normal) < 0.5) Com_Error(ERR_FATAL, "FloatPlane: bad normal\n"); // create a new plane if (num_map_planes + 2 > MAX_BSP_PLANES) Com_Error(ERR_FATAL, "MAX_BSP_PLANES\n"); p = &map_planes[num_map_planes]; VectorCopy(normal, p->normal); p->dist = dist; p->type = (p + 1)->type = PlaneTypeForNormal(p->normal); VectorSubtract(vec3_origin, normal, (p + 1)->normal); (p + 1)->dist = -dist; num_map_planes += 2; // always put axial planes facing positive first if (AXIAL(p)) { if (p->normal[0] < 0.0 || p->normal[1] < 0.0 || p->normal[2] < 0.0) { // flip order temp = *p; *p = *(p + 1); *(p + 1) = temp; AddPlaneToHash(p); AddPlaneToHash(p + 1); return num_map_planes - 1; } } AddPlaneToHash(p); AddPlaneToHash(p + 1); return num_map_planes - 2; }
/** * @brief Using a heuristic, choses one of the sides out of the brushlist * to partition the brushes with. * @return nullptr if there are no valid planes to split with.. */ side_t* SelectSplitSide (bspbrush_t* brushes, bspbrush_t* volume) { int value, bestvalue; bspbrush_t* brush, *test; side_t* bestside; int i, j, pass, numpasses; int front, back, both, facing, splits; int bsplits, epsilonbrush; bool hintsplit; if (!volume) return nullptr; /* can't split empty brush */ bestside = nullptr; bestvalue = -99999; /** * the search order goes: visible-structural, visible-detail, * nonvisible-structural, nonvisible-detail. * If any valid plane is available in a pass, no further * passes will be tried. */ numpasses = 4; for (pass = 0; pass < numpasses; pass++) { for (brush = brushes; brush; brush = brush->next) { if ((pass & 1) && !(brush->original->contentFlags & CONTENTS_DETAIL)) continue; if (!(pass & 1) && (brush->original->contentFlags & CONTENTS_DETAIL)) continue; for (i = 0; i < brush->numsides; i++) { uint16_t pnum; side_t* side = &brush->sides[i]; if (side->bevel) continue; /* never use a bevel as a spliter */ if (!side->winding) continue; /* nothing visible, so it can't split */ if (side->texinfo == TEXINFO_NODE) continue; /* already a node splitter */ if (side->tested) continue; /* we already have metrics for this plane */ if (side->surfaceFlags & SURF_SKIP) continue; /* skip surfaces are never chosen */ if (side->visible ^ (pass < 2)) continue; /* only check visible faces on first pass */ pnum = side->planenum; pnum &= ~1; /* always use positive facing plane */ if (!CheckPlaneAgainstVolume(pnum, volume)) continue; /* would produce a tiny volume */ front = 0; back = 0; both = 0; facing = 0; splits = 0; epsilonbrush = 0; hintsplit = false; for (test = brushes; test; test = test->next) { const int s = TestBrushToPlanenum(test, pnum, &bsplits, &hintsplit, &epsilonbrush); splits += bsplits; if (bsplits && (s & PSIDE_FACING)) Sys_Error("PSIDE_FACING with splits"); test->testside = s; /* if the brush shares this face, don't bother * testing that facenum as a splitter again */ if (s & PSIDE_FACING) { facing++; for (j = 0; j < test->numsides; j++) { if ((test->sides[j].planenum &~ 1) == pnum) test->sides[j].tested = true; } } if (s & PSIDE_FRONT) front++; if (s & PSIDE_BACK) back++; if (s == PSIDE_BOTH) both++; } /* give a value estimate for using this plane */ value = 5 * facing - 5 * splits - abs(front - back); if (AXIAL(&mapplanes[pnum])) value += 5; /* axial is better */ value -= epsilonbrush * 1000; /* avoid! */ /* never split a hint side except with another hint */ if (hintsplit && !(side->surfaceFlags & SURF_HINT)) value = -9999999; /* save off the side test so we don't need * to recalculate it when we actually seperate * the brushes */ if (value > bestvalue) { bestvalue = value; bestside = side; for (test = brushes; test; test = test->next) test->side = test->testside; } } } /* if we found a good plane, don't bother trying any other passes */ if (bestside) { if (pass > 1) { if (threadstate.numthreads == 1) c_nonvis++; } break; } } /* clear all the tested flags we set */ for (brush = brushes; brush; brush = brush->next) { for (i = 0; i < brush->numsides; i++) brush->sides[i].tested = false; } return bestside; }
/** * @brief Using a heuristic, chooses one of the sides out of the brush list * to partition the brushes with. * Returns NULL if there are no valid planes to split with.. */ static side_t *SelectSplitSide(brush_t *brushes, node_t *node) { int32_t value, bestvalue; brush_t *brush, *test; side_t *side, *bestside; int32_t i, j, pass, numpasses; int32_t pnum; int32_t s; int32_t front, back, both, facing, splits; int32_t bsplits; int32_t epsilonbrush; _Bool hintsplit; bestside = NULL; bestvalue = -99999; // the search order goes: visible-structural, visible-detail, // nonvisible-structural, nonvisible-detail. // If any valid plane is available in a pass, no further // passes will be tried. numpasses = 4; for (pass = 0; pass < numpasses; pass++) { for (brush = brushes; brush; brush = brush->next) { if ((pass & 1) && !(brush->original->contents & CONTENTS_DETAIL)) { continue; } if (!(pass & 1) && (brush->original->contents & CONTENTS_DETAIL)) { continue; } for (i = 0; i < brush->num_sides; i++) { side = brush->sides + i; if (side->bevel) { continue; // never use a bevel as a splitter } if (!side->winding) { continue; // nothing visible, so it can't split } if (side->texinfo == TEXINFO_NODE) { continue; // already a node splitter } if (side->tested) { continue; // we already have metrics for this plane } if (side->surf & SURF_SKIP) { continue; // skip surfaces are never chosen } if (side->visible ^ (pass < 2)) { continue; // only check visible faces on first pass } pnum = side->plane_num; pnum &= ~1; // always use positive facing plane CheckPlaneAgainstParents(pnum, node); if (!CheckPlaneAgainstVolume(pnum, node)) { continue; // would produce a tiny volume } front = 0; back = 0; both = 0; facing = 0; splits = 0; epsilonbrush = 0; hintsplit = false; for (test = brushes; test; test = test->next) { s = TestBrushToPlanenum(test, pnum, &bsplits, &hintsplit, &epsilonbrush); splits += bsplits; if (bsplits && (s & SIDE_FACING)) { Com_Error(ERROR_FATAL, "SIDE_FACING with splits\n"); } test->test_side = s; // if the brush shares this face, don't bother // testing that facenum as a splitter again if (s & SIDE_FACING) { facing++; for (j = 0; j < test->num_sides; j++) { if ((test->sides[j].plane_num & ~1) == pnum) { test->sides[j].tested = true; } } } if (s & SIDE_FRONT) { front++; } if (s & SIDE_BACK) { back++; } if (s == SIDE_BOTH) { both++; } } // give a value estimate for using this plane value = 5 * facing - 5 * splits - abs(front - back); if (AXIAL(&map_planes[pnum])) { value += 5; // axial is better } value -= epsilonbrush * 1000; // avoid! // never split a hint side except with another hint if (hintsplit && !(side->surf & SURF_HINT)) { value = -9999999; } // save off the side test so we don't need // to recalculate it when we actually seperate // the brushes if (value > bestvalue) { bestvalue = value; bestside = side; for (test = brushes; test; test = test->next) { test->side = test->test_side; } } } } // if we found a good plane, don't bother trying any other passes if (bestside) { if (pass > 1) { if (debug) { SDL_SemPost(semaphores.nonvis_nodes); } } if (pass > 0) { node->detail_seperator = true; // not needed for vis } break; } } // clear all the tested flags we set for (brush = brushes; brush; brush = brush->next) { for (i = 0; i < brush->num_sides; i++) { brush->sides[i].tested = false; } } return bestside; }