2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 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.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 extern qboolean onlyents;
28 mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
31 side_t brushsides[MAX_MAP_SIDES];
32 brush_texture_t side_brushtextures[MAX_MAP_SIDES];
35 plane_t mapplanes[MAX_MAP_PLANES];
37 #define PLANE_HASHES 1024
38 plane_t *planehash[PLANE_HASHES];
40 vec3_t map_mins, map_maxs;
42 // undefine to make plane finding use linear sort
45 void TestExpandBrushes( void );
55 =============================================================================
59 =============================================================================
68 int PlaneTypeForNormal( vec3_t normal ){
71 // NOTE: should these have an epsilon around 1.0?
72 if ( normal[0] == 1.0 || normal[0] == -1.0 ) {
75 if ( normal[1] == 1.0 || normal[1] == -1.0 ) {
78 if ( normal[2] == 1.0 || normal[2] == -1.0 ) {
82 ax = fabs( normal[0] );
83 ay = fabs( normal[1] );
84 az = fabs( normal[2] );
86 if ( ax >= ay && ax >= az ) {
89 if ( ay >= ax && ay >= az ) {
100 #define NORMAL_EPSILON 0.00001
101 #define DIST_EPSILON 0.01
102 qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist ){
105 fabs( p->normal[0] - normal[0] ) < NORMAL_EPSILON
106 && fabs( p->normal[1] - normal[1] ) < NORMAL_EPSILON
107 && fabs( p->normal[2] - normal[2] ) < NORMAL_EPSILON
108 && fabs( p->dist - dist ) < DIST_EPSILON ) {
112 if ( p->normal[0] == normal[0]
113 && p->normal[1] == normal[1]
114 && p->normal[2] == normal[2]
115 && p->dist == dist ) {
127 void AddPlaneToHash( plane_t *p ){
130 hash = (int)fabs( p->dist ) / 8;
131 hash &= ( PLANE_HASHES - 1 );
133 p->hash_chain = planehash[hash];
142 int CreateNewFloatPlane( vec3_t normal, vec_t dist ){
145 if ( VectorLength( normal ) < 0.5 ) {
146 Error( "FloatPlane: bad normal" );
148 // create a new plane
149 if ( nummapplanes + 2 > MAX_MAP_PLANES ) {
150 Error( "MAX_MAP_PLANES" );
153 p = &mapplanes[nummapplanes];
154 VectorCopy( normal, p->normal );
156 p->type = ( p + 1 )->type = PlaneTypeForNormal( p->normal );
158 VectorSubtract( vec3_origin, normal, ( p + 1 )->normal );
159 ( p + 1 )->dist = -dist;
163 // allways put axial planes facing positive first
165 if ( p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0 ) {
172 AddPlaneToHash( p + 1 );
173 return nummapplanes - 1;
178 AddPlaneToHash( p + 1 );
179 return nummapplanes - 2;
187 void SnapVector( vec3_t normal ){
190 for ( i = 0 ; i < 3 ; i++ )
192 if ( fabs( normal[i] - 1 ) < NORMAL_EPSILON ) {
193 VectorClear( normal );
197 if ( fabs( normal[i] - -1 ) < NORMAL_EPSILON ) {
198 VectorClear( normal );
210 void SnapPlane( vec3_t normal, vec_t *dist ){
211 SnapVector( normal );
213 if ( fabs( *dist - Q_rint( *dist ) ) < DIST_EPSILON ) {
214 *dist = Q_rint( *dist );
225 int FindFloatPlane( vec3_t normal, vec_t dist ){
229 SnapPlane( normal, &dist );
230 for ( i = 0, p = mapplanes ; i < nummapplanes ; i++, p++ )
232 if ( PlaneEqual( p, normal, dist ) ) {
237 return CreateNewFloatPlane( normal, dist );
240 int FindFloatPlane( vec3_t normal, vec_t dist ){
245 SnapPlane( normal, &dist );
246 hash = (int)fabs( dist ) / 8;
247 hash &= ( PLANE_HASHES - 1 );
249 // search the border bins as well
250 for ( i = -1 ; i <= 1 ; i++ )
252 h = ( hash + i ) & ( PLANE_HASHES - 1 );
253 for ( p = planehash[h] ; p ; p = p->hash_chain )
255 if ( PlaneEqual( p, normal, dist ) ) {
256 return p - mapplanes;
261 return CreateNewFloatPlane( normal, dist );
270 int PlaneFromPoints( int *p0, int *p1, int *p2 ){
271 vec3_t t1, t2, normal;
274 VectorSubtract( p0, p1, t1 );
275 VectorSubtract( p2, p1, t2 );
276 CrossProduct( t1, t2, normal );
277 VectorNormalize( normal, normal );
279 dist = DotProduct( p0, normal );
281 return FindFloatPlane( normal, dist );
285 //====================================================================
293 int BrushContents( mapbrush_t *b ){
299 s = &b->original_sides[0];
300 contents = s->contents;
301 trans = texinfo[s->texinfo].flags;
302 for ( i = 1 ; i < b->numsides ; i++, s++ )
304 s = &b->original_sides[i];
305 trans |= texinfo[s->texinfo].flags;
306 if ( s->contents != contents ) {
307 Sys_Printf( "Entity %i, Brush %i: mixed face contents\n"
308 , b->entitynum, b->brushnum );
313 // if any side is translucent, mark the contents
314 // and change solid to window
315 if ( trans & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
316 contents |= CONTENTS_TRANSLUCENT;
317 if ( contents & CONTENTS_SOLID ) {
318 contents &= ~CONTENTS_SOLID;
319 contents |= CONTENTS_WINDOW;
327 //============================================================================
333 Adds any additional planes necessary to allow the brush to be expanded
334 against axial bounding boxes
337 void AddBrushBevels( mapbrush_t *b ){
339 int i, j, k, l, order;
341 brush_texture_t tdtemp;
350 // add the axial planes
353 for ( axis = 0 ; axis < 3 ; axis++ )
355 for ( dir = -1 ; dir <= 1 ; dir += 2, order++ )
357 // see if the plane is allready present
358 for ( i = 0, s = b->original_sides ; i < b->numsides ; i++,s++ )
360 if ( mapplanes[s->planenum].normal[axis] == dir ) {
365 if ( i == b->numsides ) { // add a new side
366 if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
367 Error( "MAX_MAP_BRUSHSIDES" );
371 VectorClear( normal );
374 dist = b->maxs[axis];
377 dist = -b->mins[axis];
379 s->planenum = FindFloatPlane( normal, dist );
380 s->texinfo = b->original_sides[0].texinfo;
381 s->contents = b->original_sides[0].contents;
386 // if the plane is not in it canonical order, swap it
388 sidetemp = b->original_sides[order];
389 b->original_sides[order] = b->original_sides[i];
390 b->original_sides[i] = sidetemp;
392 j = b->original_sides - brushsides;
393 tdtemp = side_brushtextures[j + order];
394 side_brushtextures[j + order] = side_brushtextures[j + i];
395 side_brushtextures[j + i] = tdtemp;
401 // add the edge bevels
403 if ( b->numsides == 6 ) {
404 return; // pure axial
407 // test the non-axial plane edges
408 for ( i = 6 ; i < b->numsides ; i++ )
410 s = b->original_sides + i;
415 for ( j = 0 ; j < w->numpoints ; j++ )
417 k = ( j + 1 ) % w->numpoints;
418 VectorSubtract( w->p[j], w->p[k], vec );
419 if ( VectorNormalize( vec, vec ) < 0.5 ) {
423 for ( k = 0 ; k < 3 ; k++ )
424 if ( vec[k] == -1 || vec[k] == 1 ) {
428 continue; // only test non-axial edges
431 // try the six possible slanted axials from this edge
432 for ( axis = 0 ; axis < 3 ; axis++ )
434 for ( dir = -1 ; dir <= 1 ; dir += 2 )
439 CrossProduct( vec, vec2, normal );
440 if ( VectorNormalize( normal, normal ) < 0.5 ) {
443 dist = DotProduct( w->p[j], normal );
445 // if all the points on all the sides are
446 // behind this plane, it is a proper edge bevel
447 for ( k = 0 ; k < b->numsides ; k++ )
449 // if this plane has allready been used, skip it
450 if ( PlaneEqual( &mapplanes[b->original_sides[k].planenum]
455 w2 = b->original_sides[k].winding;
459 for ( l = 0 ; l < w2->numpoints ; l++ )
461 d = DotProduct( w2->p[l], normal ) - dist;
463 break; // point in front
466 if ( l != w2->numpoints ) {
471 if ( k != b->numsides ) {
472 continue; // wasn't part of the outer hull
475 if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
476 Error( "MAX_MAP_BRUSHSIDES" );
479 s2 = &b->original_sides[b->numsides];
480 s2->planenum = FindFloatPlane( normal, dist );
481 s2->texinfo = b->original_sides[0].texinfo;
482 s2->contents = b->original_sides[0].contents;
497 makes basewindigs for sides and mins / maxs for the brush
500 qboolean MakeBrushWindings( mapbrush_t *ob ){
506 ClearBounds( ob->mins, ob->maxs );
508 for ( i = 0 ; i < ob->numsides ; i++ )
510 plane = &mapplanes[ob->original_sides[i].planenum];
511 w = BaseWindingForPlane( plane->normal, plane->dist );
512 for ( j = 0 ; j < ob->numsides && w; j++ )
517 if ( ob->original_sides[j].bevel ) {
520 plane = &mapplanes[ob->original_sides[j].planenum ^ 1];
521 ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON);
524 side = &ob->original_sides[i];
527 side->visible = true;
528 for ( j = 0 ; j < w->numpoints ; j++ )
529 AddPointToBounds( w->p[j], ob->mins, ob->maxs );
533 for ( i = 0 ; i < 3 ; i++ )
535 if ( ob->mins[0] < -4096 || ob->maxs[0] > 4096 ) {
536 Sys_Printf( "entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum );
538 if ( ob->mins[0] > 4096 || ob->maxs[0] < -4096 ) {
539 Sys_Printf( "entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum );
552 void ParseBrush( entity_t *mapent ){
561 if ( nummapbrushes == MAX_MAP_BRUSHES ) {
562 Error( "nummapbrushes == MAX_MAP_BRUSHES" );
565 b = &mapbrushes[nummapbrushes];
566 b->original_sides = &brushsides[nummapbrushsides];
567 b->entitynum = num_entities - 1;
568 b->brushnum = nummapbrushes - mapent->firstbrush;
572 if ( !GetToken( true ) ) {
575 if ( !strcmp( token, "}" ) ) {
579 if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
580 Error( "MAX_MAP_BRUSHSIDES" );
582 side = &brushsides[nummapbrushsides];
584 // read the three point plane definition
585 for ( i = 0 ; i < 3 ; i++ )
590 if ( strcmp( token, "(" ) ) {
591 Error( "parsing brush" );
594 for ( j = 0 ; j < 3 ; j++ )
597 planepts[i][j] = atoi( token );
601 if ( strcmp( token, ")" ) ) {
602 Error( "parsing brush" );
609 // read the texturedef
612 strcpy( td.name, token );
615 td.shift[0] = atoi( token );
617 td.shift[1] = atoi( token );
619 td.rotate = atoi( token );
621 td.scale[0] = atof( token );
623 td.scale[1] = atof( token );
625 // find default flags and values
626 mt = FindMiptex( td.name );
627 td.flags = textureref[mt].flags;
628 td.value = textureref[mt].value;
629 side->contents = textureref[mt].contents;
630 side->surf = td.flags = textureref[mt].flags;
632 if ( TokenAvailable() ) {
634 side->contents = atoi( token );
636 side->surf = td.flags = atoi( token );
638 td.value = atoi( token );
641 // translucent objects are automatically classified as detail
642 if ( side->surf & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
643 side->contents |= CONTENTS_DETAIL;
645 if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
646 side->contents |= CONTENTS_DETAIL;
649 side->contents &= ~CONTENTS_DETAIL;
651 if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 )
652 | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST ) ) ) {
653 side->contents |= CONTENTS_SOLID;
656 // hints and skips are never detail, and have no content
657 if ( side->surf & ( SURF_HINT | SURF_SKIP ) ) {
659 side->surf &= ~CONTENTS_DETAIL;
664 // find the plane number
666 planenum = PlaneFromPoints( planepts[0], planepts[1], planepts[2] );
667 if ( planenum == -1 ) {
668 Sys_Printf( "Entity %i, Brush %i: plane with no normal\n"
669 , b->entitynum, b->brushnum );
674 // see if the plane has been used already
676 for ( k = 0 ; k < b->numsides ; k++ )
678 s2 = b->original_sides + k;
679 if ( s2->planenum == planenum ) {
680 Sys_Printf( "Entity %i, Brush %i: duplicate plane\n"
681 , b->entitynum, b->brushnum );
684 if ( s2->planenum == ( planenum ^ 1 ) ) {
685 Sys_Printf( "Entity %i, Brush %i: mirrored plane\n"
686 , b->entitynum, b->brushnum );
690 if ( k != b->numsides ) {
691 continue; // duplicated
698 side = b->original_sides + b->numsides;
699 side->planenum = planenum;
700 side->texinfo = TexinfoForBrushTexture( &mapplanes[planenum],
703 // save the td off in case there is an origin brush and we
704 // have to recalculate the texinfo
705 side_brushtextures[nummapbrushsides] = td;
711 // get the content for the entire brush
712 b->contents = BrushContents( b );
714 // allow detail brushes to be removed
715 if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) {
720 // allow water brushes to be removed
721 if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
726 // create windings for sides and bounds for brush
727 MakeBrushWindings( b );
729 // brushes that will not be visible at all will never be
730 // used as bsp splitters
731 if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
733 for ( i = 0 ; i < b->numsides ; i++ )
734 b->original_sides[i].texinfo = TEXINFO_NODE;
738 // origin brushes are removed, but they set
739 // the rotation origin for the rest of the brushes
740 // in the entity. After the entire entity is parsed,
741 // the planenums and texinfos will be adjusted for
744 if ( b->contents & CONTENTS_ORIGIN ) {
748 if ( num_entities == 1 ) {
749 Error( "Entity %i, Brush %i: origin brushes not allowed in world"
750 , b->entitynum, b->brushnum );
754 VectorAdd( b->mins, b->maxs, origin );
755 VectorScale( origin, 0.5, origin );
757 sprintf( string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] );
758 SetKeyValue( &entities[b->entitynum], "origin", string );
760 VectorCopy( origin, entities[b->entitynum].origin );
762 // don't keep this brush
771 mapent->numbrushes++;
778 Takes all of the brushes from the current entity and
779 adds them to the world's brush list.
781 Used by func_group and func_areaportal
784 void MoveBrushesToWorld( entity_t *mapent ){
790 // this is pretty gross, because the brushes are expected to be
791 // in linear order for each entity
793 newbrushes = mapent->numbrushes;
794 worldbrushes = entities[0].numbrushes;
796 temp = malloc( newbrushes * sizeof( mapbrush_t ) );
797 memcpy( temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof( mapbrush_t ) );
799 #if 0 // let them keep their original brush numbers
800 for ( i = 0 ; i < newbrushes ; i++ )
801 temp[i].entitynum = 0;
804 // make space to move the brushes (overlapped copy)
805 memmove( mapbrushes + worldbrushes + newbrushes,
806 mapbrushes + worldbrushes,
807 sizeof( mapbrush_t ) * ( nummapbrushes - worldbrushes - newbrushes ) );
809 // copy the new brushes down
810 memcpy( mapbrushes + worldbrushes, temp, sizeof( mapbrush_t ) * newbrushes );
813 entities[0].numbrushes += newbrushes;
814 for ( i = 1 ; i < num_entities ; i++ )
815 entities[i].firstbrush += newbrushes;
818 mapent->numbrushes = 0;
826 qboolean ParseMapEntity( void ){
831 int startbrush, startsides;
835 if ( !GetToken( true ) ) {
839 if ( strcmp( token, "{" ) ) {
840 Error( "ParseEntity: { not found" );
843 if ( num_entities == MAX_MAP_ENTITIES ) {
844 Error( "num_entities == MAX_MAP_ENTITIES" );
847 startbrush = nummapbrushes;
848 startsides = nummapbrushsides;
850 mapent = &entities[num_entities];
852 memset( mapent, 0, sizeof( *mapent ) );
853 mapent->firstbrush = nummapbrushes;
854 mapent->numbrushes = 0;
855 // mapent->portalareas[0] = -1;
856 // mapent->portalareas[1] = -1;
860 if ( !GetToken( true ) ) {
861 Error( "ParseEntity: EOF without closing brace" );
863 if ( !strcmp( token, "}" ) ) {
866 if ( !strcmp( token, "{" ) ) {
867 ParseBrush( mapent );
872 e->next = mapent->epairs;
877 GetVectorForKey( mapent, "origin", mapent->origin );
880 // if there was an origin brush, offset all of the planes and texinfo
882 if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) {
883 for ( i = 0 ; i < mapent->numbrushes ; i++ )
885 b = &mapbrushes[mapent->firstbrush + i];
886 for ( j = 0 ; j < b->numsides ; j++ )
888 s = &b->original_sides[j];
889 newdist = mapplanes[s->planenum].dist -
890 DotProduct( mapplanes[s->planenum].normal, mapent->origin );
891 s->planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist );
892 s->texinfo = TexinfoForBrushTexture( &mapplanes[s->planenum],
893 &side_brushtextures[s - brushsides], mapent->origin );
895 MakeBrushWindings( b );
899 // group entities are just for editor convenience
900 // toss all brushes into the world entity
901 if ( !strcmp( "func_group", ValueForKey( mapent, "classname" ) ) ) {
902 MoveBrushesToWorld( mapent );
903 mapent->numbrushes = 0;
907 // areaportal entities move their brushes, but don't eliminate
909 if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) {
912 if ( mapent->numbrushes != 1 ) {
913 Error( "Entity %i: func_areaportal can only be a single brush", num_entities - 1 );
916 b = &mapbrushes[nummapbrushes - 1];
917 b->contents = CONTENTS_AREAPORTAL;
919 mapent->areaportalnum = c_areaportals;
920 // set the portal number as "style"
921 sprintf( str, "%i", c_areaportals );
922 SetKeyValue( mapent, "style", str );
923 MoveBrushesToWorld( mapent );
930 //===================================================================
937 void LoadMapFile( char *filename ){
940 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
942 LoadScriptFile( filename );
944 nummapbrushsides = 0;
947 while ( ParseMapEntity() )
951 ClearBounds( map_mins, map_maxs );
952 for ( i = 0 ; i < entities[0].numbrushes ; i++ )
954 if ( mapbrushes[i].mins[0] > 4096 ) {
955 continue; // no valid points
957 AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs );
958 AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs );
961 Sys_FPrintf( SYS_VRB, "%5i brushes\n", nummapbrushes );
962 Sys_FPrintf( SYS_VRB, "%5i clipbrushes\n", c_clipbrushes );
963 Sys_FPrintf( SYS_VRB, "%5i total sides\n", nummapbrushsides );
964 Sys_FPrintf( SYS_VRB, "%5i boxbevels\n", c_boxbevels );
965 Sys_FPrintf( SYS_VRB, "%5i edgebevels\n", c_edgebevels );
966 Sys_FPrintf( SYS_VRB, "%5i entities\n", num_entities );
967 Sys_FPrintf( SYS_VRB, "%5i planes\n", nummapplanes );
968 Sys_FPrintf( SYS_VRB, "%5i areaportals\n", c_areaportals );
969 Sys_FPrintf( SYS_VRB, "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
970 map_maxs[0],map_maxs[1],map_maxs[2] );
972 // TestExpandBrushes ();
976 //====================================================================
983 Expands all the brush planes and saves a new map out
986 void TestExpandBrushes( void ){
991 char *name = "expanded.map";
995 Sys_Printf( "writing %s\n", name );
996 f = fopen( name, "wb" );
998 Error( "Can't write %s\b", name );
1001 fprintf( f, "{\n\"classname\" \"worldspawn\"\n" );
1003 for ( bn = 0 ; bn < nummapbrushes ; bn++ )
1005 brush = &mapbrushes[bn];
1006 fprintf( f, "{\n" );
1007 for ( i = 0 ; i < brush->numsides ; i++ )
1009 s = brush->original_sides + i;
1010 dist = mapplanes[s->planenum].dist;
1011 for ( j = 0 ; j < 3 ; j++ )
1012 dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
1014 w = BaseWindingForPlane( mapplanes[s->planenum].normal, dist );
1016 fprintf( f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] );
1017 fprintf( f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] );
1018 fprintf( f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] );
1020 fprintf( f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture );
1023 fprintf( f, "}\n" );
1025 fprintf( f, "}\n" );
1029 Error( "can't proceed after expanding brushes" );