1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29
30 /*****************************************************************************
31 * name: be_aas_sample.c
32 *
33 * desc: AAS environment sampling
34 *
35 *
36 *****************************************************************************/
37
38 #include "../qcommon/q_shared.h"
39 #include "l_memory.h"
40 #include "l_script.h"
41 #include "l_precomp.h"
42 #include "l_struct.h"
43 #include "aasfile.h"
44 #include "botlib.h"
45 #include "be_aas.h"
46 #include "be_aas_funcs.h"
47 #include "be_aas_def.h"
48
49 extern botlib_import_t botimport;
50
51 //#define AAS_SAMPLE_DEBUG
52
53 #define BBOX_NORMAL_EPSILON 0.001
54
55 #define ON_EPSILON 0 //0.0005
56
57 #define TRACEPLANE_EPSILON 0.125
58
59 typedef struct aas_tracestack_s
60 {
61 vec3_t start; //start point of the piece of line to trace
62 vec3_t end; //end point of the piece of line to trace
63 int planenum; //last plane used as splitter
64 int nodenum; //node found after splitting with planenum
65 } aas_tracestack_t;
66
67 //===========================================================================
68 //
69 // Parameter: -
70 // Returns: -
71 // Changes Globals: -
72 //===========================================================================
AAS_PresenceTypeBoundingBox(int presencetype,vec3_t mins,vec3_t maxs)73 void AAS_PresenceTypeBoundingBox( int presencetype, vec3_t mins, vec3_t maxs ) {
74 int index;
75 //bounding box size for each presence type
76 vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}};
77 vec3_t boxmaxs[3] = {{0, 0, 0}, { 15, 15, 32}, { 15, 15, 8}};
78
79 if ( presencetype == PRESENCE_NORMAL ) {
80 index = 1;
81 } else if ( presencetype == PRESENCE_CROUCH ) {
82 index = 2;
83 } else
84 {
85 botimport.Print( PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n" );
86 index = 2;
87 } //end if
88 VectorCopy( boxmins[index], mins );
89 VectorCopy( boxmaxs[index], maxs );
90 } //end of the function AAS_PresenceTypeBoundingBox
91 //===========================================================================
92 //
93 // Parameter: -
94 // Returns: -
95 // Changes Globals: -
96 //===========================================================================
AAS_InitAASLinkHeap(void)97 void AAS_InitAASLinkHeap( void ) {
98 int i, max_aaslinks;
99
100 max_aaslinks = ( *aasworld ).linkheapsize;
101 //if there's no link heap present
102 if ( !( *aasworld ).linkheap ) {
103 max_aaslinks = (int) 4096; //LibVarValue("max_aaslinks", "4096");
104 if ( max_aaslinks < 0 ) {
105 max_aaslinks = 0;
106 }
107 ( *aasworld ).linkheapsize = max_aaslinks;
108 ( *aasworld ).linkheap = (aas_link_t *) GetHunkMemory( max_aaslinks * sizeof( aas_link_t ) );
109 } //end if
110 //link the links on the heap
111 ( *aasworld ).linkheap[0].prev_ent = NULL;
112 ( *aasworld ).linkheap[0].next_ent = &( *aasworld ).linkheap[1];
113 for ( i = 1; i < max_aaslinks - 1; i++ )
114 {
115 ( *aasworld ).linkheap[i].prev_ent = &( *aasworld ).linkheap[i - 1];
116 ( *aasworld ).linkheap[i].next_ent = &( *aasworld ).linkheap[i + 1];
117 } //end for
118 ( *aasworld ).linkheap[max_aaslinks - 1].prev_ent = &( *aasworld ).linkheap[max_aaslinks - 2];
119 ( *aasworld ).linkheap[max_aaslinks - 1].next_ent = NULL;
120 //pointer to the first free link
121 ( *aasworld ).freelinks = &( *aasworld ).linkheap[0];
122 } //end of the function AAS_InitAASLinkHeap
123 //===========================================================================
124 //
125 // Parameter: -
126 // Returns: -
127 // Changes Globals: -
128 //===========================================================================
AAS_FreeAASLinkHeap(void)129 void AAS_FreeAASLinkHeap( void ) {
130 if ( ( *aasworld ).linkheap ) {
131 FreeMemory( ( *aasworld ).linkheap );
132 }
133 ( *aasworld ).linkheap = NULL;
134 ( *aasworld ).linkheapsize = 0;
135 } //end of the function AAS_FreeAASLinkHeap
136 //===========================================================================
137 //
138 // Parameter: -
139 // Returns: -
140 // Changes Globals: -
141 //===========================================================================
AAS_AllocAASLink(void)142 aas_link_t *AAS_AllocAASLink( void ) {
143 aas_link_t *link;
144
145 link = ( *aasworld ).freelinks;
146 if ( !link ) {
147 botimport.Print( PRT_FATAL, "empty aas link heap\n" );
148 return NULL;
149 } //end if
150 if ( ( *aasworld ).freelinks ) {
151 ( *aasworld ).freelinks = ( *aasworld ).freelinks->next_ent;
152 }
153 if ( ( *aasworld ).freelinks ) {
154 ( *aasworld ).freelinks->prev_ent = NULL;
155 }
156 return link;
157 } //end of the function AAS_AllocAASLink
158 //===========================================================================
159 //
160 // Parameter: -
161 // Returns: -
162 // Changes Globals: -
163 //===========================================================================
AAS_DeAllocAASLink(aas_link_t * link)164 void AAS_DeAllocAASLink( aas_link_t *link ) {
165 if ( ( *aasworld ).freelinks ) {
166 ( *aasworld ).freelinks->prev_ent = link;
167 }
168 link->prev_ent = NULL;
169 link->next_ent = ( *aasworld ).freelinks;
170 link->prev_area = NULL;
171 link->next_area = NULL;
172 ( *aasworld ).freelinks = link;
173 } //end of the function AAS_DeAllocAASLink
174 //===========================================================================
175 //
176 // Parameter: -
177 // Returns: -
178 // Changes Globals: -
179 //===========================================================================
AAS_InitAASLinkedEntities(void)180 void AAS_InitAASLinkedEntities( void ) {
181 if ( !( *aasworld ).loaded ) {
182 return;
183 }
184 if ( ( *aasworld ).arealinkedentities ) {
185 FreeMemory( ( *aasworld ).arealinkedentities );
186 }
187 ( *aasworld ).arealinkedentities = (aas_link_t **) GetClearedHunkMemory(
188 ( *aasworld ).numareas * sizeof( aas_link_t * ) );
189 } //end of the function AAS_InitAASLinkedEntities
190 //===========================================================================
191 //
192 // Parameter: -
193 // Returns: -
194 // Changes Globals: -
195 //===========================================================================
AAS_FreeAASLinkedEntities(void)196 void AAS_FreeAASLinkedEntities( void ) {
197 if ( ( *aasworld ).arealinkedentities ) {
198 FreeMemory( ( *aasworld ).arealinkedentities );
199 }
200 ( *aasworld ).arealinkedentities = NULL;
201 } //end of the function AAS_InitAASLinkedEntities
202 //===========================================================================
203 // returns the AAS area the point is in
204 //
205 // Parameter: -
206 // Returns: -
207 // Changes Globals: -
208 //===========================================================================
AAS_PointAreaNum(vec3_t point)209 int AAS_PointAreaNum( vec3_t point ) {
210 int nodenum;
211 vec_t dist;
212 aas_node_t *node;
213 aas_plane_t *plane;
214
215 if ( !( *aasworld ).loaded ) {
216 botimport.Print( PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n" );
217 return 0;
218 } //end if
219
220 //start with node 1 because node zero is a dummy used for solid leafs
221 nodenum = 1;
222 while ( nodenum > 0 )
223 {
224 // botimport.Print(PRT_MESSAGE, "[%d]", nodenum);
225 #ifdef AAS_SAMPLE_DEBUG
226 if ( nodenum >= ( *aasworld ).numnodes ) {
227 botimport.Print( PRT_ERROR, "nodenum = %d >= (*aasworld).numnodes = %d\n", nodenum, ( *aasworld ).numnodes );
228 return 0;
229 } //end if
230 #endif //AAS_SAMPLE_DEBUG
231 node = &( *aasworld ).nodes[nodenum];
232 #ifdef AAS_SAMPLE_DEBUG
233 if ( node->planenum < 0 || node->planenum >= ( *aasworld ).numplanes ) {
234 botimport.Print( PRT_ERROR, "node->planenum = %d >= (*aasworld).numplanes = %d\n", node->planenum, ( *aasworld ).numplanes );
235 return 0;
236 } //end if
237 #endif //AAS_SAMPLE_DEBUG
238 plane = &( *aasworld ).planes[node->planenum];
239 dist = DotProduct( point, plane->normal ) - plane->dist;
240 if ( dist > 0 ) {
241 nodenum = node->children[0];
242 } else { nodenum = node->children[1];}
243 } //end while
244 if ( !nodenum ) {
245 #ifdef AAS_SAMPLE_DEBUG
246 botimport.Print( PRT_MESSAGE, "in solid\n" );
247 #endif //AAS_SAMPLE_DEBUG
248 return 0;
249 } //end if
250 return -nodenum;
251 } //end of the function AAS_PointAreaNum
252 //===========================================================================
253 //
254 // Parameter: -
255 // Returns: -
256 // Changes Globals: -
257 //===========================================================================
AAS_AreaCluster(int areanum)258 int AAS_AreaCluster( int areanum ) {
259 if ( areanum <= 0 || areanum >= ( *aasworld ).numareas ) {
260 botimport.Print( PRT_ERROR, "AAS_AreaCluster: invalid area number\n" );
261 return 0;
262 } //end if
263 return ( *aasworld ).areasettings[areanum].cluster;
264 } //end of the function AAS_AreaCluster
265 //===========================================================================
266 // returns the presence types of the given area
267 //
268 // Parameter: -
269 // Returns: -
270 // Changes Globals: -
271 //===========================================================================
AAS_AreaPresenceType(int areanum)272 int AAS_AreaPresenceType( int areanum ) {
273 if ( !( *aasworld ).loaded ) {
274 return 0;
275 }
276 if ( areanum <= 0 || areanum >= ( *aasworld ).numareas ) {
277 botimport.Print( PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n" );
278 return 0;
279 } //end if
280 return ( *aasworld ).areasettings[areanum].presencetype;
281 } //end of the function AAS_AreaPresenceType
282 //===========================================================================
283 // returns the presence type at the given point
284 //
285 // Parameter: -
286 // Returns: -
287 // Changes Globals: -
288 //===========================================================================
AAS_PointPresenceType(vec3_t point)289 int AAS_PointPresenceType( vec3_t point ) {
290 int areanum;
291
292 if ( !( *aasworld ).loaded ) {
293 return 0;
294 }
295
296 areanum = AAS_PointAreaNum( point );
297 if ( !areanum ) {
298 return PRESENCE_NONE;
299 }
300 return ( *aasworld ).areasettings[areanum].presencetype;
301 } //end of the function AAS_PointPresenceType
302 //===========================================================================
303 //
304 // Parameter: -
305 // Returns: -
306 // Changes Globals: -
307 //===========================================================================
AAS_AreaEntityCollision(int areanum,vec3_t start,vec3_t end,int presencetype,int passent,aas_trace_t * trace)308 qboolean AAS_AreaEntityCollision( int areanum, vec3_t start, vec3_t end,
309 int presencetype, int passent, aas_trace_t *trace ) {
310 int collision;
311 vec3_t boxmins, boxmaxs;
312 aas_link_t *link;
313 bsp_trace_t bsptrace;
314
315 AAS_PresenceTypeBoundingBox( presencetype, boxmins, boxmaxs );
316
317 memset( &bsptrace, 0, sizeof( bsp_trace_t ) ); //make compiler happy
318 //assume no collision
319 bsptrace.fraction = 1;
320 collision = qfalse;
321 for ( link = ( *aasworld ).arealinkedentities[areanum]; link; link = link->next_ent )
322 {
323 //ignore the pass entity
324 if ( link->entnum == passent ) {
325 continue;
326 }
327 //
328 if ( AAS_EntityCollision( link->entnum, start, boxmins, boxmaxs, end,
329 CONTENTS_SOLID | CONTENTS_PLAYERCLIP, &bsptrace ) ) {
330 collision = qtrue;
331 } //end if
332 } //end for
333 if ( collision ) {
334 trace->startsolid = bsptrace.startsolid;
335 trace->ent = bsptrace.ent;
336 VectorCopy( bsptrace.endpos, trace->endpos );
337 trace->area = 0;
338 trace->planenum = 0;
339 return qtrue;
340 } //end if
341 return qfalse;
342 } //end of the function AAS_AreaEntityCollision
343 //===========================================================================
344 // recursive subdivision of the line by the BSP tree.
345 //
346 // Parameter: -
347 // Returns: -
348 // Changes Globals: -
349 //===========================================================================
AAS_TraceClientBBox(vec3_t start,vec3_t end,int presencetype,int passent)350 aas_trace_t AAS_TraceClientBBox( vec3_t start, vec3_t end, int presencetype,
351 int passent ) {
352 int side, nodenum, tmpplanenum;
353 float front, back, frac;
354 vec3_t cur_start, cur_end, cur_mid, v1, v2;
355 aas_tracestack_t tracestack[127];
356 aas_tracestack_t *tstack_p;
357 aas_node_t *aasnode;
358 aas_plane_t *plane;
359 aas_trace_t trace;
360
361 //clear the trace structure
362 memset( &trace, 0, sizeof( aas_trace_t ) );
363
364 if ( !( *aasworld ).loaded ) {
365 return trace;
366 }
367
368 tstack_p = tracestack;
369 //we start with the whole line on the stack
370 VectorCopy( start, tstack_p->start );
371 VectorCopy( end, tstack_p->end );
372 tstack_p->planenum = 0;
373 //start with node 1 because node zero is a dummy for a solid leaf
374 tstack_p->nodenum = 1; //starting at the root of the tree
375 tstack_p++;
376
377 while ( 1 )
378 {
379 //pop up the stack
380 tstack_p--;
381 //if the trace stack is empty (ended up with a piece of the
382 //line to be traced in an area)
383 if ( tstack_p < tracestack ) {
384 tstack_p++;
385 //nothing was hit
386 trace.startsolid = qfalse;
387 trace.fraction = 1.0;
388 //endpos is the end of the line
389 VectorCopy( end, trace.endpos );
390 //nothing hit
391 trace.ent = 0;
392 trace.area = 0;
393 trace.planenum = 0;
394 return trace;
395 } //end if
396 //number of the current node to test the line against
397 nodenum = tstack_p->nodenum;
398 //if it is an area
399 if ( nodenum < 0 ) {
400 #ifdef AAS_SAMPLE_DEBUG
401 if ( -nodenum > ( *aasworld ).numareasettings ) {
402 botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n" );
403 return trace;
404 } //end if
405 #endif //AAS_SAMPLE_DEBUG
406 //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
407 //if can't enter the area because it hasn't got the right presence type
408 if ( !( ( *aasworld ).areasettings[-nodenum].presencetype & presencetype ) ) {
409 //if the start point is still the initial start point
410 //NOTE: no need for epsilons because the points will be
411 //exactly the same when they're both the start point
412 if ( tstack_p->start[0] == start[0] &&
413 tstack_p->start[1] == start[1] &&
414 tstack_p->start[2] == start[2] )
415 {
416 trace.startsolid = qtrue;
417 trace.fraction = 0.0;
418 VectorClear(v1);
419 } //end if
420 else
421 {
422 trace.startsolid = qfalse;
423 VectorSubtract( end, start, v1 );
424 VectorSubtract( tstack_p->start, start, v2 );
425 trace.fraction = VectorLength( v2 ) / VectorNormalize( v1 );
426 VectorMA( tstack_p->start, -0.125, v1, tstack_p->start );
427 } //end else
428 VectorCopy( tstack_p->start, trace.endpos );
429 trace.ent = 0;
430 trace.area = -nodenum;
431 // VectorSubtract(end, start, v1);
432 trace.planenum = tstack_p->planenum;
433 //always take the plane with normal facing towards the trace start
434 plane = &( *aasworld ).planes[trace.planenum];
435 if ( DotProduct( v1, plane->normal ) > 0 ) {
436 trace.planenum ^= 1;
437 }
438 return trace;
439 } //end if
440 else
441 {
442 if ( passent >= 0 ) {
443 if ( AAS_AreaEntityCollision( -nodenum, tstack_p->start,
444 tstack_p->end, presencetype, passent,
445 &trace ) ) {
446 if ( !trace.startsolid ) {
447 VectorSubtract( end, start, v1 );
448 VectorSubtract( trace.endpos, start, v2 );
449 trace.fraction = VectorLength( v2 ) / VectorLength( v1 );
450 } //end if
451 return trace;
452 } //end if
453 } //end if
454 } //end else
455 trace.lastarea = -nodenum;
456 continue;
457 } //end if
458 //if it is a solid leaf
459 if ( !nodenum ) {
460 //if the start point is still the initial start point
461 //NOTE: no need for epsilons because the points will be
462 //exactly the same when they're both the start point
463 if ( tstack_p->start[0] == start[0] &&
464 tstack_p->start[1] == start[1] &&
465 tstack_p->start[2] == start[2] )
466 {
467 trace.startsolid = qtrue;
468 trace.fraction = 0.0;
469 VectorClear(v1);
470 } //end if
471 else
472 {
473 trace.startsolid = qfalse;
474 VectorSubtract( end, start, v1 );
475 VectorSubtract( tstack_p->start, start, v2 );
476 trace.fraction = VectorLength( v2 ) / VectorNormalize( v1 );
477 VectorMA( tstack_p->start, -0.125, v1, tstack_p->start );
478 } //end else
479 VectorCopy( tstack_p->start, trace.endpos );
480 trace.ent = 0;
481 trace.area = 0; //hit solid leaf
482 // VectorSubtract(end, start, v1);
483 trace.planenum = tstack_p->planenum;
484 //always take the plane with normal facing towards the trace start
485 plane = &( *aasworld ).planes[trace.planenum];
486 if ( DotProduct( v1, plane->normal ) > 0 ) {
487 trace.planenum ^= 1;
488 }
489 return trace;
490 } //end if
491 #ifdef AAS_SAMPLE_DEBUG
492 if ( nodenum > ( *aasworld ).numnodes ) {
493 botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n" );
494 return trace;
495 } //end if
496 #endif //AAS_SAMPLE_DEBUG
497 //the node to test against
498 aasnode = &( *aasworld ).nodes[nodenum];
499 //start point of current line to test against node
500 VectorCopy( tstack_p->start, cur_start );
501 //end point of the current line to test against node
502 VectorCopy( tstack_p->end, cur_end );
503 //the current node plane
504 plane = &( *aasworld ).planes[aasnode->planenum];
505
506 switch ( plane->type )
507 {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!!
508 //check for axial planes
509 case PLANE_X:
510 {
511 front = cur_start[0] - plane->dist;
512 back = cur_end[0] - plane->dist;
513 break;
514 } //end case
515 case PLANE_Y:
516 {
517 front = cur_start[1] - plane->dist;
518 back = cur_end[1] - plane->dist;
519 break;
520 } //end case
521 case PLANE_Z:
522 {
523 front = cur_start[2] - plane->dist;
524 back = cur_end[2] - plane->dist;
525 break;
526 } //end case*/
527 default: //gee it's not an axial plane
528 {
529 front = DotProduct( cur_start, plane->normal ) - plane->dist;
530 back = DotProduct( cur_end, plane->normal ) - plane->dist;
531 break;
532 } //end default
533 } //end switch
534
535 //calculate the hitpoint with the node (split point of the line)
536 //put the crosspoint TRACEPLANE_EPSILON pixels on the near side
537 if ( front < 0 ) {
538 frac = ( front + TRACEPLANE_EPSILON ) / ( front - back );
539 } else { frac = ( front - TRACEPLANE_EPSILON ) / ( front - back );}
540 //if the whole to be traced line is totally at the front of this node
541 //only go down the tree with the front child
542 if ( ( front >= -ON_EPSILON && back >= -ON_EPSILON ) ) {
543 //keep the current start and end point on the stack
544 //and go down the tree with the front child
545 tstack_p->nodenum = aasnode->children[0];
546 tstack_p++;
547 if ( tstack_p >= &tracestack[127] ) {
548 botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" );
549 return trace;
550 } //end if
551 } //end if
552 //if the whole to be traced line is totally at the back of this node
553 //only go down the tree with the back child
554 else if ( ( front < ON_EPSILON && back < ON_EPSILON ) ) {
555 //keep the current start and end point on the stack
556 //and go down the tree with the back child
557 tstack_p->nodenum = aasnode->children[1];
558 tstack_p++;
559 if ( tstack_p >= &tracestack[127] ) {
560 botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" );
561 return trace;
562 } //end if
563 } //end if
564 //go down the tree both at the front and back of the node
565 else
566 {
567 tmpplanenum = tstack_p->planenum;
568 //
569 if ( frac < 0 ) {
570 frac = 0.001; //0
571 } else if ( frac > 1 ) {
572 frac = 0.999; //1
573 }
574 //frac = front / (front-back);
575 //
576 cur_mid[0] = cur_start[0] + ( cur_end[0] - cur_start[0] ) * frac;
577 cur_mid[1] = cur_start[1] + ( cur_end[1] - cur_start[1] ) * frac;
578 cur_mid[2] = cur_start[2] + ( cur_end[2] - cur_start[2] ) * frac;
579
580 // AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
581 //side the front part of the line is on
582 side = front < 0;
583 //first put the end part of the line on the stack (back side)
584 VectorCopy( cur_mid, tstack_p->start );
585 //not necesary to store because still on stack
586 //VectorCopy(cur_end, tstack_p->end);
587 tstack_p->planenum = aasnode->planenum;
588 tstack_p->nodenum = aasnode->children[!side];
589 tstack_p++;
590 if ( tstack_p >= &tracestack[127] ) {
591 botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" );
592 return trace;
593 } //end if
594 //now put the part near the start of the line on the stack so we will
595 //continue with thats part first. This way we'll find the first
596 //hit of the bbox
597 VectorCopy( cur_start, tstack_p->start );
598 VectorCopy( cur_mid, tstack_p->end );
599 tstack_p->planenum = tmpplanenum;
600 tstack_p->nodenum = aasnode->children[side];
601 tstack_p++;
602 if ( tstack_p >= &tracestack[127] ) {
603 botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" );
604 return trace;
605 } //end if
606 } //end else
607 } //end while
608 // return trace;
609 } //end of the function AAS_TraceClientBBox
610 //===========================================================================
611 // recursive subdivision of the line by the BSP tree.
612 //
613 // Parameter: -
614 // Returns: -
615 // Changes Globals: -
616 //===========================================================================
AAS_TraceAreas(vec3_t start,vec3_t end,int * areas,vec3_t * points,int maxareas)617 int AAS_TraceAreas( vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas ) {
618 int side, nodenum, tmpplanenum;
619 int numareas;
620 float front, back, frac;
621 vec3_t cur_start, cur_end, cur_mid;
622 aas_tracestack_t tracestack[127];
623 aas_tracestack_t *tstack_p;
624 aas_node_t *aasnode;
625 aas_plane_t *plane;
626
627 numareas = 0;
628 areas[0] = 0;
629 if ( !( *aasworld ).loaded ) {
630 return numareas;
631 }
632
633 tstack_p = tracestack;
634 //we start with the whole line on the stack
635 VectorCopy( start, tstack_p->start );
636 VectorCopy( end, tstack_p->end );
637 tstack_p->planenum = 0;
638 //start with node 1 because node zero is a dummy for a solid leaf
639 tstack_p->nodenum = 1; //starting at the root of the tree
640 tstack_p++;
641
642 while ( 1 )
643 {
644 //pop up the stack
645 tstack_p--;
646 //if the trace stack is empty (ended up with a piece of the
647 //line to be traced in an area)
648 if ( tstack_p < tracestack ) {
649 return numareas;
650 } //end if
651 //number of the current node to test the line against
652 nodenum = tstack_p->nodenum;
653 //if it is an area
654 if ( nodenum < 0 ) {
655 #ifdef AAS_SAMPLE_DEBUG
656 if ( -nodenum > ( *aasworld ).numareasettings ) {
657 botimport.Print( PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum );
658 return numareas;
659 } //end if
660 #endif //AAS_SAMPLE_DEBUG
661 //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
662 areas[numareas] = -nodenum;
663 if ( points ) {
664 VectorCopy( tstack_p->start, points[numareas] );
665 }
666 numareas++;
667 if ( numareas >= maxareas ) {
668 return numareas;
669 }
670 continue;
671 } //end if
672 //if it is a solid leaf
673 if ( !nodenum ) {
674 continue;
675 } //end if
676 #ifdef AAS_SAMPLE_DEBUG
677 if ( nodenum > ( *aasworld ).numnodes ) {
678 botimport.Print( PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n" );
679 return numareas;
680 } //end if
681 #endif //AAS_SAMPLE_DEBUG
682 //the node to test against
683 aasnode = &( *aasworld ).nodes[nodenum];
684 //start point of current line to test against node
685 VectorCopy( tstack_p->start, cur_start );
686 //end point of the current line to test against node
687 VectorCopy( tstack_p->end, cur_end );
688 //the current node plane
689 plane = &( *aasworld ).planes[aasnode->planenum];
690
691 switch ( plane->type )
692 {/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!!
693 //check for axial planes
694 case PLANE_X:
695 {
696 front = cur_start[0] - plane->dist;
697 back = cur_end[0] - plane->dist;
698 break;
699 } //end case
700 case PLANE_Y:
701 {
702 front = cur_start[1] - plane->dist;
703 back = cur_end[1] - plane->dist;
704 break;
705 } //end case
706 case PLANE_Z:
707 {
708 front = cur_start[2] - plane->dist;
709 back = cur_end[2] - plane->dist;
710 break;
711 } //end case*/
712 default: //gee it's not an axial plane
713 {
714 front = DotProduct( cur_start, plane->normal ) - plane->dist;
715 back = DotProduct( cur_end, plane->normal ) - plane->dist;
716 break;
717 } //end default
718 } //end switch
719
720 //if the whole to be traced line is totally at the front of this node
721 //only go down the tree with the front child
722 if ( front > 0 && back > 0 ) {
723 //keep the current start and end point on the stack
724 //and go down the tree with the front child
725 tstack_p->nodenum = aasnode->children[0];
726 tstack_p++;
727 if ( tstack_p >= &tracestack[127] ) {
728 botimport.Print( PRT_ERROR, "AAS_TraceAreas: stack overflow\n" );
729 return numareas;
730 } //end if
731 } //end if
732 //if the whole to be traced line is totally at the back of this node
733 //only go down the tree with the back child
734 else if ( front <= 0 && back <= 0 ) {
735 //keep the current start and end point on the stack
736 //and go down the tree with the back child
737 tstack_p->nodenum = aasnode->children[1];
738 tstack_p++;
739 if ( tstack_p >= &tracestack[127] ) {
740 botimport.Print( PRT_ERROR, "AAS_TraceAreas: stack overflow\n" );
741 return numareas;
742 } //end if
743 } //end if
744 //go down the tree both at the front and back of the node
745 else
746 {
747 tmpplanenum = tstack_p->planenum;
748 //calculate the hitpoint with the node (split point of the line)
749 //put the crosspoint TRACEPLANE_EPSILON pixels on the near side
750 if ( front < 0 ) {
751 frac = ( front ) / ( front - back );
752 } else { frac = ( front ) / ( front - back );}
753 if ( frac < 0 ) {
754 frac = 0;
755 } else if ( frac > 1 ) {
756 frac = 1;
757 }
758 //frac = front / (front-back);
759 //
760 cur_mid[0] = cur_start[0] + ( cur_end[0] - cur_start[0] ) * frac;
761 cur_mid[1] = cur_start[1] + ( cur_end[1] - cur_start[1] ) * frac;
762 cur_mid[2] = cur_start[2] + ( cur_end[2] - cur_start[2] ) * frac;
763
764 // AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
765 //side the front part of the line is on
766 side = front < 0;
767 //first put the end part of the line on the stack (back side)
768 VectorCopy( cur_mid, tstack_p->start );
769 //not necesary to store because still on stack
770 //VectorCopy(cur_end, tstack_p->end);
771 tstack_p->planenum = aasnode->planenum;
772 tstack_p->nodenum = aasnode->children[!side];
773 tstack_p++;
774 if ( tstack_p >= &tracestack[127] ) {
775 botimport.Print( PRT_ERROR, "AAS_TraceAreas: stack overflow\n" );
776 return numareas;
777 } //end if
778 //now put the part near the start of the line on the stack so we will
779 //continue with thats part first. This way we'll find the first
780 //hit of the bbox
781 VectorCopy( cur_start, tstack_p->start );
782 VectorCopy( cur_mid, tstack_p->end );
783 tstack_p->planenum = tmpplanenum;
784 tstack_p->nodenum = aasnode->children[side];
785 tstack_p++;
786 if ( tstack_p >= &tracestack[127] ) {
787 botimport.Print( PRT_ERROR, "AAS_TraceAreas: stack overflow\n" );
788 return numareas;
789 } //end if
790 } //end else
791 } //end while
792 // return numareas;
793 } //end of the function AAS_TraceAreas
794 //===========================================================================
795 // a simple cross product
796 //
797 // Parameter: -
798 // Returns: -
799 // Changes Globals: -
800 //===========================================================================
801 // void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res)
802 #define AAS_OrthogonalToVectors( v1, v2, res ) \
803 ( res )[0] = ( ( v1 )[1] * ( v2 )[2] ) - ( ( v1 )[2] * ( v2 )[1] ); \
804 ( res )[1] = ( ( v1 )[2] * ( v2 )[0] ) - ( ( v1 )[0] * ( v2 )[2] ); \
805 ( res )[2] = ( ( v1 )[0] * ( v2 )[1] ) - ( ( v1 )[1] * ( v2 )[0] );
806 //===========================================================================
807 // tests if the given point is within the face boundaries
808 //
809 // Parameter: face : face to test if the point is in it
810 // pnormal : normal of the plane to use for the face
811 // point : point to test if inside face boundaries
812 // Returns: qtrue if the point is within the face boundaries
813 // Changes Globals: -
814 //===========================================================================
AAS_InsideFace(aas_face_t * face,vec3_t pnormal,vec3_t point,float epsilon)815 qboolean AAS_InsideFace( aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon ) {
816 int i, firstvertex, edgenum;
817 vec3_t v0;
818 vec3_t edgevec, pointvec, sepnormal;
819 aas_edge_t *edge;
820 #ifdef AAS_SAMPLE_DEBUG
821 int lastvertex = 0;
822 #endif //AAS_SAMPLE_DEBUG
823
824 if ( !( *aasworld ).loaded ) {
825 return qfalse;
826 }
827
828 for ( i = 0; i < face->numedges; i++ )
829 {
830 edgenum = ( *aasworld ).edgeindex[face->firstedge + i];
831 edge = &( *aasworld ).edges[abs( edgenum )];
832 //get the first vertex of the edge
833 firstvertex = edgenum < 0;
834 VectorCopy( ( *aasworld ).vertexes[edge->v[firstvertex]], v0 );
835 //edge vector
836 VectorSubtract( ( *aasworld ).vertexes[edge->v[!firstvertex]], v0, edgevec );
837 //
838 #ifdef AAS_SAMPLE_DEBUG
839 if ( lastvertex && lastvertex != edge->v[firstvertex] ) {
840 botimport.Print( PRT_MESSAGE, "winding not counter clockwise\n" );
841 } //end if
842 lastvertex = edge->v[!firstvertex];
843 #endif //AAS_SAMPLE_DEBUG
844 //vector from first edge point to point possible in face
845 VectorSubtract( point, v0, pointvec );
846 //get a vector pointing inside the face orthogonal to both the
847 //edge vector and the normal vector of the plane the face is in
848 //this vector defines a plane through the origin (first vertex of
849 //edge) and through both the edge vector and the normal vector
850 //of the plane
851 AAS_OrthogonalToVectors( edgevec, pnormal, sepnormal );
852 //check on wich side of the above plane the point is
853 //this is done by checking the sign of the dot product of the
854 //vector orthogonal vector from above and the vector from the
855 //origin (first vertex of edge) to the point
856 //if the dotproduct is smaller than zero the point is outside the face
857 if ( DotProduct( pointvec, sepnormal ) < -epsilon ) {
858 return qfalse;
859 }
860 } //end for
861 return qtrue;
862 } //end of the function AAS_InsideFace
863 //===========================================================================
864 //
865 // Parameter: -
866 // Returns: -
867 // Changes Globals: -
868 //===========================================================================
AAS_PointInsideFace(int facenum,vec3_t point,float epsilon)869 qboolean AAS_PointInsideFace( int facenum, vec3_t point, float epsilon ) {
870 int i, firstvertex, edgenum;
871 vec_t *v1, *v2;
872 vec3_t edgevec, pointvec, sepnormal;
873 aas_edge_t *edge;
874 aas_plane_t *plane;
875 aas_face_t *face;
876
877 if ( !( *aasworld ).loaded ) {
878 return qfalse;
879 }
880
881 face = &( *aasworld ).faces[facenum];
882 plane = &( *aasworld ).planes[face->planenum];
883 //
884 for ( i = 0; i < face->numedges; i++ )
885 {
886 edgenum = ( *aasworld ).edgeindex[face->firstedge + i];
887 edge = &( *aasworld ).edges[abs( edgenum )];
888 //get the first vertex of the edge
889 firstvertex = edgenum < 0;
890 v1 = ( *aasworld ).vertexes[edge->v[firstvertex]];
891 v2 = ( *aasworld ).vertexes[edge->v[!firstvertex]];
892 //edge vector
893 VectorSubtract( v2, v1, edgevec );
894 //vector from first edge point to point possible in face
895 VectorSubtract( point, v1, pointvec );
896 //
897 CrossProduct( edgevec, plane->normal, sepnormal );
898 //
899 if ( DotProduct( pointvec, sepnormal ) < -epsilon ) {
900 return qfalse;
901 }
902 } //end for
903 return qtrue;
904 } //end of the function AAS_PointInsideFace
905 //===========================================================================
906 // returns the ground face the given point is above in the given area
907 //
908 // Parameter: -
909 // Returns: -
910 // Changes Globals: -
911 //===========================================================================
AAS_AreaGroundFace(int areanum,vec3_t point)912 aas_face_t *AAS_AreaGroundFace( int areanum, vec3_t point ) {
913 int i, facenum;
914 vec3_t up = {0, 0, 1};
915 vec3_t normal;
916 aas_area_t *area;
917 aas_face_t *face;
918
919 if ( !( *aasworld ).loaded ) {
920 return NULL;
921 }
922
923 area = &( *aasworld ).areas[areanum];
924 for ( i = 0; i < area->numfaces; i++ )
925 {
926 facenum = ( *aasworld ).faceindex[area->firstface + i];
927 face = &( *aasworld ).faces[abs( facenum )];
928 //if this is a ground face
929 if ( face->faceflags & FACE_GROUND ) {
930 //get the up or down normal
931 if ( ( *aasworld ).planes[face->planenum].normal[2] < 0 ) {
932 VectorNegate( up, normal );
933 } else { VectorCopy( up, normal );}
934 //check if the point is in the face
935 if ( AAS_InsideFace( face, normal, point, 0.01 ) ) {
936 return face;
937 }
938 } //end if
939 } //end for
940 return NULL;
941 } //end of the function AAS_AreaGroundFace
942 //===========================================================================
943 // returns the face the trace end position is situated in
944 //
945 // Parameter: -
946 // Returns: -
947 // Changes Globals: -
948 //===========================================================================
AAS_FacePlane(int facenum,vec3_t normal,float * dist)949 void AAS_FacePlane( int facenum, vec3_t normal, float *dist ) {
950 aas_plane_t *plane;
951
952 plane = &( *aasworld ).planes[( *aasworld ).faces[facenum].planenum];
953 VectorCopy( plane->normal, normal );
954 *dist = plane->dist;
955 } //end of the function AAS_FacePlane
956 //===========================================================================
957 // returns the face the trace end position is situated in
958 //
959 // Parameter: -
960 // Returns: -
961 // Changes Globals: -
962 //===========================================================================
AAS_TraceEndFace(aas_trace_t * trace)963 aas_face_t *AAS_TraceEndFace( aas_trace_t *trace ) {
964 int i, facenum;
965 aas_area_t *area;
966 aas_face_t *face, *firstface = NULL;
967
968 if ( !( *aasworld ).loaded ) {
969 return NULL;
970 }
971
972 //if started in solid no face was hit
973 if ( trace->startsolid ) {
974 return NULL;
975 }
976 //trace->lastarea is the last area the trace was in
977 area = &( *aasworld ).areas[trace->lastarea];
978 //check which face the trace.endpos was in
979 for ( i = 0; i < area->numfaces; i++ )
980 {
981 facenum = ( *aasworld ).faceindex[area->firstface + i];
982 face = &( *aasworld ).faces[abs( facenum )];
983 //if the face is in the same plane as the trace end point
984 if ( ( face->planenum & ~1 ) == ( trace->planenum & ~1 ) ) {
985 //firstface is used for optimization, if theres only one
986 //face in the plane then it has to be the good one
987 //if there are more faces in the same plane then always
988 //check the one with the fewest edges first
989 /* if (firstface)
990 {
991 if (firstface->numedges < face->numedges)
992 {
993 if (AAS_InsideFace(firstface,
994 (*aasworld).planes[face->planenum].normal, trace->endpos))
995 {
996 return firstface;
997 } //end if
998 firstface = face;
999 } //end if
1000 else
1001 {
1002 if (AAS_InsideFace(face,
1003 (*aasworld).planes[face->planenum].normal, trace->endpos))
1004 {
1005 return face;
1006 } //end if
1007 } //end else
1008 } //end if
1009 else
1010 {
1011 firstface = face;
1012 } //end else*/
1013 if ( AAS_InsideFace( face,
1014 ( *aasworld ).planes[face->planenum].normal, trace->endpos, 0.01 ) ) {
1015 return face;
1016 }
1017 } //end if
1018 } //end for
1019 return firstface;
1020 } //end of the function AAS_TraceEndFace
1021 //===========================================================================
1022 //
1023 // Parameter: -
1024 // Returns: -
1025 // Changes Globals: -
1026 //===========================================================================
AAS_BoxOnPlaneSide2(vec3_t absmins,vec3_t absmaxs,aas_plane_t * p)1027 int AAS_BoxOnPlaneSide2( vec3_t absmins, vec3_t absmaxs, aas_plane_t *p ) {
1028 int i, sides;
1029 float dist1, dist2;
1030 vec3_t corners[2];
1031
1032 for ( i = 0; i < 3; i++ )
1033 {
1034 if ( p->normal[i] < 0 ) {
1035 corners[0][i] = absmins[i];
1036 corners[1][i] = absmaxs[i];
1037 } //end if
1038 else
1039 {
1040 corners[1][i] = absmins[i];
1041 corners[0][i] = absmaxs[i];
1042 } //end else
1043 } //end for
1044 dist1 = DotProduct( p->normal, corners[0] ) - p->dist;
1045 dist2 = DotProduct( p->normal, corners[1] ) - p->dist;
1046 sides = 0;
1047 if ( dist1 >= 0 ) {
1048 sides = 1;
1049 }
1050 if ( dist2 < 0 ) {
1051 sides |= 2;
1052 }
1053
1054 return sides;
1055 } //end of the function AAS_BoxOnPlaneSide2
1056 //===========================================================================
1057 //
1058 // Parameter: -
1059 // Returns: -
1060 // Changes Globals: -
1061 //===========================================================================
1062 //int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
1063 #define AAS_BoxOnPlaneSide( absmins, absmaxs, p ) ( \
1064 ( ( p )->type < 3 ) ? \
1065 ( \
1066 ( ( p )->dist <= ( absmins )[( p )->type] ) ? \
1067 ( \
1068 1 \
1069 ) \
1070 : \
1071 ( \
1072 ( ( p )->dist >= ( absmaxs )[( p )->type] ) ? \
1073 ( \
1074 2 \
1075 ) \
1076 : \
1077 ( \
1078 3 \
1079 ) \
1080 ) \
1081 ) \
1082 : \
1083 ( \
1084 AAS_BoxOnPlaneSide2( ( absmins ), ( absmaxs ), ( p ) ) \
1085 ) \
1086 ) //end of the function AAS_BoxOnPlaneSide
1087 //===========================================================================
1088 // remove the links to this entity from all areas
1089 //
1090 // Parameter: -
1091 // Returns: -
1092 // Changes Globals: -
1093 //===========================================================================
AAS_UnlinkFromAreas(aas_link_t * areas)1094 void AAS_UnlinkFromAreas( aas_link_t *areas ) {
1095 aas_link_t *link, *nextlink;
1096
1097 for ( link = areas; link; link = nextlink )
1098 {
1099 //next area the entity is linked in
1100 nextlink = link->next_area;
1101 //remove the entity from the linked list of this area
1102 if ( link->prev_ent ) {
1103 link->prev_ent->next_ent = link->next_ent;
1104 } else { ( *aasworld ).arealinkedentities[link->areanum] = link->next_ent;}
1105 if ( link->next_ent ) {
1106 link->next_ent->prev_ent = link->prev_ent;
1107 }
1108 //deallocate the link structure
1109 AAS_DeAllocAASLink( link );
1110 } //end for
1111 } //end of the function AAS_UnlinkFromAreas
1112 //===========================================================================
1113 // link the entity to the areas the bounding box is totally or partly
1114 // situated in. This is done with recursion down the tree using the
1115 // bounding box to test for plane sides
1116 //
1117 // Parameter: -
1118 // Returns: -
1119 // Changes Globals: -
1120 //===========================================================================
1121
1122 typedef struct
1123 {
1124 int nodenum; //node found after splitting
1125 } aas_linkstack_t;
1126
AAS_AASLinkEntity(vec3_t absmins,vec3_t absmaxs,int entnum)1127 aas_link_t *AAS_AASLinkEntity( vec3_t absmins, vec3_t absmaxs, int entnum ) {
1128 int side, nodenum;
1129 aas_linkstack_t linkstack[128];
1130 aas_linkstack_t *lstack_p;
1131 aas_node_t *aasnode;
1132 aas_plane_t *plane;
1133 aas_link_t *link, *areas;
1134
1135 if ( !( *aasworld ).loaded ) {
1136 botimport.Print( PRT_ERROR, "AAS_LinkEntity: aas not loaded\n" );
1137 return NULL;
1138 } //end if
1139
1140 areas = NULL;
1141 //
1142 lstack_p = linkstack;
1143 //we start with the whole line on the stack
1144 //start with node 1 because node zero is a dummy used for solid leafs
1145 lstack_p->nodenum = 1; //starting at the root of the tree
1146 lstack_p++;
1147
1148 while ( 1 )
1149 {
1150 //pop up the stack
1151 lstack_p--;
1152 //if the trace stack is empty (ended up with a piece of the
1153 //line to be traced in an area)
1154 if ( lstack_p < linkstack ) {
1155 break;
1156 }
1157 //number of the current node to test the line against
1158 nodenum = lstack_p->nodenum;
1159 //if it is an area
1160 if ( nodenum < 0 ) {
1161 //NOTE: the entity might have already been linked into this area
1162 // because several node children can point to the same area
1163 for ( link = ( *aasworld ).arealinkedentities[-nodenum]; link; link = link->next_ent )
1164 {
1165 if ( link->entnum == entnum ) {
1166 break;
1167 }
1168 } //end for
1169 if ( link ) {
1170 continue;
1171 }
1172 //
1173 link = AAS_AllocAASLink();
1174 if ( !link ) {
1175 return areas;
1176 }
1177 link->entnum = entnum;
1178 link->areanum = -nodenum;
1179 //put the link into the double linked area list of the entity
1180 link->prev_area = NULL;
1181 link->next_area = areas;
1182 if ( areas ) {
1183 areas->prev_area = link;
1184 }
1185 areas = link;
1186 //put the link into the double linked entity list of the area
1187 link->prev_ent = NULL;
1188 link->next_ent = ( *aasworld ).arealinkedentities[-nodenum];
1189 if ( ( *aasworld ).arealinkedentities[-nodenum] ) {
1190 ( *aasworld ).arealinkedentities[-nodenum]->prev_ent = link;
1191 }
1192 ( *aasworld ).arealinkedentities[-nodenum] = link;
1193 //
1194 continue;
1195 } //end if
1196 //if solid leaf
1197 if ( !nodenum ) {
1198 continue;
1199 }
1200 //the node to test against
1201 aasnode = &( *aasworld ).nodes[nodenum];
1202 //the current node plane
1203 plane = &( *aasworld ).planes[aasnode->planenum];
1204 //get the side(s) the box is situated relative to the plane
1205 side = AAS_BoxOnPlaneSide2( absmins, absmaxs, plane );
1206 //if on the front side of the node
1207 if ( side & 1 ) {
1208 lstack_p->nodenum = aasnode->children[0];
1209 lstack_p++;
1210 } //end if
1211 if ( lstack_p >= &linkstack[127] ) {
1212 botimport.Print( PRT_ERROR, "AAS_LinkEntity: stack overflow\n" );
1213 break;
1214 } //end if
1215 //if on the back side of the node
1216 if ( side & 2 ) {
1217 lstack_p->nodenum = aasnode->children[1];
1218 lstack_p++;
1219 } //end if
1220 if ( lstack_p >= &linkstack[127] ) {
1221 botimport.Print( PRT_ERROR, "AAS_LinkEntity: stack overflow\n" );
1222 break;
1223 } //end if
1224 } //end while
1225 return areas;
1226 } //end of the function AAS_AASLinkEntity
1227 //===========================================================================
1228 //
1229 // Parameter: -
1230 // Returns: -
1231 // Changes Globals: -
1232 //===========================================================================
AAS_LinkEntityClientBBox(vec3_t absmins,vec3_t absmaxs,int entnum,int presencetype)1233 aas_link_t *AAS_LinkEntityClientBBox( vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype ) {
1234 vec3_t mins, maxs;
1235 vec3_t newabsmins, newabsmaxs;
1236
1237 AAS_PresenceTypeBoundingBox( presencetype, mins, maxs );
1238 VectorSubtract( absmins, maxs, newabsmins );
1239 VectorSubtract( absmaxs, mins, newabsmaxs );
1240 //relink the entity
1241 return AAS_AASLinkEntity( newabsmins, newabsmaxs, entnum );
1242 } //end of the function AAS_LinkEntityClientBBox
1243 //===========================================================================
1244 //
1245 // Parameter: -
1246 // Returns: -
1247 // Changes Globals: -
1248 //===========================================================================
AAS_PlaneFromNum(int planenum)1249 aas_plane_t *AAS_PlaneFromNum( int planenum ) {
1250 if ( !( *aasworld ).loaded ) {
1251 return NULL;
1252 }
1253
1254 return &( *aasworld ).planes[planenum];
1255 } //end of the function AAS_PlaneFromNum
1256
1257 /*
1258 =============
1259 AAS_BBoxAreas
1260 =============
1261 */
AAS_BBoxAreas(vec3_t absmins,vec3_t absmaxs,int * areas,int maxareas)1262 int AAS_BBoxAreas( vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas ) {
1263 aas_link_t *linkedareas, *link;
1264 int num;
1265
1266 linkedareas = AAS_AASLinkEntity( absmins, absmaxs, -1 );
1267 num = 0;
1268 for ( link = linkedareas; link; link = link->next_area )
1269 {
1270 areas[num] = link->areanum;
1271 num++;
1272 if ( num >= maxareas ) {
1273 break;
1274 }
1275 } //end for
1276 AAS_UnlinkFromAreas( linkedareas );
1277 return num;
1278 } //end of the function AAS_BBoxAreas
1279