qbsp: add SplitBrush from q3map (not yet tested)

This commit is contained in:
Eric Wasylishen 2017-07-23 00:42:09 -06:00
parent d246d2a798
commit 3bb22ef22b
2 changed files with 320 additions and 8 deletions

View File

@ -51,5 +51,6 @@ bool PlaneInvEqual(const qbsp_plane_t *p1, const qbsp_plane_t *p2);
bool BoundBrush (brush_t *brush);
vec_t BrushVolume (const brush_t *brush);
int BrushMostlyOnSide (const brush_t *brush, const vec3_t normal, vec_t dist);
void SplitBrush (const brush_t *brush, int planenum, int planeside, brush_t **front, brush_t **back);
#endif

View File

@ -1,6 +1,7 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 1997 Greg Lewis
Copyright (C) 1999-2005 Id Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -518,6 +519,17 @@ FreeBrushes(brush_t *brushlist)
}
}
/*
=====================
FreeBrush
=====================
*/
void
FreeBrush(brush_t *brush)
{
Q_assert(brush->next == nullptr);
FreeMem(brush, BRUSH, 1);
}
/*
==============================================================================
@ -1311,6 +1323,21 @@ int BrushMostlyOnSide (const brush_t *brush, const vec3_t planenormal, vec_t pla
return side;
}
face_t *CopyFace(const face_t *face)
{
face_t *newface = (face_t *)AllocMem(FACE, 1, true);
memcpy(newface, face, sizeof(face_t));
// clear stuff that shouldn't be copied.
newface->original = nullptr;
newface->outputnumber = -1;
newface->edges = nullptr;
newface->next = nullptr;
return newface;
}
/*
==================
CopyBrush
@ -1331,14 +1358,7 @@ brush_t *CopyBrush (const brush_t *brush)
for (const face_t *face = brush->faces; face; face = face->next) {
face_t *newface = (face_t *)AllocMem(FACE, 1, true);
memcpy(newface, face, sizeof(face_t));
// clear stuff that shouldn't be copied.
newface->original = nullptr;
newface->outputnumber = -1;
newface->edges = nullptr;
face_t *newface = CopyFace(face);
// link into newbrush
newface->next = newbrush->faces;
@ -1348,3 +1368,294 @@ brush_t *CopyBrush (const brush_t *brush)
return newbrush;
}
/*
================
WindingIsTiny
Returns true if the winding would be crunched out of
existance by the vertex snapping.
from q3map
================
*/
#define EDGE_LENGTH 0.2
static qboolean
WindingIsTiny (const winding_t *w)
{
/*
if (WindingArea (w) < 1)
return qtrue;
return qfalse;
*/
int i, j;
vec_t len;
vec3_t delta;
int edges;
edges = 0;
for (i=0 ; i<w->numpoints ; i++)
{
j = i == w->numpoints - 1 ? 0 : i+1;
VectorSubtract (w->points[j], w->points[i], delta);
len = VectorLength (delta);
if (len > EDGE_LENGTH)
{
if (++edges == 3)
return false;
}
}
return true;
}
/*
================
WindingIsHuge
Returns true if the winding still has one of the points
from basewinding for plane
from q3map
================
*/
qboolean WindingIsHuge (winding_t *w)
{
int i, j;
for (i=0 ; i<w->numpoints ; i++) {
for (j=0 ; j<3 ; j++)
if (w->points[i][j] <= MIN_WORLD_COORD || w->points[i][j] >= MAX_WORLD_COORD)
return true;
}
return false;
}
static int
Brush_FaceCount (const brush_t *brush)
{
int i=0;
for (const face_t *face = brush->faces; face; face = face->next) {
i++;
}
return i;
}
/*
================
SplitBrush
Generates two new brushes, leaving the original
unchanged
from q3map
================
*/
void SplitBrush (const brush_t *brush,
int planenum,
int planeside,
brush_t **front, brush_t **back)
{
*front = nullptr;
*back = nullptr;
const qbsp_plane_t *plane = &map.planes.at(planenum);
// check all points
vec_t d_front = 0;
vec_t d_back = 0;
for (const face_t *face = brush->faces; face; face = face->next) {
const winding_t *w = &face->w;
if (!w->numpoints)
continue;
for (int j=0 ; j<w->numpoints ; j++) {
const vec_t d = DotProduct (w->points[j], plane->normal) - plane->dist;
if (d > 0 && d > d_front)
d_front = d;
if (d < 0 && d < d_back)
d_back = d;
}
}
if (d_front < 0.1) // PLANESIDE_EPSILON)
{ // only on back
*back = CopyBrush (brush);
return;
}
if (d_back > -0.1) // PLANESIDE_EPSILON)
{ // only on front
*front = CopyBrush (brush);
return;
}
// create a new winding from the split plane
winding_t *w = BaseWindingForPlane (plane);
for (const face_t *face = brush->faces; face; face = face->next) {
plane_t plane2 = Face_Plane(face);
ChopWindingInPlace (&w, plane2.normal, plane2.dist, 0); // PLANESIDE_EPSILON);
}
if (!w || WindingIsTiny (w) )
{ // the brush isn't really split
int side;
if (w)
FreeMem(w, WINDING, 1);
side = BrushMostlyOnSide (brush, plane->normal, plane->dist);
if (side == SIDE_FRONT)
*front = CopyBrush (brush);
if (side == SIDE_BACK)
*back = CopyBrush (brush);
return;
}
if (WindingIsHuge (w))
{
logprint ("WARNING: huge winding\n");
}
winding_t *midwinding = w;
brush_t *b[2];
// split it for real
// first, make two empty brushes (for the front and back side of the plane)
for (int i=0 ; i<2 ; i++)
{
b[i] = (brush_t *) AllocMem (BRUSH, 1, true);
//memcpy( b[i], brush, sizeof( brush_t ) );
// NOTE: brush copying
b[i]->contents = brush->contents;
b[i]->cflags = brush->cflags;
b[i]->lmshift = brush->lmshift;
b[i]->faces = nullptr;
b[i]->next = nullptr;
// FIXME:
//b[i]->original = brush->original;
}
// split all the current windings
for (const face_t *face = brush->faces; face; face = face->next) {
const winding_t *w = &face->w;
if (!w->numpoints)
continue;
winding_t *cw[2];
DivideWinding(w, plane, &cw[0], &cw[1]);
for (int j=0 ; j<2 ; j++)
{
if (!cw[j])
continue;
/*
if (WindingIsTiny (cw[j]))
{
FreeWinding (cw[j]);
continue;
}
*/
face_t *newface = CopyFace(face);
newface->w = *cw[j];
UpdateFaceSphere(newface);
// link it into the front or back brush we are building
newface->next = b[j]->faces;
b[j]->faces = newface;
}
}
// see if we have valid polygons on both sides
for (int i=0 ; i<2 ; i++)
{
BoundBrush (b[i]);
int j;
for (j=0 ; j<3 ; j++)
{
if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
{
logprint ("bogus brush after clip\n");
break;
}
}
if (Brush_FaceCount(b[i]) < 4 /* was 3 */ || j < 3)
{
FreeBrush (b[i]);
b[i] = nullptr;
}
}
if ( !(b[0] && b[1]) )
{
if (!b[0] && !b[1])
logprint ("split removed brush\n");
else
logprint ("split not on both sides\n");
if (b[0])
{
FreeBrush (b[0]);
*front = CopyBrush (brush);
}
if (b[1])
{
FreeBrush (b[1]);
*back = CopyBrush (brush);
}
return;
}
// add the midwinding to both sides
for (int i=0 ; i<2 ; i++)
{
// clone the first face (arbitrarily)
face_t *newface = CopyFace(b[i]->faces);
if (i == 0) {
winding_t *newwinding = FlipWinding(midwinding);
newface->w = *newwinding;
newface->planenum = planenum;
newface->planeside = !planeside;
FreeMem(newwinding, WINDING, 1);
} else {
winding_t *newwinding = CopyWinding(midwinding);
newface->w = *newwinding;
newface->planenum = planenum;
newface->planeside = planeside;
FreeMem(newwinding, WINDING, 1);
}
UpdateFaceSphere(newface);
// link it into the front or back brush
newface->next = b[i]->faces;
b[i]->faces = newface;
}
{
vec_t v1;
int i;
for (i=0 ; i<2 ; i++)
{
v1 = BrushVolume (b[i]);
if (v1 < 1.0)
{
FreeMem (b[i], BRUSH, 1);
b[i] = nullptr;
logprint ("tiny volume after clip\n");
}
}
}
*front = b[0];
*back = b[1];
}