fteqw/plugins/quake3/botlib/be_aas_sample.c

1394 lines
43 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
/*****************************************************************************
* name: be_aas_sample.c
*
* desc: AAS environment sampling
*
* $Archive: /MissionPack/code/botlib/be_aas_sample.c $
*
*****************************************************************************/
#include "q_shared.h"
#include "l_memory.h"
#include "l_script.h"
#include "l_precomp.h"
#include "l_struct.h"
#ifndef BSPC
#include "l_libvar.h"
#endif
#include "aasfile.h"
#include "botlib.h"
#include "be_aas.h"
#include "be_interface.h"
#include "be_aas_funcs.h"
#include "be_aas_def.h"
//#define AAS_SAMPLE_DEBUG
#define BBOX_NORMAL_EPSILON 0.001
#define ON_EPSILON 0 //0.0005
#define TRACEPLANE_EPSILON 0.125
typedef struct aas_tracestack_s
{
vec3_t start; //start point of the piece of line to trace
vec3_t end; //end point of the piece of line to trace
int planenum; //last plane used as splitter
int nodenum; //node found after splitting with planenum
} aas_tracestack_t;
int numaaslinks;
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs)
{
int index;
//bounding box size for each presence type
vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}};
vec3_t boxmaxs[3] = {{0, 0, 0}, { 15, 15, 32}, { 15, 15, 8}};
if (presencetype == PRESENCE_NORMAL) index = 1;
else if (presencetype == PRESENCE_CROUCH) index = 2;
else
{
botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n");
index = 2;
} //end if
VectorCopy(boxmins[index], mins);
VectorCopy(boxmaxs[index], maxs);
} //end of the function AAS_PresenceTypeBoundingBox
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_InitAASLinkHeap(void)
{
int i, max_aaslinks;
max_aaslinks = aasworld.linkheapsize;
//if there's no link heap present
if (!aasworld.linkheap)
{
#ifdef BSPC
max_aaslinks = 6144;
#else
max_aaslinks = (int) LibVarValue("max_aaslinks", "6144");
#endif
if (max_aaslinks < 0) max_aaslinks = 0;
aasworld.linkheapsize = max_aaslinks;
aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t));
} //end if
//link the links on the heap
aasworld.linkheap[0].prev_ent = NULL;
aasworld.linkheap[0].next_ent = &aasworld.linkheap[1];
for (i = 1; i < max_aaslinks-1; i++)
{
aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1];
aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1];
} //end for
aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2];
aasworld.linkheap[max_aaslinks-1].next_ent = NULL;
//pointer to the first free link
aasworld.freelinks = &aasworld.linkheap[0];
//
numaaslinks = max_aaslinks;
} //end of the function AAS_InitAASLinkHeap
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_FreeAASLinkHeap(void)
{
if (aasworld.linkheap) FreeMemory(aasworld.linkheap);
aasworld.linkheap = NULL;
aasworld.linkheapsize = 0;
} //end of the function AAS_FreeAASLinkHeap
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
aas_link_t *AAS_AllocAASLink(void)
{
aas_link_t *link;
link = aasworld.freelinks;
if (!link)
{
#ifndef BSPC
if (botDeveloper)
#endif
{
botimport.Print(PRT_FATAL, "empty aas link heap\n");
} //end if
return NULL;
} //end if
if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent;
if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL;
numaaslinks--;
return link;
} //end of the function AAS_AllocAASLink
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_DeAllocAASLink(aas_link_t *link)
{
if (aasworld.freelinks) aasworld.freelinks->prev_ent = link;
link->prev_ent = NULL;
link->next_ent = aasworld.freelinks;
link->prev_area = NULL;
link->next_area = NULL;
aasworld.freelinks = link;
numaaslinks++;
} //end of the function AAS_DeAllocAASLink
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_InitAASLinkedEntities(void)
{
if (!aasworld.loaded) return;
if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory(
aasworld.numareas * sizeof(aas_link_t *));
} //end of the function AAS_InitAASLinkedEntities
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_FreeAASLinkedEntities(void)
{
if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
aasworld.arealinkedentities = NULL;
} //end of the function AAS_InitAASLinkedEntities
//===========================================================================
// returns the AAS area the point is in
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_PointAreaNum(vec3_t point)
{
int nodenum;
vec_t dist;
aas_node_t *node;
aas_plane_t *plane;
if (!aasworld.loaded)
{
botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n");
return 0;
} //end if
//start with node 1 because node zero is a dummy used for solid leafs
nodenum = 1;
while (nodenum > 0)
{
// botimport.Print(PRT_MESSAGE, "[%d]", nodenum);
#ifdef AAS_SAMPLE_DEBUG
if (nodenum >= aasworld.numnodes)
{
botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes);
return 0;
} //end if
#endif //AAS_SAMPLE_DEBUG
node = &aasworld.nodes[nodenum];
#ifdef AAS_SAMPLE_DEBUG
if (node->planenum < 0 || node->planenum >= aasworld.numplanes)
{
botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes);
return 0;
} //end if
#endif //AAS_SAMPLE_DEBUG
plane = &aasworld.planes[node->planenum];
dist = DotProduct(point, plane->normal) - plane->dist;
if (dist > 0) nodenum = node->children[0];
else nodenum = node->children[1];
} //end while
if (!nodenum)
{
#ifdef AAS_SAMPLE_DEBUG
botimport.Print(PRT_MESSAGE, "in solid\n");
#endif //AAS_SAMPLE_DEBUG
return 0;
} //end if
return -nodenum;
} //end of the function AAS_PointAreaNum
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_PointReachabilityAreaIndex( vec3_t origin )
{
int areanum, cluster, i, index;
if (!aasworld.initialized)
return 0;
if ( !origin )
{
index = 0;
for (i = 0; i < aasworld.numclusters; i++)
{
index += aasworld.clusters[i].numreachabilityareas;
} //end for
return index;
} //end if
areanum = AAS_PointAreaNum( origin );
if ( !areanum || !AAS_AreaReachability(areanum) )
return 0;
cluster = aasworld.areasettings[areanum].cluster;
areanum = aasworld.areasettings[areanum].clusterareanum;
if (cluster < 0)
{
cluster = aasworld.portals[-cluster].frontcluster;
areanum = aasworld.portals[-cluster].clusterareanum[0];
} //end if
index = 0;
for (i = 0; i < cluster; i++)
{
index += aasworld.clusters[i].numreachabilityareas;
} //end for
index += areanum;
return index;
} //end of the function AAS_PointReachabilityAreaIndex
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_AreaCluster(int areanum)
{
if (areanum <= 0 || areanum >= aasworld.numareas)
{
botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n");
return 0;
} //end if
return aasworld.areasettings[areanum].cluster;
} //end of the function AAS_AreaCluster
//===========================================================================
// returns the presence types of the given area
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_AreaPresenceType(int areanum)
{
if (!aasworld.loaded) return 0;
if (areanum <= 0 || areanum >= aasworld.numareas)
{
botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n");
return 0;
} //end if
return aasworld.areasettings[areanum].presencetype;
} //end of the function AAS_AreaPresenceType
//===========================================================================
// returns the presence type at the given point
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_PointPresenceType(vec3_t point)
{
int areanum;
if (!aasworld.loaded) return 0;
areanum = AAS_PointAreaNum(point);
if (!areanum) return PRESENCE_NONE;
return aasworld.areasettings[areanum].presencetype;
} //end of the function AAS_PointPresenceType
//===========================================================================
// calculates the minimum distance between the origin of the box and the
// given plane when both will collide on the given side of the plane
//
// normal = normal vector of plane to calculate distance from
// mins = minimums of box relative to origin
// maxs = maximums of box relative to origin
// side = side of the plane we want to calculate the distance from
// 0 normal vector side
// 1 not normal vector side
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side)
{
vec3_t v1, v2;
int i;
//swap maxs and mins when on the other side of the plane
if (side)
{
//get a point of the box that would be one of the first
//to collide with the plane
for (i = 0; i < 3; i++)
{
if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i];
else v1[i] = 0;
} //end for
} //end if
else
{
//get a point of the box that would be one of the first
//to collide with the plane
for (i = 0; i < 3; i++)
{
if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i];
else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
else v1[i] = 0;
} //end for
} //end else
//
VectorCopy(normal, v2);
VectorInverse(v2);
// VectorNegate(normal, v2);
return DotProduct(v1, v2);
} //end of the function AAS_BoxOriginDistanceFromPlane
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end,
int presencetype, int passent, aas_trace_t *trace)
{
int collision;
vec3_t boxmins, boxmaxs;
aas_link_t *link;
bsp_trace_t bsptrace;
AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs);
Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy
//assume no collision
bsptrace.fraction = 1;
collision = qfalse;
for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent)
{
//ignore the pass entity
if (link->entnum == passent) continue;
//
if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end,
CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace))
{
collision = qtrue;
} //end if
} //end for
if (collision)
{
trace->startsolid = bsptrace.startsolid;
trace->ent = bsptrace.ent;
VectorCopy(bsptrace.endpos, trace->endpos);
trace->area = 0;
trace->planenum = 0;
return qtrue;
} //end if
return qfalse;
} //end of the function AAS_AreaEntityCollision
//===========================================================================
// recursive subdivision of the line by the BSP tree.
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype,
int passent)
{
int side, nodenum, tmpplanenum;
float front, back, frac;
vec3_t cur_start, cur_end, cur_mid, v1, v2;
aas_tracestack_t tracestack[127];
aas_tracestack_t *tstack_p;
aas_node_t *aasnode;
aas_plane_t *plane;
aas_trace_t trace;
//clear the trace structure
Com_Memset(&trace, 0, sizeof(aas_trace_t));
if (!aasworld.loaded) return trace;
tstack_p = tracestack;
//we start with the whole line on the stack
VectorCopy(start, tstack_p->start);
VectorCopy(end, tstack_p->end);
tstack_p->planenum = 0;
//start with node 1 because node zero is a dummy for a solid leaf
tstack_p->nodenum = 1; //starting at the root of the tree
tstack_p++;
while (1)
{
//pop up the stack
tstack_p--;
//if the trace stack is empty (ended up with a piece of the
//line to be traced in an area)
if (tstack_p < tracestack)
{
tstack_p++;
//nothing was hit
trace.startsolid = qfalse;
trace.fraction = 1.0;
//endpos is the end of the line
VectorCopy(end, trace.endpos);
//nothing hit
trace.ent = 0;
trace.area = 0;
trace.planenum = 0;
return trace;
} //end if
//number of the current node to test the line against
nodenum = tstack_p->nodenum;
//if it is an area
if (nodenum < 0)
{
#ifdef AAS_SAMPLE_DEBUG
if (-nodenum > aasworld.numareasettings)
{
botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n");
return trace;
} //end if
#endif //AAS_SAMPLE_DEBUG
//botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
//if can't enter the area because it hasn't got the right presence type
if (!(aasworld.areasettings[-nodenum].presencetype & presencetype))
{
//if the start point is still the initial start point
//NOTE: no need for epsilons because the points will be
//exactly the same when they're both the start point
if (tstack_p->start[0] == start[0] &&
tstack_p->start[1] == start[1] &&
tstack_p->start[2] == start[2])
{
trace.startsolid = qtrue;
trace.fraction = 0.0;
VectorClear(v1);
} //end if
else
{
trace.startsolid = qfalse;
VectorSubtract(end, start, v1);
VectorSubtract(tstack_p->start, start, v2);
trace.fraction = VectorLength(v2) / VectorNormalize(v1);
VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
} //end else
VectorCopy(tstack_p->start, trace.endpos);
trace.ent = 0;
trace.area = -nodenum;
// VectorSubtract(end, start, v1);
trace.planenum = tstack_p->planenum;
//always take the plane with normal facing towards the trace start
plane = &aasworld.planes[trace.planenum];
if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
return trace;
} //end if
else
{
if (passent >= 0)
{
if (AAS_AreaEntityCollision(-nodenum, tstack_p->start,
tstack_p->end, presencetype, passent,
&trace))
{
if (!trace.startsolid)
{
VectorSubtract(end, start, v1);
VectorSubtract(trace.endpos, start, v2);
trace.fraction = VectorLength(v2) / VectorLength(v1);
} //end if
return trace;
} //end if
} //end if
} //end else
trace.lastarea = -nodenum;
continue;
} //end if
//if it is a solid leaf
if (!nodenum)
{
//if the start point is still the initial start point
//NOTE: no need for epsilons because the points will be
//exactly the same when they're both the start point
if (tstack_p->start[0] == start[0] &&
tstack_p->start[1] == start[1] &&
tstack_p->start[2] == start[2])
{
trace.startsolid = qtrue;
trace.fraction = 0.0;
VectorClear(v1);
} //end if
else
{
trace.startsolid = qfalse;
VectorSubtract(end, start, v1);
VectorSubtract(tstack_p->start, start, v2);
trace.fraction = VectorLength(v2) / VectorNormalize(v1);
VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
} //end else
VectorCopy(tstack_p->start, trace.endpos);
trace.ent = 0;
trace.area = 0; //hit solid leaf
// VectorSubtract(end, start, v1);
trace.planenum = tstack_p->planenum;
//always take the plane with normal facing towards the trace start
plane = &aasworld.planes[trace.planenum];
if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
return trace;
} //end if
#ifdef AAS_SAMPLE_DEBUG
if (nodenum > aasworld.numnodes)
{
botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n");
return trace;
} //end if
#endif //AAS_SAMPLE_DEBUG
//the node to test against
aasnode = &aasworld.nodes[nodenum];
//start point of current line to test against node
VectorCopy(tstack_p->start, cur_start);
//end point of the current line to test against node
VectorCopy(tstack_p->end, cur_end);
//the current node plane
plane = &aasworld.planes[aasnode->planenum];
switch(plane->type)
{/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!!
//check for axial planes
case PLANE_X:
{
front = cur_start[0] - plane->dist;
back = cur_end[0] - plane->dist;
break;
} //end case
case PLANE_Y:
{
front = cur_start[1] - plane->dist;
back = cur_end[1] - plane->dist;
break;
} //end case
case PLANE_Z:
{
front = cur_start[2] - plane->dist;
back = cur_end[2] - plane->dist;
break;
} //end case*/
default: //gee it's not an axial plane
{
front = DotProduct(cur_start, plane->normal) - plane->dist;
back = DotProduct(cur_end, plane->normal) - plane->dist;
break;
} //end default
} //end switch
// bk010221 - old location of FPE hack and divide by zero expression
//if the whole to be traced line is totally at the front of this node
//only go down the tree with the front child
if ((front >= -ON_EPSILON && back >= -ON_EPSILON))
{
//keep the current start and end point on the stack
//and go down the tree with the front child
tstack_p->nodenum = aasnode->children[0];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
return trace;
} //end if
} //end if
//if the whole to be traced line is totally at the back of this node
//only go down the tree with the back child
else if ((front < ON_EPSILON && back < ON_EPSILON))
{
//keep the current start and end point on the stack
//and go down the tree with the back child
tstack_p->nodenum = aasnode->children[1];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
return trace;
} //end if
} //end if
//go down the tree both at the front and back of the node
else
{
tmpplanenum = tstack_p->planenum;
// bk010221 - new location of divide by zero (see above)
if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE
//calculate the hitpoint with the node (split point of the line)
//put the crosspoint TRACEPLANE_EPSILON pixels on the near side
if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back);
else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221
//
if (frac < 0)
frac = 0.001f; //0
else if (frac > 1)
frac = 0.999f; //1
//frac = front / (front-back);
//
cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
//side the front part of the line is on
side = front < 0;
//first put the end part of the line on the stack (back side)
VectorCopy(cur_mid, tstack_p->start);
//not necesary to store because still on stack
//VectorCopy(cur_end, tstack_p->end);
tstack_p->planenum = aasnode->planenum;
tstack_p->nodenum = aasnode->children[!side];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
return trace;
} //end if
//now put the part near the start of the line on the stack so we will
//continue with thats part first. This way we'll find the first
//hit of the bbox
VectorCopy(cur_start, tstack_p->start);
VectorCopy(cur_mid, tstack_p->end);
tstack_p->planenum = tmpplanenum;
tstack_p->nodenum = aasnode->children[side];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
return trace;
} //end if
} //end else
} //end while
// return trace;
} //end of the function AAS_TraceClientBBox
//===========================================================================
// recursive subdivision of the line by the BSP tree.
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas)
{
int side, nodenum, tmpplanenum;
int numareas;
float front, back, frac;
vec3_t cur_start, cur_end, cur_mid;
aas_tracestack_t tracestack[127];
aas_tracestack_t *tstack_p;
aas_node_t *aasnode;
aas_plane_t *plane;
numareas = 0;
areas[0] = 0;
if (!aasworld.loaded) return numareas;
tstack_p = tracestack;
//we start with the whole line on the stack
VectorCopy(start, tstack_p->start);
VectorCopy(end, tstack_p->end);
tstack_p->planenum = 0;
//start with node 1 because node zero is a dummy for a solid leaf
tstack_p->nodenum = 1; //starting at the root of the tree
tstack_p++;
while (1)
{
//pop up the stack
tstack_p--;
//if the trace stack is empty (ended up with a piece of the
//line to be traced in an area)
if (tstack_p < tracestack)
{
return numareas;
} //end if
//number of the current node to test the line against
nodenum = tstack_p->nodenum;
//if it is an area
if (nodenum < 0)
{
#ifdef AAS_SAMPLE_DEBUG
if (-nodenum > aasworld.numareasettings)
{
botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum);
return numareas;
} //end if
#endif //AAS_SAMPLE_DEBUG
//botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
areas[numareas] = -nodenum;
if (points) VectorCopy(tstack_p->start, points[numareas]);
numareas++;
if (numareas >= maxareas) return numareas;
continue;
} //end if
//if it is a solid leaf
if (!nodenum)
{
continue;
} //end if
#ifdef AAS_SAMPLE_DEBUG
if (nodenum > aasworld.numnodes)
{
botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n");
return numareas;
} //end if
#endif //AAS_SAMPLE_DEBUG
//the node to test against
aasnode = &aasworld.nodes[nodenum];
//start point of current line to test against node
VectorCopy(tstack_p->start, cur_start);
//end point of the current line to test against node
VectorCopy(tstack_p->end, cur_end);
//the current node plane
plane = &aasworld.planes[aasnode->planenum];
switch(plane->type)
{/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!!
//check for axial planes
case PLANE_X:
{
front = cur_start[0] - plane->dist;
back = cur_end[0] - plane->dist;
break;
} //end case
case PLANE_Y:
{
front = cur_start[1] - plane->dist;
back = cur_end[1] - plane->dist;
break;
} //end case
case PLANE_Z:
{
front = cur_start[2] - plane->dist;
back = cur_end[2] - plane->dist;
break;
} //end case*/
default: //gee it's not an axial plane
{
front = DotProduct(cur_start, plane->normal) - plane->dist;
back = DotProduct(cur_end, plane->normal) - plane->dist;
break;
} //end default
} //end switch
//if the whole to be traced line is totally at the front of this node
//only go down the tree with the front child
if (front > 0 && back > 0)
{
//keep the current start and end point on the stack
//and go down the tree with the front child
tstack_p->nodenum = aasnode->children[0];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
return numareas;
} //end if
} //end if
//if the whole to be traced line is totally at the back of this node
//only go down the tree with the back child
else if (front <= 0 && back <= 0)
{
//keep the current start and end point on the stack
//and go down the tree with the back child
tstack_p->nodenum = aasnode->children[1];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
return numareas;
} //end if
} //end if
//go down the tree both at the front and back of the node
else
{
tmpplanenum = tstack_p->planenum;
//calculate the hitpoint with the node (split point of the line)
//put the crosspoint TRACEPLANE_EPSILON pixels on the near side
if (front < 0) frac = (front)/(front-back);
else frac = (front)/(front-back);
if (frac < 0) frac = 0;
else if (frac > 1) frac = 1;
//frac = front / (front-back);
//
cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
//side the front part of the line is on
side = front < 0;
//first put the end part of the line on the stack (back side)
VectorCopy(cur_mid, tstack_p->start);
//not necesary to store because still on stack
//VectorCopy(cur_end, tstack_p->end);
tstack_p->planenum = aasnode->planenum;
tstack_p->nodenum = aasnode->children[!side];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
return numareas;
} //end if
//now put the part near the start of the line on the stack so we will
//continue with thats part first. This way we'll find the first
//hit of the bbox
VectorCopy(cur_start, tstack_p->start);
VectorCopy(cur_mid, tstack_p->end);
tstack_p->planenum = tmpplanenum;
tstack_p->nodenum = aasnode->children[side];
tstack_p++;
if (tstack_p >= &tracestack[127])
{
botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
return numareas;
} //end if
} //end else
} //end while
// return numareas;
} //end of the function AAS_TraceAreas
//===========================================================================
// a simple cross product
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
// void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res)
#define AAS_OrthogonalToVectors(v1, v2, res) \
(res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\
(res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\
(res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]);
//===========================================================================
// tests if the given point is within the face boundaries
//
// Parameter: face : face to test if the point is in it
// pnormal : normal of the plane to use for the face
// point : point to test if inside face boundaries
// Returns: qtrue if the point is within the face boundaries
// Changes Globals: -
//===========================================================================
qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon)
{
int i, firstvertex, edgenum;
vec3_t v0;
vec3_t edgevec, pointvec, sepnormal;
aas_edge_t *edge;
#ifdef AAS_SAMPLE_DEBUG
int lastvertex = 0;
#endif //AAS_SAMPLE_DEBUG
if (!aasworld.loaded) return qfalse;
for (i = 0; i < face->numedges; i++)
{
edgenum = aasworld.edgeindex[face->firstedge + i];
edge = &aasworld.edges[abs(edgenum)];
//get the first vertex of the edge
firstvertex = edgenum < 0;
VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0);
//edge vector
VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec);
//
#ifdef AAS_SAMPLE_DEBUG
if (lastvertex && lastvertex != edge->v[firstvertex])
{
botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n");
} //end if
lastvertex = edge->v[!firstvertex];
#endif //AAS_SAMPLE_DEBUG
//vector from first edge point to point possible in face
VectorSubtract(point, v0, pointvec);
//get a vector pointing inside the face orthogonal to both the
//edge vector and the normal vector of the plane the face is in
//this vector defines a plane through the origin (first vertex of
//edge) and through both the edge vector and the normal vector
//of the plane
AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal);
//check on wich side of the above plane the point is
//this is done by checking the sign of the dot product of the
//vector orthogonal vector from above and the vector from the
//origin (first vertex of edge) to the point
//if the dotproduct is smaller than zero the point is outside the face
if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
} //end for
return qtrue;
} //end of the function AAS_InsideFace
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon)
{
int i, firstvertex, edgenum;
vec_t *v1, *v2;
vec3_t edgevec, pointvec, sepnormal;
aas_edge_t *edge;
aas_plane_t *plane;
aas_face_t *face;
if (!aasworld.loaded) return qfalse;
face = &aasworld.faces[facenum];
plane = &aasworld.planes[face->planenum];
//
for (i = 0; i < face->numedges; i++)
{
edgenum = aasworld.edgeindex[face->firstedge + i];
edge = &aasworld.edges[abs(edgenum)];
//get the first vertex of the edge
firstvertex = edgenum < 0;
v1 = aasworld.vertexes[edge->v[firstvertex]];
v2 = aasworld.vertexes[edge->v[!firstvertex]];
//edge vector
VectorSubtract(v2, v1, edgevec);
//vector from first edge point to point possible in face
VectorSubtract(point, v1, pointvec);
//
CrossProduct(edgevec, plane->normal, sepnormal);
//
if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
} //end for
return qtrue;
} //end of the function AAS_PointInsideFace
//===========================================================================
// returns the ground face the given point is above in the given area
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point)
{
int i, facenum;
vec3_t up = {0, 0, 1};
vec3_t normal;
aas_area_t *area;
aas_face_t *face;
if (!aasworld.loaded) return NULL;
area = &aasworld.areas[areanum];
for (i = 0; i < area->numfaces; i++)
{
facenum = aasworld.faceindex[area->firstface + i];
face = &aasworld.faces[abs(facenum)];
//if this is a ground face
if (face->faceflags & FACE_GROUND)
{
//get the up or down normal
if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal);
else VectorCopy(up, normal);
//check if the point is in the face
if (AAS_InsideFace(face, normal, point, 0.01f)) return face;
} //end if
} //end for
return NULL;
} //end of the function AAS_AreaGroundFace
//===========================================================================
// returns the face the trace end position is situated in
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_FacePlane(int facenum, vec3_t normal, float *dist)
{
aas_plane_t *plane;
plane = &aasworld.planes[aasworld.faces[facenum].planenum];
VectorCopy(plane->normal, normal);
*dist = plane->dist;
} //end of the function AAS_FacePlane
//===========================================================================
// returns the face the trace end position is situated in
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
aas_face_t *AAS_TraceEndFace(aas_trace_t *trace)
{
int i, facenum;
aas_area_t *area;
aas_face_t *face, *firstface = NULL;
if (!aasworld.loaded) return NULL;
//if started in solid no face was hit
if (trace->startsolid) return NULL;
//trace->lastarea is the last area the trace was in
area = &aasworld.areas[trace->lastarea];
//check which face the trace.endpos was in
for (i = 0; i < area->numfaces; i++)
{
facenum = aasworld.faceindex[area->firstface + i];
face = &aasworld.faces[abs(facenum)];
//if the face is in the same plane as the trace end point
if ((face->planenum & ~1) == (trace->planenum & ~1))
{
//firstface is used for optimization, if theres only one
//face in the plane then it has to be the good one
//if there are more faces in the same plane then always
//check the one with the fewest edges first
/* if (firstface)
{
if (firstface->numedges < face->numedges)
{
if (AAS_InsideFace(firstface,
aasworld.planes[face->planenum].normal, trace->endpos))
{
return firstface;
} //end if
firstface = face;
} //end if
else
{
if (AAS_InsideFace(face,
aasworld.planes[face->planenum].normal, trace->endpos))
{
return face;
} //end if
} //end else
} //end if
else
{
firstface = face;
} //end else*/
if (AAS_InsideFace(face,
aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face;
} //end if
} //end for
return firstface;
} //end of the function AAS_TraceEndFace
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
{
int i, sides;
float dist1, dist2;
vec3_t corners[2];
for (i = 0; i < 3; i++)
{
if (p->normal[i] < 0)
{
corners[0][i] = absmins[i];
corners[1][i] = absmaxs[i];
} //end if
else
{
corners[1][i] = absmins[i];
corners[0][i] = absmaxs[i];
} //end else
} //end for
dist1 = DotProduct(p->normal, corners[0]) - p->dist;
dist2 = DotProduct(p->normal, corners[1]) - p->dist;
sides = 0;
if (dist1 >= 0) sides = 1;
if (dist2 < 0) sides |= 2;
return sides;
} //end of the function AAS_BoxOnPlaneSide2
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
//int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
#define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\
( (p)->type < 3) ?\
(\
( (p)->dist <= (absmins)[(p)->type]) ?\
(\
1\
)\
:\
(\
( (p)->dist >= (absmaxs)[(p)->type]) ?\
(\
2\
)\
:\
(\
3\
)\
)\
)\
:\
(\
AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\
)\
) //end of the function AAS_BoxOnPlaneSide
//===========================================================================
// remove the links to this entity from all areas
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void AAS_UnlinkFromAreas(aas_link_t *areas)
{
aas_link_t *link, *nextlink;
for (link = areas; link; link = nextlink)
{
//next area the entity is linked in
nextlink = link->next_area;
//remove the entity from the linked list of this area
if (link->prev_ent) link->prev_ent->next_ent = link->next_ent;
else aasworld.arealinkedentities[link->areanum] = link->next_ent;
if (link->next_ent) link->next_ent->prev_ent = link->prev_ent;
//deallocate the link structure
AAS_DeAllocAASLink(link);
} //end for
} //end of the function AAS_UnlinkFromAreas
//===========================================================================
// link the entity to the areas the bounding box is totally or partly
// situated in. This is done with recursion down the tree using the
// bounding box to test for plane sides
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
typedef struct
{
int nodenum; //node found after splitting
} aas_linkstack_t;
aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum)
{
int side, nodenum;
aas_linkstack_t linkstack[128];
aas_linkstack_t *lstack_p;
aas_node_t *aasnode;
aas_plane_t *plane;
aas_link_t *link, *areas;
if (!aasworld.loaded)
{
botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n");
return NULL;
} //end if
areas = NULL;
//
lstack_p = linkstack;
//we start with the whole line on the stack
//start with node 1 because node zero is a dummy used for solid leafs
lstack_p->nodenum = 1; //starting at the root of the tree
lstack_p++;
while (1)
{
//pop up the stack
lstack_p--;
//if the trace stack is empty (ended up with a piece of the
//line to be traced in an area)
if (lstack_p < linkstack) break;
//number of the current node to test the line against
nodenum = lstack_p->nodenum;
//if it is an area
if (nodenum < 0)
{
//NOTE: the entity might have already been linked into this area
// because several node children can point to the same area
for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent)
{
if (link->entnum == entnum) break;
} //end for
if (link) continue;
//
link = AAS_AllocAASLink();
if (!link) return areas;
link->entnum = entnum;
link->areanum = -nodenum;
//put the link into the double linked area list of the entity
link->prev_area = NULL;
link->next_area = areas;
if (areas) areas->prev_area = link;
areas = link;
//put the link into the double linked entity list of the area
link->prev_ent = NULL;
link->next_ent = aasworld.arealinkedentities[-nodenum];
if (aasworld.arealinkedentities[-nodenum])
aasworld.arealinkedentities[-nodenum]->prev_ent = link;
aasworld.arealinkedentities[-nodenum] = link;
//
continue;
} //end if
//if solid leaf
if (!nodenum) continue;
//the node to test against
aasnode = &aasworld.nodes[nodenum];
//the current node plane
plane = &aasworld.planes[aasnode->planenum];
//get the side(s) the box is situated relative to the plane
side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane);
//if on the front side of the node
if (side & 1)
{
lstack_p->nodenum = aasnode->children[0];
lstack_p++;
} //end if
if (lstack_p >= &linkstack[127])
{
botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n");
break;
} //end if
//if on the back side of the node
if (side & 2)
{
lstack_p->nodenum = aasnode->children[1];
lstack_p++;
} //end if
if (lstack_p >= &linkstack[127])
{
botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n");
break;
} //end if
} //end while
return areas;
} //end of the function AAS_AASLinkEntity
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype)
{
vec3_t mins, maxs;
vec3_t newabsmins, newabsmaxs;
AAS_PresenceTypeBoundingBox(presencetype, mins, maxs);
VectorSubtract(absmins, maxs, newabsmins);
VectorSubtract(absmaxs, mins, newabsmaxs);
//relink the entity
return AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum);
} //end of the function AAS_LinkEntityClientBBox
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas)
{
aas_link_t *linkedareas, *link;
int num;
linkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1);
num = 0;
for (link = linkedareas; link; link = link->next_area)
{
areas[num] = link->areanum;
num++;
if (num >= maxareas)
break;
} //end for
AAS_UnlinkFromAreas(linkedareas);
return num;
} //end of the function AAS_BBoxAreas
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
int AAS_AreaInfo( int areanum, aas_areainfo_t *info )
{
aas_areasettings_t *settings;
if (!info)
return 0;
if (areanum <= 0 || areanum >= aasworld.numareas)
{
botimport.Print(PRT_ERROR, "AAS_AreaInfo: areanum %d out of range\n", areanum);
return 0;
} //end if
settings = &aasworld.areasettings[areanum];
info->cluster = settings->cluster;
info->contents = settings->contents;
info->flags = settings->areaflags;
info->presencetype = settings->presencetype;
VectorCopy(aasworld.areas[areanum].mins, info->mins);
VectorCopy(aasworld.areas[areanum].maxs, info->maxs);
VectorCopy(aasworld.areas[areanum].center, info->center);
return sizeof(aas_areainfo_t);
} //end of the function AAS_AreaInfo
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
aas_plane_t *AAS_PlaneFromNum(int planenum)
{
if (!aasworld.loaded) return NULL;
return &aasworld.planes[planenum];
} //end of the function AAS_PlaneFromNum