Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

aas_gsubdiv.c (21784B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, 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.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 #include "qbsp.h"
     24 #include "../botlib/aasfile.h"
     25 #include "aas_create.h"
     26 #include "aas_store.h"
     27 #include "aas_cfg.h"
     28 
     29 #define FACECLIP_EPSILON			0.2
     30 #define FACE_EPSILON					1.0
     31 
     32 int numgravitationalsubdivisions = 0;
     33 int numladdersubdivisions = 0;
     34 
     35 //NOTE: only do gravitational subdivision BEFORE area merging!!!!!!!
     36 //			because the bsp tree isn't refreshes like with ladder subdivision
     37 
     38 //===========================================================================
     39 // NOTE: the original face is invalid after splitting
     40 //
     41 // Parameter:				-
     42 // Returns:					-
     43 // Changes Globals:		-
     44 //===========================================================================
     45 void AAS_SplitFace(tmp_face_t *face, vec3_t normal, float dist,
     46 							tmp_face_t **frontface, tmp_face_t **backface)
     47 {
     48 	winding_t *frontw, *backw;
     49 
     50 	//
     51 	*frontface = *backface = NULL;
     52 
     53 	ClipWindingEpsilon(face->winding, normal, dist, FACECLIP_EPSILON, &frontw, &backw);
     54 
     55 #ifdef DEBUG
     56 	//
     57 	if (frontw)
     58 	{
     59 		if (WindingIsTiny(frontw))
     60 		{
     61 			Log_Write("AAS_SplitFace: tiny back face\r\n");
     62 			FreeWinding(frontw);
     63 			frontw = NULL;
     64 		} //end if
     65 	} //end if
     66 	if (backw)
     67 	{
     68 		if (WindingIsTiny(backw))
     69 		{
     70 			Log_Write("AAS_SplitFace: tiny back face\r\n");
     71 			FreeWinding(backw);
     72 			backw = NULL;
     73 		} //end if
     74 	} //end if
     75 #endif //DEBUG
     76 	//if the winding was split
     77 	if (frontw)
     78 	{
     79 		//check bounds
     80 		(*frontface) = AAS_AllocTmpFace();
     81 		(*frontface)->planenum = face->planenum;
     82 		(*frontface)->winding = frontw;
     83 		(*frontface)->faceflags = face->faceflags;
     84 	} //end if
     85 	if (backw)
     86 	{
     87 		//check bounds
     88 		(*backface) = AAS_AllocTmpFace();
     89 		(*backface)->planenum = face->planenum;
     90 		(*backface)->winding = backw;
     91 		(*backface)->faceflags = face->faceflags;
     92 	} //end if
     93 } //end of the function AAS_SplitFace
     94 //===========================================================================
     95 //
     96 // Parameter:				-
     97 // Returns:					-
     98 // Changes Globals:		-
     99 //===========================================================================
    100 winding_t *AAS_SplitWinding(tmp_area_t *tmparea, int planenum)
    101 {
    102 	tmp_face_t *face;
    103 	plane_t *plane;
    104 	int side;
    105 	winding_t *splitwinding;
    106 
    107 	//
    108 	plane = &mapplanes[planenum];
    109 	//create a split winding, first base winding for plane
    110 	splitwinding = BaseWindingForPlane(plane->normal, plane->dist);
    111 	//chop with all the faces of the area
    112 	for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side])
    113 	{
    114 		//side of the face the original area was on
    115 		side = face->frontarea != tmparea;
    116 		plane = &mapplanes[face->planenum ^ side];
    117 		ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON);
    118 	} //end for
    119 	return splitwinding;
    120 } //end of the function AAS_SplitWinding
    121 //===========================================================================
    122 //
    123 // Parameter:				-
    124 // Returns:					-
    125 // Changes Globals:		-
    126 //===========================================================================
    127 int AAS_TestSplitPlane(tmp_area_t *tmparea, vec3_t normal, float dist,
    128 							int *facesplits, int *groundsplits, int *epsilonfaces)
    129 {
    130 	int j, side, front, back, planenum;
    131 	float d, d_front, d_back;
    132 	tmp_face_t *face;
    133 	winding_t *w;
    134 
    135 	*facesplits = *groundsplits = *epsilonfaces = 0;
    136 
    137 	planenum = FindFloatPlane(normal, dist);
    138 
    139 	w = AAS_SplitWinding(tmparea, planenum);
    140 	if (!w) return false;
    141 	FreeWinding(w);
    142 	//
    143 	for (face = tmparea->tmpfaces; face; face = face->next[side])
    144 	{
    145 		//side of the face the area is on
    146 		side = face->frontarea != tmparea;
    147 
    148 		if ((face->planenum & ~1) == (planenum & ~1))
    149 		{
    150 			Log_Print("AAS_TestSplitPlane: tried face plane as splitter\n");
    151 			return false;
    152 		} //end if
    153 		w = face->winding;
    154 		//reset distance at front and back side of plane
    155 		d_front = d_back = 0;
    156 		//reset front and back flags
    157 		front = back = 0;
    158 		for (j = 0; j < w->numpoints; j++)
    159 		{
    160 			d = DotProduct(w->p[j], normal) - dist;
    161 			if (d > d_front) d_front = d;
    162 			if (d < d_back) d_back = d;
    163 
    164 			if (d > 0.4) // PLANESIDE_EPSILON)
    165 				front = 1;
    166 			if (d < -0.4) // PLANESIDE_EPSILON)
    167 				back = 1;
    168 		} //end for
    169 		//check for an epsilon face
    170 		if ( (d_front > FACECLIP_EPSILON && d_front < FACE_EPSILON)
    171 			|| (d_back < -FACECLIP_EPSILON && d_back > -FACE_EPSILON) )
    172 		{
    173 			(*epsilonfaces)++;
    174 		} //end if
    175 		//if the face has points at both sides of the plane
    176 		if (front && back)
    177 		{
    178 			(*facesplits)++;
    179 			if (face->faceflags & FACE_GROUND)
    180 			{
    181 				(*groundsplits)++;
    182 			} //end if
    183 		} //end if
    184 	} //end for
    185 	return true;
    186 } //end of the function AAS_TestSplitPlane
    187 //===========================================================================
    188 //
    189 // Parameter:				-
    190 // Returns:					-
    191 // Changes Globals:		-
    192 //===========================================================================
    193 void AAS_SplitArea(tmp_area_t *tmparea, int planenum, tmp_area_t **frontarea, tmp_area_t **backarea)
    194 {
    195 	int side;
    196 	tmp_area_t *facefrontarea, *facebackarea, *faceotherarea;
    197 	tmp_face_t *face, *frontface, *backface, *splitface, *nextface;
    198 	winding_t *splitwinding;
    199 	plane_t *splitplane;
    200 
    201 /*
    202 #ifdef AW_DEBUG
    203 	int facesplits, groundsplits, epsilonface;
    204 	Log_Print("\n----------------------\n");
    205 	Log_Print("splitting area %d\n", areanum);
    206 	Log_Print("with normal = \'%f %f %f\', dist = %f\n", normal[0], normal[1], normal[2], dist);
    207 	AAS_TestSplitPlane(areanum, normal, dist,
    208 										&facesplits, &groundsplits, &epsilonface);
    209 	Log_Print("face splits = %d\nground splits = %d\n", facesplits, groundsplits);
    210 	if (epsilonface) Log_Print("aaahh epsilon face\n");
    211 #endif //AW_DEBUG*/
    212 	//the original area
    213 
    214 	AAS_FlipAreaFaces(tmparea);
    215 	AAS_CheckArea(tmparea);
    216 	//
    217 	splitplane = &mapplanes[planenum];
    218 /*	//create a split winding, first base winding for plane
    219 	splitwinding = BaseWindingForPlane(splitplane->normal, splitplane->dist);
    220 	//chop with all the faces of the area
    221 	for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side])
    222 	{
    223 		//side of the face the original area was on
    224 		side = face->frontarea != tmparea->areanum;
    225 		plane = &mapplanes[face->planenum ^ side];
    226 		ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON);
    227 	} //end for*/
    228 	splitwinding = AAS_SplitWinding(tmparea, planenum);
    229 	if (!splitwinding)
    230 	{
    231 /*
    232 #ifdef DEBUG
    233 		AAS_TestSplitPlane(areanum, normal, dist,
    234 											&facesplits, &groundsplits, &epsilonface);
    235 		Log_Print("\nface splits = %d\nground splits = %d\n", facesplits, groundsplits);
    236 		if (epsilonface) Log_Print("aaahh epsilon face\n");
    237 #endif //DEBUG*/
    238 		Error("AAS_SplitArea: no split winding when splitting area %d\n", tmparea->areanum);
    239 	} //end if
    240 	//create a split face
    241 	splitface = AAS_AllocTmpFace();
    242 	//get the map plane
    243 	splitface->planenum = planenum;
    244 	//store the split winding
    245 	splitface->winding = splitwinding;
    246 	//the new front area
    247 	(*frontarea) = AAS_AllocTmpArea();
    248 	(*frontarea)->presencetype = tmparea->presencetype;
    249 	(*frontarea)->contents = tmparea->contents;
    250 	(*frontarea)->modelnum = tmparea->modelnum;
    251 	(*frontarea)->tmpfaces = NULL;
    252 	//the new back area
    253 	(*backarea) = AAS_AllocTmpArea();
    254 	(*backarea)->presencetype = tmparea->presencetype;
    255 	(*backarea)->contents = tmparea->contents;
    256 	(*backarea)->modelnum = tmparea->modelnum;
    257 	(*backarea)->tmpfaces = NULL;
    258 	//add the split face to the new areas
    259 	AAS_AddFaceSideToArea(splitface, 0, (*frontarea));
    260 	AAS_AddFaceSideToArea(splitface, 1, (*backarea));
    261 
    262 	//split all the faces of the original area
    263 	for (face = tmparea->tmpfaces; face; face = nextface)
    264 	{
    265 		//side of the face the original area was on
    266 		side = face->frontarea != tmparea;
    267 		//next face of the original area
    268 		nextface = face->next[side];
    269 		//front area of the face
    270 		facefrontarea = face->frontarea;
    271 		//back area of the face
    272 		facebackarea = face->backarea;
    273 		//remove the face from both the front and back areas
    274 		if (facefrontarea) AAS_RemoveFaceFromArea(face, facefrontarea);
    275 		if (facebackarea) AAS_RemoveFaceFromArea(face, facebackarea);
    276 		//split the face
    277 		AAS_SplitFace(face, splitplane->normal, splitplane->dist, &frontface, &backface);
    278 		//free the original face
    279 		AAS_FreeTmpFace(face);
    280 		//get the number of the area at the other side of the face
    281 		if (side) faceotherarea = facefrontarea;
    282 		else faceotherarea = facebackarea;
    283 		//if there is an area at the other side of the original face
    284 		if (faceotherarea)
    285 		{
    286 			if (frontface) AAS_AddFaceSideToArea(frontface, !side, faceotherarea);
    287 			if (backface) AAS_AddFaceSideToArea(backface, !side, faceotherarea);
    288 		} //end if
    289 		//add the front and back part left after splitting the original face to the new areas
    290 		if (frontface) AAS_AddFaceSideToArea(frontface, side, (*frontarea));
    291 		if (backface) AAS_AddFaceSideToArea(backface, side, (*backarea));
    292 	} //end for
    293 
    294 	if (!(*frontarea)->tmpfaces) Log_Print("AAS_SplitArea: front area without faces\n");
    295 	if (!(*backarea)->tmpfaces) Log_Print("AAS_SplitArea: back area without faces\n");
    296 
    297 	tmparea->invalid = true;
    298 /*
    299 #ifdef AW_DEBUG
    300 	for (i = 0, face = frontarea->tmpfaces; face; face = face->next[side])
    301 	{
    302 		side = face->frontarea != frontarea->areanum;
    303 		i++;
    304 	} //end for
    305 	Log_Print("created front area %d with %d faces\n", frontarea->areanum, i);
    306 
    307 	for (i = 0, face = backarea->tmpfaces; face; face = face->next[side])
    308 	{
    309 		side = face->frontarea != backarea->areanum;
    310 		i++;
    311 	} //end for
    312 	Log_Print("created back area %d with %d faces\n", backarea->areanum, i);
    313 #endif //AW_DEBUG*/
    314 
    315 	AAS_FlipAreaFaces((*frontarea));
    316 	AAS_FlipAreaFaces((*backarea));
    317 	//
    318 	AAS_CheckArea((*frontarea));
    319 	AAS_CheckArea((*backarea));
    320 } //end of the function AAS_SplitArea
    321 //===========================================================================
    322 //
    323 // Parameter:				-
    324 // Returns:					-
    325 // Changes Globals:		-
    326 //===========================================================================
    327 int AAS_FindBestAreaSplitPlane(tmp_area_t *tmparea, vec3_t normal, float *dist)
    328 {
    329 	int side1, side2;
    330 	int foundsplitter, facesplits, groundsplits, epsilonfaces, bestepsilonfaces;
    331 	float bestvalue, value;
    332 	tmp_face_t *face1, *face2;
    333 	vec3_t tmpnormal, invgravity;
    334 	float tmpdist;
    335 
    336 	//get inverse of gravity direction
    337 	VectorCopy(cfg.phys_gravitydirection, invgravity);
    338 	VectorInverse(invgravity);
    339 
    340 	foundsplitter = false;
    341 	bestvalue = -999999;
    342 	bestepsilonfaces = 0;
    343 	//
    344 #ifdef AW_DEBUG
    345 	Log_Print("finding split plane for area %d\n", tmparea->areanum);
    346 #endif //AW_DEBUG
    347 	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
    348 	{
    349 		//side of the face the area is on
    350 		side1 = face1->frontarea != tmparea;
    351 		//
    352 		if (WindingIsTiny(face1->winding))
    353 		{
    354 			Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum);
    355 			continue;
    356 		} //end if
    357 		//if the face isn't a gap or ground there's no split edge
    358 		if (!(face1->faceflags & FACE_GROUND) && !AAS_GapFace(face1, side1)) continue;
    359 		//
    360 		for (face2 = face1->next[side1]; face2; face2 = face2->next[side2])
    361 		{
    362 			//side of the face the area is on
    363 			side2 = face2->frontarea != tmparea;
    364 			//
    365 			if (WindingIsTiny(face1->winding))
    366 			{
    367 				Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum);
    368 				continue;
    369 			} //end if
    370 			//if the face isn't a gap or ground there's no split edge
    371 			if (!(face2->faceflags & FACE_GROUND) && !AAS_GapFace(face2, side2)) continue;
    372 			//only split between gaps and ground
    373 			if (!(((face1->faceflags & FACE_GROUND) && AAS_GapFace(face2, side2)) ||
    374 					((face2->faceflags & FACE_GROUND) && AAS_GapFace(face1, side1)))) continue;
    375 			//find a plane seperating the windings of the faces
    376 			if (!FindPlaneSeperatingWindings(face1->winding, face2->winding, invgravity,
    377 														tmpnormal, &tmpdist)) continue;
    378 #ifdef AW_DEBUG
    379 			Log_Print("normal = \'%f %f %f\', dist = %f\n",
    380 							tmpnormal[0], tmpnormal[1], tmpnormal[2], tmpdist);
    381 #endif //AW_DEBUG
    382 			//get metrics for this vertical plane
    383 			if (!AAS_TestSplitPlane(tmparea, tmpnormal, tmpdist,
    384 										&facesplits, &groundsplits, &epsilonfaces))
    385 			{
    386 				continue;
    387 			} //end if
    388 #ifdef AW_DEBUG
    389 			Log_Print("face splits = %d\nground splits = %d\n",
    390 							facesplits, groundsplits);
    391 #endif //AW_DEBUG
    392 			value = 100 - facesplits - 2 * groundsplits;
    393 			//avoid epsilon faces
    394 			value += epsilonfaces * -1000;
    395 			if (value > bestvalue)
    396 			{
    397 				VectorCopy(tmpnormal, normal);
    398 				*dist = tmpdist;
    399 				bestvalue = value;
    400 				bestepsilonfaces = epsilonfaces;
    401 				foundsplitter = true;
    402 			} //end if
    403 		} //end for
    404 	} //end for
    405 	if (bestepsilonfaces)
    406 	{
    407 		Log_Write("found %d epsilon faces trying to split area %d\r\n",
    408 									epsilonfaces, tmparea->areanum);
    409 	} //end else
    410 	return foundsplitter;
    411 } //end of the function AAS_FindBestAreaSplitPlane
    412 //===========================================================================
    413 //
    414 // Parameter:				-
    415 // Returns:					-
    416 // Changes Globals:		-
    417 //===========================================================================
    418 tmp_node_t *AAS_SubdivideArea_r(tmp_node_t *tmpnode)
    419 {
    420 	int planenum;
    421 	tmp_area_t *frontarea, *backarea;
    422 	tmp_node_t *tmpnode1, *tmpnode2;
    423 	vec3_t normal;
    424 	float dist;
    425 
    426 	if (AAS_FindBestAreaSplitPlane(tmpnode->tmparea, normal, &dist))
    427 	{
    428 		qprintf("\r%6d", ++numgravitationalsubdivisions);
    429 		//
    430 		planenum = FindFloatPlane(normal, dist);
    431 		//split the area
    432 		AAS_SplitArea(tmpnode->tmparea, planenum, &frontarea, &backarea);
    433 		//
    434 		tmpnode->tmparea = NULL;
    435 		tmpnode->planenum = FindFloatPlane(normal, dist);
    436 		//
    437 		tmpnode1 = AAS_AllocTmpNode();
    438 		tmpnode1->planenum = 0;
    439 		tmpnode1->tmparea = frontarea;
    440 		//
    441 		tmpnode2 = AAS_AllocTmpNode();
    442 		tmpnode2->planenum = 0;
    443 		tmpnode2->tmparea = backarea;
    444 		//subdivide the areas created by splitting recursively
    445 		tmpnode->children[0] = AAS_SubdivideArea_r(tmpnode1);
    446 		tmpnode->children[1] = AAS_SubdivideArea_r(tmpnode2);
    447 	} //end if
    448 	return tmpnode;
    449 } //end of the function AAS_SubdivideArea_r
    450 //===========================================================================
    451 //
    452 // Parameter:				-
    453 // Returns:					-
    454 // Changes Globals:		-
    455 //===========================================================================
    456 tmp_node_t *AAS_GravitationalSubdivision_r(tmp_node_t *tmpnode)
    457 {
    458 	//if this is a solid leaf
    459 	if (!tmpnode) return NULL;
    460 	//negative so it's an area
    461 	if (tmpnode->tmparea) return AAS_SubdivideArea_r(tmpnode);
    462 	//do the children recursively
    463 	tmpnode->children[0] = AAS_GravitationalSubdivision_r(tmpnode->children[0]);
    464 	tmpnode->children[1] = AAS_GravitationalSubdivision_r(tmpnode->children[1]);
    465 	return tmpnode;
    466 } //end of the function AAS_GravitationalSubdivision_r
    467 //===========================================================================
    468 // NOTE: merge faces and melt edges first
    469 //
    470 // Parameter:				-
    471 // Returns:					-
    472 // Changes Globals:		-
    473 //===========================================================================
    474 void AAS_GravitationalSubdivision(void)
    475 {
    476 	Log_Write("AAS_GravitationalSubdivision\r\n");
    477 	numgravitationalsubdivisions = 0;
    478 	qprintf("%6i gravitational subdivisions", numgravitationalsubdivisions);
    479 	//start with the head node
    480 	AAS_GravitationalSubdivision_r(tmpaasworld.nodes);
    481 	qprintf("\n");
    482 	Log_Write("%6i gravitational subdivisions\r\n", numgravitationalsubdivisions);
    483 } //end of the function AAS_GravitationalSubdivision
    484 //===========================================================================
    485 //
    486 // Parameter:				-
    487 // Returns:					-
    488 // Changes Globals:		-
    489 //===========================================================================
    490 tmp_node_t *AAS_RefreshLadderSubdividedTree_r(tmp_node_t *tmpnode, tmp_area_t *tmparea,
    491 												  tmp_node_t *tmpnode1, tmp_node_t *tmpnode2, int planenum)
    492 {
    493 	//if this is a solid leaf
    494 	if (!tmpnode) return NULL;
    495 	//negative so it's an area
    496 	if (tmpnode->tmparea)
    497 	{
    498 		if (tmpnode->tmparea == tmparea)
    499 		{
    500 			tmpnode->tmparea = NULL;
    501 			tmpnode->planenum = planenum;
    502 			tmpnode->children[0] = tmpnode1;
    503 			tmpnode->children[1] = tmpnode2;
    504 		} //end if
    505 		return tmpnode;
    506 	} //end if
    507 	//do the children recursively
    508 	tmpnode->children[0] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[0],
    509 									tmparea, tmpnode1, tmpnode2, planenum);
    510 	tmpnode->children[1] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[1],
    511 									tmparea, tmpnode1, tmpnode2, planenum);
    512 	return tmpnode;
    513 } //end of the function AAS_RefreshLadderSubdividedTree_r
    514 //===========================================================================
    515 // find an area with ladder faces and ground faces that are not connected
    516 // split the area with a horizontal plane at the lowest vertex of all
    517 // ladder faces in the area
    518 //
    519 // Parameter:				-
    520 // Returns:					-
    521 // Changes Globals:		-
    522 //===========================================================================
    523 tmp_node_t *AAS_LadderSubdivideArea_r(tmp_node_t *tmpnode)
    524 {
    525 	int side1, i, planenum;
    526 	int foundladderface, foundgroundface;
    527 	float dist;
    528 	tmp_area_t *tmparea, *frontarea, *backarea;
    529 	tmp_face_t *face1;
    530 	tmp_node_t *tmpnode1, *tmpnode2;
    531 	vec3_t lowestpoint, normal = {0, 0, 1};
    532 	plane_t *plane;
    533 	winding_t *w;
    534 
    535 	tmparea = tmpnode->tmparea;
    536 	//skip areas with a liquid
    537 	if (tmparea->contents & (AREACONTENTS_WATER
    538 									| AREACONTENTS_LAVA
    539 									| AREACONTENTS_SLIME)) return tmpnode;
    540 	//must be possible to stand in the area
    541 	if (!(tmparea->presencetype & PRESENCE_NORMAL)) return tmpnode;
    542 	//
    543 	foundladderface = false;
    544 	foundgroundface = false;
    545 	lowestpoint[2] = 99999;
    546 	//
    547 	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
    548 	{
    549 		//side of the face the area is on
    550 		side1 = face1->frontarea != tmparea;
    551 		//if the face is a ladder face
    552 		if (face1->faceflags & FACE_LADDER)
    553 		{
    554 			plane = &mapplanes[face1->planenum];
    555 			//the ladder face plane should be pretty much vertical
    556 			if (DotProduct(plane->normal, normal) > -0.1)
    557 			{
    558 				foundladderface = true;
    559 				//find lowest point
    560 				for (i = 0; i < face1->winding->numpoints; i++)
    561 				{
    562 					if (face1->winding->p[i][2] < lowestpoint[2])
    563 					{
    564 						VectorCopy(face1->winding->p[i], lowestpoint);
    565 					} //end if
    566 				} //end for
    567 			} //end if
    568 		} //end if
    569 		else if (face1->faceflags & FACE_GROUND)
    570 		{
    571 			foundgroundface = true;
    572 		} //end else if
    573 	} //end for
    574 	//
    575 	if ((!foundladderface) || (!foundgroundface)) return tmpnode;
    576 	//
    577 	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
    578 	{
    579 		//side of the face the area is on
    580 		side1 = face1->frontarea != tmparea;
    581 		//if the face isn't a ground face
    582 		if (!(face1->faceflags & FACE_GROUND)) continue;
    583 		//the ground plane
    584 		plane = &mapplanes[face1->planenum];
    585 		//get the difference between the ground plane and the lowest point
    586 		dist = DotProduct(plane->normal, lowestpoint) - plane->dist;
    587 		//if the lowest point is very near one of the ground planes
    588 		if (dist > -1 && dist < 1)
    589 		{
    590 			return tmpnode;
    591 		} //end if
    592 	} //end for
    593 	//
    594 	dist = DotProduct(normal, lowestpoint);
    595 	planenum = FindFloatPlane(normal, dist);
    596 	//
    597 	w = AAS_SplitWinding(tmparea, planenum);
    598 	if (!w) return tmpnode;
    599 	FreeWinding(w);
    600 	//split the area with a horizontal plane through the lowest point
    601 	qprintf("\r%6d", ++numladdersubdivisions);
    602 	//
    603 	AAS_SplitArea(tmparea, planenum, &frontarea, &backarea);
    604 	//
    605 	tmpnode->tmparea = NULL;
    606 	tmpnode->planenum = planenum;
    607 	//
    608 	tmpnode1 = AAS_AllocTmpNode();
    609 	tmpnode1->planenum = 0;
    610 	tmpnode1->tmparea = frontarea;
    611 	//
    612 	tmpnode2 = AAS_AllocTmpNode();
    613 	tmpnode2->planenum = 0;
    614 	tmpnode2->tmparea = backarea;
    615 	//subdivide the areas created by splitting recursively
    616 	tmpnode->children[0] = AAS_LadderSubdivideArea_r(tmpnode1);
    617 	tmpnode->children[1] = AAS_LadderSubdivideArea_r(tmpnode2);
    618 	//refresh the tree
    619 	AAS_RefreshLadderSubdividedTree_r(tmpaasworld.nodes, tmparea, tmpnode1, tmpnode2, planenum);
    620 	//
    621 	return tmpnode;
    622 } //end of the function AAS_LadderSubdivideArea_r
    623 //===========================================================================
    624 //
    625 // Parameter:				-
    626 // Returns:					-
    627 // Changes Globals:		-
    628 //===========================================================================
    629 tmp_node_t *AAS_LadderSubdivision_r(tmp_node_t *tmpnode)
    630 {
    631 	//if this is a solid leaf
    632 	if (!tmpnode) return 0;
    633 	//negative so it's an area
    634 	if (tmpnode->tmparea) return AAS_LadderSubdivideArea_r(tmpnode);
    635 	//do the children recursively
    636 	tmpnode->children[0] = AAS_LadderSubdivision_r(tmpnode->children[0]);
    637 	tmpnode->children[1] = AAS_LadderSubdivision_r(tmpnode->children[1]);
    638 	return tmpnode;
    639 } //end of the function AAS_LadderSubdivision_r
    640 //===========================================================================
    641 //
    642 // Parameter:				-
    643 // Returns:					-
    644 // Changes Globals:		-
    645 //===========================================================================
    646 void AAS_LadderSubdivision(void)
    647 {
    648 	Log_Write("AAS_LadderSubdivision\r\n");
    649 	numladdersubdivisions = 0;
    650 	qprintf("%6i ladder subdivisions", numladdersubdivisions);
    651 	//start with the head node
    652 	AAS_LadderSubdivision_r(tmpaasworld.nodes);
    653 	//
    654 	qprintf("\n");
    655 	Log_Write("%6i ladder subdivisions\r\n", numladdersubdivisions);
    656 } //end of the function AAS_LadderSubdivision