sin-2015/path.cpp
1999-04-22 00:00:00 +00:00

454 lines
7 KiB
C++

// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
//
#include "g_local.h"
#include "entity.h"
#include "path.h"
#include "container.h"
#include "navigate.h"
#include "misc.h"
CLASS_DECLARATION( Class, Path, NULL );
ResponseDef Path::Responses[] =
{
{ NULL, NULL }
};
Path::Path()
{
pathlength = 0;
from = NULL;
to = NULL;
nextnode = 1;
}
Path::Path
(
int numnodes
)
{
pathlength = 0;
from = NULL;
to = NULL;
nextnode = 1;
pathlist.Resize( numnodes );
dirToNextNode.Resize( numnodes );
distanceToNextNode.Resize( numnodes );
}
void Path::Clear
(
void
)
{
nextnode = 1;
pathlength = 0;
from = NULL;
to = NULL;
pathlist.FreeObjectList();
dirToNextNode.FreeObjectList();
distanceToNextNode.FreeObjectList();
}
void Path::Reset
(
void
)
{
nextnode = 1;
}
PathNode *Path::Start
(
void
)
{
return from;
}
PathNode *Path::End
(
void
)
{
return to;
}
void Path::AddNode
(
PathNode *node
)
{
Vector dir;
float len;
int num;
if ( !from )
{
from = node;
}
to = node;
pathlist.AddObject( PathNodePtr( node ) );
len = 0;
distanceToNextNode.AddObject( len );
dirToNextNode.AddObject( vec_zero );
num = NumNodes();
if ( num > 1 )
{
dir = node->worldorigin - GetNode( num - 1 )->worldorigin;
len = dir.length();
dir *= 1 / len;
distanceToNextNode.SetObjectAt( num - 1, len );
dirToNextNode.SetObjectAt( num - 1, dir );
pathlength += len;
}
}
PathNode *Path::GetNode
(
int num
)
{
PathNode *node;
node = pathlist.ObjectAt( num );
assert( node != NULL );
if ( node == NULL )
{
error( "GetNode", "Null pointer in node list\n" );
}
return node;
}
PathNode *Path::NextNode
(
void
)
{
if ( nextnode <= NumNodes() )
{
return pathlist.ObjectAt( nextnode++ );
}
return NULL;
}
PathNode *Path::NextNode
(
PathNode *node
)
{
int i;
int num;
PathNode *n;
num = NumNodes();
// NOTE: We specifically DON'T check the last object (hence the i < num instead
// of the usual i <= num, so don't go doing something stupid like trying to fix
// this without keeping this in mind!! :)
for( i = 1; i < num; i++ )
{
n = pathlist.ObjectAt( i );
if ( n == node )
{
// Since we only check up to num - 1, it's ok to do this.
// We do this since the last node in the list has no next node (duh!).
return pathlist.ObjectAt( i + 1 );
}
}
return NULL;
}
Vector Path::ClosestPointOnPath
(
Vector pos
)
{
PathNode *s;
PathNode *e;
int num;
int i;
float bestdist;
Vector bestpoint;
float dist;
float segmentlength;
Vector delta;
Vector p1;
Vector p2;
Vector p3;
float t;
num = NumNodes();
s = GetNode( 1 );
bestpoint = s->worldorigin;
delta = bestpoint - pos;
bestdist = delta * delta;
for( i = 2; i <= num; i++ )
{
e = GetNode( i );
// check if we're closest to the endpoint
delta = e->worldorigin - pos;
dist = delta * delta;
if ( dist < bestdist )
{
bestdist = dist;
bestpoint = e->worldorigin;
}
// check if we're closest to the segment
segmentlength = distanceToNextNode.ObjectAt( i - 1 );
p1 = dirToNextNode.ObjectAt( i - 1 );
p2 = pos - s->worldorigin;
t = p1 * p2;
if ( ( t > 0 ) && ( t < segmentlength ) )
{
p3 = ( p1 * t ) + s->worldorigin;
delta = p3 - pos;
dist = delta * delta;
if ( dist < bestdist )
{
bestdist = dist;
bestpoint = p3;
}
}
s = e;
}
return bestpoint;
}
float Path::DistanceAlongPath
(
Vector pos
)
{
PathNode *s;
PathNode *e;
int num;
int i;
float bestdist;
float dist;
float segmentlength;
Vector delta;
Vector p1;
Vector p2;
Vector p3;
float t;
float pathdist;
float bestdistalongpath;
pathdist = 0;
num = NumNodes();
s = GetNode( 1 );
delta = s->worldorigin - pos;
bestdist = delta * delta;
bestdistalongpath = 0;
for( i = 2; i <= num; i++ )
{
e = GetNode( i );
segmentlength = distanceToNextNode.ObjectAt( i - 1 );
// check if we're closest to the endpoint
delta = e->worldorigin - pos;
dist = delta * delta;
if ( dist < bestdist )
{
bestdist = dist;
bestdistalongpath = pathdist + segmentlength;
}
// check if we're closest to the segment
p1 = dirToNextNode.ObjectAt( i - 1 );
p2 = pos - s->worldorigin;
t = p1 * p2;
if ( ( t > 0 ) && ( t < segmentlength ) )
{
p3 = ( p1 * t ) + s->worldorigin;
delta = p3 - pos;
dist = delta * delta;
if ( dist < bestdist )
{
bestdist = dist;
bestdistalongpath = pathdist + t;
}
}
s = e;
pathdist += segmentlength;
}
return bestdistalongpath;
}
Vector Path::PointAtDistance
(
float dist
)
{
PathNode *s;
PathNode *e;
int num;
int i;
float t;
float pathdist;
float segmentlength;
num = NumNodes();
s = GetNode( 1 );
pathdist = 0;
for( i = 2; i <= num; i++ )
{
e = GetNode( i );
segmentlength = distanceToNextNode.ObjectAt( i - 1 );
if ( ( pathdist + segmentlength ) > dist )
{
t = dist - pathdist;
return s->worldorigin + dirToNextNode.ObjectAt( i - 1 ) * t;
}
s = e;
pathdist += segmentlength;
}
// cap it off at start or end of path
return s->worldorigin;
}
PathNode *Path::NextNode
(
float dist
)
{
PathNode *s;
PathNode *e;
int num;
int i;
float pathdist;
float segmentlength;
num = NumNodes();
s = GetNode( 1 );
pathdist = 0;
for( i = 2; i <= num; i++ )
{
e = GetNode( i );
segmentlength = distanceToNextNode.ObjectAt( i - 1 );
if ( ( pathdist + segmentlength ) > dist )
{
return e;
}
s = e;
pathdist += segmentlength;
}
// cap it off at start or end of path
return s;
}
void Path::DrawPath
(
float r,
float g,
float b,
float time
)
{
Vector s;
Vector e;
Vector offset;
PathNode *node;
int num;
int i;
num = NumNodes();
if ( ai_debugpath->value )
{
gi.dprintf( "numnodes %d, len %d, nodes %d :", PathManager.NumNodes(), ( int )Length(), num );
for( i = 1; i <= num; i++ )
{
node = GetNode( i );
gi.dprintf( " %d", node->nodenum );
}
gi.dprintf( "\n" );
}
node = GetNode( 1 );
s = node->worldorigin;
offset = Vector( r, g, b ) * 4 + Vector( 0, 0, 20 );
for( i = 2; i <= num; i++ )
{
node = GetNode( i );
e = node->worldorigin;
G_DebugLine( s + offset, e + offset, r, g, b, 1 );
s = e;
}
}
int Path::NumNodes
(
void
)
{
return pathlist.NumObjects();
}
float Path::Length
(
void
)
{
return pathlength;
}