vhlt/hlbsp/brink.cpp
2016-09-21 00:07:53 +03:00

2066 lines
54 KiB
C++

#include "bsp5.h"
#ifdef HLBSP_BRINKHACK
#include <list>
#include <map>
#include <vector>
// TODO: we should consider corners in addition to brinks.
// TODO: use bcircle_t structure only to find out all possible "movement"s, then send then down the bsp tree to determine which leafs may incorrectly block the movement.
// The image of a typical buggy brink (with type BrinkFloorBlocking):
//
// brinkstoppoint
// x------------x
// / / \.
// / / \.
// / move >>>>xblocked
// / / \.
// / / x
// x------------x /
// | brinkstartpoint/
// | | \ / Z
// | | \ / | Y
// | | \ / | /
// x------------x----x |/
// O-------X
//
typedef struct bpartition_s
{
int planenum;
bool planeside;
int content;
bbrinklevel_e type;
bpartition_s *next;
}
bpartition_t;
typedef struct bclipnode_s
{
bool isleaf;
int planenum;
const dplane_t *plane;
bclipnode_s *children[2]; // children[0] is the front side of the plane (SIDE_FRONT = 0)
int content;
bpartition_t *partitions;
struct btreeleaf_s *treeleaf;
}
bclipnode_t;
typedef struct bbrinknode_s
{ // we will only focus on the bsp shape which encircles a brink, so we extract the clipnodes that meets with the brink and store them here
bool isleaf;
int planenum;
const dplane_t *plane;
int children[2];
int content;
bclipnode_t *clipnode;
}
bbrinknode_t;
typedef struct
{
vec3_t start, stop;
vec3_t direction;
int numnodes; // including both nodes and leafs
std::vector< bbrinknode_t > *nodes;
struct btreeedge_s *edge; // only for use in deciding brink type
}
bbrink_t;
bbrink_t *CopyBrink (bbrink_t *other)
{
bbrink_t *b;
hlassume (b = (bbrink_t *)malloc (sizeof (bbrink_t)), assume_NoMemory);
VectorCopy (other->direction, b->direction);
VectorCopy (other->start, b->start);
VectorCopy (other->stop, b->stop);
b->numnodes = other->numnodes;
b->nodes = new std::vector< bbrinknode_t >(*other->nodes);
return b;
}
void DeleteBrink (bbrink_t *b)
{
delete b->nodes;
free (b);
}
bbrink_t *CreateBrink (vec3_t start, vec3_t stop)
{
bbrink_t *b;
hlassume (b = (bbrink_t *)malloc (sizeof (bbrink_t)), assume_NoMemory);
VectorCopy (start, b->start);
VectorCopy (stop, b->stop);
VectorSubtract (stop, start, b->direction);
b->numnodes = 1;
b->nodes = new std::vector< bbrinknode_t >;
bbrinknode_t newnode;
newnode.isleaf = true;
newnode.clipnode = NULL;
b->nodes->push_back (newnode);
// CreateBrink must be followed by BrinkSplitClipnode
return b;
}
void PrintBrink (const bbrink_t *b)
{
Log ("direction %f %f %f start %f %f %f stop %f %f %f\n", b->direction[0], b->direction[1], b->direction[2], b->start[0], b->start[1], b->start[2], b->stop[0], b->stop[1], b->stop[2]);
Log ("numnodes %d\n", b->numnodes);
for (int i = 0; i < b->numnodes; i++)
{
bbrinknode_t *n = &(*b->nodes)[i];
if (n->isleaf)
{
Log ("leaf[%d] content %d\n", i, n->content);
}
else
{
Log ("node[%d]-[%d:%d] plane %f %f %f %f\n", i, n->children[0], n->children[1], n->plane->normal[0], n->plane->normal[1], n->plane->normal[2], n->plane->dist);
}
}
}
void BrinkSplitClipnode (bbrink_t *b, const dplane_t *plane, int planenum, bclipnode_t *prev, bclipnode_t *n0, bclipnode_t *n1)
{
int found;
int numfound = 0;
for (int i = 0; i < b->numnodes; i++)
{
bbrinknode_t *node = &(*b->nodes)[i];
if (node->isleaf && node->clipnode == prev)
{
found = i;
numfound++;
}
}
if (numfound == 0)
{
PrintOnce ("BrinkSplitClipnode: internal error: couldn't find clipnode");
hlassume (false, assume_first);
}
if (numfound > 1)
{
PrintOnce ("BrinkSplitClipnode: internal error: found more than one clipnode");
hlassume (false, assume_first);
}
if (n0 == n1)
{
PrintOnce ("BrinkSplitClipnode: internal error: n0 == n1");
hlassume (false, assume_first);
}
b->nodes->resize (b->numnodes + 2);
bbrinknode_t *node = &(*b->nodes)[found];
bbrinknode_t *front = &(*b->nodes)[b->numnodes];
bbrinknode_t *back = &(*b->nodes)[b->numnodes + 1];
node->clipnode = NULL;
node->isleaf = false;
node->plane = plane;
node->planenum = planenum;
node->children[0] = b->numnodes;
node->children[1] = b->numnodes + 1;
front->isleaf = true;
front->content = n0->content;
front->clipnode = n0;
back->isleaf = true;
back->content = n1->content;
back->clipnode = n1;
b->numnodes += 2;
}
void BrinkReplaceClipnode (bbrink_t *b, bclipnode_t *prev, bclipnode_t *n)
{
int found;
int numfound = 0;
for (int i = 0; i < b->numnodes; i++)
{
bbrinknode_t *node = &(*b->nodes)[i];
if (node->isleaf && node->clipnode == prev)
{
found = i;
numfound++;
}
}
if (numfound == 0)
{
PrintOnce ("BrinkSplitClipnode: internal error: couldn't find clipnode");
hlassume (false, assume_first);
}
if (numfound > 1)
{
PrintOnce ("BrinkSplitClipnode: internal error: found more than one clipnode");
hlassume (false, assume_first);
}
bbrinknode_t *node = &(*b->nodes)[found];
node->clipnode = n;
node->content = n->content;
}
// compute the structure of the whole bsp tree
struct btreepoint_s; // 0d object
struct btreeedge_s; // 1d object
struct btreeface_s; // 2d object
struct btreeleaf_s; // 3d object
typedef struct
{
btreepoint_s *p;
bool side;
}
btreepoint_r;
typedef struct
{
btreeedge_s *e;
bool side;
}
btreeedge_r;
typedef struct
{
btreeface_s *f;
bool side;
}
btreeface_r;
typedef struct
{
btreeleaf_s *l;
bool side;
}
btreeleaf_r;
typedef std::list< btreepoint_r > btreepoint_l;
typedef std::list< btreeedge_r > btreeedge_l;
typedef std::list< btreeface_r > btreeface_l;
typedef std::list< btreeleaf_r > btreeleaf_l;
typedef struct btreepoint_s
{
vec3_t v;
bool infinite;
btreeedge_l *edges; // this is a reversed reference
bool tmp_tested;
vec_t tmp_dist;
int tmp_side;
}
btreepoint_t;
typedef struct btreeedge_s
{
btreepoint_r points[2]; // pointing from points[1] to points[0]
bool infinite; // both points are infinite (i.e. this edge lies on the bounding box)
btreeface_l *faces; // this is a reversed reference
bbrink_t *brink; // not defined for infinite edges
bool tmp_tested;
int tmp_side;
#ifdef HLBSP_BRINKNOTUSEDBYLEAF_FIX
bool tmp_onleaf[2];
#endif
}
btreeedge_t;
typedef struct btreeface_s
{
btreeedge_l *edges; // empty faces are allowed (in order to preserve topological correctness)
bool infinite; // when the face is infinite, all its edges must also be infinite
btreeleaf_r leafs[2]; // pointing from leafs[0] to leafs[1] // this is a reversed reference
const dplane_t *plane; // not defined for infinite face
int planenum;
bool planeside; // if ture, this face is pointing at -plane->normal
bool tmp_tested;
int tmp_side;
}
btreeface_t;
typedef struct btreeleaf_s
{
btreeface_l *faces;
bool infinite; // note: the infinite leaf is not convex
bclipnode_t *clipnode; // not defined for infinite leaf
}
btreeleaf_t;
btreepoint_t *AllocTreepoint (int &numobjects, bool infinite)
{
numobjects++;
btreepoint_t *tp = (btreepoint_t *)malloc (sizeof (btreepoint_t));
hlassume (tp != NULL, assume_NoMemory);
tp->edges = new btreeedge_l ();
tp->infinite = infinite;
return tp;
}
btreeedge_t *AllocTreeedge (int &numobjects, bool infinite)
{
numobjects++;
btreeedge_t *te = (btreeedge_t *)malloc (sizeof (btreeedge_t));
hlassume (te != NULL, assume_NoMemory);
te->points[0].p = NULL;
te->points[0].side = false;
te->points[1].p = NULL;
te->points[1].side = true;
te->faces = new btreeface_l ();
te->infinite = infinite;
// should be followed by SetEdgePoints
return te;
}
void AttachPointToEdge (btreeedge_t *te, btreepoint_t *tp, bool side)
{
if (te->points[side].p)
{
PrintOnce ("AttachPointToEdge: internal error: point occupied.");
hlassume (false, assume_first);
}
if (te->infinite && !tp->infinite)
{
PrintOnce ("AttachPointToEdge: internal error: attaching a finite object to an infinite object.");
hlassume (false, assume_first);
}
te->points[side].p = tp;
btreeedge_r er;
er.e = te;
er.side = side;
tp->edges->push_back (er);
}
void SetEdgePoints (btreeedge_t *te, btreepoint_t *tp0, btreepoint_t *tp1)
{
AttachPointToEdge (te, tp0, false);
AttachPointToEdge (te, tp1, true);
}
btreeface_t *AllocTreeface (int &numobjects, bool infinite)
{
numobjects++;
btreeface_t *tf = (btreeface_t *)malloc (sizeof (btreeface_t));
hlassume (tf != NULL, assume_NoMemory);
tf->edges = new btreeedge_l ();
tf->leafs[0].l = NULL;
tf->leafs[0].side = false;
tf->leafs[1].l = NULL;
tf->leafs[1].side = true;
tf->infinite = infinite;
return tf;
}
void AttachEdgeToFace (btreeface_t *tf, btreeedge_t *te, int side)
{
if (tf->infinite && !te->infinite)
{
PrintOnce ("AttachEdgeToFace: internal error: attaching a finite object to an infinite object.");
hlassume (false, assume_first);
}
btreeedge_r er;
er.e = te;
er.side = side;
tf->edges->push_back (er);
btreeface_r fr;
fr.f = tf;
fr.side = side;
te->faces->push_back (fr);
}
void AttachFaceToLeaf (btreeleaf_t *tl, btreeface_t *tf, int side)
{
if (tl->infinite && !tf->infinite)
{
PrintOnce ("AttachFaceToLeaf: internal error: attaching a finite object to an infinite object.");
hlassume (false, assume_first);
}
btreeface_r fr;
fr.f = tf;
fr.side = side;
tl->faces->push_back (fr);
if (tf->leafs[side].l)
{
PrintOnce ("AttachFaceToLeaf: internal error: leaf occupied.");
hlassume (false, assume_first);
}
tf->leafs[side].l = tl;
}
void SetFaceLeafs (btreeface_t *tf, btreeleaf_t *tl0, btreeleaf_t *tl1)
{
AttachFaceToLeaf (tl0, tf, false);
AttachFaceToLeaf (tl1, tf, true);
}
btreeleaf_t *AllocTreeleaf (int &numobjects, bool infinite)
{
numobjects++;
btreeleaf_t *tl = (btreeleaf_t *)malloc (sizeof (btreeleaf_t));
hlassume (tl != NULL, assume_NoMemory);
tl->faces = new btreeface_l ();
tl->infinite = infinite;
return tl;
}
btreeleaf_t *BuildOutside (int &numobjects)
{
btreeleaf_t *leaf_outside;
leaf_outside = AllocTreeleaf (numobjects, true);
leaf_outside->clipnode = NULL;
return leaf_outside;
}
btreeleaf_t *BuildBaseCell (int &numobjects, bclipnode_t *clipnode, vec_t range, btreeleaf_t *leaf_outside)
{
btreepoint_t *tp[8];
for (int i = 0; i < 8; i++)
{
tp[i] = AllocTreepoint (numobjects, true);
if (i & 1)
tp[i]->v[0] = range;
else
tp[i]->v[0] = -range;
if (i & 2)
tp[i]->v[1] = range;
else
tp[i]->v[1] = -range;
if (i & 4)
tp[i]->v[2] = range;
else
tp[i]->v[2] = -range;
}
btreeedge_t *te[12];
for (int i = 0; i < 12; i++)
{
te[i] = AllocTreeedge (numobjects, true);
}
SetEdgePoints (te[0], tp[1], tp[0]);
SetEdgePoints (te[1], tp[3], tp[2]);
SetEdgePoints (te[2], tp[5], tp[4]);
SetEdgePoints (te[3], tp[7], tp[6]);
SetEdgePoints (te[4], tp[2], tp[0]);
SetEdgePoints (te[5], tp[3], tp[1]);
SetEdgePoints (te[6], tp[6], tp[4]);
SetEdgePoints (te[7], tp[7], tp[5]);
SetEdgePoints (te[8], tp[4], tp[0]);
SetEdgePoints (te[9], tp[5], tp[1]);
SetEdgePoints (te[10], tp[6], tp[2]);
SetEdgePoints (te[11], tp[7], tp[3]);
btreeface_t *tf[6];
for (int i = 0; i < 6; i++)
{
tf[i] = AllocTreeface (numobjects, true);
}
AttachEdgeToFace (tf[0], te[4], true);
AttachEdgeToFace (tf[0], te[6], false);
AttachEdgeToFace (tf[0], te[8], false);
AttachEdgeToFace (tf[0], te[10], true);
AttachEdgeToFace (tf[1], te[5], false);
AttachEdgeToFace (tf[1], te[7], true);
AttachEdgeToFace (tf[1], te[9], true);
AttachEdgeToFace (tf[1], te[11], false);
AttachEdgeToFace (tf[2], te[0], false);
AttachEdgeToFace (tf[2], te[2], true);
AttachEdgeToFace (tf[2], te[8], true);
AttachEdgeToFace (tf[2], te[9], false);
AttachEdgeToFace (tf[3], te[1], true);
AttachEdgeToFace (tf[3], te[3], false);
AttachEdgeToFace (tf[3], te[10], false);
AttachEdgeToFace (tf[3], te[11], true);
AttachEdgeToFace (tf[4], te[0], true);
AttachEdgeToFace (tf[4], te[1], false);
AttachEdgeToFace (tf[4], te[4], false);
AttachEdgeToFace (tf[4], te[5], true);
AttachEdgeToFace (tf[5], te[2], false);
AttachEdgeToFace (tf[5], te[3], true);
AttachEdgeToFace (tf[5], te[6], true);
AttachEdgeToFace (tf[5], te[7], false);
btreeleaf_t *tl;
tl = AllocTreeleaf (numobjects, false);
for (int i = 0; i < 6; i++)
{
SetFaceLeafs (tf[i], tl, leaf_outside);
}
tl->clipnode = clipnode;
return tl;
}
btreepoint_t *GetPointFromEdge (btreeedge_t *te, bool side)
{
if (!te->points[side].p)
{
PrintOnce ("GetPointFromEdge: internal error: point not set.");
hlassume (false, assume_first);
}
return te->points[side].p;
}
void RemoveEdgeFromList (btreeedge_l *el, btreeedge_t *te, bool side)
{
btreeedge_l::iterator ei;
for (ei = el->begin (); ei != el->end (); ei++)
{
if (ei->e == te && ei->side == side)
{
el->erase (ei);
return; // only remove one copy if there are many (in order to preserve topological correctness)
}
}
PrintOnce ("RemoveEdgeFromList: internal error: edge not found.");
hlassume (false, assume_first);
}
void RemovePointFromEdge (btreeedge_t *te, btreepoint_t *tp, bool side) // warning: the point will not be freed
{
if (te->points[side].p != tp)
{
PrintOnce ("RemovePointFromEdge: internal error: point not found.");
hlassume (false, assume_first);
}
te->points[side].p = NULL;
RemoveEdgeFromList (tp->edges, te, side);
}
void DeletePoint (int &numobjects, btreepoint_t *tp)
{
if (!tp->edges->empty ())
{
PrintOnce ("DeletePoint: internal error: point used by edge.");
hlassume (false, assume_first);
}
delete tp->edges;
free (tp);
numobjects--;
}
void RemoveFaceFromList (btreeface_l *fl, btreeface_t *tf, bool side)
{
btreeface_l::iterator fi;
for (fi = fl->begin (); fi != fl->end (); fi++)
{
if (fi->f == tf && fi->side == side)
{
fl->erase (fi);
return;
}
}
PrintOnce ("RemoveFaceFromList: internal error: face not found.");
hlassume (false, assume_first);
}
void RemoveEdgeFromFace (btreeface_t *tf, btreeedge_t *te, bool side)
{
RemoveEdgeFromList (tf->edges, te, side);
RemoveFaceFromList (te->faces, tf, side);
}
void DeleteEdge (int &numobjects, btreeedge_t *te) // warning: points in this edge could be freed if not reference by any other edges
{
if (!te->faces->empty ())
{
PrintOnce ("DeleteEdge: internal error: edge used by face.");
hlassume (false, assume_first);
}
if (!te->infinite)
{
DeleteBrink (te->brink);
}
for (int side = 0; side < 2; side++)
{
btreepoint_t *tp;
tp = GetPointFromEdge (te, side);
RemovePointFromEdge (te, tp, side);
if (tp->edges->empty ())
{
DeletePoint (numobjects, tp);
}
}
delete te->faces;
free (te);
numobjects--;
}
btreeleaf_t *GetLeafFromFace (btreeface_t *tf, bool side)
{
if (!tf->leafs[side].l)
{
PrintOnce ("GetLeafFromFace: internal error: Leaf not set.");
hlassume (false, assume_first);
}
return tf->leafs[side].l;
}
void RemoveFaceFromLeaf (btreeleaf_t *tl, btreeface_t *tf, bool side)
{
if (tf->leafs[side].l != tl)
{
PrintOnce ("RemoveFaceFromLeaf: internal error: leaf not found.");
hlassume (false, assume_first);
}
tf->leafs[side].l = NULL;
RemoveFaceFromList (tl->faces, tf, side);
}
void DeleteFace (int &numobjects, btreeface_t *tf) // warning: edges in this face could be freed if not reference by any other faces
{
btreeedge_l::iterator ei;
while ((ei = tf->edges->begin ()) != tf->edges->end ())
{
btreeedge_t *te = ei->e;
RemoveFaceFromList (te->faces, tf, ei->side);
tf->edges->erase (ei);
if (te->faces->empty ())
{
DeleteEdge (numobjects, te);
}
}
for (int side = 0; side < 2; side++)
{
if (tf->leafs[side].l)
{
PrintOnce ("DeleteFace: internal error: face used by leaf.");
hlassume (false, assume_first);
}
}
delete tf->edges;
free (tf);
numobjects--;
}
void DeleteLeaf (int &numobjects, btreeleaf_t *tl)
{
btreeface_l::iterator fi;
while ((fi = tl->faces->begin ()) != tl->faces->end ())
{
btreeface_t *tf = fi->f;
RemoveFaceFromLeaf (tl, tf, fi->side);
if (!tf->leafs[false].l && !tf->leafs[true].l)
{
DeleteFace (numobjects, tf);
}
}
delete tl->faces;
free (tl);
numobjects--;
}
void SplitTreeLeaf (int &numobjects, btreeleaf_t *tl, const dplane_t *plane, int planenum, vec_t epsilon, btreeleaf_t *&front, btreeleaf_t *&back, bclipnode_t *c0, bclipnode_t *c1)
{
btreeface_l::iterator fi;
btreeedge_l::iterator ei;
bool restart = false;
// clear all the flags
for (fi = tl->faces->begin (); fi != tl->faces->end (); fi++)
{
btreeface_t *tf = fi->f;
tf->tmp_tested = false;
for (ei = tf->edges->begin (); ei != tf->edges->end (); ei++)
{
btreeedge_t *te = ei->e;
te->tmp_tested = false;
for (int side = 0; side < 2; side++)
{
btreepoint_t *tp = GetPointFromEdge (te, side);
tp->tmp_tested = false;
}
}
}
// split each point
for (fi = tl->faces->begin (); fi != tl->faces->end (); fi++)
{
btreeface_t *tf = fi->f;
for (ei = tf->edges->begin (); ei != tf->edges->end (); ei++)
{
btreeedge_t *te = ei->e;
for (int side = 0; side < 2; side++)
{
btreepoint_t *tp = GetPointFromEdge (te, side);
if (tp->tmp_tested)
{
continue;
}
tp->tmp_tested = true;
vec_t dist = DotProduct (tp->v, plane->normal) - plane->dist;
tp->tmp_dist = dist;
if (dist > epsilon)
{
tp->tmp_side = SIDE_FRONT;
}
else if (dist < -epsilon)
{
tp->tmp_side = SIDE_BACK;
}
else
{
tp->tmp_side = SIDE_ON;
}
#ifdef HLBSP_BRINKHACK_BUGFIX
#if 0
// let's mess up something and see whether the code is fragile or robust
static int randcounter = 0;
if (randcounter++ % 8 == 0) tp->tmp_side = randcounter % 3;
#endif
#endif
}
}
}
// split each edge
for (fi = tl->faces->begin (); fi != tl->faces->end (); fi++)
{
btreeface_t *tf = fi->f;
for (ei = tf->edges->begin (); ei != tf->edges->end (); restart? restart = false, ei = tf->edges->begin (): ei++)
{
btreeedge_t *te = ei->e;
if (te->tmp_tested) // splitted
{
continue;
}
te->tmp_tested = true;
te->tmp_side = SIDE_ON;
for (int side = 0; side < 2; side++)
{
btreepoint_t *tp = GetPointFromEdge (te, side);
if (te->tmp_side == SIDE_ON)
{
te->tmp_side = tp->tmp_side;
}
else if (tp->tmp_side != SIDE_ON && tp->tmp_side != te->tmp_side)
{
te->tmp_side = SIDE_CROSS;
}
}
#ifdef HLBSP_BRINKNOTUSEDBYLEAF_FIX
// The plane does not necessarily split the leaf into two, because of epsilon problem etc., and this will cause "Error: CollectBrinks_r: not leaf" on some maps.
// In addition, by the time of this step (split edges), the plane has not splitted the leaf yet, so splitting the brink leafs now will break the integrety of the entire geometry. (We want the four steps to be as independent on each other as possible, that is, the entire geometry remains valid after each step.)
#else
if (!te->infinite)
{
if (te->tmp_side == SIDE_ON)
{
// since the plane splits the leaf into two, it must also split the brink into two
BrinkSplitClipnode (te->brink, plane, planenum, tl->clipnode, c0, c1);
}
else if (te->tmp_side == SIDE_FRONT)
{
BrinkReplaceClipnode (te->brink, tl->clipnode, c0);
}
else if (te->tmp_side == SIDE_BACK)
{
BrinkReplaceClipnode (te->brink, tl->clipnode, c1);
}
}
#endif
if (te->tmp_side == SIDE_CROSS)
{
btreepoint_t *tp0 = GetPointFromEdge (te, false);
btreepoint_t *tp1 = GetPointFromEdge (te, true);
btreepoint_t *tpmid = AllocTreepoint (numobjects, te->infinite);
tpmid->tmp_tested = true;
tpmid->tmp_dist = 0;
tpmid->tmp_side = SIDE_ON;
vec_t frac = tp0->tmp_dist / (tp0->tmp_dist - tp1->tmp_dist);
for (int k = 0; k < 3; k++)
{
tpmid->v[k] = tp0->v[k] + frac * (tp1->v[k] - tp0->v[k]);
}
btreeedge_t *te0 = AllocTreeedge (numobjects, te->infinite);
SetEdgePoints (te0, tp0, tpmid);
te0->tmp_tested = true;
te0->tmp_side = tp0->tmp_side;
if (!te0->infinite)
{
te0->brink = CopyBrink (te->brink);
VectorCopy (tpmid->v, te0->brink->start);
VectorCopy (tp0->v, te0->brink->stop);
#ifndef HLBSP_BRINKNOTUSEDBYLEAF_FIX
BrinkReplaceClipnode (te0->brink, tl->clipnode, (tp0->tmp_side == SIDE_BACK? c1: c0));
#endif
}
btreeedge_t *te1 = AllocTreeedge (numobjects, te->infinite);
SetEdgePoints (te1, tpmid, tp1);
te1->tmp_tested = true;
te1->tmp_side = tp1->tmp_side;
if (!te1->infinite)
{
te1->brink = CopyBrink (te->brink);
VectorCopy (tp1->v, te1->brink->start);
VectorCopy (tpmid->v, te1->brink->stop);
#ifndef HLBSP_BRINKNOTUSEDBYLEAF_FIX
BrinkReplaceClipnode (te1->brink, tl->clipnode, (tp1->tmp_side == SIDE_BACK? c1: c0));
#endif
}
btreeface_l::iterator fj;
while ((fj = te->faces->begin ()) != te->faces->end ())
{
AttachEdgeToFace (fj->f, te0, fj->side);
AttachEdgeToFace (fj->f, te1, fj->side);
RemoveEdgeFromFace (fj->f, te, fj->side);
}
DeleteEdge (numobjects, te);
restart = true;
}
}
}
// split each face
for (fi = tl->faces->begin (); fi != tl->faces->end (); restart? restart = false, fi = tl->faces->begin (): fi++)
{
btreeface_t *tf = fi->f;
if (tf->tmp_tested)
{
continue;
}
tf->tmp_tested = true;
tf->tmp_side = SIDE_ON;
for (ei = tf->edges->begin (); ei != tf->edges->end (); ei++)
{
if (tf->tmp_side == SIDE_ON)
{
tf->tmp_side = ei->e->tmp_side;
}
else if (ei->e->tmp_side != SIDE_ON && ei->e->tmp_side != tf->tmp_side)
{
tf->tmp_side = SIDE_CROSS;
}
}
if (tf->tmp_side == SIDE_CROSS)
{
btreeface_t *frontface, *backface;
frontface = AllocTreeface (numobjects, tf->infinite);
if (!tf->infinite)
{
frontface->plane = tf->plane;
frontface->planenum = tf->planenum;
frontface->planeside = tf->planeside;
}
SetFaceLeafs (frontface, GetLeafFromFace (tf, false), GetLeafFromFace (tf, true));
frontface->tmp_tested = true;
frontface->tmp_side = SIDE_FRONT;
backface = AllocTreeface (numobjects, tf->infinite);
if (!tf->infinite)
{
backface->plane = tf->plane;
backface->planenum = tf->planenum;
backface->planeside = tf->planeside;
}
SetFaceLeafs (backface, GetLeafFromFace (tf, false), GetLeafFromFace (tf, true));
backface->tmp_tested = true;
backface->tmp_side = SIDE_BACK;
std::map< btreepoint_t *, int > vertexes;
std::map< btreepoint_t *, int >::iterator vertex, vertex2;
for (ei = tf->edges->begin (); ei != tf->edges->end (); ei++)
{
if (ei->e->tmp_side != SIDE_BACK)
{
AttachEdgeToFace (frontface, ei->e, ei->side);
}
else
{
AttachEdgeToFace (backface, ei->e, ei->side);
btreeedge_t *e = ei->e;
for (int side = 0; side < 2; side++)
{
btreepoint_t *p = GetPointFromEdge (e, side);
vertexes[p] += ((bool)side == ei->side? 1: -1); // the default value is 0 if vertexes[p] does not exist
vertex = vertexes.find (p);
if (vertex->second == 0)
{
vertexes.erase (vertex);
}
}
}
}
if (vertexes.size () != 2)
{
Developer (DEVELOPER_LEVEL_WARNING, "SplitTreeLeaf: got invalid edge from split\n");
}
while (1)
{
for (vertex = vertexes.begin (); vertex != vertexes.end (); vertex++)
{
if (vertex->second > 0)
{
break;
}
}
for (vertex2 = vertexes.begin (); vertex2 != vertexes.end (); vertex2++)
{
if (vertex2->second < 0)
{
break;
}
}
if (vertex == vertexes.end () && vertex2 == vertexes.end ())
{
break;
}
if (vertex == vertexes.end () || vertex2 == vertexes.end ())
{
PrintOnce ("SplitTreeLeaf: internal error: couldn't link edges");
hlassume (false, assume_first);
}
if (vertex->second != 1 || vertex2->second != -1)
{
Developer (DEVELOPER_LEVEL_WARNING, "SplitTreeLeaf: got deformed edge from split\n");
}
if (vertex->first->tmp_side != SIDE_ON || vertex2->first->tmp_side != SIDE_ON)
{
PrintOnce ("SplitTreeLeaf: internal error: tmp_side != SIDE_ON");
hlassume (false, assume_first);
}
btreeedge_t *te;
te = AllocTreeedge (numobjects, tf->infinite);
SetEdgePoints (te, vertex->first, vertex2->first);
if (!te->infinite)
{
te->brink = CreateBrink (vertex2->first->v, vertex->first->v);
if (GetLeafFromFace (tf, tf->planeside)->infinite || GetLeafFromFace (tf, !tf->planeside)->infinite)
{
PrintOnce ("SplitTreeLeaf: internal error: an infinite object contains a finite object");
hlassume (false, assume_first);
}
BrinkSplitClipnode (te->brink, tf->plane, tf->planenum, NULL, GetLeafFromFace (tf, tf->planeside)->clipnode, GetLeafFromFace (tf, !tf->planeside)->clipnode);
#ifndef HLBSP_BRINKNOTUSEDBYLEAF_FIX
BrinkSplitClipnode (te->brink, plane, planenum, tl->clipnode, c0, c1);
#endif
}
te->tmp_tested = true;
te->tmp_side = SIDE_ON;
AttachEdgeToFace (frontface, te, false);
AttachEdgeToFace (backface, te, true);
vertex->second--;
vertex2->second++;
}
for (int side = 0; side < 2; side++)
{
RemoveFaceFromLeaf (GetLeafFromFace (tf, side), tf, side);
}
DeleteFace (numobjects, tf);
restart = true;
}
}
// split the leaf
{
if (tl->infinite)
{
PrintOnce ("SplitTreeLeaf: internal error: splitting the infinite leaf");
hlassume (false, assume_first);
}
front = AllocTreeleaf (numobjects, tl->infinite);
back = AllocTreeleaf (numobjects, tl->infinite);
front->clipnode = c0;
back->clipnode = c1;
int tmp_side = SIDE_ON;
for (fi = tl->faces->begin (); fi != tl->faces->end (); fi++)
{
if (tmp_side == SIDE_ON)
{
tmp_side = fi->f->tmp_side;
}
else if (fi->f->tmp_side != SIDE_ON && fi->f->tmp_side != tmp_side)
{
tmp_side = SIDE_CROSS;
}
}
std::map< btreeedge_t *, int > edges;
std::map< btreeedge_t *, int >::iterator edge;
while ((fi = tl->faces->begin ()) != tl->faces->end ())
{
btreeface_t *tf = fi->f;
int side = fi->side;
RemoveFaceFromLeaf (tl, tf, side); // because we can only store 2 leafs for a face
// fi is unusable now
if (tf->tmp_side == SIDE_FRONT || tf->tmp_side == SIDE_ON && tmp_side != SIDE_BACK)
{
AttachFaceToLeaf (front, tf, side);
}
else if (tf->tmp_side == SIDE_BACK || tf->tmp_side == SIDE_ON && tmp_side == SIDE_BACK)
{
AttachFaceToLeaf (back, tf, side);
if (tmp_side == SIDE_CROSS)
{
for (ei = tf->edges->begin (); ei != tf->edges->end (); ei++)
{
edges[ei->e] += (ei->side == (bool)side? 1: -1);
edge = edges.find (ei->e);
if (edge->second == 0)
{
edges.erase (edge);
}
}
}
}
}
if (tmp_side == SIDE_CROSS)
{
btreeface_t *tf;
tf = AllocTreeface (numobjects, tl->infinite);
if (!tf->infinite)
{
tf->plane = plane;
tf->planenum = planenum;
tf->planeside = false;
}
tf->tmp_tested = true;
tf->tmp_side = SIDE_ON;
SetFaceLeafs (tf, front, back);
for (edge = edges.begin (); edge != edges.end (); edge++)
{
if (edge->first->tmp_side != SIDE_ON)
{
PrintOnce ("SplitTreeLeaf: internal error");
hlassume (false, assume_first);
}
while (edge->second > 0)
{
AttachEdgeToFace (tf, edge->first, false);
edge->second--;
}
while (edge->second < 0)
{
AttachEdgeToFace (tf, edge->first, true);
edge->second++;
}
}
}
#ifndef HLBSP_BRINKHACK_BUGFIX
DeleteLeaf (numobjects, tl);
#endif
#ifdef HLBSP_BRINKNOTUSEDBYLEAF_FIX
btreeleaf_t *(frontback[2]) = {front, back};
for (int side = 0; side < 2; side++)
{
for (fi = frontback[side]->faces->begin (); fi != frontback[side]->faces->end (); fi++)
{
for (ei = fi->f->edges->begin (); ei != fi->f->edges->end (); ei++)
{
ei->e->tmp_onleaf[0] = ei->e->tmp_onleaf[1] = false;
ei->e->tmp_tested = false;
}
}
}
for (int side = 0; side < 2; side++)
{
for (fi = frontback[side]->faces->begin (); fi != frontback[side]->faces->end (); fi++)
{
for (ei = fi->f->edges->begin (); ei != fi->f->edges->end (); ei++)
{
ei->e->tmp_onleaf[side] = true;
}
}
}
for (int side = 0; side < 2; side++)
{
for (fi = frontback[side]->faces->begin (); fi != frontback[side]->faces->end (); fi++)
{
for (ei = fi->f->edges->begin (); ei != fi->f->edges->end (); ei++)
{
if (ei->e->tmp_tested)
{
continue;
}
ei->e->tmp_tested = true;
if (!ei->e->infinite)
{
if (ei->e->tmp_onleaf[0] && ei->e->tmp_onleaf[1])
{
if (ei->e->tmp_side != SIDE_ON)
{
PrintOnce ("SplitTreeLeaf: internal error");
hlassume (false, assume_first);
}
BrinkSplitClipnode (ei->e->brink, plane, planenum, tl->clipnode, c0, c1);
}
else if (ei->e->tmp_onleaf[0])
{
if (ei->e->tmp_side == SIDE_BACK)
{
PrintOnce ("SplitTreeLeaf: internal error");
hlassume (false, assume_first);
}
BrinkReplaceClipnode (ei->e->brink, tl->clipnode, c0);
}
else if (ei->e->tmp_onleaf[1])
{
if (ei->e->tmp_side == SIDE_FRONT)
{
PrintOnce ("SplitTreeLeaf: internal error");
hlassume (false, assume_first);
}
BrinkReplaceClipnode (ei->e->brink, tl->clipnode, c1);
}
}
}
}
}
#endif
#ifdef HLBSP_BRINKHACK_BUGFIX
DeleteLeaf (numobjects, tl);
#endif
}
}
void BuildTreeCells_r (int &numobjects, bclipnode_t *c)
{
if (c->isleaf)
{
return;
}
btreeleaf_t *tl, *front, *back;
tl = c->treeleaf;
SplitTreeLeaf (numobjects, tl, c->plane, c->planenum, ON_EPSILON, front, back, c->children[0], c->children[1]);
c->treeleaf = NULL;
c->children[0]->treeleaf = front;
c->children[1]->treeleaf = back;
BuildTreeCells_r (numobjects, c->children[0]);
BuildTreeCells_r (numobjects, c->children[1]);
}
typedef struct bbrinkinfo_s
{
int numclipnodes;
bclipnode_t *clipnodes;
int numobjects;
btreeleaf_t *leaf_outside;
int numbrinks;
bbrink_t **brinks;
}
bbrinkinfo_t;
#define MAXCLIPNODES (MAX_MAP_CLIPNODES*8)
bclipnode_t *ExpandClipnodes_r (bclipnode_t *bclipnodes, int &numbclipnodes, const dclipnode_t *clipnodes, int headnode)
{
if (numbclipnodes >= MAXCLIPNODES)
{
Error ("ExpandClipnodes_r: exceeded MAXCLIPNODES");
}
bclipnode_t *c = &bclipnodes[numbclipnodes];
numbclipnodes++;
if (headnode < 0)
{
c->isleaf = true;
c->content = headnode;
c->partitions = NULL;
}
else
{
c->isleaf = false;
c->planenum = clipnodes[headnode].planenum;
c->plane = &g_dplanes[c->planenum];
for (int k = 0; k < 2; k++)
{
c->children[k] = ExpandClipnodes_r (bclipnodes, numbclipnodes, clipnodes, clipnodes[headnode].children[k]);
}
}
return c;
}
void ExpandClipnodes (bbrinkinfo_t *info, const dclipnode_t *clipnodes, int headnode)
{
bclipnode_t *bclipnodes = (bclipnode_t *)malloc (MAXCLIPNODES * sizeof (bclipnode_t)); // 262144 * 30byte = 7.5MB
hlassume (bclipnodes != NULL, assume_NoMemory);
info->numclipnodes = 0;
ExpandClipnodes_r (bclipnodes, info->numclipnodes, clipnodes, headnode);
info->clipnodes = (bclipnode_t *)malloc (info->numclipnodes * sizeof (bclipnode_t));
hlassume (info->clipnodes != NULL, assume_NoMemory);
memcpy (info->clipnodes, bclipnodes, info->numclipnodes * sizeof (bclipnode_t));
for (int i = 0; i < info->numclipnodes; i++)
{
for (int k = 0; k < 2; k++)
{
info->clipnodes[i].children[k] = info->clipnodes + (bclipnodes[i].children[k] - bclipnodes);
}
}
free (bclipnodes);
}
void BuildTreeCells (bbrinkinfo_t *info)
{
info->numobjects = 0;
info->leaf_outside = BuildOutside (info->numobjects);
info->clipnodes[0].treeleaf = BuildBaseCell (info->numobjects, &info->clipnodes[0], BOGUS_RANGE, info->leaf_outside);
BuildTreeCells_r (info->numobjects, &info->clipnodes[0]);
}
void DeleteTreeCells_r (int &numobjects, bclipnode_t *node)
{
if (node->treeleaf)
{
DeleteLeaf (numobjects, node->treeleaf);
node->treeleaf = NULL;
}
if (!node->isleaf)
{
DeleteTreeCells_r (numobjects, node->children[0]);
DeleteTreeCells_r (numobjects, node->children[1]);
}
}
void DeleteTreeCells (bbrinkinfo_t *info)
{
DeleteLeaf (info->numobjects, info->leaf_outside);
info->leaf_outside = NULL;
DeleteTreeCells_r (info->numobjects, &info->clipnodes[0]);
if (info->numobjects != 0)
{
PrintOnce ("DeleteTreeCells: internal error: numobjects != 0");
hlassume (false, assume_first);
}
}
void ClearMarks_r (bclipnode_t *node)
{
if (node->isleaf)
{
btreeface_l::iterator fi;
btreeedge_l::iterator ei;
for (fi = node->treeleaf->faces->begin (); fi != node->treeleaf->faces->end (); fi++)
{
for (ei = fi->f->edges->begin (); ei != fi->f->edges->end (); ei++)
{
ei->e->tmp_tested = false;
}
}
}
else
{
ClearMarks_r (node->children[0]);
ClearMarks_r (node->children[1]);
}
}
void CollectBrinks_r (bclipnode_t *node, int &numbrinks, bbrink_t **brinks)
{
if (node->isleaf)
{
btreeface_l::iterator fi;
btreeedge_l::iterator ei;
for (fi = node->treeleaf->faces->begin (); fi != node->treeleaf->faces->end (); fi++)
{
for (ei = fi->f->edges->begin (); ei != fi->f->edges->end (); ei++)
{
if (ei->e->tmp_tested)
{
continue;
}
ei->e->tmp_tested = true;
if (!ei->e->infinite)
{
if (brinks != NULL)
{
brinks[numbrinks] = ei->e->brink;
brinks[numbrinks]->edge = ei->e;
for (int i = 0; i < brinks[numbrinks]->numnodes; i++)
{
bbrinknode_t *node = &(*brinks[numbrinks]->nodes)[i];
if (node->isleaf && !node->clipnode->isleaf)
{
PrintOnce ("CollectBrinks_r: internal error: not leaf");
hlassume (false, assume_first);
}
}
}
numbrinks++;
}
}
}
}
else
{
CollectBrinks_r (node->children[0], numbrinks, brinks);
CollectBrinks_r (node->children[1], numbrinks, brinks);
}
}
void CollectBrinks (bbrinkinfo_t *info)
{
info->numbrinks = 0;
ClearMarks_r (&info->clipnodes[0]);
CollectBrinks_r (&info->clipnodes[0], info->numbrinks, NULL);
hlassume (info->brinks = (bbrink_t **)malloc (info->numbrinks * sizeof (bbrink_t *)), assume_NoMemory);
info->numbrinks = 0;
ClearMarks_r (&info->clipnodes[0]);
CollectBrinks_r (&info->clipnodes[0], info->numbrinks, info->brinks);
}
void FreeBrinks (bbrinkinfo_t *info)
{
free (info->brinks);
}
struct bwedge_s;
struct bsurface_s;
typedef struct bwedge_s
{
int content;
int nodenum;
bsurface_s *prev;
bsurface_s *next;
}
bwedge_t;
typedef struct bsurface_s
{
vec3_t normal; // pointing clockwise
int nodenum;
bool nodeside;
bwedge_s *prev;
bwedge_s *next;
}
bsurface_t;
#define MAXBRINKWEDGES 64
typedef struct
{
vec3_t axis;
vec3_t basenormal;
int numwedges[2]; // the front and back side of nodes[0]
bwedge_t wedges[2][MAXBRINKWEDGES]; // in counterclosewise order
bsurface_t surfaces[2][MAXBRINKWEDGES]; // the surface between two adjacent wedges
}
bcircle_t;
bool CalculateCircle (bbrink_t *b, bcircle_t *c)
{
VectorCopy (b->direction, c->axis);
if (!VectorNormalize (c->axis))
{
return false;
}
VectorCopy ((*b->nodes)[0].plane->normal, c->basenormal);
int side, i;
for (side = 0; side < 2; side++)
{
vec3_t facing;
CrossProduct (c->basenormal, c->axis, facing);
VectorScale (facing, side? -1: 1, facing);
if (VectorNormalize (facing) < 1 - 0.01)
{
return false;
}
// sort the wedges
c->numwedges[side] = 1;
c->wedges[side][0].nodenum = (*b->nodes)[0].children[side];
c->surfaces[side][0].nodenum = 0;
c->surfaces[side][0].nodeside = !side;
while (1)
{
for (i = 0; i < c->numwedges[side]; i++)
{
int nodenum = c->wedges[side][i].nodenum;
bbrinknode_t *node = &(*b->nodes)[nodenum];
if (!node->isleaf)
{
memmove (&c->wedges[side][i + 1], &c->wedges[side][i], (c->numwedges[side] - i) * sizeof (bwedge_t));
memmove (&c->surfaces[side][i + 2], &c->surfaces[side][i + 1], (c->numwedges[side] - 1 - i) * sizeof (bsurface_t));
c->numwedges[side]++;
bool flipnode = (DotProduct (node->plane->normal, facing) < 0);
c->wedges[side][i].nodenum = node->children[flipnode];
c->wedges[side][i + 1].nodenum = node->children[!flipnode];
c->surfaces[side][i + 1].nodenum = nodenum;
c->surfaces[side][i + 1].nodeside = flipnode;
break;
}
}
if (i == c->numwedges[side])
{
break;
}
}
}
if ((c->numwedges[0] + c->numwedges[1]) * 2 - 1 != b->numnodes)
{
PrintOnce ("CalculateCircle: internal error 1");
hlassume (false, assume_first);
}
// fill in other information
for (side = 0; side < 2; side++)
{
for (i = 0; i < c->numwedges[side]; i++)
{
bwedge_t *w = &c->wedges[side][i];
bbrinknode_t *node = &(*b->nodes)[w->nodenum];
if (!node->clipnode->isleaf)
{
PrintOnce ("CalculateCircle: internal error: not leaf");
hlassume (false, assume_first);
}
w->content = node->content;
w->prev = &c->surfaces[side][i];
w->next = (i == c->numwedges[side] - 1)? &c->surfaces[!side][0]: &c->surfaces[side][i + 1];
w->prev->next = w;
w->next->prev = w;
}
for (i = 0; i < c->numwedges[side]; i++)
{
bsurface_t *s = &c->surfaces[side][i];
bbrinknode_t *node = &(*b->nodes)[s->nodenum];
VectorScale (node->plane->normal, s->nodeside? -1: 1, s->normal);
}
}
// check the normals
for (side = 0; side < 2; side++)
{
for (i = 0; i < c->numwedges[side]; i++)
{
bwedge_t *w = &c->wedges[side][i];
if (i == 0 && i == c->numwedges[side] - 1) // 180 degrees
{
continue;
}
vec3_t v;
CrossProduct (w->prev->normal, w->next->normal, v);
if (!VectorNormalize (v) ||
DotProduct (v, c->axis) < 1 - 0.01)
{
return false;
}
}
}
return true;
}
void PrintCircle (const bcircle_t *c)
{
Log ("axis %f %f %f\n", c->axis[0], c->axis[1], c->axis[2]);
Log ("basenormal %f %f %f\n", c->basenormal[0], c->basenormal[1], c->basenormal[2]);
Log ("numwedges %d %d\n", c->numwedges[0], c->numwedges[1]);
for (int side = 0; side < 2; side++)
{
for (int i = 0; i < c->numwedges[side]; i++)
{
const bwedge_t *w = &c->wedges[side][i];
const bsurface_t *s = &c->surfaces[side][i];
Log ("surface[%d][%d] nodenum %d nodeside %d normal %f %f %f\n", side, i, s->nodenum, s->nodeside, s->normal[0], s->normal[1], s->normal[2]);
Log ("wedge[%d][%d] nodenum %d content %d\n", side, i, w->nodenum, w->content);
}
}
}
bool AddPartition (bclipnode_t *clipnode, int planenum, bool planeside, int content, bbrinklevel_e brinktype)
{
// make sure we won't do any harm
btreeface_l::iterator fi;
btreeedge_l::iterator ei;
int side;
if (!clipnode->isleaf)
{
return false;
}
bool onback = false;
for (fi = clipnode->treeleaf->faces->begin (); fi != clipnode->treeleaf->faces->end (); fi++)
{
for (ei = fi->f->edges->begin (); ei != fi->f->edges->end (); ei++)
{
for (side = 0; side < 2; side++)
{
btreepoint_t *tp = GetPointFromEdge (ei->e, side);
const dplane_t *plane = &g_dplanes[planenum];
vec_t dist = DotProduct (tp->v, plane->normal) - plane->dist;
if (planeside? dist < -ON_EPSILON: dist > ON_EPSILON)
{
return false;
}
if (planeside? dist > ON_EPSILON: dist < -ON_EPSILON)
{
onback = true;
}
}
}
}
if (!onback)
{
return false; // the whole leaf is on the plane, or the leaf doesn't consist of any vertex
}
bpartition_t *p = (bpartition_t *)malloc (sizeof (bpartition_t));
hlassume (p != NULL, assume_NoMemory);
p->next = clipnode->partitions;
p->planenum = planenum;
p->planeside = planeside;
p->content = content;
p->type = brinktype;
clipnode->partitions = p;
return true;
}
void AnalyzeBrinks (bbrinkinfo_t *info)
{
int countgood = 0;
int countinvalid = 0;
int countskipped = 0;
int countfixed = 0;
int i, j, side;
for (i = 0; i < info->numbrinks; i++)
{
bbrink_t *b = info->brinks[i];
if (b->numnodes <= 5) // quickly reject the most trivial brinks
{
#ifdef HLBSP_BRINKNOTUSEDBYLEAF_FIX
if (b->numnodes != 3 && b->numnodes != 5)
{
PrintOnce ("AnalyzeBrinks: internal error 1");
hlassume (false, assume_first);
}
// because a brink won't necessarily be split twice after its creation
if (b->numnodes == 3)
{
if (g_developer >= DEVELOPER_LEVEL_FLUFF)
{
Developer (DEVELOPER_LEVEL_FLUFF, "Brink wasn't split by the second plane:\n");
PrintBrink (b);
}
countinvalid++;
}
else
{
countgood++;
}
continue;
#else
hlassume (b->numnodes == 5, assume_first);
countgood++;
continue;
#endif
}
if (b->numnodes > 2 * MAXBRINKWEDGES - 1)
{
if (g_developer >= DEVELOPER_LEVEL_MEGASPAM)
{
Developer (DEVELOPER_LEVEL_MEGASPAM, "Skipping complicated brink:\n");
PrintBrink (b);
}
countskipped++;
continue;
}
bcircle_t c;
// build the circle to find out the planes a player may move along
if (!CalculateCircle (b, &c))
{
if (g_developer >= DEVELOPER_LEVEL_FLUFF)
{
Developer (DEVELOPER_LEVEL_FLUFF, "CalculateCircle failed for brink:\n");
PrintBrink (b);
}
countinvalid++;
continue;
}
int transitionfound[2];
bsurface_t *transitionpos[2];
bool transitionside[2];
for (side = 0; side < 2; side++)
{
transitionfound[side] = 0;
for (j = 1; j < c.numwedges[side]; j++) // we will later consider the surfaces on the first split
{
bsurface_t *s = &c.surfaces[side][j];
if ((s->prev->content == CONTENTS_SOLID) != (s->next->content == CONTENTS_SOLID))
{
transitionfound[side]++;
transitionpos[side] = s;
transitionside[side] = (s->prev->content == CONTENTS_SOLID);
}
}
}
if (transitionfound[0] == 0 || transitionfound[1] == 0)
{
// at least one side of the first split is completely SOLID or EMPTY. no bugs in this case
countgood++;
continue;
}
if (transitionfound[0] > 1 || transitionfound[1] > 1 ||
(c.surfaces[0][0].prev->content == CONTENTS_SOLID) != (c.surfaces[0][0].next->content == CONTENTS_SOLID) ||
(c.surfaces[1][0].prev->content == CONTENTS_SOLID) != (c.surfaces[1][0].next->content == CONTENTS_SOLID))
{
// there must at least 3 transition surfaces now, which is too complicated. just leave it unfixed
if (g_developer >= DEVELOPER_LEVEL_MEGASPAM)
{
Developer (DEVELOPER_LEVEL_MEGASPAM, "Skipping complicated brink:\n");
PrintBrink (b);
PrintCircle (&c);
}
countskipped++;
continue;
}
if (transitionside[1] != !transitionside[0])
{
PrintOnce ("AnalyzeBrinks: internal error 2");
hlassume (false, assume_first);
}
bool bfix = false;
bool berror = false;
vec3_t vup = {0, 0, 1};
bool isfloor;
bool onfloor;
bool blocking;
{
isfloor = false;
for (int side2 = 0; side2 < 2; side2++)
{
vec3_t normal;
VectorScale (transitionpos[side2]->normal, transitionside[side2]? -1: 1, normal); // pointing from SOLID to EMPTY
if (DotProduct (normal, vup) > BRINK_FLOOR_THRESHOLD)
{
isfloor = true;
}
}
}
{
onfloor = false;
for (int side2 = 0; side2 < 2; side2++)
{
btreepoint_t *tp = GetPointFromEdge (b->edge, side2);
#ifdef HLBSP_BRINKHACK_BUGFIX
if (tp->infinite)
{
continue;
}
#endif
for (btreeedge_l::iterator ei = tp->edges->begin (); ei != tp->edges->end (); ei++)
{
for (btreeface_l::iterator fi = ei->e->faces->begin (); fi != ei->e->faces->end (); fi++)
{
#ifdef HLBSP_BRINKHACK_BUGFIX
if (fi->f->infinite || GetLeafFromFace (fi->f, false)->infinite || GetLeafFromFace (fi->f, true)->infinite)
{
PrintOnce ("AnalyzeBrinks: internal error: an infinite object contains a finite object");
hlassume (false, assume_first);
}
#endif
for (int side3 = 0; side3 < 2; side3++)
{
vec3_t normal;
VectorScale (fi->f->plane->normal, (fi->f->planeside != (bool)side3)? -1: 1, normal);
if (DotProduct (normal, vup) > BRINK_FLOOR_THRESHOLD
&& GetLeafFromFace (fi->f, side3)->clipnode->content == CONTENTS_SOLID
&& GetLeafFromFace (fi->f, !side3)->clipnode->content != CONTENTS_SOLID)
{
onfloor = true;
}
}
}
}
}
}
// this code does not fix all the bugs, it only aims to fix most of the bugs
for (side = 0; side < 2; side++)
{
bsurface_t *smovement = transitionpos[side];
bsurface_t *s;
for (s = transitionside[!side]? &c.surfaces[!side][0]: &c.surfaces[side][0]; ; s = transitionside[!side]? s->next->next: s->prev->prev)
{
bwedge_t *w = transitionside[!side]? s->next: s->prev;
bsurface_t *snext = transitionside[!side]? w->next: w->prev;
vec3_t tmp;
vec_t dot;
CrossProduct (smovement->normal, snext->normal, tmp);
dot = DotProduct (tmp, c.axis);
if (transitionside[!side]? dot < 0.01: dot > -0.01)
{
break;
}
if (w->content != CONTENTS_SOLID)
{
break;
}
if (snext == (transitionside[!side]? &c.surfaces[side][0]: &c.surfaces[!side][0]))
{
Developer (DEVELOPER_LEVEL_ERROR, "AnalyzeBrinks: surface past 0\n");
break;
}
bfix = true;
{
if (DotProduct (smovement->normal, s->normal) > 0.01)
{
blocking = false;
}
else
{
blocking = true;
}
}
bclipnode_t *clipnode = (*b->nodes)[w->nodenum].clipnode;
int planenum = (*b->nodes)[smovement->nodenum].planenum;
bool planeside = transitionside[!side]? smovement->nodeside: !smovement->nodeside;
bbrinklevel_e brinktype;
brinktype = isfloor? (blocking? BrinkFloorBlocking: BrinkFloor): onfloor? (blocking? BrinkWallBlocking: BrinkWall): BrinkAny;
if (!AddPartition (clipnode, planenum, planeside, CONTENTS_EMPTY, brinktype))
{
berror = true;
}
}
}
if (berror)
{
if (g_developer >= DEVELOPER_LEVEL_FLUFF)
{
Developer (DEVELOPER_LEVEL_FLUFF, "AddPartition failed for brink:\n");
PrintBrink (b);
}
countinvalid++;
}
else if (!bfix)
{
countgood++;
}
else
{
countfixed++;
}
}
Developer (DEVELOPER_LEVEL_MESSAGE, "brinks: good = %d skipped = %d fixed = %d invalid = %d\n", countgood, countskipped, countfixed, countinvalid);
}
void DeleteClipnodes (bbrinkinfo_t *info)
{
for (int i = 0; i < info->numclipnodes; i++)
{
#ifdef HLBSP_BRINKHACK_BUGFIX
if (!info->clipnodes[i].isleaf)
{
continue;
}
#endif
bpartition_t *p;
while ((p = info->clipnodes[i].partitions) != NULL)
{
info->clipnodes[i].partitions = p->next;
free (p);
}
}
free (info->clipnodes);
}
void SortPartitions (bbrinkinfo_t *info) // to merge same partition planes and compress clipnodes better if using HLBSP_MERGECLIPNODE
{
int countfloorblocking = 0;
int countfloor = 0;
int countwallblocking = 0;
int countwall = 0;
int countany = 0;
for (int i = 0; i < info->numclipnodes; i++)
{
bclipnode_t *clipnode = &info->clipnodes[i];
if (!clipnode->isleaf)
{
continue;
}
bpartition_t *current, **pp, *partitions;
partitions = clipnode->partitions;
clipnode->partitions = NULL;
while ((current = partitions) != NULL)
{
partitions = current->next;
if (current->content != CONTENTS_EMPTY)
{
PrintOnce ("SortPartitions: content of partition was not empty.");
hlassume (false, assume_first);
}
for (pp = &clipnode->partitions; *pp; pp = &(*pp)->next)
{
if ((*pp)->planenum > current->planenum ||
(*pp)->planenum == current->planenum && (*pp)->planeside >= current->planeside) // normally the planeside should be identical
{
break;
}
}
if (*pp && (*pp)->planenum == current->planenum && (*pp)->planeside == current->planeside)
{
(*pp)->type = qmin ((*pp)->type, current->type); // pick the lowest (most important) level from the existing partition and the current partition
free (current);
continue;
}
switch (current->type)
{
case BrinkFloorBlocking:
countfloorblocking++;
break;
case BrinkFloor:
countfloor++;
break;
case BrinkWallBlocking:
countwallblocking++;
break;
case BrinkWall:
countwall++;
break;
case BrinkAny:
countany++;
break;
default:
PrintOnce ("SortPartitions: internal error");
hlassume (false, assume_first);
break;
}
current->next = *pp;
*pp = current;
}
}
Developer (DEVELOPER_LEVEL_MESSAGE, "partitions: floorblocking = %d floor = %d wallblocking = %d wall = %d any = %d\n", countfloorblocking, countfloor, countwallblocking, countwall, countany);
}
void *CreateBrinkinfo (const dclipnode_t *clipnodes, int headnode)
{
bbrinkinfo_t *info;
try
{
hlassume (info = (bbrinkinfo_t *)malloc (sizeof (bbrinkinfo_t)), assume_NoMemory);
ExpandClipnodes (info, clipnodes, headnode);
BuildTreeCells (info);
CollectBrinks (info);
AnalyzeBrinks (info);
FreeBrinks (info);
DeleteTreeCells (info);
SortPartitions (info);
}
catch (std::bad_alloc)
{
hlassume (false, assume_NoMemory);
}
return info;
}
#ifdef HLBSP_MERGECLIPNODE
extern int count_mergedclipnodes;
typedef std::map< std::pair< int, std::pair< int, int > >, int > clipnodemap_t;
inline clipnodemap_t::key_type MakeKey (const dclipnode_t &c)
{
return std::make_pair (c.planenum, std::make_pair (c.children[0], c.children[1]));
}
#endif
bool FixBrinks_r_r (const bclipnode_t *clipnode, const bpartition_t *p, bbrinklevel_e level, int &headnode_out, dclipnode_t *begin, dclipnode_t *end, dclipnode_t *&current
#ifdef HLBSP_MERGECLIPNODE
, clipnodemap_t *outputmap
#endif
)
{
while (p && p->type > level)
{
p = p->next;
}
if (p == NULL)
{
headnode_out = clipnode->content;
return true;
}
dclipnode_t *cn;
#ifdef HLBSP_MERGECLIPNODE
dclipnode_t tmpclipnode;
cn = &tmpclipnode;
dclipnode_t *c = current;
current++;
#else
if (current >= end)
{
return false;
}
cn = current;
current++;
#endif
cn->planenum = p->planenum;
cn->children[p->planeside] = p->content;
int r;
if (!FixBrinks_r_r (clipnode, p->next, level, r, begin, end, current
#ifdef HLBSP_MERGECLIPNODE
, outputmap
#endif
))
{
return false;
}
cn->children[!p->planeside] = r;
#ifdef HLBSP_MERGECLIPNODE
clipnodemap_t::iterator output;
output = outputmap->find (MakeKey (*cn));
if (g_noclipnodemerge || output == outputmap->end ())
{
if (c >= end)
{
return false;
}
*c = *cn;
(*outputmap)[MakeKey (*cn)] = c - begin;
headnode_out = c - begin;
}
else
{
count_mergedclipnodes++;
if (current != c + 1)
{
Error ("Merge clipnodes: internal error");
}
current = c;
headnode_out = output->second; // use the existing clipnode
}
#else
headnode_out = cn - begin;
#endif
return true;
}
bool FixBrinks_r (const bclipnode_t *clipnode, bbrinklevel_e level, int &headnode_out, dclipnode_t *begin, dclipnode_t *end, dclipnode_t *&current
#ifdef HLBSP_MERGECLIPNODE
, clipnodemap_t *outputmap
#endif
)
{
if (clipnode->isleaf)
{
return FixBrinks_r_r (clipnode, clipnode->partitions, level, headnode_out, begin, end, current
#ifdef HLBSP_MERGECLIPNODE
, outputmap
#endif
);
}
else
{
dclipnode_t *cn;
#ifdef HLBSP_MERGECLIPNODE
dclipnode_t tmpclipnode;
cn = &tmpclipnode;
dclipnode_t *c = current;
current++;
#else
if (current >= end)
{
return false;
}
cn = current;
current++;
#endif
cn->planenum = clipnode->planenum;
for (int k = 0; k < 2; k++)
{
int r;
if (!FixBrinks_r (clipnode->children[k], level, r, begin, end, current
#ifdef HLBSP_MERGECLIPNODE
, outputmap
#endif
))
{
return false;
}
cn->children[k] = r;
}
#ifdef HLBSP_MERGECLIPNODE
clipnodemap_t::iterator output;
output = outputmap->find (MakeKey (*cn));
if (g_noclipnodemerge || output == outputmap->end ())
{
if (c >= end)
{
return false;
}
*c = *cn;
(*outputmap)[MakeKey (*cn)] = c - begin;
headnode_out = c - begin;
}
else
{
count_mergedclipnodes++;
if (current != c + 1)
{
Error ("Merge clipnodes: internal error");
}
current = c;
headnode_out = output->second; // use existing clipnode
}
#else
headnode_out = cn - begin;
#endif
return true;
}
}
bool FixBrinks (const void *brinkinfo, bbrinklevel_e level, int &headnode_out, dclipnode_t *clipnodes_out, int maxsize, int size, int &size_out)
{
const bbrinkinfo_t *info = (const bbrinkinfo_t *)brinkinfo;
dclipnode_t *begin = clipnodes_out;
dclipnode_t *end = &clipnodes_out[maxsize];
dclipnode_t *current = &clipnodes_out[size];
#ifdef HLBSP_MERGECLIPNODE
clipnodemap_t outputmap;
#endif
int r;
if (!FixBrinks_r (&info->clipnodes[0], level, r, begin, end, current
#ifdef HLBSP_MERGECLIPNODE
, &outputmap
#endif
))
{
return false;
}
headnode_out = r;
size_out = current - begin;
return true;
}
void DeleteBrinkinfo (void *brinkinfo)
{
bbrinkinfo_t *info = (bbrinkinfo_t *)brinkinfo;
DeleteClipnodes (info);
free (info);
}
#endif