/** * @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; }
side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node) { int value, bestvalue; bspbrush_t *brush, *test; side_t *side, *bestside; int i, j, pass, numpasses; int pnum; int s; int front, back, both, facing, splits; int bsplits; int bestsplits; int epsilonbrush; qboolean hintsplit = false; bestside = NULL; bestvalue = -99999; bestsplits = 0; // the search order goes: visible-structural, nonvisible-structural // If any valid plane is available in a pass, no further // passes will be tried. numpasses = 2; for (pass = 0 ; pass < numpasses ; pass++) { for (brush = brushes ; brush ; brush=brush->next) { for (i=0 ; i<brush->numsides ; i++) { 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; // allready a node splitter if (side->tested) continue; // we allready have metrics for this plane if (side->surf & SURF_SKIP) continue; // skip surfaces are never chosen if ( side->visible ^ (pass<1) ) continue; // only check visible faces on first pass pnum = side->planenum; pnum &= ~1; // allways 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; for (test = brushes ; test ; test=test->next) { s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush); splits += bsplits; if (bsplits && (s&PSIDE_FACING) ) 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); // value = -5*splits; // value = 5*facing - 5*splits; if (g_MainMap->mapplanes[pnum].type < 3) value+=5; // axial is better value -= epsilonbrush*1000; // avoid! // trans should split last if ( side->surf & SURF_TRANS ) { value -= 500; } // never split a hint side except with another hint if (hintsplit && !(side->surf & SURF_HINT) ) value = -9999999; // water should split first if (side->contents & (CONTENTS_WATER | CONTENTS_SLIME)) 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; bestsplits = splits; 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 > 0) { if (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; }
side_t *SelectSplitSide( bspbrush_t *brushes, node_t *node ) { int value, bestvalue; bspbrush_t *brush, *test; side_t *side, *bestside; int i, pass, numpasses; //int j; int pnum; int s; int front, back, both, facing, splits; int bsplits; int bestsplits; int epsilonbrush; qboolean hintsplit = false; bestside = NULL; bestvalue = -99999; bestsplits = 0; // 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 = 2; for ( pass = 0; pass < numpasses; pass++ ) { for ( brush = brushes; brush; brush = brush->next ) { // only check detail the second pass // if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) ) // continue; // if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) ) // continue; for ( i = 0; i < brush->numsides; i++ ) { side = brush->sides + i; // if (side->flags & SFL_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; // allready a node splitter } if ( side->flags & SFL_TESTED ) { continue; // we allready have metrics for this plane } // if (side->surf & SURF_SKIP) // continue; // skip surfaces are never chosen // if (!(side->flags & SFL_VISIBLE) && (pass < 2)) // continue; // only check visible faces on first pass if ( ( side->flags & SFL_CURVE ) && ( pass < 1 ) ) { continue; // only check curves on the second pass } pnum = side->planenum; pnum &= ~1; // allways 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; //inner loop: optimize for ( test = brushes; test; test = test->next ) { s = TestBrushToPlanenum( test, pnum, &bsplits, &hintsplit, &epsilonbrush ); splits += bsplits; // if (bsplits && (s&PSIDE_FACING) ) // Error ("PSIDE_FACING with splits"); test->testside = s; // if ( s & PSIDE_FACING ) { facing++; } if ( s & PSIDE_FRONT ) { front++; } if ( s & PSIDE_BACK ) { back++; } if ( s == PSIDE_BOTH ) { both++; } } //end for // give a value estimate for using this plane value = 5 * facing - 5 * splits - abs( front - back ); // value = -5*splits; // value = 5*facing - 5*splits; if ( mapplanes[pnum].type < 3 ) { 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; bestsplits = splits; for ( test = brushes; test ; test = test->next ) test->side = test->testside; } //end if } //end for } //end for (brush = brushes; // if we found a good plane, don't bother trying any // other passes if ( bestside ) { if ( pass > 1 ) { if ( numthreads == 1 ) { c_nonvis++; } } if ( pass > 0 ) { node->detail_seperator = true; // not needed for vis } break; } //end if } //end for (pass = 0; // // clear all the tested flags we set // for ( brush = brushes ; brush ; brush = brush->next ) { for ( i = 0; i < brush->numsides; i++ ) { brush->sides[i].flags &= ~SFL_TESTED; } //end for } //end for return bestside; } //end of the function SelectSplitSide