1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
41 /* -------------------------------------------------------------------------------
45 ------------------------------------------------------------------------------- */
48 AllocSideRef() - ydnar
49 allocates and assigns a brush side reference
52 sideRef_t *AllocSideRef( side_t *side, sideRef_t *next ){
61 /* allocate and return */
62 sideRef = safe_malloc( sizeof( *sideRef ) );
72 counts the number of brushes in a brush linked list
75 int CountBrushList( brush_t *brushes ){
80 for ( ; brushes != NULL; brushes = brushes->next )
92 brush_t *AllocBrush( int numSides ){
96 /* allocate and clear */
97 /*if ( numSides <= 0 ) {
98 Error( "AllocBrush called with numsides = %d", numSides );
100 c = (size_t)&( ( (brush_t*) 0 )->sides[ numSides ] );*/
101 c = sizeof(*bb) + (numSides > 6 ? sizeof(side_t)*(numSides - 6) : 0);
102 bb = safe_malloc( c );
104 if ( numthreads == 1 ) {
116 frees a single brush and all sides/windings
119 void FreeBrush( brush_t *b ){
124 if ( *( (unsigned int*) b ) == 0xFEFEFEFE ) {
125 Sys_FPrintf( SYS_VRB, "WARNING: Attempt to free an already freed brush!\n" );
129 /* free brush sides */
130 for ( i = 0; i < b->numsides; i++ )
131 if ( b->sides[i].winding != NULL ) {
132 FreeWinding( b->sides[ i ].winding );
135 /* ydnar: overwrite it */
136 memset( b, 0xFE, (size_t)&( ( (brush_t*) 0 )->sides[ b->numsides ] ) );
137 *( (unsigned int*) b ) = 0xFEFEFEFE;
141 if ( numthreads == 1 ) {
150 frees a linked list of brushes
153 void FreeBrushList( brush_t *brushes ){
157 /* walk brush list */
158 for ( ; brushes != NULL; brushes = next )
160 next = brushes->next;
161 FreeBrush( brushes );
169 duplicates the brush, sides, and windings
172 brush_t *CopyBrush( brush_t *brush ){
179 size = (size_t)&( ( (brush_t*) 0 )->sides[ brush->numsides ] );
180 newBrush = AllocBrush( brush->numsides );
181 memcpy( newBrush, brush, size );
183 /* ydnar: nuke linked list */
184 newBrush->next = NULL;
187 for ( i = 0; i < brush->numsides; i++ )
189 if ( brush->sides[ i ].winding != NULL ) {
190 newBrush->sides[ i ].winding = CopyWinding( brush->sides[ i ].winding );
203 sets the mins/maxs based on the windings
204 returns false if the brush doesn't enclose a valid volume
207 qboolean BoundBrush( brush_t *brush ){
212 ClearBounds( brush->mins, brush->maxs );
213 for ( i = 0; i < brush->numsides; i++ )
215 w = brush->sides[ i ].winding;
219 for ( j = 0; j < w->numpoints; j++ )
220 AddPointToBounds( w->p[ j ], brush->mins, brush->maxs );
223 for ( i = 0; i < 3; i++ )
225 if ( brush->mins[ i ] < MIN_WORLD_COORD || brush->maxs[ i ] > MAX_WORLD_COORD || brush->mins[i] >= brush->maxs[ i ] ) {
237 SnapWeldVector() - ydnar
238 welds two vec3_t's into a third, taking into account nearest-to-integer
242 #define SNAP_EPSILON 0.01
244 void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out ){
250 if ( a == NULL || b == NULL || out == NULL ) {
254 /* do each element */
255 for ( i = 0; i < 3; i++ )
257 /* round to integer */
258 ai = Q_rint( a[ i ] );
259 bi = Q_rint( b[ i ] );
261 /* prefer exact integer */
262 if ( ai == a[ i ] ) {
265 else if ( bi == b[ i ] ) {
270 else if ( fabs( ai - a[ i ] ) < fabs( bi - b[ i ] ) ) {
278 outi = Q_rint( out[ i ] );
279 if ( fabs( outi - out[ i ] ) <= SNAP_EPSILON ) {
291 Welds two vectors into a third, taking into account nearest-to-integer
292 instead of averaging.
295 void SnapWeldVectorAccu( vec3_accu_t a, vec3_accu_t b, vec3_accu_t out ){
296 // I'm just preserving what I think was the intended logic of the original
297 // SnapWeldVector(). I'm not actually sure where this function should even
298 // be used. I'd like to know which kinds of problems this function addresses.
300 // TODO: I thought we're snapping all coordinates to nearest 1/8 unit?
301 // So what is natural about snapping to the nearest integer? Maybe we should
302 // be snapping to the nearest 1/8 unit instead?
305 vec_accu_t ai, bi, ad, bd;
307 if ( a == NULL || b == NULL || out == NULL ) {
308 Error( "SnapWeldVectorAccu: NULL argument" );
311 for ( i = 0; i < 3; i++ )
313 ai = Q_rintAccu( a[i] );
314 bi = Q_rintAccu( b[i] );
315 ad = fabs( ai - a[i] );
316 bd = fabs( bi - b[i] );
319 if ( ad < SNAP_EPSILON ) {
322 else{out[i] = a[i]; }
326 if ( bd < SNAP_EPSILON ) {
329 else{out[i] = b[i]; }
338 removes degenerate edges from a winding
339 returns qtrue if the winding is valid
342 #define DEGENERATE_EPSILON 0.1
344 qboolean FixWinding( winding_t *w ){
345 qboolean valid = qtrue;
356 /* check all verts */
357 for ( i = 0; i < w->numpoints; i++ )
359 /* don't remove points if winding is a triangle */
360 if ( w->numpoints == 3 ) {
364 /* get second point index */
365 j = ( i + 1 ) % w->numpoints;
367 /* degenerate edge? */
368 VectorSubtract( w->p[ i ], w->p[ j ], vec );
369 dist = VectorLength( vec );
370 if ( dist < DEGENERATE_EPSILON ) {
372 //Sys_FPrintf( SYS_VRB, "WARNING: Degenerate winding edge found, fixing...\n" );
374 /* create an average point (ydnar 2002-01-26: using nearest-integer weld preference) */
375 SnapWeldVector( w->p[ i ], w->p[ j ], vec );
376 VectorCopy( vec, w->p[ i ] );
377 //VectorAdd( w->p[ i ], w->p[ j ], vec );
378 //VectorScale( vec, 0.5, w->p[ i ] );
380 /* move the remaining verts */
381 for ( k = i + 2; k < w->numpoints; k++ )
383 VectorCopy( w->p[ k ], w->p[ k - 1 ] );
389 /* one last check and return */
390 if ( w->numpoints < 3 ) {
400 Removes degenerate edges (edges that are too short) from a winding.
401 Returns qtrue if the winding has been altered by this function.
402 Returns qfalse if the winding is untouched by this function.
404 It's advised that you check the winding after this function exits to make
405 sure it still has at least 3 points. If that is not the case, the winding
406 cannot be considered valid. The winding may degenerate to one or two points
407 if the some of the winding's points are close together.
410 qboolean FixWindingAccu( winding_accu_t *w ){
414 qboolean done, altered;
417 Error( "FixWindingAccu: NULL argument" );
424 if ( w->numpoints < 2 ) {
425 break; // Don't remove the only remaining point.
428 for ( i = 0; i < w->numpoints; i++ )
430 j = ( ( ( i + 1 ) == w->numpoints ) ? 0 : ( i + 1 ) );
432 VectorSubtractAccu( w->p[i], w->p[j], vec );
433 dist = VectorLengthAccu( vec );
434 if ( dist < DEGENERATE_EPSILON ) {
435 // TODO: I think the "snap weld vector" was written before
436 // some of the math precision fixes, and its purpose was
437 // probably to address math accuracy issues. We can think
438 // about changing the logic here. Maybe once plane distance
439 // gets 64 bits, we can look at it then.
440 SnapWeldVectorAccu( w->p[i], w->p[j], vec );
441 VectorCopyAccu( vec, w->p[i] );
442 for ( k = j + 1; k < w->numpoints; k++ )
444 VectorCopyAccu( w->p[k], w->p[k - 1] );
448 // The only way to finish off fixing the winding consistently and
449 // accurately is by fixing the winding all over again. For example,
450 // the point at index i and the point at index i-1 could now be
451 // less than the epsilon distance apart. There are too many special
452 // case problems we'd need to handle if we didn't start from the
455 break; // This will cause us to return to the "while" loop.
468 CreateBrushWindings()
469 makes basewindigs for sides and mins/maxs for the brush
470 returns false if the brush doesn't enclose a valid volume
473 qboolean CreateBrushWindings( brush_t *brush ){
475 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
484 /* walk the list of brush sides */
485 for ( i = 0; i < brush->numsides; i++ )
487 /* get side and plane */
488 side = &brush->sides[ i ];
489 plane = &mapplanes[ side->planenum ];
491 /* make huge winding */
492 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
493 w = BaseWindingForPlaneAccu( plane->normal, plane->dist );
495 w = BaseWindingForPlane( plane->normal, plane->dist );
498 /* walk the list of brush sides */
499 for ( j = 0; j < brush->numsides && w != NULL; j++ )
504 if ( brush->sides[ j ].planenum == ( brush->sides[ i ].planenum ^ 1 ) ) {
505 continue; /* back side clipaway */
507 if ( brush->sides[ j ].bevel ) {
510 plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ];
511 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
512 ChopWindingInPlaceAccu( &w, plane->normal, plane->dist, 0 );
514 ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON );
517 /* ydnar: fix broken windings that would generate trifans */
518 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
519 // I think it's better to FixWindingAccu() once after we chop with all planes
520 // so that error isn't multiplied. There is nothing natural about welding
521 // the points unless they are the final endpoints. ChopWindingInPlaceAccu()
522 // is able to handle all kinds of degenerate windings.
528 /* set side winding */
529 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
532 if ( w->numpoints < 3 ) {
533 FreeWindingAccu( w );
537 side->winding = ( w ? CopyWindingAccuToRegular( w ) : NULL );
539 FreeWindingAccu( w );
546 /* find brush bounds */
547 return BoundBrush( brush );
557 Creates a new axial brush
560 brush_t *BrushFromBounds( vec3_t mins, vec3_t maxs ){
568 for ( i = 0 ; i < 3 ; i++ )
570 VectorClear( normal );
573 b->sides[i].planenum = FindFloatPlane( normal, dist, 1, (vec3_t*) &maxs );
577 b->sides[3 + i].planenum = FindFloatPlane( normal, dist, 1, (vec3_t*) &mins );
580 CreateBrushWindings( b );
591 vec_t BrushVolume( brush_t *brush ){
595 vec_t d, area, volume;
602 // grab the first valid point as the corner
605 for ( i = 0 ; i < brush->numsides ; i++ )
607 w = brush->sides[i].winding;
615 VectorCopy( w->p[0], corner );
617 // make tetrahedrons to all other faces
620 for ( ; i < brush->numsides ; i++ )
622 w = brush->sides[i].winding;
626 plane = &mapplanes[brush->sides[i].planenum];
627 d = -( DotProduct( corner, plane->normal ) - plane->dist );
628 area = WindingArea( w );
640 writes a map with the split bsp brushes
643 void WriteBSPBrushMap( char *name, brush_t *list ){
651 Sys_Printf( "Writing %s\n", name );
653 /* open the map file */
654 f = fopen( name, "wb" );
656 Error( "Can't write %s\b", name );
659 fprintf( f, "{\n\"classname\" \"worldspawn\"\n" );
661 for ( ; list ; list = list->next )
664 for ( i = 0,s = list->sides ; i < list->numsides ; i++,s++ )
666 // TODO: See if we can use a smaller winding to prevent resolution loss.
667 // Is WriteBSPBrushMap() used only to decompile maps?
668 w = BaseWindingForPlane( mapplanes[s->planenum].normal, mapplanes[s->planenum].dist );
670 fprintf( f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] );
671 fprintf( f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] );
672 fprintf( f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] );
674 fprintf( f, "notexture 0 0 0 1 1\n" );
688 FilterBrushIntoTree_r()
689 adds brush reference to any intersecting bsp leafnode
692 int FilterBrushIntoTree_r( brush_t *b, node_t *node ){
693 brush_t *front, *back;
702 /* add it to the leaf list */
703 if ( node->planenum == PLANENUM_LEAF ) {
704 /* something somewhere is hammering brushlist */
705 b->next = node->brushlist;
708 /* classify the leaf by the structural brush */
711 node->opaque = qtrue;
712 node->areaportal = qfalse;
714 else if ( b->compileFlags & C_AREAPORTAL ) {
715 if ( !node->opaque ) {
716 node->areaportal = qtrue;
724 /* split it by the node plane */
726 SplitBrush( b, node->planenum, &front, &back );
730 c += FilterBrushIntoTree_r( front, node->children[ 0 ] );
731 c += FilterBrushIntoTree_r( back, node->children[ 1 ] );
739 FilterDetailBrushesIntoTree
740 fragment all the detail brushes into the structural leafs
743 void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree ){
746 int c_unique, c_clusters;
751 Sys_FPrintf( SYS_VRB, "--- FilterDetailBrushesIntoTree ---\n" );
753 /* walk the list of brushes */
756 for ( b = e->brushes; b; b = b->next )
762 newb = CopyBrush( b );
763 r = FilterBrushIntoTree_r( newb, tree->headnode );
766 /* mark all sides as visible so drawsurfs are created */
768 for ( i = 0; i < b->numsides; i++ )
770 if ( b->sides[ i ].winding ) {
771 b->sides[ i ].visible = qtrue;
777 /* emit some statistics */
778 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_unique );
779 Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
783 =====================
784 FilterStructuralBrushesIntoTree
786 Mark the leafs as opaque and areaportals
787 =====================
789 void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
792 int c_unique, c_clusters;
795 Sys_FPrintf( SYS_VRB, "--- FilterStructuralBrushesIntoTree ---\n" );
799 for ( b = e->brushes ; b ; b = b->next ) {
804 newb = CopyBrush( b );
805 r = FilterBrushIntoTree_r( newb, tree->headnode );
808 // mark all sides as visible so drawsurfs are created
810 for ( i = 0 ; i < b->numsides ; i++ ) {
811 if ( b->sides[i].winding ) {
812 b->sides[i].visible = qtrue;
818 /* emit some statistics */
819 Sys_FPrintf( SYS_VRB, "%9d structural brushes\n", c_unique );
820 Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
830 tree_t *AllocTree( void ){
833 tree = safe_malloc( sizeof( *tree ) );
834 memset( tree, 0, sizeof( *tree ) );
835 ClearBounds( tree->mins, tree->maxs );
845 node_t *AllocNode( void ){
848 node = safe_malloc( sizeof( *node ) );
849 memset( node, 0, sizeof( *node ) );
859 Returns true if the winding would be crunched out of
860 existance by the vertex snapping.
863 #define EDGE_LENGTH 0.2
864 qboolean WindingIsTiny( winding_t *w ){
866 if (WindingArea (w) < 1)
876 for ( i = 0 ; i < w->numpoints ; i++ )
878 j = i == w->numpoints - 1 ? 0 : i + 1;
879 VectorSubtract( w->p[j], w->p[i], delta );
880 len = VectorLength( delta );
881 if ( len > EDGE_LENGTH ) {
882 if ( ++edges == 3 ) {
894 Returns true if the winding still has one of the points
895 from basewinding for plane
898 qboolean WindingIsHuge( winding_t *w ){
901 for ( i = 0 ; i < w->numpoints ; i++ )
903 for ( j = 0 ; j < 3 ; j++ )
904 if ( w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD ) {
911 //============================================================
919 int BrushMostlyOnSide( brush_t *brush, plane_t *plane ){
927 for ( i = 0 ; i < brush->numsides ; i++ )
929 w = brush->sides[i].winding;
933 for ( j = 0 ; j < w->numpoints ; j++ )
935 d = DotProduct( w->p[j], plane->normal ) - plane->dist;
953 generates two new brushes, leaving the original unchanged
956 void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back ){
959 winding_t *w, *cw[2], *midwinding;
960 plane_t *plane, *plane2;
962 float d, d_front, d_back;
967 plane = &mapplanes[planenum];
970 d_front = d_back = 0;
971 for ( i = 0 ; i < brush->numsides ; i++ )
973 w = brush->sides[i].winding;
977 for ( j = 0 ; j < w->numpoints ; j++ )
979 d = DotProduct( w->p[j], plane->normal ) - plane->dist;
980 if ( d > 0 && d > d_front ) {
983 if ( d < 0 && d < d_back ) {
989 if ( d_front < 0.1 ) { // PLANESIDE_EPSILON)
991 *back = CopyBrush( brush );
995 if ( d_back > -0.1 ) { // PLANESIDE_EPSILON)
997 *front = CopyBrush( brush );
1001 // create a new winding from the split plane
1002 w = BaseWindingForPlane( plane->normal, plane->dist );
1003 for ( i = 0 ; i < brush->numsides && w ; i++ )
1005 plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
1006 ChopWindingInPlace( &w, plane2->normal, plane2->dist, 0 ); // PLANESIDE_EPSILON);
1009 if ( !w || WindingIsTiny( w ) ) { // the brush isn't really split
1012 side = BrushMostlyOnSide( brush, plane );
1013 if ( side == PSIDE_FRONT ) {
1014 *front = CopyBrush( brush );
1016 if ( side == PSIDE_BACK ) {
1017 *back = CopyBrush( brush );
1022 if ( WindingIsHuge( w ) ) {
1023 Sys_FPrintf( SYS_VRB,"WARNING: huge winding\n" );
1028 // split it for real
1030 for ( i = 0 ; i < 2 ; i++ )
1032 b[i] = AllocBrush( brush->numsides + 1 );
1033 memcpy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) );
1036 b[i]->original = brush->original;
1039 // split all the current windings
1041 for ( i = 0 ; i < brush->numsides ; i++ )
1043 s = &brush->sides[i];
1048 /* strict, in parallel case we get the face back because it also is the midwinding */
1049 ClipWindingEpsilonStrict( w, plane->normal, plane->dist,
1050 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] );
1051 for ( j = 0 ; j < 2 ; j++ )
1056 cs = &b[j]->sides[b[j]->numsides];
1059 cs->winding = cw[j];
1064 // see if we have valid polygons on both sides
1065 for ( i = 0 ; i < 2 ; i++ )
1067 if ( b[i]->numsides < 3 || !BoundBrush( b[i] ) ) {
1068 if ( b[i]->numsides >= 3 ) {
1069 Sys_FPrintf( SYS_VRB,"bogus brush after clip\n" );
1076 if ( !( b[0] && b[1] ) ) {
1077 if ( !b[0] && !b[1] ) {
1078 Sys_FPrintf( SYS_VRB,"split removed brush\n" );
1081 Sys_FPrintf( SYS_VRB,"split not on both sides\n" );
1085 *front = CopyBrush( brush );
1089 *back = CopyBrush( brush );
1094 // add the midwinding to both sides
1095 for ( i = 0 ; i < 2 ; i++ )
1097 cs = &b[i]->sides[b[i]->numsides];
1100 cs->planenum = planenum ^ i ^ 1;
1101 cs->shaderInfo = NULL;
1103 cs->winding = CopyWinding( midwinding );
1106 cs->winding = midwinding;
1115 for ( i = 0 ; i < 2 ; i++ )
1117 v1 = BrushVolume( b[i] );
1121 // Sys_FPrintf (SYS_VRB,"tiny volume after clip\n");