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
40 /* -------------------------------------------------------------------------------
\r
44 ------------------------------------------------------------------------------- */
\r
47 AllocSideRef() - ydnar
\r
48 allocates and assigns a brush side reference
\r
51 sideRef_t *AllocSideRef( side_t *side, sideRef_t *next )
\r
60 /* allocate and return */
\r
61 sideRef = safe_malloc( sizeof( *sideRef ) );
\r
62 sideRef->side = side;
\r
63 sideRef->next = next;
\r
71 counts the number of brushes in a brush linked list
\r
74 int CountBrushList( brush_t *brushes )
\r
80 for( brushes; brushes != NULL; brushes = brushes->next )
\r
89 allocates a new brush
\r
92 brush_t *AllocBrush( int numSides )
\r
98 /* allocate and clear */
\r
100 Error( "AllocBrush called with numsides = %d", numSides );
\r
101 c = (int) &(((brush_t*) 0)->sides[ numSides ]);
\r
102 bb = safe_malloc( c );
\r
103 memset( bb, 0, c );
\r
104 if( numthreads == 1 )
\r
105 numActiveBrushes++;
\r
115 frees a single brush and all sides/windings
\r
118 void FreeBrush( brush_t *b )
\r
124 if( *((int*) b) == 0xFEFEFEFE )
\r
126 Sys_FPrintf( SYS_VRB, "WARNING: Attempt to free an already freed brush!\n" );
\r
130 /* free brush sides */
\r
131 for( i = 0; i < b->numsides; i++ )
\r
132 if( b->sides[i].winding != NULL )
\r
133 FreeWinding( b->sides[ i ].winding );
\r
135 /* ydnar: overwrite it */
\r
136 memset( b, 0xFE, (int) &(((brush_t*) 0)->sides[ b->numsides ]) );
\r
137 *((int*) b) = 0xFEFEFEFE;
\r
141 if( numthreads == 1 )
\r
142 numActiveBrushes--;
\r
149 frees a linked list of brushes
\r
152 void FreeBrushList( brush_t *brushes )
\r
157 /* walk brush list */
\r
158 for( brushes; brushes != NULL; brushes = next )
\r
160 next = brushes->next;
\r
161 FreeBrush( brushes );
\r
169 duplicates the brush, sides, and windings
\r
172 brush_t *CopyBrush( brush_t *brush )
\r
180 size = (int) &(((brush_t*) 0)->sides[ brush->numsides ]);
\r
181 newBrush = AllocBrush( brush->numsides );
\r
182 memcpy( newBrush, brush, size );
\r
184 /* ydnar: nuke linked list */
\r
185 newBrush->next = NULL;
\r
188 for( i = 0; i < brush->numsides; i++ )
\r
190 if( brush->sides[ i ].winding != NULL )
\r
191 newBrush->sides[ i ].winding = CopyWinding( brush->sides[ i ].winding );
\r
203 sets the mins/maxs based on the windings
\r
204 returns false if the brush doesn't enclose a valid volume
\r
207 qboolean BoundBrush( brush_t *brush )
\r
213 ClearBounds( brush->mins, brush->maxs );
\r
214 for( i = 0; i < brush->numsides; i++ )
\r
216 w = brush->sides[ i ].winding;
\r
219 for( j = 0; j < w->numpoints; j++ )
\r
220 AddPointToBounds( w->p[ j ], brush->mins, brush->maxs );
\r
223 for( i = 0; i < 3; i++ )
\r
225 if( brush->mins[ i ] < MIN_WORLD_COORD || brush->maxs[ i ] > MAX_WORLD_COORD || brush->mins[i] >= brush->maxs[ i ] )
\r
236 SnapWeldVector() - ydnar
\r
237 welds two vec3_t's into a third, taking into account nearest-to-integer
\r
238 instead of averaging
\r
241 #define SNAP_EPSILON 0.01
\r
243 void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out )
\r
246 vec_t ai, bi, outi;
\r
250 if( a == NULL || b == NULL || out == NULL )
\r
253 /* do each element */
\r
254 for( i = 0; i < 3; i++ )
\r
256 /* round to integer */
\r
257 ai = Q_rint( a[ i ] );
\r
258 bi = Q_rint( a[ i ] );
\r
260 /* prefer exact integer */
\r
263 else if( bi == b[ i ] )
\r
267 else if( fabs( ai - a[ i ] ) < fabs( bi < b[ i ] ) )
\r
273 outi = Q_rint( out[ i ] );
\r
274 if( fabs( outi - out[ i ] ) <= SNAP_EPSILON )
\r
282 FixWinding() - ydnar
\r
283 removes degenerate edges from a winding
\r
284 returns qtrue if the winding is valid
\r
287 #define DEGENERATE_EPSILON 0.1
\r
289 qboolean FixWinding( winding_t *w )
\r
291 qboolean valid = qtrue;
\r
301 /* check all verts */
\r
302 for( i = 0; i < w->numpoints; i++ )
\r
304 /* don't remove points if winding is a triangle */
\r
305 if( w->numpoints == 3 )
\r
308 /* get second point index */
\r
309 j = (i + 1) % w->numpoints;
\r
311 /* degenerate edge? */
\r
312 VectorSubtract( w->p[ i ], w->p[ j ], vec );
\r
313 dist = VectorLength( vec );
\r
314 if( dist < DEGENERATE_EPSILON )
\r
317 //Sys_FPrintf( SYS_VRB, "WARNING: Degenerate winding edge found, fixing...\n" );
\r
319 /* create an average point (ydnar 2002-01-26: using nearest-integer weld preference) */
\r
320 SnapWeldVector( w->p[ i ], w->p[ j ], vec );
\r
321 VectorCopy( vec, w->p[ i ] );
\r
322 //VectorAdd( w->p[ i ], w->p[ j ], vec );
\r
323 //VectorScale( vec, 0.5, w->p[ i ] );
\r
325 /* move the remaining verts */
\r
326 for( k = i + 2; k < w->numpoints; k++ )
\r
328 VectorCopy( w->p[ k ], w->p[ k - 1 ] );
\r
334 /* one last check and return */
\r
335 if( w->numpoints < 3 )
\r
347 CreateBrushWindings()
\r
348 makes basewindigs for sides and mins/maxs for the brush
\r
349 returns false if the brush doesn't enclose a valid volume
\r
352 qboolean CreateBrushWindings( brush_t *brush )
\r
360 /* walk the list of brush sides */
\r
361 for( i = 0; i < brush->numsides; i++ )
\r
363 /* get side and plane */
\r
364 side = &brush->sides[ i ];
\r
365 plane = &mapplanes[ side->planenum ];
\r
367 /* make huge winding */
\r
368 w = BaseWindingForPlane( plane->normal, plane->dist );
\r
370 /* walk the list of brush sides */
\r
371 for( j = 0; j < brush->numsides && w != NULL; j++ )
\r
375 if( brush->sides[ j ].planenum == (brush->sides[ i ].planenum ^ 1) )
\r
376 continue; /* back side clipaway */
\r
377 if( brush->sides[ j ].bevel )
\r
379 if( brush->sides[ j ].backSide )
\r
381 plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ];
\r
382 ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON );
\r
384 /* ydnar: fix broken windings that would generate trifans */
\r
388 /* set side winding */
\r
392 /* find brush bounds */
\r
393 return BoundBrush( brush );
\r
403 Creates a new axial brush
\r
406 brush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
\r
413 b = AllocBrush (6);
\r
415 for (i=0 ; i<3 ; i++)
\r
417 VectorClear (normal);
\r
420 b->sides[i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &maxs );
\r
424 b->sides[3+i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &mins );
\r
427 CreateBrushWindings (b);
\r
438 vec_t BrushVolume (brush_t *brush)
\r
443 vec_t d, area, volume;
\r
449 // grab the first valid point as the corner
\r
452 for (i=0 ; i<brush->numsides ; i++)
\r
454 w = brush->sides[i].winding;
\r
460 VectorCopy (w->p[0], corner);
\r
462 // make tetrahedrons to all other faces
\r
465 for ( ; i<brush->numsides ; i++)
\r
467 w = brush->sides[i].winding;
\r
470 plane = &mapplanes[brush->sides[i].planenum];
\r
471 d = -(DotProduct (corner, plane->normal) - plane->dist);
\r
472 area = WindingArea (w);
\r
484 writes a map with the split bsp brushes
\r
487 void WriteBSPBrushMap( char *name, brush_t *list )
\r
496 Sys_Printf( "Writing %s\n", name );
\r
498 /* open the map file */
\r
499 f = fopen( name, "wb" );
\r
501 Error( "Can't write %s\b", name );
\r
503 fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
\r
505 for ( ; list ; list=list->next )
\r
507 fprintf (f, "{\n");
\r
508 for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
\r
510 w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
\r
512 fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
\r
513 fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
\r
514 fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
\r
516 fprintf (f, "notexture 0 0 0 1 1\n" );
\r
519 fprintf (f, "}\n");
\r
521 fprintf (f, "}\n");
\r
530 FilterBrushIntoTree_r()
\r
531 adds brush reference to any intersecting bsp leafnode
\r
534 int FilterBrushIntoTree_r( brush_t *b, node_t *node )
\r
536 brush_t *front, *back;
\r
544 /* add it to the leaf list */
\r
545 if( node->planenum == PLANENUM_LEAF )
\r
547 /* something somewhere is hammering brushlist */
\r
548 b->next = node->brushlist;
\r
549 node->brushlist = b;
\r
551 /* classify the leaf by the structural brush */
\r
556 node->opaque = qtrue;
\r
557 node->areaportal = qfalse;
\r
559 else if( b->compileFlags & C_AREAPORTAL )
\r
561 if( !node->opaque )
\r
562 node->areaportal = qtrue;
\r
569 /* split it by the node plane */
\r
571 SplitBrush( b, node->planenum, &front, &back );
\r
575 c += FilterBrushIntoTree_r( front, node->children[ 0 ] );
\r
576 c += FilterBrushIntoTree_r( back, node->children[ 1 ] );
\r
584 FilterDetailBrushesIntoTree
\r
585 fragment all the detail brushes into the structural leafs
\r
588 void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree )
\r
592 int c_unique, c_clusters;
\r
597 Sys_FPrintf( SYS_VRB, "--- FilterDetailBrushesIntoTree ---\n" );
\r
599 /* walk the list of brushes */
\r
602 for( b = e->brushes; b; b = b->next )
\r
607 newb = CopyBrush( b );
\r
608 r = FilterBrushIntoTree_r( newb, tree->headnode );
\r
611 /* mark all sides as visible so drawsurfs are created */
\r
614 for( i = 0; i < b->numsides; i++ )
\r
616 if( b->sides[ i ].winding )
\r
617 b->sides[ i ].visible = qtrue;
\r
622 /* emit some statistics */
\r
623 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_unique );
\r
624 Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
\r
628 =====================
\r
629 FilterStructuralBrushesIntoTree
\r
631 Mark the leafs as opaque and areaportals
\r
632 =====================
\r
634 void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
\r
637 int c_unique, c_clusters;
\r
640 Sys_FPrintf (SYS_VRB, "--- FilterStructuralBrushesIntoTree ---\n");
\r
644 for ( b = e->brushes ; b ; b = b->next ) {
\r
649 newb = CopyBrush( b );
\r
650 r = FilterBrushIntoTree_r( newb, tree->headnode );
\r
653 // mark all sides as visible so drawsurfs are created
\r
655 for ( i = 0 ; i < b->numsides ; i++ ) {
\r
656 if ( b->sides[i].winding ) {
\r
657 b->sides[i].visible = qtrue;
\r
663 /* emit some statistics */
\r
664 Sys_FPrintf( SYS_VRB, "%9d structural brushes\n", c_unique );
\r
665 Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
\r
675 tree_t *AllocTree (void)
\r
679 tree = safe_malloc(sizeof(*tree));
\r
680 memset (tree, 0, sizeof(*tree));
\r
681 ClearBounds (tree->mins, tree->maxs);
\r
691 node_t *AllocNode (void)
\r
695 node = safe_malloc(sizeof(*node));
\r
696 memset (node, 0, sizeof(*node));
\r
706 Returns true if the winding would be crunched out of
\r
707 existance by the vertex snapping.
\r
710 #define EDGE_LENGTH 0.2
\r
711 qboolean WindingIsTiny (winding_t *w)
\r
714 if (WindingArea (w) < 1)
\r
724 for (i=0 ; i<w->numpoints ; i++)
\r
726 j = i == w->numpoints - 1 ? 0 : i+1;
\r
727 VectorSubtract (w->p[j], w->p[i], delta);
\r
728 len = VectorLength (delta);
\r
729 if (len > EDGE_LENGTH)
\r
742 Returns true if the winding still has one of the points
\r
743 from basewinding for plane
\r
746 qboolean WindingIsHuge (winding_t *w)
\r
750 for (i=0 ; i<w->numpoints ; i++)
\r
752 for (j=0 ; j<3 ; j++)
\r
753 if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
\r
759 //============================================================
\r
767 int BrushMostlyOnSide (brush_t *brush, plane_t *plane)
\r
775 side = PSIDE_FRONT;
\r
776 for (i=0 ; i<brush->numsides ; i++)
\r
778 w = brush->sides[i].winding;
\r
781 for (j=0 ; j<w->numpoints ; j++)
\r
783 d = DotProduct (w->p[j], plane->normal) - plane->dist;
\r
787 side = PSIDE_FRONT;
\r
803 generates two new brushes, leaving the original unchanged
\r
806 void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back )
\r
810 winding_t *w, *cw[2], *midwinding;
\r
811 plane_t *plane, *plane2;
\r
813 float d, d_front, d_back;
\r
818 plane = &mapplanes[planenum];
\r
820 // check all points
\r
821 d_front = d_back = 0;
\r
822 for (i=0 ; i<brush->numsides ; i++)
\r
824 w = brush->sides[i].winding;
\r
827 for (j=0 ; j<w->numpoints ; j++)
\r
829 d = DotProduct (w->p[j], plane->normal) - plane->dist;
\r
830 if (d > 0 && d > d_front)
\r
832 if (d < 0 && d < d_back)
\r
837 if (d_front < 0.1) // PLANESIDE_EPSILON)
\r
839 *back = CopyBrush( brush );
\r
843 if (d_back > -0.1) // PLANESIDE_EPSILON)
\r
845 *front = CopyBrush( brush );
\r
849 // create a new winding from the split plane
\r
850 w = BaseWindingForPlane (plane->normal, plane->dist);
\r
851 for (i=0 ; i<brush->numsides && w ; i++)
\r
853 if ( brush->sides[i].backSide ) {
\r
854 continue; // fake back-sided polygons never split
\r
856 plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
\r
857 ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
\r
860 if (!w || WindingIsTiny (w) )
\r
861 { // the brush isn't really split
\r
864 side = BrushMostlyOnSide (brush, plane);
\r
865 if (side == PSIDE_FRONT)
\r
866 *front = CopyBrush (brush);
\r
867 if (side == PSIDE_BACK)
\r
868 *back = CopyBrush (brush);
\r
872 if( WindingIsHuge( w ) )
\r
873 Sys_FPrintf( SYS_VRB,"WARNING: huge winding\n" );
\r
877 // split it for real
\r
879 for (i=0 ; i<2 ; i++)
\r
881 b[i] = AllocBrush (brush->numsides+1);
\r
882 memcpy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) );
\r
883 b[i]->numsides = 0;
\r
885 b[i]->original = brush->original;
\r
888 // split all the current windings
\r
890 for (i=0 ; i<brush->numsides ; i++)
\r
892 s = &brush->sides[i];
\r
896 ClipWindingEpsilon (w, plane->normal, plane->dist,
\r
897 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
\r
898 for (j=0 ; j<2 ; j++)
\r
902 cs = &b[j]->sides[b[j]->numsides];
\r
905 cs->winding = cw[j];
\r
910 // see if we have valid polygons on both sides
\r
911 for (i=0 ; i<2 ; i++)
\r
914 for (j=0 ; j<3 ; j++)
\r
916 if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
\r
918 Sys_FPrintf (SYS_VRB,"bogus brush after clip\n");
\r
923 if (b[i]->numsides < 3 || j < 3)
\r
930 if ( !(b[0] && b[1]) )
\r
932 if (!b[0] && !b[1])
\r
933 Sys_FPrintf (SYS_VRB,"split removed brush\n");
\r
935 Sys_FPrintf (SYS_VRB,"split not on both sides\n");
\r
939 *front = CopyBrush (brush);
\r
944 *back = CopyBrush (brush);
\r
949 // add the midwinding to both sides
\r
950 for (i=0 ; i<2 ; i++)
\r
952 cs = &b[i]->sides[b[i]->numsides];
\r
955 cs->planenum = planenum^i^1;
\r
956 cs->shaderInfo = NULL;
\r
958 cs->winding = CopyWinding (midwinding);
\r
960 cs->winding = midwinding;
\r
968 for (i=0 ; i<2 ; i++)
\r
970 v1 = BrushVolume (b[i]);
\r
975 // Sys_FPrintf (SYS_VRB,"tiny volume after clip\n");
\r