vhlt/hlvis/flow.cpp
2016-09-21 00:07:53 +03:00

1771 lines
48 KiB
C++

#include "vis.h"
// =====================================================================================
// CheckStack
// =====================================================================================
#ifdef USE_CHECK_STACK
static void CheckStack(const leaf_t* const leaf, const threaddata_t* const thread)
{
pstack_t* p;
for (p = thread->pstack_head.next; p; p = p->next)
{
if (p->leaf == leaf)
Error("CheckStack: leaf recursion");
}
}
#endif
// =====================================================================================
// AllocStackWinding
// =====================================================================================
inline static winding_t* AllocStackWinding(pstack_t* const stack)
{
int i;
for (i = 0; i < 3; i++)
{
if (stack->freewindings[i])
{
stack->freewindings[i] = 0;
return &stack->windings[i];
}
}
Error("AllocStackWinding: failed");
return NULL;
}
// =====================================================================================
// FreeStackWinding
// =====================================================================================
inline static void FreeStackWinding(const winding_t* const w, pstack_t* const stack)
{
int i;
i = w - stack->windings;
if (i < 0 || i > 2)
return; // not from local
if (stack->freewindings[i])
Error("FreeStackWinding: allready free");
stack->freewindings[i] = 1;
}
// =====================================================================================
// ChopWinding
// =====================================================================================
inline winding_t* ChopWinding(winding_t* const in, pstack_t* const stack, const plane_t* const split)
{
vec_t dists[128];
int sides[128];
int counts[3];
vec_t dot;
int i;
vec3_t mid;
winding_t* neww;
counts[0] = counts[1] = counts[2] = 0;
if (in->numpoints > (sizeof(sides) / sizeof(*sides)))
{
Error("Winding with too many sides!");
}
// determine sides for each point
for (i = 0; i < in->numpoints; i++)
{
dot = DotProduct(in->points[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > ON_EPSILON)
{
sides[i] = SIDE_FRONT;
}
else if (dot < -ON_EPSILON)
{
sides[i] = SIDE_BACK;
}
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
if (!counts[1])
{
return in; // completely on front side
}
if (!counts[0])
{
FreeStackWinding(in, stack);
return NULL;
}
sides[i] = sides[0];
dists[i] = dists[0];
neww = AllocStackWinding(stack);
neww->numpoints = 0;
for (i = 0; i < in->numpoints; i++)
{
vec_t* p1 = in->points[i];
if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
{
Warning("ChopWinding : rejected(1) due to too many points\n");
FreeStackWinding(neww, stack);
return in; // can't chop -- fall back to original
}
if (sides[i] == SIDE_ON)
{
VectorCopy(p1, neww->points[neww->numpoints]);
neww->numpoints++;
continue;
}
else if (sides[i] == SIDE_FRONT)
{
VectorCopy(p1, neww->points[neww->numpoints]);
neww->numpoints++;
}
if ((sides[i + 1] == SIDE_ON) | (sides[i + 1] == sides[i])) // | instead of || for branch optimization
{
continue;
}
if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
{
Warning("ChopWinding : rejected(2) due to too many points\n");
FreeStackWinding(neww, stack);
return in; // can't chop -- fall back to original
}
// generate a split point
{
unsigned tmp = i + 1;
if (tmp >= in->numpoints)
{
tmp = 0;
}
const vec_t* p2 = in->points[tmp];
dot = dists[i] / (dists[i] - dists[i + 1]);
const vec_t* normal = split->normal;
const vec_t dist = split->dist;
unsigned int j;
for (j = 0; j < 3; j++)
{ // avoid round off error when possible
if (normal[j] < (1.0 - NORMAL_EPSILON))
{
if (normal[j] > (-1.0 + NORMAL_EPSILON))
{
mid[j] = p1[j] + dot * (p2[j] - p1[j]);
}
else
{
mid[j] = -dist;
}
}
else
{
mid[j] = dist;
}
}
}
VectorCopy(mid, neww->points[neww->numpoints]);
neww->numpoints++;
}
// free the original winding
FreeStackWinding(in, stack);
return neww;
}
// =====================================================================================
// AddPlane
// =====================================================================================
#ifdef RVIS_LEVEL_2
inline static void AddPlane(pstack_t* const stack, const plane_t* const split)
{
int j;
if (stack->clipPlaneCount)
{
for (j = 0; j < stack->clipPlaneCount; j++)
{
if (fabs((stack->clipPlane[j]).dist - split->dist) <= EQUAL_EPSILON &&
VectorCompare((stack->clipPlane[j]).normal, split->normal))
{
return;
}
}
}
stack->clipPlane[stack->clipPlaneCount] = *split;
stack->clipPlaneCount++;
}
#endif
// =====================================================================================
// ClipToSeperators
// Source, pass, and target are an ordering of portals.
// Generates seperating planes canidates by taking two points from source and one
// point from pass, and clips target by them.
// If the target argument is NULL, then a list of clipping planes is built in
// stack instead.
// If target is totally clipped away, that portal can not be seen through.
// Normal clip keeps target on the same side as pass, which is correct if the
// order goes source, pass, target. If the order goes pass, source, target then
// flipclip should be set.
// =====================================================================================
inline static winding_t* ClipToSeperators(
const winding_t* const source,
const winding_t* const pass,
winding_t* const a_target,
const bool flipclip,
pstack_t* const stack)
{
int i, j, k, l;
plane_t plane;
vec3_t v1, v2;
float d;
int counts[3];
bool fliptest;
winding_t* target = a_target;
const unsigned int numpoints = source->numpoints;
// check all combinations
for (i=0, l=1; i < numpoints; i++, l++)
{
if (l == numpoints)
{
l = 0;
}
VectorSubtract(source->points[l], source->points[i], v1);
// fing a vertex of pass that makes a plane that puts all of the
// vertexes of pass on the front side and all of the vertexes of
// source on the back side
for (j = 0; j < pass->numpoints; j++)
{
VectorSubtract(pass->points[j], source->points[i], v2);
CrossProduct(v1, v2, plane.normal);
if (VectorNormalize(plane.normal) < ON_EPSILON)
{
continue;
}
plane.dist = DotProduct(pass->points[j], plane.normal);
// find out which side of the generated seperating plane has the
// source portal
fliptest = false;
for (k = 0; k < numpoints; k++)
{
if ((k == i) | (k == l)) // | instead of || for branch optimization
{
continue;
}
d = DotProduct(source->points[k], plane.normal) - plane.dist;
if (d < -ON_EPSILON)
{ // source is on the negative side, so we want all
// pass and target on the positive side
fliptest = false;
break;
}
else if (d > ON_EPSILON)
{ // source is on the positive side, so we want all
// pass and target on the negative side
fliptest = true;
break;
}
}
if (k == numpoints)
{
continue; // planar with source portal
}
// flip the normal if the source portal is backwards
if (fliptest)
{
VectorSubtract(vec3_origin, plane.normal, plane.normal);
plane.dist = -plane.dist;
}
// if all of the pass portal points are now on the positive side,
// this is the seperating plane
counts[0] = counts[1] = counts[2] = 0;
for (k = 0; k < pass->numpoints; k++)
{
if (k == j)
{
continue;
}
d = DotProduct(pass->points[k], plane.normal) - plane.dist;
if (d < -ON_EPSILON)
{
break;
}
else if (d > ON_EPSILON)
{
counts[0]++;
}
else
{
counts[2]++;
}
}
if (k != pass->numpoints)
{
continue; // points on negative side, not a seperating plane
}
if (!counts[0])
{
continue; // planar with seperating plane
}
// flip the normal if we want the back side
if (flipclip)
{
VectorSubtract(vec3_origin, plane.normal, plane.normal);
plane.dist = -plane.dist;
}
if (target != NULL)
{
// clip target by the seperating plane
target = ChopWinding(target, stack, &plane);
if (!target)
{
return NULL; // target is not visible
}
}
else
{
AddPlane(stack, &plane);
}
#ifdef RVIS_LEVEL_1
break; /* Antony was here */
#endif
}
}
return target;
}
// =====================================================================================
// RecursiveLeafFlow
// Flood fill through the leafs
// If src_portal is NULL, this is the originating leaf
// =====================================================================================
inline static void RecursiveLeafFlow(const int leafnum, const threaddata_t* const thread, const pstack_t* const prevstack)
{
pstack_t stack;
leaf_t* leaf;
leaf = &g_leafs[leafnum];
#ifdef USE_CHECK_STACK
CheckStack(leaf, thread);
#endif
{
const unsigned offset = leafnum >> 3;
const unsigned bit = (1 << (leafnum & 7));
// mark the leaf as visible
if (!(thread->leafvis[offset] & bit))
{
thread->leafvis[offset] |= bit;
thread->base->numcansee++;
}
}
#ifdef USE_CHECK_STACK
prevstack->next = &stack;
stack.next = NULL;
#endif
stack.head = prevstack->head;
stack.leaf = leaf;
stack.portal = NULL;
#ifdef RVIS_LEVEL_2
stack.clipPlaneCount = -1;
stack.clipPlane = NULL;
#endif
// check all portals for flowing into other leafs
unsigned i;
portal_t** plist = leaf->portals;
for (i = 0; i < leaf->numportals; i++, plist++)
{
portal_t* p = *plist;
#if ZHLT_ZONES
portal_t * head_p = stack.head->portal;
if (g_Zones->check(head_p->zone, p->zone))
{
continue;
}
#endif
{
const unsigned offset = p->leaf >> 3;
const unsigned bit = 1 << (p->leaf & 7);
if (!(stack.head->mightsee[offset] & bit))
{
continue; // can't possibly see it
}
if (!(prevstack->mightsee[offset] & bit))
{
continue; // can't possibly see it
}
}
// if the portal can't see anything we haven't allready seen, skip it
{
long* test;
if (p->status == stat_done)
{
test = (long*)p->visbits;
}
else
{
test = (long*)p->mightsee;
}
{
const int bitlongs = g_bitlongs;
{
long* prevmight = (long*)prevstack->mightsee;
long* might = (long*)stack.mightsee;
unsigned j;
for (j = 0; j < bitlongs; j++, test++, might++, prevmight++)
{
(*might) = (*prevmight) & (*test);
}
}
{
long* might = (long*)stack.mightsee;
long* vis = (long*)thread->leafvis;
unsigned j;
for (j = 0; j < bitlongs; j++, might++, vis++)
{
if ((*might) & ~(*vis))
{
break;
}
}
if (j == g_bitlongs)
{ // can't see anything new
continue;
}
}
}
}
// get plane of portal, point normal into the neighbor leaf
stack.portalplane = &p->plane;
plane_t backplane;
VectorSubtract(vec3_origin, p->plane.normal, backplane.normal);
backplane.dist = -p->plane.dist;
if (VectorCompare(prevstack->portalplane->normal, backplane.normal))
{
continue; // can't go out a coplanar face
}
stack.portal = p;
#ifdef USE_CHECK_STACK
stack.next = NULL;
#endif
stack.freewindings[0] = 1;
stack.freewindings[1] = 1;
stack.freewindings[2] = 1;
stack.pass = ChopWinding(p->winding, &stack, thread->pstack_head.portalplane);
if (!stack.pass)
{
continue;
}
stack.source = ChopWinding(prevstack->source, &stack, &backplane);
if (!stack.source)
{
continue;
}
if (!prevstack->pass)
{ // the second leaf can only be blocked if coplanar
RecursiveLeafFlow(p->leaf, thread, &stack);
continue;
}
stack.pass = ChopWinding(stack.pass, &stack, prevstack->portalplane);
if (!stack.pass)
{
continue;
}
#ifdef RVIS_LEVEL_2
if (stack.clipPlaneCount == -1)
{
stack.clipPlaneCount = 0;
stack.clipPlane = (plane_t*)alloca(sizeof(plane_t) * prevstack->source->numpoints * prevstack->pass->numpoints);
ClipToSeperators(prevstack->source, prevstack->pass, NULL, false, &stack);
ClipToSeperators(prevstack->pass, prevstack->source, NULL, true, &stack);
}
if (stack.clipPlaneCount > 0)
{
unsigned j;
for (j = 0; j < stack.clipPlaneCount && stack.pass != NULL; j++)
{
stack.pass = ChopWinding(stack.pass, &stack, &(stack.clipPlane[j]));
}
if (stack.pass == NULL)
continue;
}
#else
stack.pass = ClipToSeperators(stack.source, prevstack->pass, stack.pass, false, &stack);
if (!stack.pass)
{
continue;
}
stack.pass = ClipToSeperators(prevstack->pass, stack.source, stack.pass, true, &stack);
if (!stack.pass)
{
continue;
}
#endif
if (g_fullvis)
{
stack.source = ClipToSeperators(stack.pass, prevstack->pass, stack.source, false, &stack);
if (!stack.source)
{
continue;
}
stack.source = ClipToSeperators(prevstack->pass, stack.pass, stack.source, true, &stack);
if (!stack.source)
{
continue;
}
}
// flow through it for real
RecursiveLeafFlow(p->leaf, thread, &stack);
}
#ifdef RVIS_LEVEL_2
#if 0
if (stack.clipPlane != NULL)
{
free(stack.clipPlane);
}
#endif
#endif
}
// =====================================================================================
// PortalFlow
// =====================================================================================
void PortalFlow(portal_t* p)
{
threaddata_t data;
unsigned i;
if (p->status != stat_working)
Error("PortalFlow: reflowed");
p->visbits = (byte*)calloc(1, g_bitbytes);
memset(&data, 0, sizeof(data));
data.leafvis = p->visbits;
data.base = p;
data.pstack_head.head = &data.pstack_head;
data.pstack_head.portal = p;
data.pstack_head.source = p->winding;
data.pstack_head.portalplane = &p->plane;
for (i = 0; i < g_bitlongs; i++)
{
((long*)data.pstack_head.mightsee)[i] = ((long*)p->mightsee)[i];
}
RecursiveLeafFlow(p->leaf, &data, &data.pstack_head);
#ifdef ZHLT_NETVIS
p->fromclient = g_clientid;
#endif
p->status = stat_done;
#ifdef ZHLT_NETVIS
Flag_VIS_DONE_PORTAL(g_visportalindex);
#endif
}
// =====================================================================================
// SimpleFlood
// This is a rough first-order aproximation that is used to trivially reject some
// of the final calculations.
// =====================================================================================
static void SimpleFlood(byte* const srcmightsee, const int leafnum, byte* const portalsee, unsigned int* const c_leafsee)
{
unsigned i;
leaf_t* leaf;
portal_t* p;
{
const unsigned offset = leafnum >> 3;
const unsigned bit = (1 << (leafnum & 7));
if (srcmightsee[offset] & bit)
{
return;
}
else
{
srcmightsee[offset] |= bit;
}
}
(*c_leafsee)++;
leaf = &g_leafs[leafnum];
for (i = 0; i < leaf->numportals; i++)
{
p = leaf->portals[i];
if (!portalsee[p - g_portals])
{
continue;
}
SimpleFlood(srcmightsee, p->leaf, portalsee, c_leafsee);
}
}
#define PORTALSEE_SIZE (MAX_PORTALS*2)
#ifdef SYSTEM_WIN32
#pragma warning(push)
#pragma warning(disable: 4100) // unreferenced formal parameter
#endif
#ifdef HLVIS_MAXDIST
#ifndef HLVIS_MAXDIST_NEW
// AJM: MVD
// =====================================================================================
// BlockVis
// =====================================================================================
void BlockVis(int unused)
{
int i, j, k, l, m;
portal_t *p;
visblocker_t *v;
visblocker_t *v2;
leaf_t *leaf;
while(1)
{
i = GetThreadWork();
if(i == -1)
break;
v = &g_visblockers[i];
// See which visblockers we need
for(j = 0; j < v->numnames; j++)
{
// Find visblocker
if(!(v2 = GetVisBlock(v->blocknames[j])))
continue;
// For each leaf in v2, eliminate visibility from v1
for(k = 0; k < v->numleafs; k++)
{
leaf = &g_leafs[v->blockleafs[k]];
for(l = 0; l < leaf->numportals; l++)
{
p = leaf->portals[l];
for(m = 0; m < v2->numleafs; m++)
{
const unsigned offset = v2->blockleafs[m] >> 3;
const unsigned bit = (1 << (v2->blockleafs[m] & 7));
p->mightsee[offset] &= ~bit;
}
}
}
}
}
}
// AJM: MVD
// =====================================================================================
// GetSplitPortal
// This function returns a portal on leaf1 that sucessfully seperates leaf1
// and leaf2
// =====================================================================================
static portal_t *GetSplitPortal(leaf_t *leaf1, leaf_t *leaf2)
{
int i, k, l;
portal_t *p1;
portal_t *t;
float check_dist;
for(i = 0, p1 = leaf1->portals[0]; i < leaf1->numportals; i++, p1++)
{
hlassert(p1->winding->numpoints >= 3);
// Check to make sure all the points on the other leaf are in front of the portal plane
for(k = 0, t = leaf2->portals[0]; k < leaf2->numportals; k++, t++)
{
for(l = 0; l < t->winding->numpoints; l++)
{
check_dist = DotProduct(t->winding->points[l], p1->plane.normal) - p1->plane.dist;
// We make the assumption that all portals face away from their parent leaf
if(check_dist < -ON_EPSILON)
goto PostLoop;
}
}
PostLoop:
// If we didn't check all the leaf2 portals, then this leaf1 portal doesn't work
if(k < leaf2->numportals)
continue;
// If we reach this point, we found a good portal
return p1;
}
// Didn't find any
return NULL;
}
// AJM: MVD
// =====================================================================================
// MakeSplitPortalList
// This function returns a portal on leaf1 that sucessfully seperates leaf1
// and leaf2
// =====================================================================================
static void MakeSplitPortalList(leaf_t *leaf1, leaf_t *leaf2, portal_t **portals, int *num_portals)
{
int i, k, l;
portal_t *p1;
portal_t *t;
*num_portals = 0;
float check_dist;
portal_t p_list[MAX_PORTALS_ON_LEAF];
int c_portal = 0;
if(*portals)
delete [] *portals;
for(i = 0, p1 = leaf1->portals[0]; i < leaf1->numportals; i++, p1++)
{
hlassert(p1->winding->numpoints >= 3);
// Check to make sure all the points on the other leaf are in front of the portal plane
for(k = 0, t = leaf2->portals[0]; k < leaf2->numportals; k++, t++)
{
for(l = 0; l < t->winding->numpoints; l++)
{
check_dist = DotProduct(t->winding->points[l], p1->plane.normal) - p1->plane.dist;
// We make the assumption that all portals face away from their parent leaf
if(check_dist < -ON_EPSILON)
goto PostLoop;
}
}
PostLoop:
// If we didn't check all the leaf2 portals, then this leaf1 portal doesn't work
if(k < leaf2->numportals)
continue;
// If we reach this point, we found a good portal
memcpy(&p_list[c_portal++], p1, sizeof(portal_t));
if(c_portal >= MAX_PORTALS_ON_LEAF)
Error("c_portal > MAX_PORTALS_ON_LEAF");
}
if(!c_portal)
return;
*num_portals = c_portal;
*portals = new portal_t[c_portal];
memcpy(*portals, p_list, c_portal * sizeof(portal_t));
}
// AJM: MVD
// =====================================================================================
// DisjointLeafVis
// This function returns TRUE if neither leaf can see the other
// Returns FALSE otherwise
// =====================================================================================
static bool DisjointLeafVis(int leaf1, int leaf2)
{
leaf_t *l = g_leafs + leaf1;
leaf_t *tl = g_leafs + leaf2;
const unsigned offset_l = leaf1 >> 3;
const unsigned bit_l = (1 << (leaf1 & 7));
const unsigned offset_tl = leaf2 >> 3;
const unsigned bit_tl = (1 << (leaf2 & 7));
for(int k = 0; k < l->numportals; k++)
{
for(int m = 0; m < tl->numportals; m++)
{
if(l->portals[k]->mightsee[offset_tl] & bit_tl)
goto RetFalse;
if(tl->portals[m]->mightsee[offset_l] & bit_l)
goto RetFalse;
if(l->portals[k]->status != stat_none)
{
if(l->portals[k]->visbits[offset_tl] & bit_tl)
goto RetFalse;
}
if(tl->portals[m]->status != stat_none)
{
if(tl->portals[m]->visbits[offset_l] & bit_l)
goto RetFalse;
}
}
}
return true;
RetFalse:
return false;
}
// AJM: MVD
// =====================================================================================
// GetPortalBounds
// This function take a portal and finds its bounds
// parallel to the normal of the portal. They will face inwards
// =====================================================================================
static void GetPortalBounds(portal_t *p, plane_t **bounds)
{
int i;
vec3_t vec1, vec2;
hlassert(p->winding->numpoints >= 3);
if(*bounds)
delete [] *bounds;
*bounds = new plane_t[p->winding->numpoints];
// Loop through each set of points and create a plane boundary for each
for(i = 0; i < p->winding->numpoints; i++)
{
VectorSubtract(p->winding->points[(i + 1) % p->winding->numpoints],p->winding->points[i],vec1);
// Create inward normal for this boundary
CrossProduct(p->plane.normal, vec1, vec2);
VectorNormalize(vec2);
VectorCopy(vec2, (*bounds)[i].normal);
(*bounds)[i].dist = DotProduct(p->winding->points[i], vec2);
}
}
// AJM: MVD
// =====================================================================================
// ClipWindingsToBounds
// clips all the windings with all the planes (including original face) and outputs
// what's left int "out"
// =====================================================================================
static void ClipWindingsToBounds(winding_t *windings, int numwindings, plane_t *bounds, int numbounds, plane_t &original_plane, winding_t **out, int &num_out)
{
hlassert(windings);
hlassert(bounds);
winding_t out_windings[MAX_PORTALS_ON_LEAF];
num_out = 0;
int h, i;
*out = NULL;
Winding wind;
for(h = 0; h < numwindings; h++)
{
// For each winding...
// Create a winding with CWinding
wind.initFromPoints(windings[h].points, windings[h].numpoints);
// Clip winding to original plane
wind.Chop(original_plane.normal, original_plane.dist);
for(i = 0; i < numbounds, wind.Valid(); i++)
{
// For each bound...
// Chop the winding to the bounds
wind.Chop(bounds[i].normal, bounds[i].dist);
}
if(wind.Valid())
{
// We have a valid winding, copy to array
wind.CopyPoints(&out_windings[num_out].points[0], out_windings[num_out].numpoints);
num_out++;
}
}
if(!num_out) // Everything was clipped away
return;
// Otherwise, create out
*out = new winding_t[num_out];
memcpy(*out, out_windings, num_out * sizeof(winding_t));
}
// AJM: MVD
// =====================================================================================
// GenerateWindingList
// This function generates a list of windings for a leaf through its portals
// =====================================================================================
static void GenerateWindingList(leaf_t *leaf, winding_t **winds)
{
winding_t windings[MAX_PORTALS_ON_LEAF];
int numwinds = 0;
int i;
for(i = 0; i < leaf->numportals; i++)
{
memcpy(&windings[numwinds++], leaf->portals[i]->winding, sizeof(winding_t));
}
if(!numwinds)
return;
*winds = new winding_t[numwinds];
memcpy(*winds, &windings, sizeof(winding_t) * numwinds);
}
// AJM: MVD
// =====================================================================================
// CalcPortalBoundsAndClipPortals
// =====================================================================================
static void CalcPortalBoundsAndClipPortals(portal_t *portal, leaf_t *leaf, winding_t **out, int &numout)
{
plane_t *bounds = NULL;
winding_t *windings = NULL;
GetPortalBounds(portal, &bounds);
GenerateWindingList(leaf, &windings);
ClipWindingsToBounds(windings, leaf->numportals, bounds, portal->winding->numpoints, portal->plane, out, numout);
delete bounds;
delete windings;
}
// AJM: MVD
// =====================================================================================
// GetShortestDistance
// Gets the shortest distance between both leaves
// =====================================================================================
static float GetShortestDistance(leaf_t *leaf1, leaf_t *leaf2)
{
winding_t *final = NULL;
int num_finals = 0;
int i, x, y;
float check;
for(i = 0; i < leaf1->numportals; i++)
{
CalcPortalBoundsAndClipPortals(leaf1->portals[i], leaf2, &final, num_finals);
// Minimum point distance
for(x = 0; x < num_finals; x++)
{
for(y = 0; y < final[x].numpoints; y++)
{
check = DotProduct(leaf1->portals[i]->plane.normal, final[x].points[y]) - leaf1->portals[i]->plane.dist;
if(check <= g_maxdistance)
return check;
}
}
delete final;
}
// Switch leaf 1 and 2
for(i = 0; i < leaf2->numportals; i++)
{
CalcPortalBoundsAndClipPortals(leaf2->portals[i], leaf1, &final, num_finals);
// Minimum point distance
for(x = 0; x < num_finals; x++)
{
for(y = 0; y < final[x].numpoints; y++)
{
check = DotProduct(leaf2->portals[i]->plane.normal, final[x].points[y]) - leaf2->portals[i]->plane.dist;
if(check <= g_maxdistance)
return check;
}
}
delete final;
}
return 9E10;
}
// AJM: MVD
// =====================================================================================
// CalcSplitsAndDotProducts
// This function finds the splits of the leaf, and generates windings (if applicable)
// =====================================================================================
static float CalcSplitsAndDotProducts(plane_t *org_split_plane, leaf_t *leaf1, leaf_t *leaf2, plane_t *bounds, int num_bounds)
{
int i, j, k, l;
portal_t *splits = NULL;
int num_splits;
float dist;
float min_dist = 999999999.999;
vec3_t i_points[MAX_POINTS_ON_FIXED_WINDING * MAX_PORTALS_ON_LEAF * 2];
vec3_t delta;
int num_points = 0;
// First get splits
MakeSplitPortalList(leaf1, leaf2, &splits, &num_splits);
if(!num_splits)
return min_dist;
// If the number of splits = 1, then clip the plane using the boundary windings
if(num_splits == 1)
{
Winding wind(splits[0].plane.normal, splits[0].plane.dist);
for(i = 0; i < num_bounds; i++)
{
wind.Chop(bounds[i].normal, bounds[i].dist);
}
// The wind is chopped - get closest dot product
for(i = 0; i < wind.m_NumPoints; i++)
{
dist = DotProduct(wind.m_Points[i], org_split_plane->normal) - org_split_plane->dist;
min_dist = qmin(min_dist, dist);
}
return min_dist;
}
// In this case, we have more than one split point, and we must calculate all intersections
// Properties of convex objects allow us to assume that these intersections will be the closest
// points to the other leaf, and our other checks before this eliminate exception cases
// Loop through each split portal, and using an inside loop, loop through every OTHER split portal
// Common portal points in more than one split portal are intersections!
for(i = 0; i < num_splits; i++)
{
for(j = 0; j < num_splits; j++)
{
if(i == j)
{
continue;
}
// Loop through each point on both portals
for(k = 0; k < splits[i].winding->numpoints; k++)
{
for(l = 0; l < splits[j].winding->numpoints; l++)
{
VectorSubtract(splits[i].winding->points[k], splits[j].winding->points[l], delta);
if(VectorLength(delta) < EQUAL_EPSILON)
{
memcpy(i_points[num_points++], splits[i].winding->points[k], sizeof(vec3_t));
}
}
}
}
}
// Loop through each intersection point and check
for(i = 0; i < num_points; i++)
{
dist = DotProduct(i_points[i], org_split_plane->normal) - org_split_plane->dist;
min_dist = qmin(min_dist, dist);
}
if(splits)
delete [] splits;
return min_dist;
}
#endif
#endif // HLVIS_MAXDIST
// =====================================================================================
// BasePortalVis
// =====================================================================================
void BasePortalVis(int unused)
{
int i, j, k;
portal_t* tp;
portal_t* p;
float d;
winding_t* w;
byte portalsee[PORTALSEE_SIZE];
const int portalsize = (g_numportals * 2);
#ifdef ZHLT_NETVIS
{
i = unused;
#else
while (1)
{
i = GetThreadWork();
if (i == -1)
break;
#endif
p = g_portals + i;
p->mightsee = (byte*)calloc(1, g_bitbytes);
memset(portalsee, 0, portalsize);
#if ZHLT_ZONES
UINT32 zone = p->zone;
#endif
for (j = 0, tp = g_portals; j < portalsize; j++, tp++)
{
if (j == i)
{
continue;
}
#if ZHLT_ZONES
if (g_Zones->check(zone, tp->zone))
{
continue;
}
#endif
w = tp->winding;
for (k = 0; k < w->numpoints; k++)
{
d = DotProduct(w->points[k], p->plane.normal) - p->plane.dist;
if (d > ON_EPSILON)
{
break;
}
}
if (k == w->numpoints)
{
continue; // no points on front
}
w = p->winding;
for (k = 0; k < w->numpoints; k++)
{
d = DotProduct(w->points[k], tp->plane.normal) - tp->plane.dist;
if (d < -ON_EPSILON)
{
break;
}
}
if (k == w->numpoints)
{
continue; // no points on front
}
portalsee[j] = 1;
}
SimpleFlood(p->mightsee, p->leaf, portalsee, &p->nummightsee);
Verbose("portal:%4i nummightsee:%4i \n", i, p->nummightsee);
}
}
#ifdef HLVIS_MAXDIST
#ifdef HLVIS_MAXDIST_NEW
bool BestNormalFromWinding (const vec3_t *points, int numpoints, vec3_t &normal_out)
{
const vec3_t *pt1, *pt2, *pt3;
int k;
vec3_t d, normal, edge;
vec_t dist, maxdist;
if (numpoints < 3)
{
return false;
}
pt1 = &points[0];
maxdist = -1;
for (k = 0; k < numpoints; k++)
{
if (&points[k] == pt1)
{
continue;
}
VectorSubtract (points[k], *pt1, edge);
dist = DotProduct (edge, edge);
if (dist > maxdist)
{
maxdist = dist;
pt2 = &points[k];
}
}
if (maxdist <= ON_EPSILON * ON_EPSILON)
{
return false;
}
maxdist = -1;
VectorSubtract (*pt2, *pt1, edge);
VectorNormalize (edge);
for (k = 0; k < numpoints; k++)
{
if (&points[k] == pt1 || &points[k] == pt2)
{
continue;
}
VectorSubtract (points[k], *pt1, d);
CrossProduct (edge, d, normal);
dist = DotProduct (normal, normal);
if (dist > maxdist)
{
maxdist = dist;
pt3 = &points[k];
}
}
if (maxdist <= ON_EPSILON * ON_EPSILON)
{
return false;
}
VectorSubtract (*pt3, *pt1, d);
CrossProduct (edge, d, normal);
VectorNormalize (normal);
if (pt3 < pt2)
{
VectorScale (normal, -1, normal);
}
VectorCopy (normal, normal_out);
return true;
}
vec_t WindingDist (const winding_t *w[2])
{
vec_t minsqrdist = 99999999.0 * 99999999.0;
vec_t sqrdist;
int a, b;
// point to point
for (a = 0; a < w[0]->numpoints; a++)
{
for (b = 0; b < w[1]->numpoints; b++)
{
vec3_t v;
VectorSubtract (w[0]->points[a], w[1]->points[b], v);
sqrdist = DotProduct (v, v);
if (sqrdist < minsqrdist)
{
minsqrdist = sqrdist;
}
}
}
// point to edge
for (int side = 0; side < 2; side++)
{
for (a = 0; a < w[side]->numpoints; a++)
{
for (b = 0; b < w[!side]->numpoints; b++)
{
const vec3_t &p = w[side]->points[a];
const vec3_t &p1 = w[!side]->points[b];
const vec3_t &p2 = w[!side]->points[(b + 1) % w[!side]->numpoints];
vec3_t delta;
vec_t frac;
vec3_t v;
VectorSubtract (p2, p1, delta);
if (VectorNormalize (delta) <= ON_EPSILON)
{
continue;
}
frac = DotProduct (p, delta) - DotProduct (p1, delta);
if (frac <= ON_EPSILON || frac >= (DotProduct (p2, delta) - DotProduct (p1, delta)) - ON_EPSILON)
{
// p1 or p2 is closest to p
continue;
}
VectorMA (p1, frac, delta, v);
VectorSubtract (p, v, v);
sqrdist = DotProduct (v, v);
if (sqrdist < minsqrdist)
{
minsqrdist = sqrdist;
}
}
}
}
// edge to edge
for (a = 0; a < w[0]->numpoints; a++)
{
for (b = 0; b < w[1]->numpoints; b++)
{
const vec3_t &p1 = w[0]->points[a];
const vec3_t &p2 = w[0]->points[(a + 1) % w[0]->numpoints];
const vec3_t &p3 = w[1]->points[b];
const vec3_t &p4 = w[1]->points[(b + 1) % w[1]->numpoints];
vec3_t delta1;
vec3_t delta2;
vec3_t normal;
vec3_t normal1;
vec3_t normal2;
VectorSubtract (p2, p1, delta1);
VectorSubtract (p4, p3, delta2);
CrossProduct (delta1, delta2, normal);
if (!VectorNormalize (normal))
{
continue;
}
CrossProduct (normal, delta1, normal1); // same direction as delta2
CrossProduct (delta2, normal, normal2); // same direction as delta1
if (VectorNormalize (normal1) <= ON_EPSILON || VectorNormalize (normal2) <= ON_EPSILON)
{
continue;
}
if (DotProduct (p3, normal1) >= DotProduct (p1, normal1) - ON_EPSILON ||
DotProduct (p4, normal1) <= DotProduct (p1, normal1) + ON_EPSILON ||
DotProduct (p1, normal2) >= DotProduct (p3, normal2) - ON_EPSILON ||
DotProduct (p2, normal2) <= DotProduct (p3, normal2) + ON_EPSILON )
{
// the edges are not crossing when viewed along normal
continue;
}
sqrdist = DotProduct (p3, normal) - DotProduct (p1, normal);
sqrdist = sqrdist * sqrdist;
if (sqrdist < minsqrdist)
{
minsqrdist = sqrdist;
}
}
}
// point to face and edge to face
for (int side = 0; side < 2; side++)
{
vec3_t planenormal;
vec_t planedist;
vec3_t *boundnormals;
vec_t *bounddists;
if (!BestNormalFromWinding (w[!side]->points, w[!side]->numpoints, planenormal))
{
continue;
}
planedist = DotProduct (planenormal, w[!side]->points[0]);
hlassume (boundnormals = (vec3_t *)malloc (w[!side]->numpoints * sizeof (vec3_t)), assume_NoMemory);
hlassume (bounddists = (vec_t *)malloc (w[!side]->numpoints * sizeof (vec_t)), assume_NoMemory);
// build boundaries
for (b = 0; b < w[!side]->numpoints; b++)
{
vec3_t v;
const vec3_t &p1 = w[!side]->points[b];
const vec3_t &p2 = w[!side]->points[(b + 1) % w[!side]->numpoints];
VectorSubtract (p2, p1, v);
CrossProduct (v, planenormal, boundnormals[b]);
if (!VectorNormalize (boundnormals[b]))
{
bounddists[b] = 1.0;
}
else
{
bounddists[b] = DotProduct (p1, boundnormals[b]);
}
}
for (a = 0; a < w[side]->numpoints; a++)
{
const vec3_t &p = w[side]->points[a];
for (b = 0; b < w[!side]->numpoints; b++)
{
if (DotProduct (p, boundnormals[b]) - bounddists[b] >= -ON_EPSILON)
{
break;
}
}
if (b < w[!side]->numpoints)
{
continue;
}
sqrdist = DotProduct (p, planenormal) - planedist;
sqrdist = sqrdist * sqrdist;
if (sqrdist < minsqrdist)
{
minsqrdist = sqrdist;
}
}
for (a = 0; a < w[side]->numpoints; a++)
{
const vec3_t &p1 = w[side]->points[a];
const vec3_t &p2 = w[side]->points[(a + 1) % w[side]->numpoints];
vec_t dist1 = DotProduct (p1, planenormal) - planedist;
vec_t dist2 = DotProduct (p2, planenormal) - planedist;
vec3_t delta;
vec_t frac;
vec3_t v;
if (dist1 > ON_EPSILON && dist2 < -ON_EPSILON || dist1 < -ON_EPSILON && dist2 > ON_EPSILON)
{
frac = dist1 / (dist1 - dist2);
VectorSubtract (p2, p1, delta);
VectorMA (p1, frac, delta, v);
for (b = 0; b < w[!side]->numpoints; b++)
{
if (DotProduct (v, boundnormals[b]) - bounddists[b] >= -ON_EPSILON)
{
break;
}
}
if (b < w[!side]->numpoints)
{
continue;
}
minsqrdist = 0;
}
}
free (boundnormals);
free (bounddists);
}
return (sqrt (minsqrdist));
}
#endif
// AJM: MVD
// =====================================================================================
// MaxDistVis
// =====================================================================================
void MaxDistVis(int unused)
{
int i, j, k, m;
int a, b, c, d;
leaf_t *l;
leaf_t *tl;
plane_t *boundary = NULL;
vec3_t delta;
float new_dist;
unsigned offset_l;
unsigned bit_l;
unsigned offset_tl;
unsigned bit_tl;
while(1)
{
i = GetThreadWork();
if (i == -1)
break;
l = &g_leafs[i];
for(j = i + 1, tl = g_leafs + j; j < g_portalleafs; j++, tl++)
{
#ifdef HLVIS_MAXDIST_NEW
offset_l = i >> 3;
bit_l = (1 << (i & 7));
offset_tl = j >> 3;
bit_tl = (1 << (j & 7));
{
bool visible = false;
for (k = 0; k < l->numportals; k++)
{
if (l->portals[k]->visbits[offset_tl] & bit_tl)
{
visible = true;
}
}
for (m = 0; m < tl->numportals; m++)
{
if (tl->portals[m]->visbits[offset_l] & bit_l)
{
visible = true;
}
}
if (!visible)
{
goto NoWork;
}
}
// rough check
{
vec3_t v;
vec_t dist;
const winding_t *w;
const leaf_t *leaf[2] = {l, tl};
vec3_t center[2];
vec_t radius[2];
int count[2];
for (int side = 0; side < 2; side++)
{
count[side] = 0;
VectorClear (center[side]);
for (a = 0; a < leaf[side]->numportals; a++)
{
w = leaf[side]->portals[a]->winding;
for (b = 0; b < w->numpoints; b++)
{
VectorAdd (w->points[b], center[side], center[side]);
count[side]++;
}
}
}
if (!count[0] && !count[1])
{
goto Work;
}
for (int side = 0; side < 2; side++)
{
VectorScale (center[side], 1.0 / (vec_t)count[side], center[side]);
radius[side] = 0;
for (a = 0; a < leaf[side]->numportals; a++)
{
w = leaf[side]->portals[a]->winding;
for (b = 0; b < w->numpoints; b++)
{
VectorSubtract (w->points[b], center[side], v);
dist = DotProduct (v, v);
radius[side] = qmax (radius[side], dist);
}
}
radius[side] = sqrt (radius[side]);
}
VectorSubtract (center[0], center[1], v);
dist = VectorLength (v);
if (qmax (dist - radius[0] - radius[1], 0) >= g_maxdistance - ON_EPSILON)
{
goto Work;
}
if (dist + radius[0] + radius[1] < g_maxdistance - ON_EPSILON)
{
goto NoWork;
}
}
// exact check
{
vec_t mindist = 9999999999;
vec_t dist;
for (k = 0; k < l->numportals; k++)
{
for (m = 0; m < tl->numportals; m++)
{
const winding_t *w[2];
w[0] = l->portals[k]->winding;
w[1] = tl->portals[m]->winding;
dist = WindingDist (w);
mindist = qmin (dist, mindist);
}
}
if (mindist >= g_maxdistance - ON_EPSILON)
{
goto Work;
}
else
{
goto NoWork;
}
}
#else
if(j == i) // Ideally, should never be true
{
continue;
}
// If they already can't see each other, no use checking
if(DisjointLeafVis(i, j))
{
continue;
}
new_dist = GetShortestDistance(l, tl);
if(new_dist <= g_maxdistance)
continue;
// Try out our NEW, IMPROVED ALGORITHM!!!!
// Get a portal on Leaf 1 that completely seperates the two leafs
/*split = GetSplitPortal(l, tl);
if(!split)
continue;
// We have a split, so create the bounds
GetPortalBounds(split, &boundary);
// Now get the dot product for all points on the other leaf
max_dist = 999999999.999;
/// Do the first check if mode is >= 2
if(g_mdmode >= 2)
{
for(k = 0; k < tl->numportals; k++)
{
for(m = 0; m < tl->portals[k]->winding->numpoints; m++)
{
for(n = 0; n < split->winding->numpoints; n++) // numpoints of split portals = number of boundaries
{
dist = DotProduct(tl->portals[k]->winding->points[m], boundary[n].normal) - boundary[n].dist;
if(dist < -ON_EPSILON)
{
// Outside boundaries
//max_dot = MaxDotProduct(tl->portals[k]->winding->points[m], boundary, split->winding->numpoints);
//max_dist = qmin(max_dist, max_dot);
// Break so we don't do inside boundary check
break;
}
}
if(n < split->winding->numpoints)
continue;
// We found a point that's inside all the boundries!
new_dist = DotProduct(tl->portals[k]->winding->points[m], split->plane.normal) - split->plane.dist;
max_dist = qmin(max_dist, new_dist);
}
}
}
// This is now a special check. If Leaf 2 has a split plane, we generate a polygon by clipping the plane
// with the borders. We then get the minimum dot products. If more than one split plane, use intersection.
// Only do this is g_mdmode is 3
if(g_mdmode >= 3) // For future mode expansion
{
new_dist = CalcSplitsAndDotProducts(&split->plane, tl, l, boundary, split->winding->numpoints);
max_dist = qmin(max_dist, new_dist);
}*/
// Third and final check. If the whole of leaf2 is outside of leaf1 boundaries, this one will catch it
// Basic "every point to every point" type of deal :)
// This is done by default all the time
for(a = 0; a < l->numportals; a++)
{
for(b = 0; b < tl->numportals; b++)
{
for(c = 0; c < l->portals[a]->winding->numpoints; c++)
{
for(d = 0; d < tl->portals[b]->winding->numpoints; d++)
{
VectorSubtract(l->portals[a]->winding->points[c], tl->portals[b]->winding->points[d], delta);
if(VectorLength(delta) <= g_maxdistance)
goto NoWork;
}
}
}
}
#endif
#ifdef HLVIS_MAXDIST_NEW
Work:
ThreadLock ();
for (k = 0; k < l->numportals; k++)
{
l->portals[k]->visbits[offset_tl] &= ~bit_tl;
}
for (m = 0; m < tl->numportals; m++)
{
tl->portals[m]->visbits[offset_l] &= ~bit_l;
}
ThreadUnlock ();
#else
offset_l = i >> 3;
bit_l = (1 << (i & 7));
offset_tl = j >> 3;
bit_tl = (1 << (j & 7));
for(k = 0; k < l->numportals; k++)
{
for(m = 0; m < tl->numportals; m++)
{
if(l->portals[k]->status != stat_none)
l->portals[k]->visbits[offset_tl] &= ~bit_tl;
else
l->portals[k]->mightsee[offset_tl] &= ~bit_tl;
if(tl->portals[m]->status != stat_none)
tl->portals[m]->visbits[offset_l] &= ~bit_l;
else
tl->portals[m]->mightsee[offset_l] &= ~bit_l;
}
}
#endif
NoWork:
continue; // Hack to keep label from causing compile error
}
}
// Release potential memory
if(boundary)
delete [] boundary;
}
#endif // HLVIS_MAXDIST
#ifdef SYSTEM_WIN32
#pragma warning(pop)
#endif