/* ============ idAASLocal::EdgeSplitPoint calculates split point of the edge with the plane returns true if the split point is between the edge vertices ============ */ bool idAASLocal::EdgeSplitPoint( idVec3 &split, int edgeNum, const idPlane &plane ) const { const aasEdge_t *edge; idVec3 v1, v2; float d1, d2; edge = &file->GetEdge( edgeNum ); v1 = file->GetVertex( edge->vertexNum[0] ); v2 = file->GetVertex( edge->vertexNum[1] ); d1 = v1 * plane.Normal() - plane.Dist(); d2 = v2 * plane.Normal() - plane.Dist(); //if ( (d1 < CM_CLIP_EPSILON && d2 < CM_CLIP_EPSILON) || (d1 > -CM_CLIP_EPSILON && d2 > -CM_CLIP_EPSILON) ) { if( FLOATSIGNBITSET( d1 ) == FLOATSIGNBITSET( d2 ) ) { return false; } split = v1 + ( d1 / ( d1 - d2 ) ) * ( v2 - v1 ); return true; }
/* ============ idBrush::Split ============ */ int idBrush::Split( const idPlane& plane, int planeNum, idBrush** front, idBrush** back ) const { int res, i, j; idBrushSide* side, *frontSide, *backSide; float dist, maxBack, maxFront, *maxBackWinding, *maxFrontWinding; idWinding* w, *mid; assert( windingsValid ); if( front ) { *front = NULL; } if( back ) { *back = NULL; } res = bounds.PlaneSide( plane, -BRUSH_EPSILON ); if( res == PLANESIDE_FRONT ) { if( front ) { *front = Copy(); } return res; } if( res == PLANESIDE_BACK ) { if( back ) { *back = Copy(); } return res; } maxBackWinding = ( float* ) _alloca16( sides.Num() * sizeof( float ) ); maxFrontWinding = ( float* ) _alloca16( sides.Num() * sizeof( float ) ); maxFront = maxBack = 0.0f; for( i = 0; i < sides.Num(); i++ ) { side = sides[i]; w = side->winding; if( !w ) { continue; } maxBackWinding[i] = 10.0f; maxFrontWinding[i] = -10.0f; for( j = 0; j < w->GetNumPoints(); j++ ) { dist = plane.Distance( ( *w )[j].ToVec3() ); if( dist > maxFrontWinding[i] ) { maxFrontWinding[i] = dist; } if( dist < maxBackWinding[i] ) { maxBackWinding[i] = dist; } } if( maxFrontWinding[i] > maxFront ) { maxFront = maxFrontWinding[i]; } if( maxBackWinding[i] < maxBack ) { maxBack = maxBackWinding[i]; } } if( maxFront < BRUSH_EPSILON ) { if( back ) { *back = Copy(); } return PLANESIDE_BACK; } if( maxBack > -BRUSH_EPSILON ) { if( front ) { *front = Copy(); } return PLANESIDE_FRONT; } mid = new idWinding( plane.Normal(), plane.Dist() ); for( i = 0; i < sides.Num() && mid; i++ ) { mid = mid->Clip( -sides[i]->plane, BRUSH_EPSILON, false ); } if( mid ) { if( mid->IsTiny() ) { delete mid; mid = NULL; } else if( mid->IsHuge() ) { // if the winding is huge then the brush is unbounded common->Warning( "brush %d on entity %d is unbounded" "( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )", primitiveNum, entityNum, bounds[0][0], bounds[0][1], bounds[0][2], bounds[1][0], bounds[1][1], bounds[1][2], bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[0][1], bounds[1][2] - bounds[0][2] ); delete mid; mid = NULL; } } if( !mid ) { if( maxFront > - maxBack ) { if( front ) { *front = Copy(); } return PLANESIDE_FRONT; } else { if( back ) { *back = Copy(); } return PLANESIDE_BACK; } } if( !front && !back ) { delete mid; return PLANESIDE_CROSS; } *front = new idBrush(); ( *front )->SetContents( contents ); ( *front )->SetEntityNum( entityNum ); ( *front )->SetPrimitiveNum( primitiveNum ); *back = new idBrush(); ( *back )->SetContents( contents ); ( *back )->SetEntityNum( entityNum ); ( *back )->SetPrimitiveNum( primitiveNum ); for( i = 0; i < sides.Num(); i++ ) { side = sides[i]; if( !side->winding ) { continue; } // if completely at the front if( maxBackWinding[i] >= BRUSH_EPSILON ) { ( *front )->sides.Append( side->Copy() ); } // if completely at the back else if( maxFrontWinding[i] <= -BRUSH_EPSILON ) { ( *back )->sides.Append( side->Copy() ); } else { // split the side side->Split( plane, &frontSide, &backSide ); if( frontSide ) { ( *front )->sides.Append( frontSide ); } else if( maxFrontWinding[i] > -BRUSH_EPSILON ) { // favor an overconstrained brush side = side->Copy(); side->winding = side->winding->Clip( idPlane( plane.Normal(), ( plane.Dist() - ( BRUSH_EPSILON + 0.02f ) ) ), 0.01f, true ); assert( side->winding ); ( *front )->sides.Append( side ); } if( backSide ) { ( *back )->sides.Append( backSide ); } else if( maxBackWinding[i] < BRUSH_EPSILON ) { // favor an overconstrained brush side = side->Copy(); side->winding = side->winding->Clip( idPlane( -plane.Normal(), -( plane.Dist() + ( BRUSH_EPSILON + 0.02f ) ) ), 0.01f, true ); assert( side->winding ); ( *back )->sides.Append( side ); } } } side = new idBrushSide( -plane, planeNum ^ 1 ); side->winding = mid->Reverse(); side->flags |= SFL_SPLIT; ( *front )->sides.Append( side ); ( *front )->windingsValid = true; ( *front )->BoundBrush( this ); side = new idBrushSide( plane, planeNum ); side->winding = mid; side->flags |= SFL_SPLIT; ( *back )->sides.Append( side ); ( *back )->windingsValid = true; ( *back )->BoundBrush( this ); return PLANESIDE_CROSS; }
/* ============ idBrushBSPNode::Split ============ */ bool idBrushBSPNode::Split( const idPlane &splitPlane, int splitPlaneNum ) { int s, i; idWinding *mid; idBrushBSPPortal *p, *midPortal, *newPortals[2]; idBrushBSPNode *newNodes[2]; mid = new idWinding( splitPlane.Normal(), splitPlane.Dist() ); for ( p = portals; p && mid; p = p->next[s] ) { s = (p->nodes[1] == this); if ( s ) { mid = mid->Clip( -p->plane, 0.1f, false ); } else { mid = mid->Clip( p->plane, 0.1f, false ); } } if ( !mid ) { return false; } // allocate two new nodes for ( i = 0; i < 2; i++ ) { newNodes[i] = new idBrushBSPNode(); newNodes[i]->flags = flags; newNodes[i]->contents = contents; newNodes[i]->parent = this; } // split all portals of the node for ( p = portals; p; p = portals ) { s = (p->nodes[1] == this); p->Split( splitPlane, &newPortals[0], &newPortals[1] ); for ( i = 0; i < 2; i++ ) { if ( newPortals[i] ) { if ( s ) { newPortals[i]->AddToNodes( p->nodes[0], newNodes[i] ); } else { newPortals[i]->AddToNodes( newNodes[i], p->nodes[1] ); } } } p->RemoveFromNode( p->nodes[0] ); p->RemoveFromNode( p->nodes[1] ); delete p; } // add seperating portal midPortal = new idBrushBSPPortal(); midPortal->plane = splitPlane; midPortal->planeNum = splitPlaneNum; midPortal->winding = mid; midPortal->AddToNodes( newNodes[0], newNodes[1] ); // set new child nodes children[0] = newNodes[0]; children[1] = newNodes[1]; plane = splitPlane; return true; }
/* ============ idAASLocal::FloorEdgeSplitPoint calculates either the closest or furthest point on the floor of the area which also lies on the pathPlane the point has to be on the front side of the frontPlane to be valid ============ */ bool idAASLocal::FloorEdgeSplitPoint( idVec3 &bestSplit, int areaNum, const idPlane &pathPlane, const idPlane &frontPlane, bool closest ) const { int i, j, faceNum, edgeNum; const aasArea_t *area; const aasFace_t *face; idVec3 split; float dist, bestDist; const aasEdge_t *edge; idVec3 v1, v2; float d1, d2; area = &file->GetArea( areaNum ); if ( closest ) { bestDist = maxWalkPathDistance; for ( i = area->numFaces-1; i >= 0; i-- ) { faceNum = file->GetFaceIndex( area->firstFace + i ); face = &file->GetFace( abs(faceNum) ); if ( !(face->flags & FACE_FLOOR ) ) { continue; } for ( j = face->numEdges-1; j >= 0; j-- ) { edgeNum = file->GetEdgeIndex( face->firstEdge + j ); edge = &file->GetEdge( abs( edgeNum ) ); v1 = file->GetVertex( edge->vertexNum[0] ); v2 = file->GetVertex( edge->vertexNum[1] ); d1 = v1 * pathPlane.Normal() - pathPlane.Dist(); d2 = v2 * pathPlane.Normal() - pathPlane.Dist(); //if ( (d1 < CM_CLIP_EPSILON && d2 < CM_CLIP_EPSILON) || (d1 > -CM_CLIP_EPSILON && d2 > -CM_CLIP_EPSILON) ) { if ( FLOATSIGNBITSET( d1 ) == FLOATSIGNBITSET( d2 ) ) { continue; } split = v1 + (d1 / (d1 - d2)) * (v2 - v1); dist = frontPlane.Distance( split ); if ( dist >= -0.1f && dist < bestDist ) { bestDist = dist; bestSplit = split; } } } return ( bestDist < maxWalkPathDistance ); } else { bestDist = -0.1f; for ( i = area->numFaces-1; i >= 0; i-- ) { faceNum = file->GetFaceIndex( area->firstFace + i ); face = &file->GetFace( abs(faceNum) ); if ( !(face->flags & FACE_FLOOR ) ) { continue; } for ( j = face->numEdges-1; j >= 0; j-- ) { edgeNum = file->GetEdgeIndex( face->firstEdge + j ); edge = &file->GetEdge( abs( edgeNum ) ); v1 = file->GetVertex( edge->vertexNum[0] ); v2 = file->GetVertex( edge->vertexNum[1] ); d1 = v1 * pathPlane.Normal() - pathPlane.Dist(); d2 = v2 * pathPlane.Normal() - pathPlane.Dist(); //if ( (d1 < CM_CLIP_EPSILON && d2 < CM_CLIP_EPSILON) || (d1 > -CM_CLIP_EPSILON && d2 > -CM_CLIP_EPSILON) ) { if ( FLOATSIGNBITSET( d1 ) == FLOATSIGNBITSET( d2 ) ) { continue; } split = v1 + (d1 / (d1 - d2)) * (v2 - v1); dist = frontPlane.Distance( split ); if ( dist > bestDist ) { bestDist = dist; bestSplit = split; } } } return ( bestDist > -0.1f ); } }