/* =========================================================================== Return to Castle Wolfenstein single player GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). RTCW SP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW SP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW SP Source Code. If not, see . In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ //=========================================================================== // // Name: aas_create.c // Function: Creation of AAS // Programmer: Mr Elusive (MrElusive@demigod.demon.nl) // Last update: 1997-12-04 // Tab Size: 3 //=========================================================================== #include "qbsp.h" #include "../botlib/aasfile.h" #include "aas_create.h" #include "aas_store.h" #include "aas_gsubdiv.h" #include "aas_facemerging.h" #include "aas_areamerging.h" #include "aas_edgemelting.h" #include "aas_prunenodes.h" #include "aas_cfg.h" #include "../game/surfaceflags.h" #define AREAONFACESIDE( face, area ) ( face->frontarea != area ) tmp_aas_t tmpaasworld; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitTmpAAS( void ) { //tmp faces tmpaasworld.numfaces = 0; tmpaasworld.facenum = 0; tmpaasworld.faces = NULL; //tmp convex areas tmpaasworld.numareas = 0; tmpaasworld.areanum = 0; tmpaasworld.areas = NULL; //tmp nodes tmpaasworld.numnodes = 0; tmpaasworld.nodes = NULL; // tmpaasworld.nodebuffer = NULL; } //end of the function AAS_InitTmpAAS //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeTmpAAS( void ) { tmp_face_t *f, *nextf; tmp_area_t *a, *nexta; tmp_nodebuf_t *nb, *nextnb; //free all the faces for ( f = tmpaasworld.faces; f; f = nextf ) { nextf = f->l_next; if ( f->winding ) { FreeWinding( f->winding ); } FreeMemory( f ); } //end if //free all tmp areas for ( a = tmpaasworld.areas; a; a = nexta ) { nexta = a->l_next; if ( a->settings ) { FreeMemory( a->settings ); } FreeMemory( a ); } //end for //free all the tmp nodes for ( nb = tmpaasworld.nodebuffer; nb; nb = nextnb ) { nextnb = nb->next; FreeMemory( nb ); } //end for } //end of the function AAS_FreeTmpAAS //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== tmp_face_t *AAS_AllocTmpFace( void ) { tmp_face_t *tmpface; tmpface = (tmp_face_t *) GetClearedMemory( sizeof( tmp_face_t ) ); tmpface->num = tmpaasworld.facenum++; tmpface->l_prev = NULL; tmpface->l_next = tmpaasworld.faces; if ( tmpaasworld.faces ) { tmpaasworld.faces->l_prev = tmpface; } tmpaasworld.faces = tmpface; tmpaasworld.numfaces++; return tmpface; } //end of the function AAS_AllocTmpFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeTmpFace( tmp_face_t *tmpface ) { if ( tmpface->l_next ) { tmpface->l_next->l_prev = tmpface->l_prev; } if ( tmpface->l_prev ) { tmpface->l_prev->l_next = tmpface->l_next; } else { tmpaasworld.faces = tmpface->l_next;} //free the winding if ( tmpface->winding ) { FreeWinding( tmpface->winding ); } //free the face FreeMemory( tmpface ); tmpaasworld.numfaces--; } //end of the function AAS_FreeTmpFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== tmp_area_t *AAS_AllocTmpArea( void ) { tmp_area_t *tmparea; tmparea = (tmp_area_t *) GetClearedMemory( sizeof( tmp_area_t ) ); tmparea->areanum = tmpaasworld.areanum++; tmparea->l_prev = NULL; tmparea->l_next = tmpaasworld.areas; if ( tmpaasworld.areas ) { tmpaasworld.areas->l_prev = tmparea; } tmpaasworld.areas = tmparea; tmpaasworld.numareas++; return tmparea; } //end of the function AAS_AllocTmpArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeTmpArea( tmp_area_t *tmparea ) { if ( tmparea->l_next ) { tmparea->l_next->l_prev = tmparea->l_prev; } if ( tmparea->l_prev ) { tmparea->l_prev->l_next = tmparea->l_next; } else { tmpaasworld.areas = tmparea->l_next;} if ( tmparea->settings ) { FreeMemory( tmparea->settings ); } FreeMemory( tmparea ); tmpaasworld.numareas--; } //end of the function AAS_FreeTmpArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== tmp_node_t *AAS_AllocTmpNode( void ) { tmp_nodebuf_t *nodebuf; if ( !tmpaasworld.nodebuffer || tmpaasworld.nodebuffer->numnodes >= NODEBUF_SIZE ) { nodebuf = (tmp_nodebuf_t *) GetClearedMemory( sizeof( tmp_nodebuf_t ) ); nodebuf->next = tmpaasworld.nodebuffer; nodebuf->numnodes = 0; tmpaasworld.nodebuffer = nodebuf; } //end if tmpaasworld.numnodes++; return &tmpaasworld.nodebuffer->nodes[tmpaasworld.nodebuffer->numnodes++]; } //end of the function AAS_AllocTmpNode //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeTmpNode( tmp_node_t *tmpnode ) { tmpaasworld.numnodes--; } //end of the function AAS_FreeTmpNode //=========================================================================== // returns true if the face is a gap from the given side // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_GapFace( tmp_face_t *tmpface, int side ) { vec3_t invgravity; //if the face is a solid or ground face it can't be a gap if ( tmpface->faceflags & ( FACE_GROUND | FACE_SOLID ) ) { return 0; } VectorCopy( cfg.phys_gravitydirection, invgravity ); VectorInverse( invgravity ); return ( DotProduct( invgravity, mapplanes[tmpface->planenum ^ side].normal ) > cfg.phys_maxsteepness ); } //end of the function AAS_GapFace //=========================================================================== // returns true if the face is a ground face // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_GroundFace( tmp_face_t *tmpface ) { vec3_t invgravity; //must be a solid face if ( !( tmpface->faceflags & FACE_SOLID ) ) { return 0; } VectorCopy( cfg.phys_gravitydirection, invgravity ); VectorInverse( invgravity ); return ( DotProduct( invgravity, mapplanes[tmpface->planenum].normal ) > cfg.phys_maxsteepness ); } //end of the function AAS_GroundFace //=========================================================================== // adds the side of a face to an area // // side : 0 = front side // 1 = back side // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_AddFaceSideToArea( tmp_face_t *tmpface, int side, tmp_area_t *tmparea ) { int tmpfaceside; if ( side ) { if ( tmpface->backarea ) { Error( "AAS_AddFaceSideToArea: already a back area\n" ); } } //end if else { if ( tmpface->frontarea ) { Error( "AAS_AddFaceSideToArea: already a front area\n" ); } } //end else if ( side ) { tmpface->backarea = tmparea; } else { tmpface->frontarea = tmparea;} if ( tmparea->tmpfaces ) { tmpfaceside = tmparea->tmpfaces->frontarea != tmparea; tmparea->tmpfaces->prev[tmpfaceside] = tmpface; } //end if tmpface->next[side] = tmparea->tmpfaces; tmpface->prev[side] = NULL; tmparea->tmpfaces = tmpface; } //end of the function AAS_AddFaceSideToArea //=========================================================================== // remove (a side of) a face from an area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveFaceFromArea( tmp_face_t *tmpface, tmp_area_t *tmparea ) { int side, prevside, nextside; if ( tmpface->frontarea != tmparea && tmpface->backarea != tmparea ) { Error( "AAS_RemoveFaceFromArea: face not part of the area" ); } //end if side = tmpface->frontarea != tmparea; if ( tmpface->prev[side] ) { prevside = tmpface->prev[side]->frontarea != tmparea; tmpface->prev[side]->next[prevside] = tmpface->next[side]; } //end if else { tmparea->tmpfaces = tmpface->next[side]; } //end else if ( tmpface->next[side] ) { nextside = tmpface->next[side]->frontarea != tmparea; tmpface->next[side]->prev[nextside] = tmpface->prev[side]; } //end if //remove the area number from the face depending on the side if ( side ) { tmpface->backarea = NULL; } else { tmpface->frontarea = NULL;} tmpface->prev[side] = NULL; tmpface->next[side] = NULL; } //end of the function AAS_RemoveFaceFromArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CheckArea( tmp_area_t *tmparea ) { int side; tmp_face_t *face; plane_t *plane; vec3_t wcenter, acenter = {0, 0, 0}; vec3_t normal; float n, dist; if ( tmparea->invalid ) { Log_Print( "AAS_CheckArea: invalid area\n" ); } for ( n = 0, face = tmparea->tmpfaces; face; face = face->next[side] ) { //side of the face the area is on side = face->frontarea != tmparea; WindingCenter( face->winding, wcenter ); VectorAdd( acenter, wcenter, acenter ); n++; } //end for n = 1 / n; VectorScale( acenter, n, acenter ); for ( face = tmparea->tmpfaces; face; face = face->next[side] ) { //side of the face the area is on side = face->frontarea != tmparea; #ifdef L_DEBUG if ( WindingError( face->winding ) ) { Log_Write( "AAS_CheckArea: area %d face %d: %s\r\n", tmparea->areanum, face->num, WindingErrorString() ); } //end if #endif L_DEBUG plane = &mapplanes[face->planenum ^ side]; if ( DotProduct( plane->normal, acenter ) - plane->dist < 0 ) { Log_Print( "AAS_CheckArea: area %d face %d is flipped\n", tmparea->areanum, face->num ); Log_Print( "AAS_CheckArea: area %d center is %f %f %f\n", tmparea->areanum, acenter[0], acenter[1], acenter[2] ); } //end if //check if the winding plane is the same as the face plane WindingPlane( face->winding, normal, &dist ); plane = &mapplanes[face->planenum]; #ifdef L_DEBUG if ( fabs( dist - plane->dist ) > 0.4 || fabs( normal[0] - plane->normal[0] ) > 0.0001 || fabs( normal[1] - plane->normal[1] ) > 0.0001 || fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { Log_Write( "AAS_CheckArea: area %d face %d winding plane unequal to face plane\r\n", tmparea->areanum, face->num ); } //end if #endif L_DEBUG } //end for } //end of the function AAS_CheckArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CheckFaceWindingPlane( tmp_face_t *face ) { float dist, sign1, sign2; vec3_t normal; plane_t *plane; winding_t *w; //check if the winding plane is the same as the face plane WindingPlane( face->winding, normal, &dist ); plane = &mapplanes[face->planenum]; // sign1 = DotProduct( plane->normal, normal ); // if ( fabs( dist - plane->dist ) > 0.4 || fabs( normal[0] - plane->normal[0] ) > 0.0001 || fabs( normal[1] - plane->normal[1] ) > 0.0001 || fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { VectorInverse( normal ); dist = -dist; if ( fabs( dist - plane->dist ) > 0.4 || fabs( normal[0] - plane->normal[0] ) > 0.0001 || fabs( normal[1] - plane->normal[1] ) > 0.0001 || fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { Log_Write( "AAS_CheckFaceWindingPlane: face %d winding plane unequal to face plane\r\n", face->num ); // sign2 = DotProduct( plane->normal, normal ); if ( ( sign1 < 0 && sign2 > 0 ) || ( sign1 > 0 && sign2 < 0 ) ) { Log_Write( "AAS_CheckFaceWindingPlane: face %d winding reversed\r\n", face->num ); w = face->winding; face->winding = ReverseWinding( w ); FreeWinding( w ); } //end if } //end if else { Log_Write( "AAS_CheckFaceWindingPlane: face %d winding reversed\r\n", face->num ); w = face->winding; face->winding = ReverseWinding( w ); FreeWinding( w ); } //end else } //end if } //end of the function AAS_CheckFaceWindingPlane //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CheckAreaWindingPlanes( void ) { int side; tmp_area_t *tmparea; tmp_face_t *face; Log_Write( "AAS_CheckAreaWindingPlanes:\r\n" ); for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) { if ( tmparea->invalid ) { continue; } for ( face = tmparea->tmpfaces; face; face = face->next[side] ) { side = face->frontarea != tmparea; AAS_CheckFaceWindingPlane( face ); } //end for } //end for } //end of the function AAS_CheckAreaWindingPlanes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FlipAreaFaces( tmp_area_t *tmparea ) { int side; tmp_face_t *face; plane_t *plane; vec3_t wcenter, acenter = {0, 0, 0}; //winding_t *w; float n; for ( n = 0, face = tmparea->tmpfaces; face; face = face->next[side] ) { if ( !face->frontarea ) { Error( "face %d has no front area\n", face->num ); } //side of the face the area is on side = face->frontarea != tmparea; WindingCenter( face->winding, wcenter ); VectorAdd( acenter, wcenter, acenter ); n++; } //end for n = 1 / n; VectorScale( acenter, n, acenter ); for ( face = tmparea->tmpfaces; face; face = face->next[side] ) { //side of the face the area is on side = face->frontarea != tmparea; plane = &mapplanes[face->planenum ^ side]; if ( DotProduct( plane->normal, acenter ) - plane->dist < 0 ) { Log_Print( "area %d face %d flipped: front area %d, back area %d\n", tmparea->areanum, face->num, face->frontarea ? face->frontarea->areanum : 0, face->backarea ? face->backarea->areanum : 0 ); /* face->planenum = face->planenum ^ 1; w = face->winding; face->winding = ReverseWinding(w); FreeWinding(w); */ } //end if #ifdef L_DEBUG { float dist; vec3_t normal; //check if the winding plane is the same as the face plane WindingPlane( face->winding, normal, &dist ); plane = &mapplanes[face->planenum]; if ( fabs( dist - plane->dist ) > 0.4 || fabs( normal[0] - plane->normal[0] ) > 0.0001 || fabs( normal[1] - plane->normal[1] ) > 0.0001 || fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { Log_Write( "area %d face %d winding plane unequal to face plane\r\n", tmparea->areanum, face->num ); } //end if } #endif } //end for } //end of the function AAS_FlipAreaFaces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveAreaFaceColinearPoints( void ) { int side; tmp_face_t *face; tmp_area_t *tmparea; //FIXME: loop over the faces instead of area->faces for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) { for ( face = tmparea->tmpfaces; face; face = face->next[side] ) { side = face->frontarea != tmparea; RemoveColinearPoints( face->winding ); // RemoveEqualPoints(face->winding, 0.1); } //end for } //end for } //end of the function AAS_RemoveAreaFaceColinearPoints //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveTinyFaces( void ) { int side, num; tmp_face_t *face, *nextface; tmp_area_t *tmparea; //FIXME: loop over the faces instead of area->faces Log_Write( "AAS_RemoveTinyFaces\r\n" ); num = 0; for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) { for ( face = tmparea->tmpfaces; face; face = nextface ) { side = face->frontarea != tmparea; nextface = face->next[side]; // if ( WindingArea( face->winding ) < 1 ) { if ( face->frontarea ) { AAS_RemoveFaceFromArea( face, face->frontarea ); } if ( face->backarea ) { AAS_RemoveFaceFromArea( face, face->backarea ); } AAS_FreeTmpFace( face ); //Log_Write("area %d face %d is tiny\r\n", tmparea->areanum, face->num); num++; } //end if } //end for } //end for Log_Write( "%d tiny faces removed\r\n", num ); } //end of the function AAS_RemoveTinyFaces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateAreaSettings( void ) { int i, flags, side, numgrounded, numladderareas, numliquidareas; tmp_face_t *face; tmp_area_t *tmparea; int count; numgrounded = 0; numladderareas = 0; numliquidareas = 0; Log_Write( "AAS_CreateAreaSettings\r\n" ); i = 0; qprintf( "%6d areas provided with settings", i ); for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) { //if the area is invalid there no need to create settings for it if ( tmparea->invalid ) { continue; } tmparea->settings = (tmp_areasettings_t *) GetClearedMemory( sizeof( tmp_areasettings_t ) ); tmparea->settings->contents = tmparea->contents; tmparea->settings->modelnum = tmparea->modelnum; flags = 0; count = 0; tmparea->settings->groundsteepness = 0.0; for ( face = tmparea->tmpfaces; face; face = face->next[side] ) { side = face->frontarea != tmparea; flags |= face->faceflags; // Ridah, add this face's steepness if ( face->faceflags & FACE_GROUND ) { tmparea->settings->groundsteepness += ( 1.0 - mapplanes[face->planenum ^ side].normal[2] ); count++; } } //end for tmparea->settings->groundsteepness /= (float)count; if ( tmparea->settings->groundsteepness > 1.0 ) { tmparea->settings->groundsteepness = 1.0; } if ( tmparea->settings->groundsteepness < 0.0 ) { tmparea->settings->groundsteepness = 0.0; } tmparea->settings->areaflags = 0; if ( flags & FACE_GROUND ) { tmparea->settings->areaflags |= AREA_GROUNDED; numgrounded++; } //end if if ( flags & FACE_LADDER ) { tmparea->settings->areaflags |= AREA_LADDER; numladderareas++; } //end if if ( tmparea->contents & ( AREACONTENTS_WATER | AREACONTENTS_SLIME | AREACONTENTS_LAVA ) ) { tmparea->settings->areaflags |= AREA_LIQUID; numliquidareas++; } //end if //presence type of the area tmparea->settings->presencetype = tmparea->presencetype; // qprintf( "\r%6d", ++i ); } //end for qprintf( "\n" ); #ifdef AASINFO Log_Print( "%6d grounded areas\n", numgrounded ); Log_Print( "%6d ladder areas\n", numladderareas ); Log_Print( "%6d liquid areas\n", numliquidareas ); #endif //AASINFO } //end of the function AAS_CreateAreaSettings //=========================================================================== // create a tmp AAS area from a leaf node // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== //portal_t *globalPortalDebug; tmp_node_t *AAS_CreateArea( node_t *node ) { int pside; int areafaceflags; portal_t *p; tmp_face_t *tmpface; tmp_area_t *tmparea; tmp_node_t *tmpnode; //vec3_t up = {0, 0, 1}; // TTimo: unused //create an area from this leaf tmparea = AAS_AllocTmpArea(); tmparea->tmpfaces = NULL; //clear the area face flags areafaceflags = 0; //make aas faces from the portals for ( p = node->portals; p; p = p->next[pside] ) { pside = ( p->nodes[1] == node ); //don't create faces from very small portals // if (WindingArea(p->winding) < 1) continue; //if there's already a face created for this portal if ( p->tmpface ) { //add the back side of the face to the area AAS_AddFaceSideToArea( p->tmpface, 1, tmparea ); } //end if else { tmpface = AAS_AllocTmpFace(); //set the face pointer at the portal so we can see from //the portal there's a face created for it p->tmpface = tmpface; //FIXME: test this change //tmpface->planenum = (p->planenum & ~1) | pside; tmpface->planenum = p->planenum ^ pside; if ( pside ) { tmpface->winding = ReverseWinding( p->winding ); } else { tmpface->winding = CopyWinding( p->winding );} #ifdef L_DEBUG // AAS_CheckFaceWindingPlane( tmpface ); #endif //L_DEBUG //if there's solid at the other side of the portal if ( p->nodes[!pside]->contents & ( CONTENTS_SOLID | CONTENTS_PLAYERCLIP ) ) { tmpface->faceflags |= FACE_SOLID; } //end if //else there is no solid at the other side and if there //is a liquid at this side else if ( node->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { tmpface->faceflags |= FACE_LIQUID; //if there's no liquid at the other side if ( !( p->nodes[!pside]->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) { tmpface->faceflags |= FACE_LIQUIDSURFACE; } //end if } //end else //if there's ladder contents at other side of the portal if ( ( p->nodes[pside]->contents & CONTENTS_LADDER ) || ( p->nodes[!pside]->contents & CONTENTS_LADDER ) ) { //NOTE: doesn't have to be solid at the other side because // when standing one can use a crouch area (which is not solid) // as a ladder // imagine a ladder one can walk underthrough, // under the ladder against the ladder is a crouch area // the (vertical) sides of this crouch area area also used as // ladder sides when standing (not crouched) tmpface->faceflags |= FACE_LADDER; } //end if //if it is possible to stand on the face if ( AAS_GroundFace( tmpface ) ) { tmpface->faceflags |= FACE_GROUND; } //end if // areafaceflags |= tmpface->faceflags; //no aas face number yet (zero is a dummy in the aasworld faces) tmpface->aasfacenum = 0; //add the front side of the face to the area AAS_AddFaceSideToArea( tmpface, 0, tmparea ); } //end else } //end for qprintf( "\r%6d", tmparea->areanum ); //presence type in the area tmparea->presencetype = ~node->expansionbboxes & cfg.allpresencetypes; // tmparea->contents = 0; if ( node->contents & CONTENTS_CLUSTERPORTAL ) { tmparea->contents |= AREACONTENTS_CLUSTERPORTAL; } if ( node->contents & CONTENTS_MOVER ) { tmparea->contents |= AREACONTENTS_MOVER; } if ( node->contents & CONTENTS_TELEPORTER ) { tmparea->contents |= AREACONTENTS_TELEPORTER; } if ( node->contents & CONTENTS_JUMPPAD ) { tmparea->contents |= AREACONTENTS_JUMPPAD; } if ( node->contents & CONTENTS_DONOTENTER ) { tmparea->contents |= AREACONTENTS_DONOTENTER; } if ( node->contents & CONTENTS_DONOTENTER_LARGE ) { tmparea->contents |= AREACONTENTS_DONOTENTER_LARGE; } if ( node->contents & CONTENTS_WATER ) { tmparea->contents |= AREACONTENTS_WATER; } if ( node->contents & CONTENTS_LAVA ) { tmparea->contents |= AREACONTENTS_LAVA; } if ( node->contents & CONTENTS_SLIME ) { tmparea->contents |= AREACONTENTS_SLIME; } //store the bsp model that's inside this node tmparea->modelnum = node->modelnum; //sorta check for flipped area faces (remove??) AAS_FlipAreaFaces( tmparea ); //check if the area is ok (remove??) AAS_CheckArea( tmparea ); // tmpnode = AAS_AllocTmpNode(); tmpnode->planenum = 0; tmpnode->children[0] = 0; tmpnode->children[1] = 0; tmpnode->tmparea = tmparea; // return tmpnode; } //end of the function AAS_CreateArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== tmp_node_t *AAS_CreateAreas_r( node_t *node ) { tmp_node_t *tmpnode; //recurse down to leafs if ( node->planenum != PLANENUM_LEAF ) { //the first tmp node is a dummy tmpnode = AAS_AllocTmpNode(); tmpnode->planenum = node->planenum; tmpnode->children[0] = AAS_CreateAreas_r( node->children[0] ); tmpnode->children[1] = AAS_CreateAreas_r( node->children[1] ); return tmpnode; } //end if //areas won't be created for solid leafs if ( node->contents & CONTENTS_SOLID ) { //just return zero for a solid leaf (in tmp AAS NULL is a solid leaf) return NULL; } //end if return AAS_CreateArea( node ); } //end of the function AAS_CreateAreas_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateAreas( node_t *node ) { Log_Write( "AAS_CreateAreas\r\n" ); qprintf( "%6d areas created", 0 ); tmpaasworld.nodes = AAS_CreateAreas_r( node ); qprintf( "\n" ); Log_Write( "%6d areas created\r\n", tmpaasworld.numareas ); } //end of the function AAS_CreateAreas //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_PrintNumGroundFaces( void ) { tmp_face_t *tmpface; int numgroundfaces = 0; for ( tmpface = tmpaasworld.faces; tmpface; tmpface = tmpface->l_next ) { if ( tmpface->faceflags & FACE_GROUND ) { numgroundfaces++; } //end if } //end for qprintf( "%6d ground faces\n", numgroundfaces ); } //end of the function AAS_PrintNumGroundFaces //=========================================================================== // checks the number of shared faces between the given two areas // since areas are convex they should only have ONE shared face // however due to crappy face merging there are sometimes several // shared faces // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CheckAreaSharedFaces( tmp_area_t *tmparea1, tmp_area_t *tmparea2 ) { int numsharedfaces, side; tmp_face_t *face1, *sharedface; if ( tmparea1->invalid || tmparea2->invalid ) { return; } sharedface = NULL; numsharedfaces = 0; for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side] ) { side = face1->frontarea != tmparea1; if ( face1->backarea == tmparea2 || face1->frontarea == tmparea2 ) { sharedface = face1; numsharedfaces++; } //end if } //end if if ( !sharedface ) { return; } //the areas should only have one shared face if ( numsharedfaces > 1 ) { Log_Write( "---- tmp area %d and %d have %d shared faces\r\n", tmparea1->areanum, tmparea2->areanum, numsharedfaces ); for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side] ) { side = face1->frontarea != tmparea1; if ( face1->backarea == tmparea2 || face1->frontarea == tmparea2 ) { Log_Write( "face %d, planenum = %d, face->frontarea = %d face->backarea = %d\r\n", face1->num, face1->planenum, face1->frontarea->areanum, face1->backarea->areanum ); } //end if } //end if } //end if } //end of the function AAS_CheckAreaSharedFaces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CheckSharedFaces( void ) { tmp_area_t *tmparea1, *tmparea2; for ( tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next ) { for ( tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next ) { if ( tmparea1 == tmparea2 ) { continue; } AAS_CheckAreaSharedFaces( tmparea1, tmparea2 ); } //end for } //end for } //end of the function AAS_CheckSharedFaces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FlipFace( tmp_face_t *face ) { tmp_area_t *frontarea, *backarea; winding_t *w; frontarea = face->frontarea; backarea = face->backarea; //must have an area at both sides before flipping is allowed if ( !frontarea || !backarea ) { return; } //flip the face winding w = face->winding; face->winding = ReverseWinding( w ); FreeWinding( w ); //flip the face plane face->planenum ^= 1; //flip the face areas AAS_RemoveFaceFromArea( face, frontarea ); AAS_RemoveFaceFromArea( face, backarea ); AAS_AddFaceSideToArea( face, 1, frontarea ); AAS_AddFaceSideToArea( face, 0, backarea ); } //end of the function AAS_FlipFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== /* void AAS_FlipAreaSharedFaces(tmp_area_t *tmparea1, tmp_area_t *tmparea2) { int numsharedfaces, side, area1facing, area2facing; tmp_face_t *face1, *sharedface; if (tmparea1->invalid || tmparea2->invalid) return; sharedface = NULL; numsharedfaces = 0; area1facing = 0; //number of shared faces facing towards area 1 area2facing = 0; //number of shared faces facing towards area 2 for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) { side = face1->frontarea != tmparea1; if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) { sharedface = face1; numsharedfaces++; if (face1->frontarea == tmparea1) area1facing++; else area2facing++; } //end if } //end if if (!sharedface) return; //if there's only one shared face if (numsharedfaces <= 1) return; //if all the shared faces are facing to the same area if (numsharedfaces == area1facing || numsharedfaces == area2facing) return; // do { for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) { side = face1->frontarea != tmparea1; if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) { if (face1->frontarea != tmparea1) { AAS_FlipFace(face1); break; } //end if } //end if } //end for } while(face1); } //end of the function AAS_FlipAreaSharedFaces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FlipSharedFaces(void) { int i; tmp_area_t *tmparea1, *tmparea2; i = 0; qprintf("%6d areas checked for shared face flipping", i); for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next) { if (tmparea1->invalid) continue; for (tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next) { if (tmparea2->invalid) continue; if (tmparea1 == tmparea2) continue; AAS_FlipAreaSharedFaces(tmparea1, tmparea2); } //end for qprintf("\r%6d", ++i); } //end for Log_Print("\r%6d areas checked for shared face flipping\n", i); } //end of the function AAS_FlipSharedFaces */ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FlipSharedFaces( void ) { int i, side1, side2; tmp_area_t *tmparea1; tmp_face_t *face1, *face2; i = 0; qprintf( "%6d areas checked for shared face flipping", i ); for ( tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next ) { if ( tmparea1->invalid ) { continue; } for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1] ) { side1 = face1->frontarea != tmparea1; if ( !face1->frontarea || !face1->backarea ) { continue; } // for ( face2 = face1->next[side1]; face2; face2 = face2->next[side2] ) { side2 = face2->frontarea != tmparea1; if ( !face2->frontarea || !face2->backarea ) { continue; } // if ( face1->frontarea == face2->backarea && face1->backarea == face2->frontarea ) { AAS_FlipFace( face2 ); } //end if //recheck side side2 = face2->frontarea != tmparea1; } //end for } //end for qprintf( "\r%6d", ++i ); } //end for qprintf( "\n" ); Log_Print( "%6d areas checked for shared face flipping\r\n", i ); } //end of the function AAS_FlipSharedFaces //=========================================================================== // creates an .AAS file with the given name // a MAP should be loaded before calling this // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Create( char *aasfile ) { entity_t *e; tree_t *tree; double start_time; //for a possible leak file strcpy( source, aasfile ); StripExtension( source ); //the time started start_time = I_FloatTime(); //set the default number of threads (depends on number of processors) ThreadSetDefault(); //set the global entity number to the world model entity_num = 0; //the world entity e = &entities[entity_num]; //process the whole world tree = ProcessWorldBrushes( e->firstbrush, e->firstbrush + e->numbrushes ); //if the conversion is cancelled if ( cancelconversion ) { Tree_Free( tree ); return; } //end if //display BSP tree creation time Log_Print( "BSP tree created in %5.0f seconds\n", I_FloatTime() - start_time ); //prune the bsp tree Tree_PruneNodes( tree->headnode ); //if the conversion is cancelled if ( cancelconversion ) { Tree_Free( tree ); return; } //end if //create the tree portals MakeTreePortals( tree ); //if the conversion is cancelled if ( cancelconversion ) { Tree_Free( tree ); return; } //end if //Marks all nodes that can be reached by entites if ( FloodEntities( tree ) ) { //fill out nodes that can't be reached FillOutside( tree->headnode ); } //end if else { LeakFile( tree ); Error( "**** leaked ****\n" ); return; } //end else //create AAS from the BSP tree //========================================== //initialize tmp aas AAS_InitTmpAAS(); //create the convex areas from the leaves AAS_CreateAreas( tree->headnode ); //free the BSP tree because it isn't used anymore if ( freetree ) { Tree_Free( tree ); } //try to merge area faces AAS_MergeAreaFaces(); //do gravitational subdivision AAS_GravitationalSubdivision(); //merge faces if possible AAS_MergeAreaFaces(); AAS_RemoveAreaFaceColinearPoints(); //merge areas if possible AAS_MergeAreas(); //NOTE: prune nodes directly after area merging AAS_PruneNodes(); //flip shared faces so they are all facing to the same area AAS_FlipSharedFaces(); AAS_RemoveAreaFaceColinearPoints(); //merge faces if possible AAS_MergeAreaFaces(); //merge area faces in the same plane AAS_MergeAreaPlaneFaces(); //do ladder subdivision AAS_LadderSubdivision(); //FIXME: melting is buggy AAS_MeltAreaFaceWindings(); //remove tiny faces AAS_RemoveTinyFaces(); //create area settings AAS_CreateAreaSettings(); //check if the winding plane is equal to the face plane //AAS_CheckAreaWindingPlanes(); // //AAS_CheckSharedFaces(); //========================================== //if the conversion is cancelled if ( cancelconversion ) { Tree_Free( tree ); AAS_FreeTmpAAS(); return; } //end if //store the created AAS stuff in the AAS file format and write the file AAS_StoreFile( aasfile ); //free the temporary AAS memory AAS_FreeTmpAAS(); //display creation time Log_Print( "\nAAS created in %5.0f seconds\n", I_FloatTime() - start_time ); } //end of the function AAS_Create