Example #1
0
/**
 * @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;
}
Example #2
0
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;
}
Example #3
0
/*
 * @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;
}
Example #4
0
/**
 * @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;
}
Example #5
0
/**
 * @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;
}