1
This commit is contained in:
30
utils/vvis/WaterDist.cpp
Normal file
30
utils/vvis/WaterDist.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "bsplib.h"
|
||||
|
||||
// input:
|
||||
// from bsplib.h:
|
||||
// numleafs
|
||||
// dleafs
|
||||
|
||||
void EmitDistanceToWaterInfo( void )
|
||||
{
|
||||
int leafID;
|
||||
for( leafID = 0; leafID < numleafs; leafID++ )
|
||||
{
|
||||
dleaf_t *pLeaf = &dleafs[leafID];
|
||||
if( pLeaf->leafWaterDataID == -1 )
|
||||
{
|
||||
// FIXME: set the distance to water to infinity here just in case.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the vis set for this leaf.
|
||||
|
||||
}
|
||||
}
|
||||
|
881
utils/vvis/flow.cpp
Normal file
881
utils/vvis/flow.cpp
Normal file
@ -0,0 +1,881 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "vis.h"
|
||||
#include "vmpi.h"
|
||||
|
||||
int g_TraceClusterStart = -1;
|
||||
int g_TraceClusterStop = -1;
|
||||
/*
|
||||
|
||||
each portal will have a list of all possible to see from first portal
|
||||
|
||||
if (!thread->portalmightsee[portalnum])
|
||||
|
||||
portal mightsee
|
||||
|
||||
for p2 = all other portals in leaf
|
||||
get sperating planes
|
||||
for all portals that might be seen by p2
|
||||
mark as unseen if not present in seperating plane
|
||||
flood fill a new mightsee
|
||||
save as passagemightsee
|
||||
|
||||
|
||||
void CalcMightSee (leaf_t *leaf,
|
||||
*/
|
||||
|
||||
int CountBits (byte *bits, int numbits)
|
||||
{
|
||||
int i;
|
||||
int c;
|
||||
|
||||
c = 0;
|
||||
for (i=0 ; i<numbits ; i++)
|
||||
if ( CheckBit( bits, i ) )
|
||||
c++;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int c_fullskip;
|
||||
int c_portalskip, c_leafskip;
|
||||
int c_vistest, c_mighttest;
|
||||
|
||||
int c_chop, c_nochop;
|
||||
|
||||
int active;
|
||||
|
||||
extern bool g_bVMPIEarlyExit;
|
||||
|
||||
|
||||
void CheckStack (leaf_t *leaf, threaddata_t *thread)
|
||||
{
|
||||
pstack_t *p, *p2;
|
||||
|
||||
for (p=thread->pstack_head.next ; p ; p=p->next)
|
||||
{
|
||||
// Msg ("=");
|
||||
if (p->leaf == leaf)
|
||||
Error ("CheckStack: leaf recursion");
|
||||
for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
|
||||
if (p2->leaf == p->leaf)
|
||||
Error ("CheckStack: late leaf recursion");
|
||||
}
|
||||
// Msg ("\n");
|
||||
}
|
||||
|
||||
|
||||
winding_t *AllocStackWinding (pstack_t *stack)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if (stack->freewindings[i])
|
||||
{
|
||||
stack->freewindings[i] = 0;
|
||||
return &stack->windings[i];
|
||||
}
|
||||
}
|
||||
|
||||
Error ("Out of memory. AllocStackWinding: failed");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FreeStackWinding (winding_t *w, pstack_t *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
|
||||
|
||||
==============
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning (disable:4701)
|
||||
#endif
|
||||
|
||||
winding_t *ChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
|
||||
{
|
||||
vec_t dists[128];
|
||||
int sides[128];
|
||||
int counts[3];
|
||||
vec_t dot;
|
||||
int i, j;
|
||||
Vector mid;
|
||||
winding_t *neww;
|
||||
|
||||
counts[0] = counts[1] = counts[2] = 0;
|
||||
|
||||
// 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_VIS_EPSILON)
|
||||
sides[i] = SIDE_FRONT;
|
||||
else if (dot < -ON_VIS_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++)
|
||||
{
|
||||
Vector& p1 = in->points[i];
|
||||
|
||||
if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (sides[i] == SIDE_FRONT)
|
||||
{
|
||||
VectorCopy (p1, neww->points[neww->numpoints]);
|
||||
neww->numpoints++;
|
||||
}
|
||||
|
||||
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
||||
continue;
|
||||
|
||||
if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
|
||||
{
|
||||
FreeStackWinding (neww, stack);
|
||||
return in; // can't chop -- fall back to original
|
||||
}
|
||||
|
||||
// generate a split point
|
||||
Vector& p2 = in->points[(i+1)%in->numpoints];
|
||||
|
||||
dot = dists[i] / (dists[i]-dists[i+1]);
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{ // avoid round off error when possible
|
||||
if (split->normal[j] == 1)
|
||||
mid[j] = split->dist;
|
||||
else if (split->normal[j] == -1)
|
||||
mid[j] = -split->dist;
|
||||
else
|
||||
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
|
||||
}
|
||||
|
||||
VectorCopy (mid, neww->points[neww->numpoints]);
|
||||
neww->numpoints++;
|
||||
}
|
||||
|
||||
// free the original winding
|
||||
FreeStackWinding (in, stack);
|
||||
|
||||
return neww;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning (default:4701)
|
||||
#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 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.
|
||||
==============
|
||||
*/
|
||||
winding_t *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, bool flipclip, pstack_t *stack)
|
||||
{
|
||||
int i, j, k, l;
|
||||
plane_t plane;
|
||||
Vector v1, v2;
|
||||
float d;
|
||||
vec_t length;
|
||||
int counts[3];
|
||||
bool fliptest;
|
||||
|
||||
// check all combinations
|
||||
for (i=0 ; i<source->numpoints ; i++)
|
||||
{
|
||||
l = (i+1)%source->numpoints;
|
||||
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);
|
||||
|
||||
plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
||||
plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
||||
plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
||||
|
||||
// if points don't make a valid plane, skip it
|
||||
|
||||
length = plane.normal[0] * plane.normal[0]
|
||||
+ plane.normal[1] * plane.normal[1]
|
||||
+ plane.normal[2] * plane.normal[2];
|
||||
|
||||
if (length < ON_VIS_EPSILON)
|
||||
continue;
|
||||
|
||||
length = 1/sqrt(length);
|
||||
|
||||
plane.normal[0] *= length;
|
||||
plane.normal[1] *= length;
|
||||
plane.normal[2] *= length;
|
||||
|
||||
plane.dist = DotProduct (pass->points[j], plane.normal);
|
||||
|
||||
//
|
||||
// find out which side of the generated seperating plane has the
|
||||
// source portal
|
||||
//
|
||||
#if 1
|
||||
fliptest = false;
|
||||
for (k=0 ; k<source->numpoints ; k++)
|
||||
{
|
||||
if (k == i || k == l)
|
||||
continue;
|
||||
d = DotProduct (source->points[k], plane.normal) - plane.dist;
|
||||
if (d < -ON_VIS_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_VIS_EPSILON)
|
||||
{ // source is on the positive side, so we want all
|
||||
// pass and target on the negative side
|
||||
fliptest = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == source->numpoints)
|
||||
continue; // planar with source portal
|
||||
#else
|
||||
fliptest = flipclip;
|
||||
#endif
|
||||
//
|
||||
// flip the normal if the source portal is backwards
|
||||
//
|
||||
if (fliptest)
|
||||
{
|
||||
VectorSubtract (vec3_origin, plane.normal, plane.normal);
|
||||
plane.dist = -plane.dist;
|
||||
}
|
||||
#if 1
|
||||
//
|
||||
// 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_VIS_EPSILON)
|
||||
break;
|
||||
else if (d > ON_VIS_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
|
||||
#else
|
||||
k = (j+1)%pass->numpoints;
|
||||
d = DotProduct (pass->points[k], plane.normal) - plane.dist;
|
||||
if (d < -ON_VIS_EPSILON)
|
||||
continue;
|
||||
k = (j+pass->numpoints-1)%pass->numpoints;
|
||||
d = DotProduct (pass->points[k], plane.normal) - plane.dist;
|
||||
if (d < -ON_VIS_EPSILON)
|
||||
continue;
|
||||
#endif
|
||||
//
|
||||
// flip the normal if we want the back side
|
||||
//
|
||||
if (flipclip)
|
||||
{
|
||||
VectorSubtract (vec3_origin, plane.normal, plane.normal);
|
||||
plane.dist = -plane.dist;
|
||||
}
|
||||
|
||||
//
|
||||
// clip target by the seperating plane
|
||||
//
|
||||
target = ChopWinding (target, stack, &plane);
|
||||
if (!target)
|
||||
return NULL; // target is not visible
|
||||
|
||||
// JAY: End the loop, no need to find additional separators on this edge ?
|
||||
// j = pass->numpoints;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
class CPortalTrace
|
||||
{
|
||||
public:
|
||||
CUtlVector<Vector> m_list;
|
||||
CThreadFastMutex m_mutex;
|
||||
} g_PortalTrace;
|
||||
|
||||
void WindingCenter (winding_t *w, Vector ¢er)
|
||||
{
|
||||
int i;
|
||||
float scale;
|
||||
|
||||
VectorCopy (vec3_origin, center);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
VectorAdd (w->points[i], center, center);
|
||||
|
||||
scale = 1.0/w->numpoints;
|
||||
VectorScale (center, scale, center);
|
||||
}
|
||||
|
||||
Vector ClusterCenter( int cluster )
|
||||
{
|
||||
Vector mins, maxs;
|
||||
ClearBounds(mins, maxs);
|
||||
int count = leafs[cluster].portals.Count();
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
winding_t *w = leafs[cluster].portals[i]->winding;
|
||||
for ( int j = 0; j < w->numpoints; j++ )
|
||||
{
|
||||
AddPointToBounds( w->points[j], mins, maxs );
|
||||
}
|
||||
}
|
||||
return (mins + maxs) * 0.5f;
|
||||
}
|
||||
|
||||
|
||||
void DumpPortalTrace( pstack_t *pStack )
|
||||
{
|
||||
AUTO_LOCK(g_PortalTrace.m_mutex);
|
||||
if ( g_PortalTrace.m_list.Count() )
|
||||
return;
|
||||
|
||||
Warning("Dumped cluster trace!!!\n");
|
||||
Vector mid;
|
||||
mid = ClusterCenter( g_TraceClusterStart );
|
||||
g_PortalTrace.m_list.AddToTail(mid);
|
||||
for ( ; pStack != NULL; pStack = pStack->next )
|
||||
{
|
||||
winding_t *w = pStack->pass ? pStack->pass : pStack->portal->winding;
|
||||
WindingCenter (w, mid);
|
||||
g_PortalTrace.m_list.AddToTail(mid);
|
||||
for ( int i = 0; i < w->numpoints; i++ )
|
||||
{
|
||||
g_PortalTrace.m_list.AddToTail(w->points[i]);
|
||||
g_PortalTrace.m_list.AddToTail(mid);
|
||||
}
|
||||
for ( int i = 0; i < w->numpoints; i++ )
|
||||
{
|
||||
g_PortalTrace.m_list.AddToTail(w->points[i]);
|
||||
}
|
||||
g_PortalTrace.m_list.AddToTail(w->points[0]);
|
||||
g_PortalTrace.m_list.AddToTail(mid);
|
||||
}
|
||||
mid = ClusterCenter( g_TraceClusterStop );
|
||||
g_PortalTrace.m_list.AddToTail(mid);
|
||||
}
|
||||
|
||||
void WritePortalTrace( const char *source )
|
||||
{
|
||||
Vector mid;
|
||||
FILE *linefile;
|
||||
char filename[1024];
|
||||
|
||||
if ( !g_PortalTrace.m_list.Count() )
|
||||
{
|
||||
Warning("No trace generated from %d to %d\n", g_TraceClusterStart, g_TraceClusterStop );
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf (filename, "%s.lin", source);
|
||||
linefile = fopen (filename, "w");
|
||||
if (!linefile)
|
||||
Error ("Couldn't open %s\n", filename);
|
||||
|
||||
for ( int i = 0; i < g_PortalTrace.m_list.Count(); i++ )
|
||||
{
|
||||
Vector p = g_PortalTrace.m_list[i];
|
||||
fprintf (linefile, "%f %f %f\n", p[0], p[1], p[2]);
|
||||
}
|
||||
fclose (linefile);
|
||||
Warning("Wrote %s!!!\n", filename);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RecursiveLeafFlow
|
||||
|
||||
Flood fill through the leafs
|
||||
If src_portal is NULL, this is the originating leaf
|
||||
==================
|
||||
*/
|
||||
void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
|
||||
{
|
||||
pstack_t stack;
|
||||
portal_t *p;
|
||||
plane_t backplane;
|
||||
leaf_t *leaf;
|
||||
int i, j;
|
||||
long *test, *might, *vis, more;
|
||||
int pnum;
|
||||
|
||||
// Early-out if we're a VMPI worker that's told to exit. If we don't do this here, then the
|
||||
// worker might spin its wheels for a while on an expensive work unit and not be available to the pool.
|
||||
// This is pretty common in vis.
|
||||
if ( g_bVMPIEarlyExit )
|
||||
return;
|
||||
|
||||
if ( leafnum == g_TraceClusterStop )
|
||||
{
|
||||
DumpPortalTrace(&thread->pstack_head);
|
||||
return;
|
||||
}
|
||||
thread->c_chains++;
|
||||
|
||||
leaf = &leafs[leafnum];
|
||||
|
||||
prevstack->next = &stack;
|
||||
|
||||
stack.next = NULL;
|
||||
stack.leaf = leaf;
|
||||
stack.portal = NULL;
|
||||
|
||||
might = (long *)stack.mightsee;
|
||||
vis = (long *)thread->base->portalvis;
|
||||
|
||||
// check all portals for flowing into other leafs
|
||||
for (i=0 ; i<leaf->portals.Count() ; i++)
|
||||
{
|
||||
|
||||
p = leaf->portals[i];
|
||||
pnum = p - portals;
|
||||
|
||||
if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
|
||||
{
|
||||
continue; // can't possibly see it
|
||||
}
|
||||
|
||||
// if the portal can't see anything we haven't allready seen, skip it
|
||||
if (p->status == stat_done)
|
||||
{
|
||||
test = (long *)p->portalvis;
|
||||
}
|
||||
else
|
||||
{
|
||||
test = (long *)p->portalflood;
|
||||
}
|
||||
|
||||
more = 0;
|
||||
for (j=0 ; j<portallongs ; j++)
|
||||
{
|
||||
might[j] = ((long *)prevstack->mightsee)[j] & test[j];
|
||||
more |= (might[j] & ~vis[j]);
|
||||
}
|
||||
|
||||
if ( !more && CheckBit( thread->base->portalvis, pnum ) )
|
||||
{ // can't see anything new
|
||||
continue;
|
||||
}
|
||||
|
||||
// get plane of portal, point normal into the neighbor leaf
|
||||
stack.portalplane = p->plane;
|
||||
VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
|
||||
backplane.dist = -p->plane.dist;
|
||||
|
||||
stack.portal = p;
|
||||
stack.next = NULL;
|
||||
stack.freewindings[0] = 1;
|
||||
stack.freewindings[1] = 1;
|
||||
stack.freewindings[2] = 1;
|
||||
|
||||
float d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
|
||||
d -= thread->pstack_head.portalplane.dist;
|
||||
if (d < -p->radius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (d > p->radius)
|
||||
{
|
||||
stack.pass = p->winding;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
|
||||
if (!stack.pass)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
d = DotProduct (thread->base->origin, p->plane.normal);
|
||||
d -= p->plane.dist;
|
||||
if (d > thread->base->radius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (d < -thread->base->radius)
|
||||
{
|
||||
stack.source = prevstack->source;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.source = ChopWinding (prevstack->source, &stack, &backplane);
|
||||
if (!stack.source)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!prevstack->pass)
|
||||
{ // the second leaf can only be blocked if coplanar
|
||||
|
||||
// mark the portal as visible
|
||||
SetBit( thread->base->portalvis, pnum );
|
||||
|
||||
RecursiveLeafFlow (p->leaf, thread, &stack);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// mark the portal as visible
|
||||
SetBit( thread->base->portalvis, pnum );
|
||||
|
||||
// flow through it for real
|
||||
RecursiveLeafFlow (p->leaf, thread, &stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
PortalFlow
|
||||
|
||||
generates the portalvis bit vector
|
||||
===============
|
||||
*/
|
||||
void PortalFlow (int iThread, int portalnum)
|
||||
{
|
||||
threaddata_t data;
|
||||
int i;
|
||||
portal_t *p;
|
||||
int c_might, c_can;
|
||||
|
||||
p = sorted_portals[portalnum];
|
||||
p->status = stat_working;
|
||||
|
||||
c_might = CountBits (p->portalflood, g_numportals*2);
|
||||
|
||||
memset (&data, 0, sizeof(data));
|
||||
data.base = p;
|
||||
|
||||
data.pstack_head.portal = p;
|
||||
data.pstack_head.source = p->winding;
|
||||
data.pstack_head.portalplane = p->plane;
|
||||
for (i=0 ; i<portallongs ; i++)
|
||||
((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
|
||||
|
||||
RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
|
||||
|
||||
|
||||
p->status = stat_done;
|
||||
|
||||
c_can = CountBits (p->portalvis, g_numportals*2);
|
||||
|
||||
qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
|
||||
(int)(p - portals), c_might, c_can, data.c_chains);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
This is a rough first-order aproximation that is used to trivially reject some
|
||||
of the final calculations.
|
||||
|
||||
|
||||
Calculates portalfront and portalflood bit vectors
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
int c_flood, c_vis;
|
||||
|
||||
/*
|
||||
==================
|
||||
SimpleFlood
|
||||
|
||||
==================
|
||||
*/
|
||||
void SimpleFlood (portal_t *srcportal, int leafnum)
|
||||
{
|
||||
int i;
|
||||
leaf_t *leaf;
|
||||
portal_t *p;
|
||||
int pnum;
|
||||
|
||||
leaf = &leafs[leafnum];
|
||||
|
||||
for (i=0 ; i<leaf->portals.Count(); i++)
|
||||
{
|
||||
p = leaf->portals[i];
|
||||
pnum = p - portals;
|
||||
if ( !CheckBit( srcportal->portalfront, pnum ) )
|
||||
continue;
|
||||
|
||||
if ( CheckBit( srcportal->portalflood, pnum ) )
|
||||
continue;
|
||||
|
||||
SetBit( srcportal->portalflood, pnum );
|
||||
|
||||
SimpleFlood (srcportal, p->leaf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
BasePortalVis
|
||||
==============
|
||||
*/
|
||||
void BasePortalVis (int iThread, int portalnum)
|
||||
{
|
||||
int j, k;
|
||||
portal_t *tp, *p;
|
||||
float d;
|
||||
winding_t *w;
|
||||
Vector segment;
|
||||
double dist2, minDist2;
|
||||
|
||||
// get the portal
|
||||
p = portals+portalnum;
|
||||
|
||||
//
|
||||
// allocate memory for bitwise vis solutions for this portal
|
||||
//
|
||||
p->portalfront = (byte*)malloc (portalbytes);
|
||||
memset (p->portalfront, 0, portalbytes);
|
||||
|
||||
p->portalflood = (byte*)malloc (portalbytes);
|
||||
memset (p->portalflood, 0, portalbytes);
|
||||
|
||||
p->portalvis = (byte*)malloc (portalbytes);
|
||||
memset (p->portalvis, 0, portalbytes);
|
||||
|
||||
//
|
||||
// test the given portal against all of the portals in the map
|
||||
//
|
||||
for (j=0, tp = portals ; j<g_numportals*2 ; j++, tp++)
|
||||
{
|
||||
// don't test against itself
|
||||
if (j == portalnum)
|
||||
continue;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
w = tp->winding;
|
||||
for (k=0 ; k<w->numpoints ; k++)
|
||||
{
|
||||
d = DotProduct (w->points[k], p->plane.normal) - p->plane.dist;
|
||||
if (d > ON_VIS_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_VIS_EPSILON)
|
||||
break;
|
||||
}
|
||||
if (k == w->numpoints)
|
||||
continue; // no points on front
|
||||
|
||||
//
|
||||
// if using radius visibility -- check to see if any portal points lie inside of the
|
||||
// radius given
|
||||
//
|
||||
if( g_bUseRadius )
|
||||
{
|
||||
w = tp->winding;
|
||||
minDist2 = 1024000000.0; // 32000^2
|
||||
for( k = 0; k < w->numpoints; k++ )
|
||||
{
|
||||
VectorSubtract( w->points[k], p->origin, segment );
|
||||
dist2 = ( segment[0] * segment[0] ) + ( segment[1] * segment[1] ) + ( segment[2] * segment[2] );
|
||||
if( dist2 < minDist2 )
|
||||
{
|
||||
minDist2 = dist2;
|
||||
}
|
||||
}
|
||||
|
||||
if( minDist2 > g_VisRadius )
|
||||
continue;
|
||||
}
|
||||
|
||||
// add current portal to given portal's list of visible portals
|
||||
SetBit( p->portalfront, j );
|
||||
}
|
||||
|
||||
SimpleFlood (p, p->leaf);
|
||||
|
||||
p->nummightsee = CountBits (p->portalflood, g_numportals*2);
|
||||
// Msg ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
|
||||
c_flood += p->nummightsee;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
This is a second order aproximation
|
||||
|
||||
Calculates portalvis bit vector
|
||||
|
||||
WAAAAAAY too slow.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
RecursiveLeafBitFlow
|
||||
|
||||
==================
|
||||
*/
|
||||
void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
|
||||
{
|
||||
portal_t *p;
|
||||
leaf_t *leaf;
|
||||
int i, j;
|
||||
long more;
|
||||
int pnum;
|
||||
byte newmight[MAX_PORTALS/8];
|
||||
|
||||
leaf = &leafs[leafnum];
|
||||
|
||||
// check all portals for flowing into other leafs
|
||||
for (i=0 ; i<leaf->portals.Count(); i++)
|
||||
{
|
||||
p = leaf->portals[i];
|
||||
pnum = p - portals;
|
||||
|
||||
// if some previous portal can't see it, skip
|
||||
if ( !CheckBit( mightsee, pnum ) )
|
||||
continue;
|
||||
|
||||
// if this portal can see some portals we mightsee, recurse
|
||||
more = 0;
|
||||
for (j=0 ; j<portallongs ; j++)
|
||||
{
|
||||
((long *)newmight)[j] = ((long *)mightsee)[j]
|
||||
& ((long *)p->portalflood)[j];
|
||||
more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
|
||||
}
|
||||
|
||||
if (!more)
|
||||
continue; // can't see anything new
|
||||
|
||||
SetBit( cansee, pnum );
|
||||
|
||||
RecursiveLeafBitFlow (p->leaf, newmight, cansee);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
BetterPortalVis
|
||||
==============
|
||||
*/
|
||||
void BetterPortalVis (int portalnum)
|
||||
{
|
||||
portal_t *p;
|
||||
|
||||
p = portals+portalnum;
|
||||
|
||||
RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
|
||||
|
||||
// build leaf vis information
|
||||
p->nummightsee = CountBits (p->portalvis, g_numportals*2);
|
||||
c_vis += p->nummightsee;
|
||||
}
|
||||
|
||||
|
640
utils/vvis/mpivis.cpp
Normal file
640
utils/vvis/mpivis.cpp
Normal file
@ -0,0 +1,640 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "vis.h"
|
||||
#include "threads.h"
|
||||
#include "stdlib.h"
|
||||
#include "pacifier.h"
|
||||
#include "mpi_stats.h"
|
||||
#include "vmpi.h"
|
||||
#include "vmpi_dispatch.h"
|
||||
#include "vmpi_filesystem.h"
|
||||
#include "vmpi_distribute_work.h"
|
||||
#include "iphelpers.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "vmpi_tools_shared.h"
|
||||
#include <conio.h>
|
||||
#include "scratchpad_helpers.h"
|
||||
|
||||
|
||||
#define VMPI_VVIS_PACKET_ID 1
|
||||
// Sub packet IDs.
|
||||
#define VMPI_SUBPACKETID_DISCONNECT_NOTIFY 3 // We send ourselves this when there is a disconnect.
|
||||
#define VMPI_SUBPACKETID_BASEPORTALVIS 5
|
||||
#define VMPI_SUBPACKETID_PORTALFLOW 6
|
||||
#define VMPI_BASEPORTALVIS_RESULTS 7
|
||||
#define VMPI_BASEPORTALVIS_WORKER_DONE 8
|
||||
#define VMPI_PORTALFLOW_RESULTS 9
|
||||
#define VMPI_SUBPACKETID_BASEPORTALVIS_SYNC 11
|
||||
#define VMPI_SUBPACKETID_PORTALFLOW_SYNC 12
|
||||
#define VMPI_SUBPACKETID_MC_ADDR 13
|
||||
|
||||
// DistributeWork owns this packet ID.
|
||||
#define VMPI_DISTRIBUTEWORK_PACKETID 2
|
||||
|
||||
|
||||
extern bool fastvis;
|
||||
|
||||
// The worker waits until these are true.
|
||||
bool g_bBasePortalVisSync = false;
|
||||
bool g_bPortalFlowSync = false;
|
||||
|
||||
CUtlVector<char> g_BasePortalVisResultsFilename;
|
||||
|
||||
CCycleCount g_CPUTime;
|
||||
|
||||
|
||||
// This stuff is all for the multicast channel the master uses to send out the portal results.
|
||||
ISocket *g_pPortalMCSocket = NULL;
|
||||
CIPAddr g_PortalMCAddr;
|
||||
bool g_bGotMCAddr = false;
|
||||
HANDLE g_hMCThread = NULL;
|
||||
CEvent g_MCThreadExitEvent;
|
||||
unsigned long g_PortalMCThreadUniqueID = 0;
|
||||
int g_nMulticastPortalsReceived = 0;
|
||||
|
||||
|
||||
// Handle VVIS packets.
|
||||
bool VVIS_DispatchFn( MessageBuffer *pBuf, int iSource, int iPacketID )
|
||||
{
|
||||
switch ( pBuf->data[1] )
|
||||
{
|
||||
case VMPI_SUBPACKETID_MC_ADDR:
|
||||
{
|
||||
pBuf->setOffset( 2 );
|
||||
pBuf->read( &g_PortalMCAddr, sizeof( g_PortalMCAddr ) );
|
||||
g_bGotMCAddr = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case VMPI_SUBPACKETID_DISCONNECT_NOTIFY:
|
||||
{
|
||||
// This is just used to cause nonblocking dispatches to jump out so loops like the one
|
||||
// in AppBarrier can handle the fact that there are disconnects.
|
||||
return true;
|
||||
}
|
||||
|
||||
case VMPI_SUBPACKETID_BASEPORTALVIS_SYNC:
|
||||
{
|
||||
g_bBasePortalVisSync = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case VMPI_SUBPACKETID_PORTALFLOW_SYNC:
|
||||
{
|
||||
g_bPortalFlowSync = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case VMPI_BASEPORTALVIS_RESULTS:
|
||||
{
|
||||
const char *pFilename = &pBuf->data[2];
|
||||
g_BasePortalVisResultsFilename.CopyArray( pFilename, strlen( pFilename ) + 1 );
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
CDispatchReg g_VVISDispatchReg( VMPI_VVIS_PACKET_ID, VVIS_DispatchFn ); // register to handle the messages we want
|
||||
CDispatchReg g_DistributeWorkReg( VMPI_DISTRIBUTEWORK_PACKETID, DistributeWorkDispatch );
|
||||
|
||||
|
||||
|
||||
void VMPI_DeletePortalMCSocket()
|
||||
{
|
||||
// Stop the thread if it exists.
|
||||
if ( g_hMCThread )
|
||||
{
|
||||
g_MCThreadExitEvent.SetEvent();
|
||||
WaitForSingleObject( g_hMCThread, INFINITE );
|
||||
CloseHandle( g_hMCThread );
|
||||
g_hMCThread = NULL;
|
||||
}
|
||||
|
||||
if ( g_pPortalMCSocket )
|
||||
{
|
||||
g_pPortalMCSocket->Release();
|
||||
g_pPortalMCSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VVIS_SetupMPI( int &argc, char **&argv )
|
||||
{
|
||||
if ( !VMPI_FindArg( argc, argv, "-mpi", "" ) && !VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Worker ), "" ) )
|
||||
return;
|
||||
|
||||
CmdLib_AtCleanup( VMPI_Stats_Term );
|
||||
CmdLib_AtCleanup( VMPI_DeletePortalMCSocket );
|
||||
|
||||
VMPI_Stats_InstallSpewHook();
|
||||
|
||||
// Force local mode?
|
||||
VMPIRunMode mode;
|
||||
if ( VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Local ), "" ) )
|
||||
mode = VMPI_RUN_LOCAL;
|
||||
else
|
||||
mode = VMPI_RUN_NETWORKED;
|
||||
|
||||
//
|
||||
// Extract mpi specific arguments
|
||||
//
|
||||
Msg( "Initializing VMPI...\n" );
|
||||
if ( !VMPI_Init( argc, argv, "dependency_info_vvis.txt", HandleMPIDisconnect, mode ) )
|
||||
{
|
||||
Error( "MPI_Init failed." );
|
||||
}
|
||||
|
||||
StatsDB_InitStatsDatabase( argc, argv, "dbinfo_vvis.txt" );
|
||||
}
|
||||
|
||||
|
||||
void ProcessBasePortalVis( int iThread, uint64 iPortal, MessageBuffer *pBuf )
|
||||
{
|
||||
CTimeAdder adder( &g_CPUTime );
|
||||
|
||||
BasePortalVis( iThread, iPortal );
|
||||
|
||||
// Send my result to the master
|
||||
if ( pBuf )
|
||||
{
|
||||
portal_t * p = &portals[iPortal];
|
||||
pBuf->write( p->portalfront, portalbytes );
|
||||
pBuf->write( p->portalflood, portalbytes );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ReceiveBasePortalVis( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
|
||||
{
|
||||
portal_t * p = &portals[iWorkUnit];
|
||||
if ( p->portalflood != 0 || p->portalfront != 0 || p->portalvis != 0)
|
||||
{
|
||||
Msg("Duplicate portal %llu\n", iWorkUnit);
|
||||
}
|
||||
|
||||
if ( pBuf->getLen() - pBuf->getOffset() != portalbytes*2 )
|
||||
Error( "Invalid packet in ReceiveBasePortalVis." );
|
||||
|
||||
//
|
||||
// allocate memory for bitwise vis solutions for this portal
|
||||
//
|
||||
p->portalfront = (byte*)malloc (portalbytes);
|
||||
pBuf->read( p->portalfront, portalbytes );
|
||||
|
||||
p->portalflood = (byte*)malloc (portalbytes);
|
||||
pBuf->read( p->portalflood, portalbytes );
|
||||
|
||||
p->portalvis = (byte*)malloc (portalbytes);
|
||||
memset (p->portalvis, 0, portalbytes);
|
||||
|
||||
p->nummightsee = CountBits( p->portalflood, g_numportals*2 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------
|
||||
//
|
||||
// Run BasePortalVis across all available processing nodes
|
||||
// Then collect and redistribute the results.
|
||||
//
|
||||
void RunMPIBasePortalVis()
|
||||
{
|
||||
int i;
|
||||
|
||||
Msg( "\n\nportalbytes: %d\nNum Work Units: %d\nTotal data size: %d\n", portalbytes, g_numportals*2, portalbytes*g_numportals*2 );
|
||||
Msg("%-20s ", "BasePortalVis:");
|
||||
if ( g_bMPIMaster )
|
||||
StartPacifier("");
|
||||
|
||||
|
||||
VMPI_SetCurrentStage( "RunMPIBasePortalVis" );
|
||||
|
||||
// Note: we're aiming for about 1500 portals in a map, so about 3000 work units.
|
||||
g_CPUTime.Init();
|
||||
double elapsed = DistributeWork(
|
||||
g_numportals * 2, // # work units
|
||||
VMPI_DISTRIBUTEWORK_PACKETID, // packet ID
|
||||
ProcessBasePortalVis, // Worker function to process work units
|
||||
ReceiveBasePortalVis // Master function to receive work results
|
||||
);
|
||||
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
EndPacifier( false );
|
||||
Msg( " (%d)\n", (int)elapsed );
|
||||
}
|
||||
|
||||
//
|
||||
// Distribute the results to all the workers.
|
||||
//
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
if ( !fastvis )
|
||||
{
|
||||
VMPI_SetCurrentStage( "SendPortalResults" );
|
||||
|
||||
// Store all the portal results in a temp file and multicast that to the workers.
|
||||
CUtlVector<char> allPortalData;
|
||||
allPortalData.SetSize( g_numportals * 2 * portalbytes * 2 );
|
||||
|
||||
char *pOut = allPortalData.Base();
|
||||
for ( i=0; i < g_numportals * 2; i++)
|
||||
{
|
||||
portal_t *p = &portals[i];
|
||||
|
||||
memcpy( pOut, p->portalfront, portalbytes );
|
||||
pOut += portalbytes;
|
||||
|
||||
memcpy( pOut, p->portalflood, portalbytes );
|
||||
pOut += portalbytes;
|
||||
}
|
||||
|
||||
const char *pVirtualFilename = "--portal-results--";
|
||||
VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, allPortalData.Base(), allPortalData.Count() );
|
||||
|
||||
char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_BASEPORTALVIS_RESULTS };
|
||||
VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VMPI_SetCurrentStage( "RecvPortalResults" );
|
||||
|
||||
// Wait until we've received the filename from the master.
|
||||
while ( g_BasePortalVisResultsFilename.Count() == 0 )
|
||||
{
|
||||
VMPI_DispatchNextMessage();
|
||||
}
|
||||
|
||||
// Open
|
||||
FileHandle_t fp = g_pFileSystem->Open( g_BasePortalVisResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID );
|
||||
if ( !fp )
|
||||
Error( "Can't open '%s' to read portal info.", g_BasePortalVisResultsFilename.Base() );
|
||||
|
||||
for ( i=0; i < g_numportals * 2; i++)
|
||||
{
|
||||
portal_t *p = &portals[i];
|
||||
|
||||
p->portalfront = (byte*)malloc (portalbytes);
|
||||
g_pFileSystem->Read( p->portalfront, portalbytes, fp );
|
||||
|
||||
p->portalflood = (byte*)malloc (portalbytes);
|
||||
g_pFileSystem->Read( p->portalflood, portalbytes, fp );
|
||||
|
||||
p->portalvis = (byte*)malloc (portalbytes);
|
||||
memset (p->portalvis, 0, portalbytes);
|
||||
|
||||
p->nummightsee = CountBits (p->portalflood, g_numportals*2);
|
||||
}
|
||||
|
||||
g_pFileSystem->Close( fp );
|
||||
}
|
||||
|
||||
|
||||
if ( !g_bMPIMaster )
|
||||
{
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "\n%% worker CPU utilization during BasePortalVis: %.1f\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ProcessPortalFlow( int iThread, uint64 iPortal, MessageBuffer *pBuf )
|
||||
{
|
||||
// Process Portal and distribute results
|
||||
CTimeAdder adder( &g_CPUTime );
|
||||
|
||||
PortalFlow( iThread, iPortal );
|
||||
|
||||
// Send my result to root and potentially the other slaves
|
||||
// The slave results are read in RecursiveLeafFlow
|
||||
//
|
||||
if ( pBuf )
|
||||
{
|
||||
portal_t * p = sorted_portals[iPortal];
|
||||
pBuf->write( p->portalvis, portalbytes );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ReceivePortalFlow( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
|
||||
{
|
||||
portal_t *p = sorted_portals[iWorkUnit];
|
||||
|
||||
if ( p->status != stat_done )
|
||||
{
|
||||
pBuf->read( p->portalvis, portalbytes );
|
||||
p->status = stat_done;
|
||||
|
||||
|
||||
// Multicast the status of this portal out.
|
||||
if ( g_pPortalMCSocket )
|
||||
{
|
||||
char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_PORTALFLOW_RESULTS };
|
||||
void *chunks[4] = { cPacketID, &g_PortalMCThreadUniqueID, &iWorkUnit, p->portalvis };
|
||||
int chunkLengths[4] = { sizeof( cPacketID ), sizeof( g_PortalMCThreadUniqueID ), sizeof( iWorkUnit ), portalbytes };
|
||||
|
||||
g_pPortalMCSocket->SendChunksTo( &g_PortalMCAddr, chunks, chunkLengths, ARRAYSIZE( chunks ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI PortalMCThreadFn( LPVOID p )
|
||||
{
|
||||
CUtlVector<char> data;
|
||||
data.SetSize( portalbytes + 128 );
|
||||
|
||||
DWORD waitTime = 0;
|
||||
while ( WaitForSingleObject( g_MCThreadExitEvent.GetEventHandle(), waitTime ) != WAIT_OBJECT_0 )
|
||||
{
|
||||
CIPAddr ipFrom;
|
||||
int len = g_pPortalMCSocket->RecvFrom( data.Base(), data.Count(), &ipFrom );
|
||||
if ( len == -1 )
|
||||
{
|
||||
waitTime = 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
// These lengths must match exactly what is sent in ReceivePortalFlow.
|
||||
if ( len == 2 + sizeof( g_PortalMCThreadUniqueID ) + sizeof( int ) + portalbytes )
|
||||
{
|
||||
// Perform more validation...
|
||||
if ( data[0] == VMPI_VVIS_PACKET_ID && data[1] == VMPI_PORTALFLOW_RESULTS )
|
||||
{
|
||||
if ( *((unsigned long*)&data[2]) == g_PortalMCThreadUniqueID )
|
||||
{
|
||||
int iWorkUnit = *((int*)&data[6]);
|
||||
if ( iWorkUnit >= 0 && iWorkUnit < g_numportals*2 )
|
||||
{
|
||||
portal_t *p = sorted_portals[iWorkUnit];
|
||||
if ( p )
|
||||
{
|
||||
++g_nMulticastPortalsReceived;
|
||||
memcpy( p->portalvis, &data[10], portalbytes );
|
||||
p->status = stat_done;
|
||||
waitTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void MCThreadCleanupFn()
|
||||
{
|
||||
g_MCThreadExitEvent.SetEvent();
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------- //
|
||||
// Cheesy hack to let them stop the job early and keep the results of what has
|
||||
// been done so far.
|
||||
// --------------------------------------------------------------------------------- //
|
||||
|
||||
class CVisDistributeWorkCallbacks : public IWorkUnitDistributorCallbacks
|
||||
{
|
||||
public:
|
||||
CVisDistributeWorkCallbacks()
|
||||
{
|
||||
m_bExitedEarly = false;
|
||||
m_iState = STATE_NONE;
|
||||
}
|
||||
|
||||
virtual bool Update()
|
||||
{
|
||||
if ( kbhit() )
|
||||
{
|
||||
int key = toupper( getch() );
|
||||
if ( m_iState == STATE_NONE )
|
||||
{
|
||||
if ( key == 'M' )
|
||||
{
|
||||
m_iState = STATE_AT_MENU;
|
||||
Warning("\n\n"
|
||||
"----------------------\n"
|
||||
"1. Write scratchpad file.\n"
|
||||
"2. Exit early and use fast vis for remaining portals.\n"
|
||||
"\n"
|
||||
"0. Exit menu.\n"
|
||||
"----------------------\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ( m_iState == STATE_AT_MENU )
|
||||
{
|
||||
if ( key == '1' )
|
||||
{
|
||||
Warning(
|
||||
"\n"
|
||||
"\nWriting scratchpad file."
|
||||
"\nCommand line: scratchpad3dviewer -file scratch.pad\n"
|
||||
"\nRed portals are the portals that are fast vis'd."
|
||||
"\n"
|
||||
);
|
||||
m_iState = STATE_NONE;
|
||||
IScratchPad3D *pPad = ScratchPad3D_Create( "scratch.pad" );
|
||||
if ( pPad )
|
||||
{
|
||||
ScratchPad_DrawWorld( pPad, false );
|
||||
|
||||
// Draw the portals that haven't been vis'd.
|
||||
for ( int i=0; i < g_numportals*2; i++ )
|
||||
{
|
||||
portal_t *p = sorted_portals[i];
|
||||
ScratchPad_DrawWinding( pPad, p->winding->numpoints, p->winding->points, Vector( 1, 0, 0 ), Vector( .3, .3, .3 ) );
|
||||
}
|
||||
|
||||
pPad->Release();
|
||||
}
|
||||
}
|
||||
else if ( key == '2' )
|
||||
{
|
||||
// Exit the process early.
|
||||
m_bExitedEarly = true;
|
||||
return true;
|
||||
}
|
||||
else if ( key == '0' )
|
||||
{
|
||||
m_iState = STATE_NONE;
|
||||
Warning( "\n\nExited menu.\n\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
STATE_NONE,
|
||||
STATE_AT_MENU
|
||||
};
|
||||
|
||||
bool m_bExitedEarly;
|
||||
int m_iState; // STATE_ enum.
|
||||
};
|
||||
|
||||
|
||||
CVisDistributeWorkCallbacks g_VisDistributeWorkCallbacks;
|
||||
|
||||
|
||||
void CheckExitedEarly()
|
||||
{
|
||||
if ( g_VisDistributeWorkCallbacks.m_bExitedEarly )
|
||||
{
|
||||
Warning( "\nExited early, using fastvis results...\n" );
|
||||
Warning( "Exited early, using fastvis results...\n" );
|
||||
|
||||
// Use the fastvis results for portals that we didn't get results for.
|
||||
for ( int i=0; i < g_numportals*2; i++ )
|
||||
{
|
||||
if ( sorted_portals[i]->status != stat_done )
|
||||
{
|
||||
sorted_portals[i]->portalvis = sorted_portals[i]->portalflood;
|
||||
sorted_portals[i]->status = stat_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------
|
||||
//
|
||||
// Run PortalFlow across all available processing nodes
|
||||
//
|
||||
void RunMPIPortalFlow()
|
||||
{
|
||||
Msg( "%-20s ", "MPIPortalFlow:" );
|
||||
if ( g_bMPIMaster )
|
||||
StartPacifier("");
|
||||
|
||||
// Workers wait until we get the MC socket address.
|
||||
g_PortalMCThreadUniqueID = StatsDB_GetUniqueJobID();
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
CCycleCount cnt;
|
||||
cnt.Sample();
|
||||
CUniformRandomStream randomStream;
|
||||
randomStream.SetSeed( cnt.GetMicroseconds() );
|
||||
|
||||
g_PortalMCAddr.port = randomStream.RandomInt( 22000, 25000 ); // Pulled out of something else.
|
||||
g_PortalMCAddr.ip[0] = (unsigned char)RandomInt( 225, 238 );
|
||||
g_PortalMCAddr.ip[1] = (unsigned char)RandomInt( 0, 255 );
|
||||
g_PortalMCAddr.ip[2] = (unsigned char)RandomInt( 0, 255 );
|
||||
g_PortalMCAddr.ip[3] = (unsigned char)RandomInt( 3, 255 );
|
||||
|
||||
g_pPortalMCSocket = CreateIPSocket();
|
||||
int i=0;
|
||||
for ( i; i < 5; i++ )
|
||||
{
|
||||
if ( g_pPortalMCSocket->BindToAny( randomStream.RandomInt( 20000, 30000 ) ) )
|
||||
break;
|
||||
}
|
||||
if ( i == 5 )
|
||||
{
|
||||
Error( "RunMPIPortalFlow: can't open a socket to multicast on." );
|
||||
}
|
||||
|
||||
char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_SUBPACKETID_MC_ADDR };
|
||||
VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), &g_PortalMCAddr, sizeof( g_PortalMCAddr ), VMPI_PERSISTENT );
|
||||
}
|
||||
else
|
||||
{
|
||||
VMPI_SetCurrentStage( "wait for MC address" );
|
||||
|
||||
while ( !g_bGotMCAddr )
|
||||
{
|
||||
VMPI_DispatchNextMessage();
|
||||
}
|
||||
|
||||
// Open our multicast receive socket.
|
||||
g_pPortalMCSocket = CreateMulticastListenSocket( g_PortalMCAddr );
|
||||
if ( !g_pPortalMCSocket )
|
||||
{
|
||||
char err[512];
|
||||
IP_GetLastErrorString( err, sizeof( err ) );
|
||||
Error( "RunMPIPortalFlow: CreateMulticastListenSocket failed. (%s).", err );
|
||||
}
|
||||
|
||||
// Make a thread to listen for the data on the multicast socket.
|
||||
DWORD dwDummy = 0;
|
||||
g_MCThreadExitEvent.Init( false, false );
|
||||
|
||||
// Make sure we kill the MC thread if the app exits ungracefully.
|
||||
CmdLib_AtCleanup( MCThreadCleanupFn );
|
||||
|
||||
g_hMCThread = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
PortalMCThreadFn,
|
||||
NULL,
|
||||
0,
|
||||
&dwDummy );
|
||||
|
||||
if ( !g_hMCThread )
|
||||
{
|
||||
Error( "RunMPIPortalFlow: CreateThread failed for multicast receive thread." );
|
||||
}
|
||||
}
|
||||
|
||||
VMPI_SetCurrentStage( "RunMPIBasePortalFlow" );
|
||||
|
||||
|
||||
g_pDistributeWorkCallbacks = &g_VisDistributeWorkCallbacks;
|
||||
|
||||
g_CPUTime.Init();
|
||||
double elapsed = DistributeWork(
|
||||
g_numportals * 2, // # work units
|
||||
VMPI_DISTRIBUTEWORK_PACKETID, // packet ID
|
||||
ProcessPortalFlow, // Worker function to process work units
|
||||
ReceivePortalFlow // Master function to receive work results
|
||||
);
|
||||
|
||||
g_pDistributeWorkCallbacks = NULL;
|
||||
|
||||
CheckExitedEarly();
|
||||
|
||||
// Stop the multicast stuff.
|
||||
VMPI_DeletePortalMCSocket();
|
||||
|
||||
if( !g_bMPIMaster )
|
||||
{
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
{
|
||||
Msg( "Received %d (out of %d) portals from multicast.\n", g_nMulticastPortalsReceived, g_numportals * 2 );
|
||||
Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
|
||||
}
|
||||
|
||||
Msg( "VVIS worker finished. Over and out.\n" );
|
||||
VMPI_SetCurrentStage( "worker done" );
|
||||
|
||||
CmdLib_Exit( 0 );
|
||||
}
|
||||
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
EndPacifier( false );
|
||||
Msg( " (%d)\n", (int)elapsed );
|
||||
}
|
||||
}
|
||||
|
21
utils/vvis/mpivis.h
Normal file
21
utils/vvis/mpivis.h
Normal file
@ -0,0 +1,21 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MPIVIS_H
|
||||
#define MPIVIS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
void VVIS_SetupMPI( int &argc, char **&argv );
|
||||
|
||||
|
||||
void RunMPIBasePortalVis();
|
||||
void RunMPIPortalFlow();
|
||||
|
||||
|
||||
#endif // MPIVIS_H
|
125
utils/vvis/vis.h
Normal file
125
utils/vvis/vis.h
Normal file
@ -0,0 +1,125 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// vis.h
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "bsplib.h"
|
||||
|
||||
|
||||
#define MAX_PORTALS 65536
|
||||
|
||||
#define PORTALFILE "PRT1"
|
||||
|
||||
extern bool g_bUseRadius; // prototyping TF2, "radius vis" solution
|
||||
extern double g_VisRadius; // the radius for the TF2 "radius vis"
|
||||
|
||||
struct plane_t
|
||||
{
|
||||
Vector normal;
|
||||
float dist;
|
||||
};
|
||||
|
||||
#define MAX_POINTS_ON_WINDING 64
|
||||
#define MAX_POINTS_ON_FIXED_WINDING 12
|
||||
|
||||
struct winding_t
|
||||
{
|
||||
qboolean original; // don't free, it's part of the portal
|
||||
int numpoints;
|
||||
Vector points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized
|
||||
};
|
||||
|
||||
winding_t *NewWinding (int points);
|
||||
void FreeWinding (winding_t *w);
|
||||
winding_t *CopyWinding (winding_t *w);
|
||||
|
||||
|
||||
typedef enum {stat_none, stat_working, stat_done} vstatus_t;
|
||||
struct portal_t
|
||||
{
|
||||
plane_t plane; // normal pointing into neighbor
|
||||
int leaf; // neighbor
|
||||
|
||||
Vector origin; // for fast clip testing
|
||||
float radius;
|
||||
|
||||
winding_t *winding;
|
||||
vstatus_t status;
|
||||
byte *portalfront; // [portals], preliminary
|
||||
byte *portalflood; // [portals], intermediate
|
||||
byte *portalvis; // [portals], final
|
||||
|
||||
int nummightsee; // bit count on portalflood for sort
|
||||
};
|
||||
|
||||
struct leaf_t
|
||||
{
|
||||
CUtlVector<portal_t *> portals;
|
||||
};
|
||||
|
||||
|
||||
struct pstack_t
|
||||
{
|
||||
byte mightsee[MAX_PORTALS/8]; // bit string
|
||||
pstack_t *next;
|
||||
leaf_t *leaf;
|
||||
portal_t *portal; // portal exiting
|
||||
winding_t *source;
|
||||
winding_t *pass;
|
||||
|
||||
winding_t windings[3]; // source, pass, temp in any order
|
||||
int freewindings[3];
|
||||
|
||||
plane_t portalplane;
|
||||
};
|
||||
|
||||
struct threaddata_t
|
||||
{
|
||||
portal_t *base;
|
||||
int c_chains;
|
||||
pstack_t pstack_head;
|
||||
};
|
||||
|
||||
extern int g_numportals;
|
||||
extern int portalclusters;
|
||||
|
||||
extern portal_t *portals;
|
||||
extern leaf_t *leafs;
|
||||
|
||||
extern int c_portaltest, c_portalpass, c_portalcheck;
|
||||
extern int c_portalskip, c_leafskip;
|
||||
extern int c_vistest, c_mighttest;
|
||||
extern int c_chains;
|
||||
|
||||
extern byte *vismap, *vismap_p, *vismap_end; // past visfile
|
||||
|
||||
extern int testlevel;
|
||||
|
||||
extern byte *uncompressed;
|
||||
|
||||
extern int leafbytes, leaflongs;
|
||||
extern int portalbytes, portallongs;
|
||||
|
||||
|
||||
void LeafFlow (int leafnum);
|
||||
|
||||
|
||||
void BasePortalVis (int iThread, int portalnum);
|
||||
void BetterPortalVis (int portalnum);
|
||||
void PortalFlow (int iThread, int portalnum);
|
||||
void WritePortalTrace( const char *source );
|
||||
|
||||
extern portal_t *sorted_portals[MAX_MAP_PORTALS*2];
|
||||
extern int g_TraceClusterStart, g_TraceClusterStop;
|
||||
|
||||
int CountBits (byte *bits, int numbits);
|
||||
|
||||
#define CheckBit( bitstring, bitNumber ) ( (bitstring)[ ((bitNumber) >> 3) ] & ( 1 << ( (bitNumber) & 7 ) ) )
|
||||
#define SetBit( bitstring, bitNumber ) ( (bitstring)[ ((bitNumber) >> 3) ] |= ( 1 << ( (bitNumber) & 7 ) ) )
|
||||
#define ClearBit( bitstring, bitNumber ) ( (bitstring)[ ((bitNumber) >> 3) ] &= ~( 1 << ( (bitNumber) & 7 ) ) )
|
1247
utils/vvis/vvis.cpp
Normal file
1247
utils/vvis/vvis.cpp
Normal file
File diff suppressed because it is too large
Load Diff
102
utils/vvis/vvis_dll.vpc
Normal file
102
utils/vvis/vvis_dll.vpc
Normal file
@ -0,0 +1,102 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// VVIS_DLL.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\.."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,..\common,..\vmpi,..\vmpi\mysql\include"
|
||||
$PreprocessorDefinitions "$BASE;MPI;PROTECTED_THINGS_DISABLE"
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalDependencies "$BASE odbc32.lib odbccp32.lib ws2_32.lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Vvis_dll"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
-$File "$SRCDIR\public\tier0\memoverride.cpp"
|
||||
|
||||
$File "..\common\bsplib.cpp"
|
||||
$File "..\common\cmdlib.cpp"
|
||||
$File "$SRCDIR\public\collisionutils.cpp"
|
||||
$File "$SRCDIR\public\filesystem_helpers.cpp"
|
||||
$File "flow.cpp"
|
||||
$File "$SRCDIR\public\loadcmdline.cpp"
|
||||
$File "$SRCDIR\public\lumpfiles.cpp"
|
||||
$File "..\common\mpi_stats.cpp"
|
||||
$File "mpivis.cpp"
|
||||
$File "..\common\MySqlDatabase.cpp"
|
||||
$File "..\common\pacifier.cpp"
|
||||
$File "$SRCDIR\public\scratchpad3d.cpp"
|
||||
$File "..\common\scratchpad_helpers.cpp"
|
||||
$File "..\common\scriplib.cpp"
|
||||
$File "..\common\threads.cpp"
|
||||
$File "..\common\tools_minidump.cpp"
|
||||
$File "..\common\tools_minidump.h"
|
||||
$File "..\common\vmpi_tools_shared.cpp"
|
||||
$File "vvis.cpp"
|
||||
$File "WaterDist.cpp"
|
||||
$File "$SRCDIR\public\zip_utils.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "$SRCDIR\public\mathlib\amd3dx.h"
|
||||
$File "$SRCDIR\public\tier0\basetypes.h"
|
||||
$File "$SRCDIR\public\BSPFILE.H"
|
||||
$File "$SRCDIR\public\bspflags.h"
|
||||
$File "..\common\bsplib.h"
|
||||
$File "$SRCDIR\public\BSPTreeData.h"
|
||||
$File "$SRCDIR\public\mathlib\bumpvects.h"
|
||||
$File "$SRCDIR\public\tier1\byteswap.h"
|
||||
$File "$SRCDIR\public\tier1\checksum_crc.h"
|
||||
$File "$SRCDIR\public\tier1\checksum_md5.h"
|
||||
$File "..\common\cmdlib.h"
|
||||
$File "$SRCDIR\public\cmodel.h"
|
||||
$File "$SRCDIR\public\tier0\commonmacros.h"
|
||||
$File "$SRCDIR\public\GameBSPFile.h"
|
||||
$File "..\common\ISQLDBReplyTarget.h"
|
||||
$File "$SRCDIR\public\mathlib\mathlib.h"
|
||||
$File "mpivis.h"
|
||||
$File "..\common\MySqlDatabase.h"
|
||||
$File "..\common\pacifier.h"
|
||||
$File "..\common\scriplib.h"
|
||||
$File "$SRCDIR\public\tier1\strtools.h"
|
||||
$File "..\common\threads.h"
|
||||
$File "$SRCDIR\public\tier1\utlbuffer.h"
|
||||
$File "$SRCDIR\public\tier1\utllinkedlist.h"
|
||||
$File "$SRCDIR\public\tier1\utlmemory.h"
|
||||
$File "$SRCDIR\public\tier1\utlrbtree.h"
|
||||
$File "$SRCDIR\public\tier1\utlsymbol.h"
|
||||
$File "$SRCDIR\public\tier1\utlvector.h"
|
||||
$File "$SRCDIR\public\vcollide.h"
|
||||
$File "$SRCDIR\public\mathlib\vector.h"
|
||||
$File "$SRCDIR\public\mathlib\vector2d.h"
|
||||
$File "vis.h"
|
||||
$File "..\vmpi\vmpi_distribute_work.h"
|
||||
$File "..\common\vmpi_tools_shared.h"
|
||||
$File "$SRCDIR\public\vstdlib\vstdlib.h"
|
||||
$File "$SRCDIR\public\wadtypes.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib mathlib
|
||||
$Lib tier2
|
||||
$Lib vmpi
|
||||
$Lib "$LIBCOMMON/lzma"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user