2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
21 ----------------------------------------------------------------------------------
\r
23 This code has been altered significantly from its original form, to support
\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
\r
26 ------------------------------------------------------------------------------- */
\r
31 #define LIGHT_TRACE_C
\r
44 #define Vector2Copy( a, b ) ((b)[ 0 ] = (a)[ 0 ], (b)[ 1 ] = (a)[ 1 ])
\r
45 #define Vector4Copy( a, b ) ((b)[ 0 ] = (a)[ 0 ], (b)[ 1 ] = (a)[ 1 ], (b)[ 2 ] = (a)[ 2 ], (b)[ 3 ] = (a)[ 3 ])
\r
47 #define MAX_NODE_ITEMS 5
\r
48 #define MAX_NODE_TRIANGLES 5
\r
49 #define MAX_TRACE_DEPTH 32
\r
50 #define MIN_NODE_SIZE 32.0f
\r
52 #define GROW_TRACE_INFOS 32768 //% 4096
\r
53 #define GROW_TRACE_WINDINGS 65536 //% 32768
\r
54 #define GROW_TRACE_TRIANGLES 131072 //% 32768
\r
55 #define GROW_TRACE_NODES 16384 //% 16384
\r
56 #define GROW_NODE_ITEMS 16 //% 256
\r
58 #define MAX_TW_VERTS 12
\r
60 #define TRACE_ON_EPSILON 0.1f
\r
62 #define TRACE_LEAF -1
\r
63 #define TRACE_LEAF_SOLID -2
\r
65 typedef struct traceVert_s
\r
72 typedef struct traceInfo_s
\r
75 int surfaceNum, castShadows, padding;
\r
79 typedef struct traceWinding_s
\r
82 int infoNum, numVerts;
\r
83 traceVert_t v[ MAX_TW_VERTS ];
\r
87 typedef struct traceTriangle_s
\r
89 vec3_t edge1, edge2;
\r
90 int infoNum, padding;
\r
95 typedef struct traceNode_s
\r
101 int numItems, maxItems;
\r
107 int noDrawContentFlags, noDrawSurfaceFlags, noDrawCompileFlags;
\r
109 int numTraceInfos = 0, maxTraceInfos = 0, firstTraceInfo = 0;
\r
110 traceInfo_t *traceInfos = NULL;
\r
112 int numTraceWindings = 0, maxTraceWindings = 0, deadWinding = -1;
\r
113 traceWinding_t *traceWindings = NULL;
\r
115 int numTraceTriangles = 0, maxTraceTriangles = 0, deadTriangle = -1;
\r
116 traceTriangle_t *traceTriangles = NULL;
\r
118 int headNodeNum = 0, skyboxNodeNum = 0, maxTraceDepth = 0, numTraceLeafNodes = 0;
\r
119 int numTraceNodes = 0, maxTraceNodes = 0;
\r
120 traceNode_t *traceNodes = NULL;
\r
124 /* -------------------------------------------------------------------------------
\r
126 allocation and list management
\r
128 ------------------------------------------------------------------------------- */
\r
131 AddTraceInfo() - ydnar
\r
132 adds a trace info structure to the pool
\r
135 static int AddTraceInfo( traceInfo_t *ti )
\r
141 /* find an existing info */
\r
142 for( num = firstTraceInfo; num < numTraceInfos; num++ )
\r
144 if( traceInfos[ num ].si == ti->si &&
\r
145 traceInfos[ num ].surfaceNum == ti->surfaceNum &&
\r
146 traceInfos[ num ].castShadows == ti->castShadows )
\r
150 /* enough space? */
\r
151 if( numTraceInfos >= maxTraceInfos )
\r
153 /* allocate more room */
\r
154 maxTraceInfos += GROW_TRACE_INFOS;
\r
155 temp = safe_malloc( maxTraceInfos * sizeof( *traceInfos ) );
\r
156 if( traceInfos != NULL )
\r
158 memcpy( temp, traceInfos, numTraceInfos * sizeof( *traceInfos ) );
\r
159 free( traceInfos );
\r
161 traceInfos = (traceInfo_t*) temp;
\r
165 memcpy( &traceInfos[ num ], ti, sizeof( *traceInfos ) );
\r
166 if( num == numTraceInfos )
\r
169 /* return the ti number */
\r
176 AllocTraceNode() - ydnar
\r
177 allocates a new trace node
\r
180 static int AllocTraceNode( void )
\r
185 /* enough space? */
\r
186 if( numTraceNodes >= maxTraceNodes )
\r
188 /* reallocate more room */
\r
189 maxTraceNodes += GROW_TRACE_NODES;
\r
190 temp = safe_malloc( maxTraceNodes * sizeof( traceNode_t ) );
\r
191 if( traceNodes != NULL )
\r
193 memcpy( temp, traceNodes, numTraceNodes * sizeof( traceNode_t ) );
\r
194 free( traceNodes );
\r
200 memset( &traceNodes[ numTraceNodes ], 0, sizeof( traceNode_t ) );
\r
201 traceNodes[ numTraceNodes ].type = TRACE_LEAF;
\r
202 ClearBounds( traceNodes[ numTraceNodes ].mins, traceNodes[ numTraceNodes ].maxs );
\r
205 /* return the count */
\r
206 return (numTraceNodes - 1);
\r
212 AddTraceWinding() - ydnar
\r
213 adds a winding to the raytracing pool
\r
216 static int AddTraceWinding( traceWinding_t *tw )
\r
222 /* check for a dead winding */
\r
223 if( deadWinding >= 0 && deadWinding < numTraceWindings )
\r
227 /* put winding at the end of the list */
\r
228 num = numTraceWindings;
\r
230 /* enough space? */
\r
231 if( numTraceWindings >= maxTraceWindings )
\r
233 /* allocate more room */
\r
234 maxTraceWindings += GROW_TRACE_WINDINGS;
\r
235 temp = safe_malloc( maxTraceWindings * sizeof( *traceWindings ) );
\r
236 if( traceWindings != NULL )
\r
238 memcpy( temp, traceWindings, numTraceWindings * sizeof( *traceWindings ) );
\r
239 free( traceWindings );
\r
241 traceWindings = (traceWinding_t*) temp;
\r
245 /* add the winding */
\r
246 memcpy( &traceWindings[ num ], tw, sizeof( *traceWindings ) );
\r
247 if( num == numTraceWindings )
\r
248 numTraceWindings++;
\r
251 /* return the winding number */
\r
258 AddTraceTriangle() - ydnar
\r
259 adds a triangle to the raytracing pool
\r
262 static int AddTraceTriangle( traceTriangle_t *tt )
\r
268 /* check for a dead triangle */
\r
269 if( deadTriangle >= 0 && deadTriangle < numTraceTriangles )
\r
270 num = deadTriangle;
\r
273 /* put triangle at the end of the list */
\r
274 num = numTraceTriangles;
\r
276 /* enough space? */
\r
277 if( numTraceTriangles >= maxTraceTriangles )
\r
279 /* allocate more room */
\r
280 maxTraceTriangles += GROW_TRACE_TRIANGLES;
\r
281 temp = safe_malloc( maxTraceTriangles * sizeof( *traceTriangles ) );
\r
282 if( traceTriangles != NULL )
\r
284 memcpy( temp, traceTriangles, numTraceTriangles * sizeof( *traceTriangles ) );
\r
285 free( traceTriangles );
\r
287 traceTriangles = (traceTriangle_t*) temp;
\r
291 /* find vectors for two edges sharing the first vert */
\r
292 VectorSubtract( tt->v[ 1 ].xyz, tt->v[ 0 ].xyz, tt->edge1 );
\r
293 VectorSubtract( tt->v[ 2 ].xyz, tt->v[ 0 ].xyz, tt->edge2 );
\r
295 /* add the triangle */
\r
296 memcpy( &traceTriangles[ num ], tt, sizeof( *traceTriangles ) );
\r
297 if( num == numTraceTriangles )
\r
298 numTraceTriangles++;
\r
301 /* return the triangle number */
\r
308 AddItemToTraceNode() - ydnar
\r
309 adds an item reference (winding or triangle) to a trace node
\r
312 static int AddItemToTraceNode( traceNode_t *node, int num )
\r
321 /* enough space? */
\r
322 if( node->numItems >= node->maxItems )
\r
324 /* allocate more room */
\r
325 if( node == traceNodes )
\r
326 node->maxItems *= 2;
\r
328 node->maxItems += GROW_NODE_ITEMS;
\r
329 temp = safe_malloc( node->maxItems * sizeof( *node->items ) );
\r
330 if( node->items != NULL )
\r
332 memcpy( temp, node->items, node->numItems * sizeof( *node->items ) );
\r
333 free( node->items );
\r
335 node->items = (int*) temp;
\r
339 node->items[ node->numItems ] = num;
\r
342 /* return the count */
\r
343 return (node->numItems - 1);
\r
349 /* -------------------------------------------------------------------------------
\r
353 ------------------------------------------------------------------------------- */
\r
356 SetupTraceNodes_r() - ydnar
\r
357 recursively create the initial trace node structure from the bsp tree
\r
360 static int SetupTraceNodes_r( int bspNodeNum )
\r
362 int i, nodeNum, bspLeafNum;
\r
364 bspNode_t *bspNode;
\r
367 /* get bsp node and plane */
\r
368 bspNode = &bspNodes[ bspNodeNum ];
\r
369 plane = &bspPlanes[ bspNode->planeNum ];
\r
371 /* allocate a new trace node */
\r
372 nodeNum = AllocTraceNode();
\r
374 /* setup trace node */
\r
375 traceNodes[ nodeNum ].type = PlaneTypeForNormal( plane->normal );
\r
376 VectorCopy( plane->normal, traceNodes[ nodeNum ].plane );
\r
377 traceNodes[ nodeNum ].plane[ 3 ] = plane->dist;
\r
379 /* setup children */
\r
380 for( i = 0; i < 2; i++ )
\r
383 if( bspNode->children[ i ] < 0 )
\r
385 bspLeafNum = -bspNode->children[ i ] - 1;
\r
389 if( bspLeafs[ bspLeafNum ].cluster == -1 )
\r
390 traceNodes[ nodeNum ].children[ i ] = -1;
\r
392 /* passable leaf */
\r
394 traceNodes[ nodeNum ].children[ i ] = AllocTraceNode();
\r
398 traceNodes[ nodeNum ].children[ i ] = AllocTraceNode();
\r
399 if( bspLeafs[ bspLeafNum ].cluster == -1 )
\r
400 traceNodes[ traceNodes[ nodeNum ].children[ i ] ].type = TRACE_LEAF_SOLID;
\r
405 traceNodes[ nodeNum ].children[ i ] = SetupTraceNodes_r( bspNode->children[ i ] );
\r
408 /* return node number */
\r
415 ClipTraceWinding() - ydnar
\r
416 clips a trace winding against a plane into one or two parts
\r
419 #define TW_ON_EPSILON 0.25f
\r
421 void ClipTraceWinding( traceWinding_t *tw, vec4_t plane, traceWinding_t *front, traceWinding_t *back )
\r
424 int sides[ MAX_TW_VERTS ], counts[ 3 ] = { 0, 0, 0 };
\r
425 float dists[ MAX_TW_VERTS ];
\r
427 traceVert_t *a, *b, mid;
\r
430 /* clear front and back */
\r
431 front->numVerts = 0;
\r
432 back->numVerts = 0;
\r
434 /* classify points */
\r
435 for( i = 0; i < tw->numVerts; i++ )
\r
437 dists[ i ] = DotProduct( tw->v[ i ].xyz, plane ) - plane[ 3 ];
\r
438 if( dists[ i ] < -TW_ON_EPSILON )
\r
439 sides[ i ] = SIDE_BACK;
\r
440 else if( dists[ i ] > TW_ON_EPSILON )
\r
441 sides[ i ] = SIDE_FRONT;
\r
443 sides[ i ] = SIDE_ON;
\r
444 counts[ sides[ i ] ]++;
\r
447 /* entirely on front? */
\r
448 if( counts[ SIDE_BACK ] == 0 )
\r
449 memcpy( front, tw, sizeof( *front ) );
\r
451 /* entirely on back? */
\r
452 else if( counts[ SIDE_FRONT ] == 0 )
\r
453 memcpy( back, tw, sizeof( *back ) );
\r
455 /* straddles the plane */
\r
458 /* setup front and back */
\r
459 memcpy( front, tw, sizeof( *front ) );
\r
460 front->numVerts = 0;
\r
461 memcpy( back, tw, sizeof( *back ) );
\r
462 back->numVerts = 0;
\r
464 /* split the winding */
\r
465 for( i = 0; i < tw->numVerts; i++ )
\r
468 j = (i + 1) % tw->numVerts;
\r
474 /* handle points on the splitting plane */
\r
475 switch( sides[ i ] )
\r
478 if( front->numVerts >= MAX_TW_VERTS )
\r
479 Error( "MAX_TW_VERTS (%d) exceeded", MAX_TW_VERTS );
\r
480 front->v[ front->numVerts++ ] = *a;
\r
484 if( back->numVerts >= MAX_TW_VERTS )
\r
485 Error( "MAX_TW_VERTS (%d) exceeded", MAX_TW_VERTS );
\r
486 back->v[ back->numVerts++ ] = *a;
\r
490 if( front->numVerts >= MAX_TW_VERTS || back->numVerts >= MAX_TW_VERTS )
\r
491 Error( "MAX_TW_VERTS (%d) exceeded", MAX_TW_VERTS );
\r
492 front->v[ front->numVerts++ ] = *a;
\r
493 back->v[ back->numVerts++ ] = *a;
\r
497 /* check next point to see if we need to split the edge */
\r
498 if( sides[ j ] == SIDE_ON || sides[ j ] == sides[ i ] )
\r
502 if( front->numVerts >= MAX_TW_VERTS || back->numVerts >= MAX_TW_VERTS )
\r
503 Error( "MAX_TW_VERTS (%d) exceeded", MAX_TW_VERTS );
\r
505 /* generate a split point */
\r
506 frac = dists[ i ] / (dists[ i ] - dists[ j ]);
\r
507 for( k = 0; k < 3; k++ )
\r
509 /* minimize fp precision errors */
\r
510 if( plane[ k ] == 1.0f )
\r
511 mid.xyz[ k ] = plane[ 3 ];
\r
512 else if( plane[ k ] == -1.0f )
\r
513 mid.xyz[ k ] = -plane[ 3 ];
\r
515 mid.xyz[ k ] = a->xyz[ k ] + frac * (b->xyz[ k ] - a->xyz[ k ]);
\r
517 /* set texture coordinates */
\r
520 mid.st[ 0 ] = a->st[ 0 ] + frac * (b->st[ 0 ] - a->st[ 0 ]);
\r
521 mid.st[ 1 ] = a->st[ 1 ] + frac * (b->st[ 1 ] - a->st[ 1 ]);
\r
524 /* copy midpoint to front and back polygons */
\r
525 front->v[ front->numVerts++ ] = mid;
\r
526 back->v[ back->numVerts++ ] = mid;
\r
534 FilterPointToTraceNodes_r() - ydnar
\r
538 static int FilterPointToTraceNodes_r( vec3_t pt, int nodeNum )
\r
544 if( nodeNum < 0 || nodeNum >= numTraceNodes )
\r
547 node = &traceNodes[ nodeNum ];
\r
549 if( node->type >= 0 )
\r
551 dot = DotProduct( pt, node->plane ) - node->plane[ 3 ];
\r
552 if( dot > -0.001f )
\r
553 FilterPointToTraceNodes_r( pt, node->children[ 0 ] );
\r
555 FilterPointToTraceNodes_r( pt, node->children[ 1 ] );
\r
559 Sys_Printf( "%d ", nodeNum );
\r
567 FilterTraceWindingIntoNodes_r() - ydnar
\r
568 filters a trace winding into the raytracing tree
\r
571 static void FilterTraceWindingIntoNodes_r( traceWinding_t *tw, int nodeNum )
\r
574 vec4_t plane1, plane2, reverse;
\r
576 traceWinding_t front, back;
\r
579 /* don't filter if passed a bogus node (solid, etc) */
\r
580 if( nodeNum < 0 || nodeNum >= numTraceNodes )
\r
584 node = &traceNodes[ nodeNum ];
\r
586 /* is this a decision node? */
\r
587 if( node->type >= 0 )
\r
589 /* create winding plane if necessary, filtering out bogus windings as well */
\r
590 if( nodeNum == headNodeNum )
\r
592 if( !PlaneFromPoints( tw->plane, tw->v[ 0 ].xyz, tw->v[ 1 ].xyz, tw->v[ 2 ].xyz ) )
\r
596 /* validate the node */
\r
597 if( node->children[ 0 ] == 0 || node->children[ 1 ] == 0 )
\r
598 Error( "Invalid tracenode: %d", nodeNum );
\r
600 /* get node plane */
\r
601 Vector4Copy( node->plane, plane1 );
\r
603 /* get winding plane */
\r
604 Vector4Copy( tw->plane, plane2 );
\r
606 /* invert surface plane */
\r
607 VectorSubtract( vec3_origin, plane2, reverse );
\r
608 reverse[ 3 ] = -plane2[ 3 ];
\r
611 if( DotProduct( plane1, plane2 ) > 0.999f && fabs( plane1[ 3 ] - plane2[ 3 ] ) < 0.001f )
\r
613 FilterTraceWindingIntoNodes_r( tw, node->children[ 0 ] );
\r
618 if( DotProduct( plane1, reverse ) > 0.999f && fabs( plane1[ 3 ] - reverse[ 3 ] ) < 0.001f )
\r
620 FilterTraceWindingIntoNodes_r( tw, node->children[ 1 ] );
\r
624 /* clip the winding by node plane */
\r
625 ClipTraceWinding( tw, plane1, &front, &back );
\r
627 /* filter by node plane */
\r
628 if( front.numVerts >= 3 )
\r
629 FilterTraceWindingIntoNodes_r( &front, node->children[ 0 ] );
\r
630 if( back.numVerts >= 3 )
\r
631 FilterTraceWindingIntoNodes_r( &back, node->children[ 1 ] );
\r
633 /* return to caller */
\r
637 /* add winding to leaf node */
\r
638 num = AddTraceWinding( tw );
\r
639 AddItemToTraceNode( node, num );
\r
645 SubdivideTraceNode_r() - ydnar
\r
646 recursively subdivides a tracing node until it meets certain size and complexity criteria
\r
649 static void SubdivideTraceNode_r( int nodeNum, int depth )
\r
651 int i, j, count, num, frontNum, backNum, type;
\r
654 double average[ 3 ];
\r
655 traceNode_t *node, *frontNode, *backNode;
\r
656 traceWinding_t *tw, front, back;
\r
660 if( nodeNum < 0 || nodeNum >= numTraceNodes )
\r
664 node = &traceNodes[ nodeNum ];
\r
666 /* runaway recursion check */
\r
667 if( depth >= MAX_TRACE_DEPTH )
\r
669 //% Sys_Printf( "Depth: (%d items)\n", node->numItems );
\r
670 numTraceLeafNodes++;
\r
675 /* is this a decision node? */
\r
676 if( node->type >= 0 )
\r
678 /* subdivide children */
\r
679 frontNum = node->children[ 0 ];
\r
680 backNum = node->children[ 1 ];
\r
681 SubdivideTraceNode_r( frontNum, depth );
\r
682 SubdivideTraceNode_r( backNum, depth );
\r
686 /* bound the node */
\r
687 ClearBounds( node->mins, node->maxs );
\r
688 VectorClear( average );
\r
690 for( i = 0; i < node->numItems; i++ )
\r
693 tw = &traceWindings[ node->items[ i ] ];
\r
695 /* walk its verts */
\r
696 for( j = 0; j < tw->numVerts; j++ )
\r
698 AddPointToBounds( tw->v[ j ].xyz, node->mins, node->maxs );
\r
699 average[ 0 ] += tw->v[ j ].xyz[ 0 ];
\r
700 average[ 1 ] += tw->v[ j ].xyz[ 1 ];
\r
701 average[ 2 ] += tw->v[ j ].xyz[ 2 ];
\r
706 /* check triangle limit */
\r
707 //% if( node->numItems <= MAX_NODE_ITEMS )
\r
708 if( (count - (node->numItems * 2)) < MAX_NODE_TRIANGLES )
\r
710 //% Sys_Printf( "Limit: (%d triangles)\n", (count - (node->numItems * 2)) );
\r
711 numTraceLeafNodes++;
\r
715 /* the largest dimension of the bounding box will be the split axis */
\r
716 VectorSubtract( node->maxs, node->mins, size );
\r
717 if( size[ 0 ] >= size[ 1 ] && size[ 0 ] >= size[ 2 ] )
\r
719 else if( size[ 1 ] >= size[ 0 ] && size[ 1 ] >= size[ 2 ] )
\r
724 /* don't split small nodes */
\r
725 if( size[ type ] <= MIN_NODE_SIZE )
\r
727 //% Sys_Printf( "Limit: %f %f %f (%d items)\n", size[ 0 ], size[ 1 ], size[ 2 ], node->numItems );
\r
728 numTraceLeafNodes++;
\r
732 /* set max trace depth */
\r
733 if( depth > maxTraceDepth )
\r
734 maxTraceDepth = depth;
\r
736 /* snap the average */
\r
737 dist = floor( average[ type ] / count );
\r
739 /* dummy check it */
\r
740 if( dist <= node->mins[ type ] || dist >= node->maxs[ type ] )
\r
741 dist = floor( 0.5f * (node->mins[ type ] + node->maxs[ type ]) );
\r
743 /* allocate child nodes */
\r
744 frontNum = AllocTraceNode();
\r
745 backNum = AllocTraceNode();
\r
747 /* reset pointers */
\r
748 node = &traceNodes[ nodeNum ];
\r
749 frontNode = &traceNodes[ frontNum ];
\r
750 backNode = &traceNodes[ backNum ];
\r
752 /* attach children */
\r
754 node->plane[ type ] = 1.0f;
\r
755 node->plane[ 3 ] = dist;
\r
756 node->children[ 0 ] = frontNum;
\r
757 node->children[ 1 ] = backNum;
\r
759 /* setup front node */
\r
760 frontNode->maxItems = (node->maxItems >> 1);
\r
761 frontNode->items = safe_malloc( frontNode->maxItems * sizeof( *frontNode->items ) );
\r
763 /* setup back node */
\r
764 backNode->maxItems = (node->maxItems >> 1);
\r
765 backNode->items = safe_malloc( backNode->maxItems * sizeof( *backNode->items ) );
\r
767 /* filter windings into child nodes */
\r
768 for( i = 0; i < node->numItems; i++ )
\r
771 tw = &traceWindings[ node->items[ i ] ];
\r
773 /* clip the winding by the new split plane */
\r
774 ClipTraceWinding( tw, node->plane, &front, &back );
\r
776 /* kill the existing winding */
\r
777 if( front.numVerts >= 3 || back.numVerts >= 3 )
\r
778 deadWinding = node->items[ i ];
\r
780 /* add front winding */
\r
781 if( front.numVerts >= 3 )
\r
783 num = AddTraceWinding( &front );
\r
784 AddItemToTraceNode( frontNode, num );
\r
787 /* add back winding */
\r
788 if( back.numVerts >= 3 )
\r
790 num = AddTraceWinding( &back );
\r
791 AddItemToTraceNode( backNode, num );
\r
795 /* free original node winding list */
\r
796 node->numItems = 0;
\r
797 node->maxItems = 0;
\r
798 free( node->items );
\r
799 node->items = NULL;
\r
801 /* check children */
\r
802 if( frontNode->numItems <= 0 )
\r
804 frontNode->maxItems = 0;
\r
805 free( frontNode->items );
\r
806 frontNode->items = NULL;
\r
809 if( backNode->numItems <= 0 )
\r
811 backNode->maxItems = 0;
\r
812 free( backNode->items );
\r
813 backNode->items = NULL;
\r
816 /* subdivide children */
\r
817 SubdivideTraceNode_r( frontNum, depth );
\r
818 SubdivideTraceNode_r( backNum, depth );
\r
824 TriangulateTraceNode_r()
\r
825 optimizes the tracing data by changing trace windings into triangles
\r
828 static int TriangulateTraceNode_r( int nodeNum )
\r
830 int i, j, num, frontNum, backNum, numWindings, *windings;
\r
832 traceWinding_t *tw;
\r
833 traceTriangle_t tt;
\r
837 if( nodeNum < 0 || nodeNum >= numTraceNodes )
\r
841 node = &traceNodes[ nodeNum ];
\r
843 /* is this a decision node? */
\r
844 if( node->type >= 0 )
\r
846 /* triangulate children */
\r
847 frontNum = node->children[ 0 ];
\r
848 backNum = node->children[ 1 ];
\r
849 node->numItems = TriangulateTraceNode_r( frontNum );
\r
850 node->numItems += TriangulateTraceNode_r( backNum );
\r
851 return node->numItems;
\r
855 if( node->numItems == 0 )
\r
857 node->maxItems = 0;
\r
858 if( node->items != NULL )
\r
859 free( node->items );
\r
860 return node->numItems;
\r
863 /* store off winding data */
\r
864 numWindings = node->numItems;
\r
865 windings = node->items;
\r
868 node->numItems = 0;
\r
869 node->maxItems = numWindings * 2;
\r
870 node->items = safe_malloc( node->maxItems * sizeof( tt ) );
\r
872 /* walk winding list */
\r
873 for( i = 0; i < numWindings; i++ )
\r
876 tw = &traceWindings[ windings[ i ] ];
\r
878 /* initial setup */
\r
879 tt.infoNum = tw->infoNum;
\r
880 tt.v[ 0 ] = tw->v[ 0 ];
\r
882 /* walk vertex list */
\r
883 for( j = 1; j + 1 < tw->numVerts; j++ )
\r
886 tt.v[ 1 ] = tw->v[ j ];
\r
887 tt.v[ 2 ] = tw->v[ j + 1 ];
\r
889 /* find vectors for two edges sharing the first vert */
\r
890 VectorSubtract( tt.v[ 1 ].xyz, tt.v[ 0 ].xyz, tt.edge1 );
\r
891 VectorSubtract( tt.v[ 2 ].xyz, tt.v[ 0 ].xyz, tt.edge2 );
\r
893 /* add it to the node */
\r
894 num = AddTraceTriangle( &tt );
\r
895 AddItemToTraceNode( node, num );
\r
899 /* free windings */
\r
900 if( windings != NULL )
\r
903 /* return item count */
\r
904 return node->numItems;
\r
909 /* -------------------------------------------------------------------------------
\r
911 shadow casting item setup (triangles, patches, entities)
\r
913 ------------------------------------------------------------------------------- */
\r
916 PopulateWithBSPModel() - ydnar
\r
917 filters a bsp model's surfaces into the raytracing tree
\r
920 static void PopulateWithBSPModel( bspModel_t *model, m4x4_t transform )
\r
922 int i, j, x, y, pw[ 5 ], r, nodeNum;
\r
923 bspDrawSurface_t *ds;
\r
924 surfaceInfo_t *info;
\r
925 bspDrawVert_t *verts;
\r
927 mesh_t srcMesh, *mesh, *subdivided;
\r
933 if( model == NULL || transform == NULL )
\r
936 /* walk the list of surfaces in this model and fill out the info structs */
\r
937 for( i = 0; i < model->numBSPSurfaces; i++ )
\r
939 /* get surface and info */
\r
940 ds = &bspDrawSurfaces[ model->firstBSPSurface + i ];
\r
941 info = &surfaceInfos[ model->firstBSPSurface + i ];
\r
942 if( info->si == NULL )
\r
946 if( !info->castShadows )
\r
949 /* patchshadows? */
\r
950 if( ds->surfaceType == MST_PATCH && patchShadows == qfalse )
\r
953 /* some surfaces in the bsp might have been tagged as nodraw, with a bogus shader */
\r
954 if( (bspShaders[ ds->shaderNum ].contentFlags & noDrawContentFlags) ||
\r
955 (bspShaders[ ds->shaderNum ].surfaceFlags & noDrawSurfaceFlags) )
\r
958 /* translucent surfaces that are neither alphashadow or lightfilter don't cast shadows */
\r
959 if( (info->si->compileFlags & C_NODRAW) )
\r
961 if( (info->si->compileFlags & C_TRANSLUCENT) &&
\r
962 !(info->si->compileFlags & C_ALPHASHADOW) &&
\r
963 !(info->si->compileFlags & C_LIGHTFILTER) )
\r
966 /* setup trace info */
\r
968 ti.castShadows = info->castShadows;
\r
969 ti.surfaceNum = model->firstBSPBrush + i;
\r
971 /* setup trace winding */
\r
972 memset( &tw, 0, sizeof( tw ) );
\r
973 tw.infoNum = AddTraceInfo( &ti );
\r
976 /* choose which node (normal or skybox) */
\r
977 if( info->parentSurfaceNum >= 0 )
\r
978 nodeNum = skyboxNodeNum;
\r
980 nodeNum = headNodeNum;
\r
982 /* switch on type */
\r
983 switch( ds->surfaceType )
\r
985 /* handle patches */
\r
987 /* subdivide the surface */
\r
988 srcMesh.width = ds->patchWidth;
\r
989 srcMesh.height = ds->patchHeight;
\r
990 srcMesh.verts = &bspDrawVerts[ ds->firstVert ];
\r
991 //% subdivided = SubdivideMesh( srcMesh, 8, 512 );
\r
992 subdivided = SubdivideMesh2( srcMesh, info->patchIterations );
\r
994 /* fit it to the curve and remove colinear verts on rows/columns */
\r
995 PutMeshOnCurve( *subdivided );
\r
996 mesh = RemoveLinearMeshColumnsRows( subdivided );
\r
997 FreeMesh( subdivided );
\r
1000 verts = mesh->verts;
\r
1002 /* subdivide each quad to place the models */
\r
1003 for( y = 0; y < (mesh->height - 1); y++ )
\r
1005 for( x = 0; x < (mesh->width - 1); x++ )
\r
1008 pw[ 0 ] = x + (y * mesh->width);
\r
1009 pw[ 1 ] = x + ((y + 1) * mesh->width);
\r
1010 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
\r
1011 pw[ 3 ] = x + 1 + (y * mesh->width);
\r
1012 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
\r
1017 /* make first triangle */
\r
1018 VectorCopy( verts[ pw[ r + 0 ] ].xyz, tw.v[ 0 ].xyz );
\r
1019 Vector2Copy( verts[ pw[ r + 0 ] ].st, tw.v[ 0 ].st );
\r
1020 VectorCopy( verts[ pw[ r + 1 ] ].xyz, tw.v[ 1 ].xyz );
\r
1021 Vector2Copy( verts[ pw[ r + 1 ] ].st, tw.v[ 1 ].st );
\r
1022 VectorCopy( verts[ pw[ r + 2 ] ].xyz, tw.v[ 2 ].xyz );
\r
1023 Vector2Copy( verts[ pw[ r + 2 ] ].st, tw.v[ 2 ].st );
\r
1024 m4x4_transform_point( transform, tw.v[ 0 ].xyz );
\r
1025 m4x4_transform_point( transform, tw.v[ 1 ].xyz );
\r
1026 m4x4_transform_point( transform, tw.v[ 2 ].xyz );
\r
1027 FilterTraceWindingIntoNodes_r( &tw, nodeNum );
\r
1029 /* make second triangle */
\r
1030 VectorCopy( verts[ pw[ r + 0 ] ].xyz, tw.v[ 0 ].xyz );
\r
1031 Vector2Copy( verts[ pw[ r + 0 ] ].st, tw.v[ 0 ].st );
\r
1032 VectorCopy( verts[ pw[ r + 2 ] ].xyz, tw.v[ 1 ].xyz );
\r
1033 Vector2Copy( verts[ pw[ r + 2 ] ].st, tw.v[ 1 ].st );
\r
1034 VectorCopy( verts[ pw[ r + 3 ] ].xyz, tw.v[ 2 ].xyz );
\r
1035 Vector2Copy( verts[ pw[ r + 3 ] ].st, tw.v[ 2 ].st );
\r
1036 m4x4_transform_point( transform, tw.v[ 0 ].xyz );
\r
1037 m4x4_transform_point( transform, tw.v[ 1 ].xyz );
\r
1038 m4x4_transform_point( transform, tw.v[ 2 ].xyz );
\r
1039 FilterTraceWindingIntoNodes_r( &tw, nodeNum );
\r
1043 /* free the subdivided mesh */
\r
1047 /* handle triangle surfaces */
\r
1048 case MST_TRIANGLE_SOUP:
\r
1050 /* set verts and indexes */
\r
1051 verts = &bspDrawVerts[ ds->firstVert ];
\r
1052 indexes = &bspDrawIndexes[ ds->firstIndex ];
\r
1054 /* walk the triangle list */
\r
1055 for( j = 0; j < ds->numIndexes; j += 3 )
\r
1057 VectorCopy( verts[ indexes[ j ] ].xyz, tw.v[ 0 ].xyz );
\r
1058 Vector2Copy( verts[ indexes[ j ] ].st, tw.v[ 0 ].st );
\r
1059 VectorCopy( verts[ indexes[ j + 1 ] ].xyz, tw.v[ 1 ].xyz );
\r
1060 Vector2Copy( verts[ indexes[ j + 1 ] ].st, tw.v[ 1 ].st );
\r
1061 VectorCopy( verts[ indexes[ j + 2 ] ].xyz, tw.v[ 2 ].xyz );
\r
1062 Vector2Copy( verts[ indexes[ j + 2 ] ].st, tw.v[ 2 ].st );
\r
1063 m4x4_transform_point( transform, tw.v[ 0 ].xyz );
\r
1064 m4x4_transform_point( transform, tw.v[ 1 ].xyz );
\r
1065 m4x4_transform_point( transform, tw.v[ 2 ].xyz );
\r
1066 FilterTraceWindingIntoNodes_r( &tw, nodeNum );
\r
1070 /* other surface types do not cast shadows */
\r
1080 PopulateWithPicoModel() - ydnar
\r
1081 filters a picomodel's surfaces into the raytracing tree
\r
1084 static void PopulateWithPicoModel( int castShadows, picoModel_t *model, m4x4_t transform )
\r
1086 int i, j, k, numSurfaces, numIndexes;
\r
1087 picoSurface_t *surface;
\r
1088 picoShader_t *shader;
\r
1089 picoVec_t *xyz, *st;
\r
1090 picoIndex_t *indexes;
\r
1092 traceWinding_t tw;
\r
1096 if( model == NULL || transform == NULL )
\r
1100 numSurfaces = PicoGetModelNumSurfaces( model );
\r
1102 /* walk the list of surfaces in this model and fill out the info structs */
\r
1103 for( i = 0; i < numSurfaces; i++ )
\r
1106 surface = PicoGetModelSurface( model, i );
\r
1107 if( surface == NULL )
\r
1110 /* only handle triangle surfaces initially (fixme: support patches) */
\r
1111 if( PicoGetSurfaceType( surface ) != PICO_TRIANGLES )
\r
1114 /* get shader (fixme: support shader remapping) */
\r
1115 shader = PicoGetSurfaceShader( surface );
\r
1116 if( shader == NULL )
\r
1118 ti.si = ShaderInfoForShader( PicoGetShaderName( shader ) );
\r
1119 if( ti.si == NULL )
\r
1122 /* translucent surfaces that are neither alphashadow or lightfilter don't cast shadows */
\r
1123 if( (ti.si->compileFlags & C_NODRAW) )
\r
1125 if( (ti.si->compileFlags & C_TRANSLUCENT) &&
\r
1126 !(ti.si->compileFlags & C_ALPHASHADOW) &&
\r
1127 !(ti.si->compileFlags & C_LIGHTFILTER) )
\r
1130 /* setup trace info */
\r
1131 ti.castShadows = castShadows;
\r
1132 ti.surfaceNum = -1;
\r
1134 /* setup trace winding */
\r
1135 memset( &tw, 0, sizeof( tw ) );
\r
1136 tw.infoNum = AddTraceInfo( &ti );
\r
1140 numIndexes = PicoGetSurfaceNumIndexes( surface );
\r
1141 indexes = PicoGetSurfaceIndexes( surface, 0 );
\r
1143 /* walk the triangle list */
\r
1144 for( j = 0; j < numIndexes; j += 3, indexes += 3 )
\r
1146 for( k = 0; k < 3; k++ )
\r
1148 xyz = PicoGetSurfaceXYZ( surface, indexes[ k ] );
\r
1149 st = PicoGetSurfaceST( surface, 0, indexes[ k ] );
\r
1150 VectorCopy( xyz, tw.v[ k ].xyz );
\r
1151 Vector2Copy( st, tw.v[ k ].st );
\r
1152 m4x4_transform_point( transform, tw.v[ k ].xyz );
\r
1154 FilterTraceWindingIntoNodes_r( &tw, headNodeNum );
\r
1162 PopulateTraceNodes() - ydnar
\r
1163 fills the raytracing tree with world and entity occluders
\r
1166 static void PopulateTraceNodes( void )
\r
1168 int i, m, frame, castShadows;
\r
1171 const char *value;
\r
1172 picoModel_t *model;
\r
1173 vec3_t origin, scale, angles;
\r
1177 /* add worldspawn triangles */
\r
1178 m4x4_identity( transform );
\r
1179 PopulateWithBSPModel( &bspModels[ 0 ], transform );
\r
1181 /* walk each entity list */
\r
1182 for( i = 1; i < numEntities; i++ )
\r
1185 e = &entities[ i ];
\r
1187 /* get shadow flags */
\r
1188 castShadows = ENTITY_CAST_SHADOWS;
\r
1189 GetEntityShadowFlags( e, NULL, &castShadows, NULL );
\r
1192 if( !castShadows )
\r
1195 /* get entity origin */
\r
1196 GetVectorForKey( e, "origin", origin );
\r
1199 scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = 1.0f;
\r
1200 temp = FloatForKey( e, "modelscale" );
\r
1201 if( temp != 0.0f )
\r
1202 scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = temp;
\r
1203 value = ValueForKey( e, "modelscale_vec" );
\r
1204 if( value[ 0 ] != '\0' )
\r
1205 sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
\r
1207 /* get "angle" (yaw) or "angles" (pitch yaw roll) */
\r
1208 angles[ 0 ] = angles[ 1 ] = angles[ 2 ] = 0.0f;
\r
1209 angles[ 2 ] = FloatForKey( e, "angle" );
\r
1210 value = ValueForKey( e, "angles" );
\r
1211 if( value[ 0 ] != '\0' )
\r
1212 sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
\r
1214 /* set transform matrix (thanks spog) */
\r
1215 m4x4_identity( transform );
\r
1216 m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );
\r
1218 /* hack: Stable-1_2 and trunk have differing row/column major matrix order
\r
1219 this transpose is necessary with Stable-1_2
\r
1220 uncomment the following line with old m4x4_t (non 1.3/spog_branch) code */
\r
1221 //% m4x4_transpose( transform );
\r
1224 value = ValueForKey( e, "model" );
\r
1226 /* switch on model type */
\r
1227 switch( value[ 0 ] )
\r
1235 m = atoi( &value[ 1 ] );
\r
1236 if( m <= 0 || m >= numBSPModels )
\r
1238 PopulateWithBSPModel( &bspModels[ m ], transform );
\r
1241 /* external model */
\r
1243 frame = IntForKey( e, "_frame" );
\r
1244 model = LoadModel( (char*) value, frame );
\r
1245 if( model == NULL )
\r
1247 PopulateWithPicoModel( castShadows, model, transform );
\r
1252 value = ValueForKey( e, "model2" );
\r
1254 /* switch on model type */
\r
1255 switch( value[ 0 ] )
\r
1263 m = atoi( &value[ 1 ] );
\r
1264 if( m <= 0 || m >= numBSPModels )
\r
1266 PopulateWithBSPModel( &bspModels[ m ], transform );
\r
1269 /* external model */
\r
1271 frame = IntForKey( e, "_frame2" );
\r
1272 model = LoadModel( (char*) value, frame );
\r
1273 if( model == NULL )
\r
1275 PopulateWithPicoModel( castShadows, model, transform );
\r
1284 /* -------------------------------------------------------------------------------
\r
1286 trace initialization
\r
1288 ------------------------------------------------------------------------------- */
\r
1291 SetupTraceNodes() - ydnar
\r
1292 creates a balanced bsp with axis-aligned splits for efficient raytracing
\r
1295 void SetupTraceNodes( void )
\r
1298 Sys_FPrintf( SYS_VRB, "--- SetupTraceNodes ---\n" );
\r
1300 /* find nodraw bit */
\r
1301 noDrawContentFlags = noDrawSurfaceFlags = noDrawCompileFlags = 0;
\r
1302 ApplySurfaceParm( "nodraw", &noDrawContentFlags, &noDrawSurfaceFlags, &noDrawCompileFlags );
\r
1304 /* create the baseline raytracing tree from the bsp tree */
\r
1305 headNodeNum = SetupTraceNodes_r( 0 );
\r
1307 /* create outside node for skybox surfaces */
\r
1308 skyboxNodeNum = AllocTraceNode();
\r
1310 /* populate the tree with triangles from the world and shadow casting entities */
\r
1311 PopulateTraceNodes();
\r
1313 /* create the raytracing bsp */
\r
1314 if( loMem == qfalse )
\r
1316 SubdivideTraceNode_r( headNodeNum, 0 );
\r
1317 SubdivideTraceNode_r( skyboxNodeNum, 0 );
\r
1320 /* create triangles from the trace windings */
\r
1321 TriangulateTraceNode_r( headNodeNum );
\r
1322 TriangulateTraceNode_r( skyboxNodeNum );
\r
1324 /* emit some stats */
\r
1325 //% Sys_FPrintf( SYS_VRB, "%9d original triangles\n", numOriginalTriangles );
\r
1326 Sys_FPrintf( SYS_VRB, "%9d trace windings (%.2fMB)\n", numTraceWindings, (float) (numTraceWindings * sizeof( *traceWindings )) / (1024.0f * 1024.0f) );
\r
1327 Sys_FPrintf( SYS_VRB, "%9d trace triangles (%.2fMB)\n", numTraceTriangles, (float) (numTraceTriangles * sizeof( *traceTriangles )) / (1024.0f * 1024.0f) );
\r
1328 Sys_FPrintf( SYS_VRB, "%9d trace nodes (%.2fMB)\n", numTraceNodes, (float) (numTraceNodes * sizeof( *traceNodes )) / (1024.0f * 1024.0f) );
\r
1329 Sys_FPrintf( SYS_VRB, "%9d leaf nodes (%.2fMB)\n", numTraceLeafNodes, (float) (numTraceLeafNodes * sizeof( *traceNodes )) / (1024.0f * 1024.0f) );
\r
1330 //% Sys_FPrintf( SYS_VRB, "%9d average triangles per leaf node\n", numTraceTriangles / numTraceLeafNodes );
\r
1331 Sys_FPrintf( SYS_VRB, "%9d average windings per leaf node\n", numTraceWindings / (numTraceLeafNodes + 1) );
\r
1332 Sys_FPrintf( SYS_VRB, "%9d max trace depth\n", maxTraceDepth );
\r
1334 /* free trace windings */
\r
1335 free( traceWindings );
\r
1336 numTraceWindings = 0;
\r
1337 maxTraceWindings = 0;
\r
1340 /* debug code: write out trace triangles to an alias obj file */
\r
1345 char filename[ 1024 ];
\r
1346 traceWinding_t *tw;
\r
1349 /* open the file */
\r
1350 strcpy( filename, source );
\r
1351 StripExtension( filename );
\r
1352 strcat( filename, ".lin" );
\r
1353 Sys_Printf( "Opening light trace file %s...\n", filename );
\r
1354 file = fopen( filename, "w" );
\r
1355 if( file == NULL )
\r
1356 Error( "Error opening %s for writing", filename );
\r
1358 /* walk node list */
\r
1359 for( i = 0; i < numTraceWindings; i++ )
\r
1361 tw = &traceWindings[ i ];
\r
1362 for( j = 0; j < tw->numVerts + 1; j++ )
\r
1363 fprintf( file, "%f %f %f\n",
\r
1364 tw->v[ j % tw->numVerts ].xyz[ 0 ], tw->v[ j % tw->numVerts ].xyz[ 1 ], tw->v[ j % tw->numVerts ].xyz[ 2 ] );
\r
1375 /* -------------------------------------------------------------------------------
\r
1379 ------------------------------------------------------------------------------- */
\r
1383 based on code written by william 'spog' joseph
\r
1384 based on code originally written by tomas moller and ben trumbore, journal of graphics tools, 2(1):21-28, 1997
\r
1387 #define BARY_EPSILON 0.01f
\r
1388 #define ASLF_EPSILON 0.0001f /* so to not get double shadows */
\r
1389 #define COPLANAR_EPSILON 0.25f //% 0.000001f
\r
1390 #define NEAR_SHADOW_EPSILON 1.5f //% 1.25f
\r
1391 #define SELF_SHADOW_EPSILON 0.5f
\r
1393 qboolean TraceTriangle( traceInfo_t *ti, traceTriangle_t *tt, trace_t *trace )
\r
1396 float tvec[ 3 ], pvec[ 3 ], qvec[ 3 ];
\r
1397 float det, invDet, depth;
\r
1398 float u, v, w, s, t;
\r
1405 /* don't double-trace against sky */
\r
1407 if( trace->compileFlags & si->compileFlags & C_SKY )
\r
1410 /* receive shadows from worldspawn group only */
\r
1411 if( trace->recvShadows == 1 )
\r
1413 if( ti->castShadows != 1 )
\r
1417 /* receive shadows from same group and worldspawn group */
\r
1418 else if( trace->recvShadows > 1 )
\r
1420 if( ti->castShadows != 1 && abs( ti->castShadows ) != abs( trace->recvShadows ) )
\r
1422 //% Sys_Printf( "%d:%d ", tt->castShadows, trace->recvShadows );
\r
1425 /* receive shadows from the same group only (< 0) */
\r
1428 if( abs( ti->castShadows ) != abs( trace->recvShadows ) )
\r
1432 /* begin calculating determinant - also used to calculate u parameter */
\r
1433 CrossProduct( trace->direction, tt->edge2, pvec );
\r
1435 /* if determinant is near zero, trace lies in plane of triangle */
\r
1436 det = DotProduct( tt->edge1, pvec );
\r
1438 /* the non-culling branch */
\r
1439 if( det > -COPLANAR_EPSILON && det < COPLANAR_EPSILON )
\r
1441 invDet = 1.0f / det;
\r
1443 /* calculate distance from first vertex to ray origin */
\r
1444 VectorSubtract( trace->origin, tt->v[ 0 ].xyz, tvec );
\r
1446 /* calculate u parameter and test bounds */
\r
1447 u = DotProduct( tvec, pvec ) * invDet;
\r
1448 if( u < -BARY_EPSILON || u > (1.0f + BARY_EPSILON) )
\r
1451 /* prepare to test v parameter */
\r
1452 CrossProduct( tvec, tt->edge1, qvec );
\r
1454 /* calculate v parameter and test bounds */
\r
1455 v = DotProduct( trace->direction, qvec ) * invDet;
\r
1456 if( v < -BARY_EPSILON || (u + v) > (1.0f + BARY_EPSILON) )
\r
1459 /* calculate t (depth) */
\r
1460 depth = DotProduct( tt->edge2, qvec ) * invDet;
\r
1461 //% if( depth <= SELF_SHADOW_EPSILON || depth >= (trace->dist - SELF_SHADOW_EPSILON) )
\r
1462 //% return qfalse;
\r
1463 if( depth <= trace->inhibitRadius || depth >= trace->distance )
\r
1466 /* if hitpoint is really close to trace origin (sample point), then check for self-shadowing */
\r
1467 if( depth <= SELF_SHADOW_EPSILON )
\r
1469 /* don't self-shadow */
\r
1470 for( i = 0; i < trace->numSurfaces; i++ )
\r
1472 if( ti->surfaceNum == trace->surfaces[ i ] )
\r
1477 /* stack compile flags */
\r
1478 trace->compileFlags |= si->compileFlags;
\r
1480 /* don't trace against sky */
\r
1481 if( si->compileFlags & C_SKY )
\r
1484 /* most surfaces are completely opaque */
\r
1485 if( !(si->compileFlags & (C_ALPHASHADOW | C_LIGHTFILTER)) ||
\r
1486 si->lightImage == NULL || si->lightImage->pixels == NULL )
\r
1488 VectorClear( trace->color );
\r
1489 trace->opaque = qtrue;
\r
1493 /* try to avoid double shadows near triangle seams */
\r
1494 if( u < -ASLF_EPSILON || u > (1.0f + ASLF_EPSILON) ||
\r
1495 v < -ASLF_EPSILON || (u + v) > (1.0f + ASLF_EPSILON) )
\r
1498 /* calculate w parameter */
\r
1499 w = 1.0f - (u + v);
\r
1501 /* calculate st from uvw (barycentric) coordinates */
\r
1502 s = w * tt->v[ 0 ].st[ 0 ] + u * tt->v[ 1 ].st[ 0 ] + v * tt->v[ 2 ].st[ 0 ];
\r
1503 t = w * tt->v[ 0 ].st[ 1 ] + u * tt->v[ 1 ].st[ 1 ] + v * tt->v[ 2 ].st[ 1 ];
\r
1504 s = s - floor( s );
\r
1505 t = t - floor( t );
\r
1506 is = s * si->lightImage->width;
\r
1507 it = t * si->lightImage->height;
\r
1510 pixel = si->lightImage->pixels + 4 * (it * si->lightImage->width + is);
\r
1512 /* ydnar: color filter */
\r
1513 if( si->compileFlags & C_LIGHTFILTER )
\r
1515 /* filter by texture color */
\r
1516 trace->color[ 0 ] *= ((1.0f / 255.0f) * pixel[ 0 ]);
\r
1517 trace->color[ 1 ] *= ((1.0f / 255.0f) * pixel[ 1 ]);
\r
1518 trace->color[ 2 ] *= ((1.0f / 255.0f) * pixel[ 2 ]);
\r
1521 /* ydnar: alpha filter */
\r
1522 if( si->compileFlags & C_ALPHASHADOW )
\r
1524 /* filter by inverse texture alpha */
\r
1525 shadow = (1.0f / 255.0f) * (255 - pixel[ 3 ]);
\r
1526 trace->color[ 0 ] *= shadow;
\r
1527 trace->color[ 1 ] *= shadow;
\r
1528 trace->color[ 2 ] *= shadow;
\r
1531 /* check filter for opaque */
\r
1532 if( trace->color[ 0 ] <= 0.001f && trace->color[ 1 ] <= 0.001f && trace->color[ 2 ] <= 0.001f )
\r
1534 trace->opaque = qtrue;
\r
1538 /* continue tracing */
\r
1545 TraceWinding() - ydnar
\r
1549 qboolean TraceWinding( traceWinding_t *tw, trace_t *trace )
\r
1552 traceTriangle_t tt;
\r
1555 /* initial setup */
\r
1556 tt.infoNum = tw->infoNum;
\r
1557 tt.v[ 0 ] = tw->v[ 0 ];
\r
1559 /* walk vertex list */
\r
1560 for( i = 1; i + 1 < tw->numVerts; i++ )
\r
1563 tt.v[ 1 ] = tw->v[ i ];
\r
1564 tt.v[ 2 ] = tw->v[ i + 1 ];
\r
1566 /* find vectors for two edges sharing the first vert */
\r
1567 VectorSubtract( tt.v[ 1 ].xyz, tt.v[ 0 ].xyz, tt.edge1 );
\r
1568 VectorSubtract( tt.v[ 2 ].xyz, tt.v[ 0 ].xyz, tt.edge2 );
\r
1571 if( TraceTriangle( &traceInfos[ tt.infoNum ], &tt, trace ) )
\r
1584 returns qtrue if something is hit and tracing can stop
\r
1587 static qboolean TraceLine_r( int nodeNum, vec3_t origin, vec3_t end, trace_t *trace )
\r
1589 traceNode_t *node;
\r
1591 float front, back, frac;
\r
1596 /* bogus node number means solid, end tracing unless testing all */
\r
1599 trace->passSolid = qtrue;
\r
1604 node = &traceNodes[ nodeNum ];
\r
1607 if( node->type == TRACE_LEAF_SOLID )
\r
1609 trace->passSolid = qtrue;
\r
1614 if( node->type < 0 )
\r
1616 /* note leaf and return */
\r
1617 if( node->numItems > 0 && trace->numTestNodes < MAX_TRACE_TEST_NODES )
\r
1618 trace->testNodes[ trace->numTestNodes++ ] = nodeNum;
\r
1622 /* ydnar 2003-09-07: don't test branches of the bsp with nothing in them when testall is enabled */
\r
1623 if( trace->testAll && node->numItems == 0 )
\r
1626 /* classify beginning and end points */
\r
1627 switch( node->type )
\r
1630 front = origin[ 0 ] - node->plane[ 3 ];
\r
1631 back = end[ 0 ] - node->plane[ 3 ];
\r
1635 front = origin[ 1 ] - node->plane[ 3 ];
\r
1636 back = end[ 1 ] - node->plane[ 3 ];
\r
1640 front = origin[ 2 ] - node->plane[ 3 ];
\r
1641 back = end[ 2 ] - node->plane[ 3 ];
\r
1645 front = DotProduct( origin, node->plane ) - node->plane[ 3 ];
\r
1646 back = DotProduct( end, node->plane ) - node->plane[ 3 ];
\r
1650 /* entirely in front side? */
\r
1651 if( front >= -TRACE_ON_EPSILON && back >= -TRACE_ON_EPSILON )
\r
1652 return TraceLine_r( node->children[ 0 ], origin, end, trace );
\r
1654 /* entirely on back side? */
\r
1655 if( front < TRACE_ON_EPSILON && back < TRACE_ON_EPSILON )
\r
1656 return TraceLine_r( node->children[ 1 ], origin, end, trace );
\r
1661 /* calculate intercept point */
\r
1662 frac = front / (front - back);
\r
1663 mid[ 0 ] = origin[ 0 ] + (end[ 0 ] - origin[ 0 ]) * frac;
\r
1664 mid[ 1 ] = origin[ 1 ] + (end[ 1 ] - origin[ 1 ]) * frac;
\r
1665 mid[ 2 ] = origin[ 2 ] + (end[ 2 ] - origin[ 2 ]) * frac;
\r
1667 /* fixme: check inhibit radius, then solid nodes and ignore */
\r
1669 /* trace first side */
\r
1670 r = TraceLine_r( node->children[ side ], origin, mid, trace );
\r
1674 /* trace other side */
\r
1675 return TraceLine_r( node->children[ !side ], mid, end, trace );
\r
1681 TraceLine() - ydnar
\r
1682 rewrote this function a bit :)
\r
1685 void TraceLine( trace_t *trace )
\r
1688 traceNode_t *node;
\r
1689 traceTriangle_t *tt;
\r
1693 /* setup output (note: this code assumes the input data is completely filled out) */
\r
1694 trace->passSolid = qfalse;
\r
1695 trace->opaque = qfalse;
\r
1696 trace->compileFlags = 0;
\r
1697 trace->numTestNodes = 0;
\r
1700 if( !trace->recvShadows || !trace->testOcclusion || trace->distance <= 0.00001f )
\r
1703 /* trace through nodes */
\r
1704 TraceLine_r( headNodeNum, trace->origin, trace->end, trace );
\r
1705 if( (trace->passSolid && !trace->testAll) )
\r
1707 trace->opaque = qtrue;
\r
1711 /* skip surfaces? */
\r
1715 /* testall means trace through sky */
\r
1716 if( trace->testAll && trace->numTestNodes < MAX_TRACE_TEST_NODES &&
\r
1717 (trace->numSurfaces == 0 || surfaceInfos[ trace->surfaces[ 0 ] ].childSurfaceNum < 0) )
\r
1719 //% trace->testNodes[ trace->numTestNodes++ ] = skyboxNodeNum;
\r
1720 TraceLine_r( skyboxNodeNum, trace->origin, trace->end, trace );
\r
1723 /* walk node list */
\r
1724 for( i = 0; i < trace->numTestNodes; i++ )
\r
1727 node = &traceNodes[ trace->testNodes[ i ] ];
\r
1729 /* walk node item list */
\r
1730 for( j = 0; j < node->numItems; j++ )
\r
1732 tt = &traceTriangles[ node->items[ j ] ];
\r
1733 ti = &traceInfos[ tt->infoNum ];
\r
1734 if( TraceTriangle( ti, tt, trace ) )
\r
1736 //% if( TraceWinding( &traceWindings[ node->items[ j ] ], trace ) )
\r
1745 SetupTrace() - ydnar
\r
1746 sets up certain trace values
\r
1749 float SetupTrace( trace_t *trace )
\r
1751 VectorSubtract( trace->end, trace->origin, trace->displacement );
\r
1752 trace->distance = VectorNormalize( trace->displacement, trace->direction );
\r
1753 return trace->distance;
\r