1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 #include "cm_local.h"
23
24
25 /*
26 ==================
27 CM_PointLeafnum_r
28
29 ==================
30 */
CM_PointLeafnum_r(const vec3_t p,int num)31 int CM_PointLeafnum_r( const vec3_t p, int num ) {
32 float d;
33 cNode_t *node;
34 cplane_t *plane;
35
36 while (num >= 0)
37 {
38 node = cm.nodes + num;
39 plane = node->plane;
40
41 if (plane->type < 3)
42 d = p[plane->type] - plane->dist;
43 else
44 d = DotProduct (plane->normal, p) - plane->dist;
45 if (d < 0)
46 num = node->children[1];
47 else
48 num = node->children[0];
49 }
50
51 c_pointcontents++; // optimize counter
52
53 return -1 - num;
54 }
55
CM_PointLeafnum(const vec3_t p)56 int CM_PointLeafnum( const vec3_t p ) {
57 if ( !cm.numNodes ) { // map not loaded
58 return 0;
59 }
60 return CM_PointLeafnum_r (p, 0);
61 }
62
63
64 /*
65 ======================================================================
66
67 LEAF LISTING
68
69 ======================================================================
70 */
71
72
CM_StoreLeafs(leafList_t * ll,int nodenum)73 void CM_StoreLeafs( leafList_t *ll, int nodenum ) {
74 int leafNum;
75
76 leafNum = -1 - nodenum;
77
78 // store the lastLeaf even if the list is overflowed
79 if ( cm.leafs[ leafNum ].cluster != -1 ) {
80 ll->lastLeaf = leafNum;
81 }
82
83 if ( ll->count >= ll->maxcount) {
84 ll->overflowed = qtrue;
85 return;
86 }
87 ll->list[ ll->count++ ] = leafNum;
88 }
89
CM_StoreBrushes(leafList_t * ll,int nodenum)90 void CM_StoreBrushes( leafList_t *ll, int nodenum ) {
91 int i, k;
92 int leafnum;
93 int brushnum;
94 cLeaf_t *leaf;
95 cbrush_t *b;
96
97 leafnum = -1 - nodenum;
98
99 leaf = &cm.leafs[leafnum];
100
101 for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) {
102 brushnum = cm.leafbrushes[leaf->firstLeafBrush+k];
103 b = &cm.brushes[brushnum];
104 if ( b->checkcount == cm.checkcount ) {
105 continue; // already checked this brush in another leaf
106 }
107 b->checkcount = cm.checkcount;
108 for ( i = 0 ; i < 3 ; i++ ) {
109 if ( b->bounds[0][i] >= ll->bounds[1][i] || b->bounds[1][i] <= ll->bounds[0][i] ) {
110 break;
111 }
112 }
113 if ( i != 3 ) {
114 continue;
115 }
116 if ( ll->count >= ll->maxcount) {
117 ll->overflowed = qtrue;
118 return;
119 }
120 ((cbrush_t **)ll->list)[ ll->count++ ] = b;
121 }
122 #if 0
123 // store patches?
124 for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) {
125 patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstleafsurface + k ] ];
126 if ( !patch ) {
127 continue;
128 }
129 }
130 #endif
131 }
132
133 /*
134 =============
135 CM_BoxLeafnums
136
137 Fills in a list of all the leafs touched
138 =============
139 */
CM_BoxLeafnums_r(leafList_t * ll,int nodenum)140 void CM_BoxLeafnums_r( leafList_t *ll, int nodenum ) {
141 cplane_t *plane;
142 cNode_t *node;
143 int s;
144
145 while (1) {
146 if (nodenum < 0) {
147 ll->storeLeafs( ll, nodenum );
148 return;
149 }
150
151 node = &cm.nodes[nodenum];
152 plane = node->plane;
153 s = BoxOnPlaneSide( ll->bounds[0], ll->bounds[1], plane );
154 if (s == 1) {
155 nodenum = node->children[0];
156 } else if (s == 2) {
157 nodenum = node->children[1];
158 } else {
159 // go down both
160 CM_BoxLeafnums_r( ll, node->children[0] );
161 nodenum = node->children[1];
162 }
163
164 }
165 }
166
167 /*
168 ==================
169 CM_BoxLeafnums
170 ==================
171 */
CM_BoxLeafnums(const vec3_t mins,const vec3_t maxs,int * list,int listsize,int * lastLeaf)172 int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *lastLeaf) {
173 leafList_t ll;
174
175 cm.checkcount++;
176
177 VectorCopy( mins, ll.bounds[0] );
178 VectorCopy( maxs, ll.bounds[1] );
179 ll.count = 0;
180 ll.maxcount = listsize;
181 ll.list = list;
182 ll.storeLeafs = CM_StoreLeafs;
183 ll.lastLeaf = 0;
184 ll.overflowed = qfalse;
185
186 CM_BoxLeafnums_r( &ll, 0 );
187
188 *lastLeaf = ll.lastLeaf;
189 return ll.count;
190 }
191
192 /*
193 ==================
194 CM_BoxBrushes
195 ==================
196 */
CM_BoxBrushes(const vec3_t mins,const vec3_t maxs,cbrush_t ** list,int listsize)197 int CM_BoxBrushes( const vec3_t mins, const vec3_t maxs, cbrush_t **list, int listsize ) {
198 leafList_t ll;
199
200 cm.checkcount++;
201
202 VectorCopy( mins, ll.bounds[0] );
203 VectorCopy( maxs, ll.bounds[1] );
204 ll.count = 0;
205 ll.maxcount = listsize;
206 ll.list = (void *)list;
207 ll.storeLeafs = CM_StoreBrushes;
208 ll.lastLeaf = 0;
209 ll.overflowed = qfalse;
210
211 CM_BoxLeafnums_r( &ll, 0 );
212
213 return ll.count;
214 }
215
216
217 //====================================================================
218
219
220 /*
221 ==================
222 CM_PointContents
223
224 ==================
225 */
CM_PointContents(const vec3_t p,clipHandle_t model)226 int CM_PointContents( const vec3_t p, clipHandle_t model ) {
227 int leafnum;
228 int i, k;
229 int brushnum;
230 cLeaf_t *leaf;
231 cbrush_t *b;
232 int contents;
233 float d;
234 cmodel_t *clipm;
235
236 if (!cm.numNodes) { // map not loaded
237 return 0;
238 }
239
240 if ( model ) {
241 clipm = CM_ClipHandleToModel( model );
242 leaf = &clipm->leaf;
243 } else {
244 leafnum = CM_PointLeafnum_r (p, 0);
245 leaf = &cm.leafs[leafnum];
246 }
247
248 contents = 0;
249 for (k=0 ; k<leaf->numLeafBrushes ; k++) {
250 brushnum = cm.leafbrushes[leaf->firstLeafBrush+k];
251 b = &cm.brushes[brushnum];
252
253 if ( !CM_BoundsIntersectPoint( b->bounds[0], b->bounds[1], p ) ) {
254 continue;
255 }
256
257 // see if the point is in the brush
258 for ( i = 0 ; i < b->numsides ; i++ ) {
259 d = DotProduct( p, b->sides[i].plane->normal );
260 // FIXME test for Cash
261 // if ( d >= b->sides[i].plane->dist ) {
262 if ( d > b->sides[i].plane->dist ) {
263 break;
264 }
265 }
266
267 if ( i == b->numsides ) {
268 contents |= b->contents;
269 }
270 }
271
272 return contents;
273 }
274
275 /*
276 ==================
277 CM_TransformedPointContents
278
279 Handles offseting and rotation of the end points for moving and
280 rotating entities
281 ==================
282 */
CM_TransformedPointContents(const vec3_t p,clipHandle_t model,const vec3_t origin,const vec3_t angles)283 int CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles) {
284 vec3_t p_l;
285 vec3_t temp;
286 vec3_t forward, right, up;
287
288 // subtract origin offset
289 VectorSubtract (p, origin, p_l);
290
291 // rotate start and end into the models frame of reference
292 if ( model != BOX_MODEL_HANDLE &&
293 (angles[0] || angles[1] || angles[2]) )
294 {
295 AngleVectors (angles, forward, right, up);
296
297 VectorCopy (p_l, temp);
298 p_l[0] = DotProduct (temp, forward);
299 p_l[1] = -DotProduct (temp, right);
300 p_l[2] = DotProduct (temp, up);
301 }
302
303 return CM_PointContents( p_l, model );
304 }
305
306
307
308 /*
309 ===============================================================================
310
311 PVS
312
313 ===============================================================================
314 */
315
CM_ClusterPVS(int cluster)316 byte *CM_ClusterPVS (int cluster) {
317 if (cluster < 0 || cluster >= cm.numClusters || !cm.vised ) {
318 return cm.visibility;
319 }
320
321 return cm.visibility + cluster * cm.clusterBytes;
322 }
323
324
325
326 /*
327 ===============================================================================
328
329 AREAPORTALS
330
331 ===============================================================================
332 */
333
CM_FloodArea_r(int areaNum,int floodnum)334 void CM_FloodArea_r( int areaNum, int floodnum) {
335 int i;
336 cArea_t *area;
337 int *con;
338
339 area = &cm.areas[ areaNum ];
340
341 if ( area->floodvalid == cm.floodvalid ) {
342 if (area->floodnum == floodnum)
343 return;
344 Com_Error (ERR_DROP, "FloodArea_r: reflooded");
345 }
346
347 area->floodnum = floodnum;
348 area->floodvalid = cm.floodvalid;
349 con = cm.areaPortals + areaNum * cm.numAreas;
350 for ( i=0 ; i < cm.numAreas ; i++ ) {
351 if ( con[i] > 0 ) {
352 CM_FloodArea_r( i, floodnum );
353 }
354 }
355 }
356
357 /*
358 ====================
359 CM_FloodAreaConnections
360
361 ====================
362 */
CM_FloodAreaConnections(void)363 void CM_FloodAreaConnections( void ) {
364 int i;
365 cArea_t *area;
366 int floodnum;
367
368 // all current floods are now invalid
369 cm.floodvalid++;
370 floodnum = 0;
371
372 for (i = 0 ; i < cm.numAreas ; i++) {
373 area = &cm.areas[i];
374 if (area->floodvalid == cm.floodvalid) {
375 continue; // already flooded into
376 }
377 floodnum++;
378 CM_FloodArea_r (i, floodnum);
379 }
380
381 }
382
383 /*
384 ====================
385 CM_AdjustAreaPortalState
386
387 ====================
388 */
CM_AdjustAreaPortalState(int area1,int area2,qboolean open)389 void CM_AdjustAreaPortalState( int area1, int area2, qboolean open ) {
390 if ( area1 < 0 || area2 < 0 ) {
391 return;
392 }
393
394 if ( area1 >= cm.numAreas || area2 >= cm.numAreas ) {
395 Com_Error (ERR_DROP, "CM_ChangeAreaPortalState: bad area number");
396 }
397
398 if ( open ) {
399 cm.areaPortals[ area1 * cm.numAreas + area2 ]++;
400 cm.areaPortals[ area2 * cm.numAreas + area1 ]++;
401 } else {
402 cm.areaPortals[ area1 * cm.numAreas + area2 ]--;
403 cm.areaPortals[ area2 * cm.numAreas + area1 ]--;
404 if ( cm.areaPortals[ area2 * cm.numAreas + area1 ] < 0 ) {
405 Com_Error (ERR_DROP, "CM_AdjustAreaPortalState: negative reference count");
406 }
407 }
408
409 CM_FloodAreaConnections ();
410 }
411
412 /*
413 ====================
414 CM_AreasConnected
415
416 ====================
417 */
CM_AreasConnected(int area1,int area2)418 qboolean CM_AreasConnected( int area1, int area2 ) {
419 #ifndef BSPC
420 if ( cm_noAreas->integer ) {
421 return qtrue;
422 }
423 #endif
424
425 if ( area1 < 0 || area2 < 0 ) {
426 return qfalse;
427 }
428
429 if (area1 >= cm.numAreas || area2 >= cm.numAreas) {
430 Com_Error (ERR_DROP, "area >= cm.numAreas");
431 }
432
433 if (cm.areas[area1].floodnum == cm.areas[area2].floodnum) {
434 return qtrue;
435 }
436 return qfalse;
437 }
438
439
440 /*
441 =================
442 CM_WriteAreaBits
443
444 Writes a bit vector of all the areas
445 that are in the same flood as the area parameter
446 Returns the number of bytes needed to hold all the bits.
447
448 The bits are OR'd in, so you can CM_WriteAreaBits from multiple
449 viewpoints and get the union of all visible areas.
450
451 This is used to cull non-visible entities from snapshots
452 =================
453 */
CM_WriteAreaBits(byte * buffer,int area)454 int CM_WriteAreaBits (byte *buffer, int area)
455 {
456 int i;
457 int floodnum;
458 int bytes;
459
460 bytes = (cm.numAreas+7)>>3;
461
462 #ifndef BSPC
463 if (cm_noAreas->integer || area == -1)
464 #else
465 if ( area == -1)
466 #endif
467 { // for debugging, send everything
468 Com_Memset (buffer, 255, bytes);
469 }
470 else
471 {
472 floodnum = cm.areas[area].floodnum;
473 for (i=0 ; i<cm.numAreas ; i++)
474 {
475 if (cm.areas[i].floodnum == floodnum || area == -1)
476 buffer[i>>3] |= 1<<(i&7);
477 }
478 }
479
480 return bytes;
481 }
482
483 /*
484 ====================
485 CM_BoundsIntersect
486 ====================
487 */
CM_BoundsIntersect(const vec3_t mins,const vec3_t maxs,const vec3_t mins2,const vec3_t maxs2)488 qboolean CM_BoundsIntersect( const vec3_t mins, const vec3_t maxs, const vec3_t mins2, const vec3_t maxs2 )
489 {
490 if (maxs[0] < mins2[0] - SURFACE_CLIP_EPSILON ||
491 maxs[1] < mins2[1] - SURFACE_CLIP_EPSILON ||
492 maxs[2] < mins2[2] - SURFACE_CLIP_EPSILON ||
493 mins[0] > maxs2[0] + SURFACE_CLIP_EPSILON ||
494 mins[1] > maxs2[1] + SURFACE_CLIP_EPSILON ||
495 mins[2] > maxs2[2] + SURFACE_CLIP_EPSILON)
496 {
497 return qfalse;
498 }
499
500 return qtrue;
501 }
502
503 /*
504 ====================
505 CM_BoundsIntersectPoint
506 ====================
507 */
CM_BoundsIntersectPoint(const vec3_t mins,const vec3_t maxs,const vec3_t point)508 qboolean CM_BoundsIntersectPoint( const vec3_t mins, const vec3_t maxs, const vec3_t point )
509 {
510 if (maxs[0] < point[0] - SURFACE_CLIP_EPSILON ||
511 maxs[1] < point[1] - SURFACE_CLIP_EPSILON ||
512 maxs[2] < point[2] - SURFACE_CLIP_EPSILON ||
513 mins[0] > point[0] + SURFACE_CLIP_EPSILON ||
514 mins[1] > point[1] + SURFACE_CLIP_EPSILON ||
515 mins[2] > point[2] + SURFACE_CLIP_EPSILON)
516 {
517 return qfalse;
518 }
519
520 return qtrue;
521 }
522