/* ================= ChopWinding Returns the fragment of in that is on the front side of the cliping plane. The original is freed. ================= */ winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist) { winding_t *f, *b; ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b); FreeWinding (in); if (b) FreeWinding (b); return f; }
// NOTE: This is identical to ClipWindingEpsilon, but it does a pre/post translation to improve precision void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset ) { TranslateWinding( in, offset ); ClipWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back ); TranslateWinding( in, -offset ); if ( front && *front ) { TranslateWinding( *front, -offset ); } if ( back && *back ) { TranslateWinding( *back, -offset ); } }
//=========================================================================== // NOTE: the original face is invalid after splitting // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_SplitFace(tmp_face_t *face, vec3_t normal, float dist, tmp_face_t **frontface, tmp_face_t **backface) { winding_t *frontw, *backw; // *frontface = *backface = NULL; ClipWindingEpsilon(face->winding, normal, dist, FACECLIP_EPSILON, &frontw, &backw); #ifdef DEBUG // if (frontw) { if (WindingIsTiny(frontw)) { Log_Write("AAS_SplitFace: tiny back face\r\n"); FreeWinding(frontw); frontw = NULL; } //end if } //end if if (backw) { if (WindingIsTiny(backw)) { Log_Write("AAS_SplitFace: tiny back face\r\n"); FreeWinding(backw); backw = NULL; } //end if } //end if #endif //DEBUG //if the winding was split if (frontw) { //check bounds (*frontface) = AAS_AllocTmpFace(); (*frontface)->planenum = face->planenum; (*frontface)->winding = frontw; (*frontface)->faceflags = face->faceflags; } //end if if (backw) { //check bounds (*backface) = AAS_AllocTmpFace(); (*backface)->planenum = face->planenum; (*backface)->winding = backw; (*backface)->faceflags = face->faceflags; } //end if } //end of the function AAS_SplitFace
/* ============= DicePatch Chops the patch by a global grid ============= */ void DicePatch( patch_t *patch ){ winding_t *w, *o1, *o2; vec3_t mins, maxs; vec3_t split; vec_t dist; int i; patch_t *newp; w = patch->winding; WindingBounds( w, mins, maxs ); for ( i = 0 ; i < 3 ; i++ ) if ( floor( ( mins[i] + 1 ) / subdiv ) < floor( ( maxs[i] - 1 ) / subdiv ) ) { break; } if ( i == 3 ) { // no splitting needed return; } // // split the winding // VectorCopy( vec3_origin, split ); split[i] = 1; dist = subdiv * ( 1 + floor( ( mins[i] + 1 ) / subdiv ) ); ClipWindingEpsilon( w, split, dist, ON_EPSILON, &o1, &o2 ); // // create a new patch // if ( num_patches == MAX_PATCHES ) { Error( "MAX_PATCHES" ); } newp = &patches[num_patches]; num_patches++; newp->next = patch->next; patch->next = newp; patch->winding = o1; newp->winding = o2; FinishSplit( patch, newp ); DicePatch( patch ); DicePatch( newp ); }
/** * @brief Chops the patch by a global grid */ static void SubdividePatch(patch_t* patch) { winding_t* w, *o1, *o2; vec3_t mins, maxs; vec3_t split; vec_t dist; int i; patch_t* newp; w = patch->winding; WindingBounds(w, mins, maxs); VectorClear(split); for (i = 0; i < 3; i++) { if (floor((mins[i] + 1) / PATCH_SUBDIVIDE) < floor((maxs[i] - 1) / PATCH_SUBDIVIDE)) { split[i] = 1.0; break; } } /* no splitting needed */ if (i == 3) return; /* split the winding */ dist = PATCH_SUBDIVIDE * (1 + floor((mins[i] + 1) / PATCH_SUBDIVIDE)); ClipWindingEpsilon(w, split, dist, ON_EPSILON, &o1, &o2); /* create a new patch */ newp = Mem_AllocType(patch_t); newp->next = patch->next; patch->next = newp; patch->winding = o1; newp->winding = o2; FinishSubdividePatch(patch, newp); SubdividePatch(patch); SubdividePatch(newp); }
bool DWinding::ChopWinding(DPlane* chopPlane) { DWinding *f, *b; ClipWindingEpsilon (chopPlane, (float)ON_EPSILON, &f, &b); if (b) delete (b); if(!f) { delete this; return FALSE; } delete[] p; p = f->p; f->p = NULL; numpoints = f->numpoints; delete f; return TRUE; }
//=========================================================================== // Move or split the portals that bound node so that the node's // children have portals instead of node. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void SplitNodePortals(node_t *node) { portal_t *p, *next_portal, *new_portal; node_t *f, *b, *other_node; int side = 0; // TTimo: init plane_t *plane; winding_t *frontwinding, *backwinding; plane = &mapplanes[node->planenum]; f = node->children[0]; b = node->children[1]; for(p = node->portals ; p ; p = next_portal) { if(p->nodes[0] == node) { side = 0; } else if(p->nodes[1] == node) { side = 1; } else { Error("SplitNodePortals: mislinked portal"); } next_portal = p->next[side]; other_node = p->nodes[!side]; RemovePortalFromNode(p, p->nodes[0]); RemovePortalFromNode(p, p->nodes[1]); // // cut the portal into two portals, one on each side of the cut plane // ClipWindingEpsilon(p->winding, plane->normal, plane->dist, SPLIT_WINDING_EPSILON, &frontwinding, &backwinding); if(frontwinding && WindingIsTiny(frontwinding)) { FreeWinding(frontwinding); frontwinding = NULL; c_tinyportals++; } //end if if(backwinding && WindingIsTiny(backwinding)) { FreeWinding(backwinding); backwinding = NULL; c_tinyportals++; } //end if #ifdef DEBUG /* //NOTE: don't use this winding ok check // all the invalid windings only have a degenerate edge if (frontwinding && WindingError(frontwinding)) { Log_Print("SplitNodePortals: front %s\n", WindingErrorString()); FreeWinding(frontwinding); frontwinding = NULL; } //end if if (backwinding && WindingError(backwinding)) { Log_Print("SplitNodePortals: back %s\n", WindingErrorString()); FreeWinding(backwinding); backwinding = NULL; } //end if*/ #endif //DEBUG if(!frontwinding && !backwinding) // tiny windings on both sides { continue; } if(!frontwinding) { FreeWinding(backwinding); if(side == 0) { AddPortalToNodes(p, b, other_node); } else { AddPortalToNodes(p, other_node, b); } continue; } if(!backwinding) { FreeWinding(frontwinding); if(side == 0) { AddPortalToNodes(p, f, other_node); } else { AddPortalToNodes(p, other_node, f); } continue; } // the winding is split new_portal = AllocPortal(); *new_portal = *p; new_portal->winding = backwinding; FreeWinding(p->winding); p->winding = frontwinding; if(side == 0) { AddPortalToNodes(p, f, other_node); AddPortalToNodes(new_portal, b, other_node); } //end if else { AddPortalToNodes(p, other_node, f); AddPortalToNodes(new_portal, other_node, b); } //end else } node->portals = NULL; } //end of the function SplitNodePortals
void BuildFaceTree_r(node_t * node, face_t * list) { face_t *split; face_t *next; int side; plane_t *plane; face_t *newFace; face_t *childLists[2]; winding_t *frontWinding, *backWinding; int i; int splitPlaneNum, compileFlags; qboolean isstruct = qfalse; int splits, front, back; /* count faces left */ i = CountFaceList(list); #if defined(DEBUG_SPLITS) Sys_FPrintf(SYS_VRB, "faces left = %d\n", i); #endif /* select the best split plane */ SelectSplitPlaneNum(node, list, &splitPlaneNum, &compileFlags); /* if we don't have any more faces, this is a leaf */ if(splitPlaneNum == -1) { node->planenum = PLANENUM_LEAF; node->has_structural_children = qfalse; c_faceLeafs++; return; } /* partition the list */ node->planenum = splitPlaneNum; node->compileFlags = compileFlags; node->has_structural_children = !(compileFlags & C_DETAIL) && !node->opaque; plane = &mapplanes[splitPlaneNum]; childLists[0] = NULL; childLists[1] = NULL; splits = front = back = 0; for(split = list; split; split = next) { /* set next */ next = split->next; /* don't split by identical plane */ if(split->planenum == node->planenum) { FreeBspFace(split); continue; } if(!(split->compileFlags & C_DETAIL)) isstruct = 1; /* determine which side the face falls on */ side = WindingOnPlaneSide(split->w, plane->normal, plane->dist); /* switch on side */ if(side == SIDE_CROSS) { splits++; ClipWindingEpsilon(split->w, plane->normal, plane->dist, CLIP_EPSILON * 2, &frontWinding, &backWinding); if(frontWinding) { newFace = AllocBspFace(); newFace->w = frontWinding; newFace->next = childLists[0]; newFace->planenum = split->planenum; newFace->priority = split->priority; newFace->compileFlags = split->compileFlags; childLists[0] = newFace; front++; } if(backWinding) { newFace = AllocBspFace(); newFace->w = backWinding; newFace->next = childLists[1]; newFace->planenum = split->planenum; newFace->priority = split->priority; newFace->compileFlags = split->compileFlags; childLists[1] = newFace; back++; } FreeBspFace(split); } else if(side == SIDE_FRONT) { split->next = childLists[0]; childLists[0] = split; front++; } else if(side == SIDE_BACK) { split->next = childLists[1]; childLists[1] = split; back++; } } // recursively process children for(i = 0; i < 2; i++) { node->children[i] = AllocNode(); node->children[i]->parent = node; VectorCopy(node->mins, node->children[i]->mins); VectorCopy(node->maxs, node->children[i]->maxs); c_faceNodes++; } for(i = 0; i < 3; i++) { if(plane->normal[i] == 1) { node->children[0]->mins[i] = plane->dist; node->children[1]->maxs[i] = plane->dist; break; } } #if 1 if(drawBSP && drawTree) { drawChildLists[0] = childLists[0]; drawChildLists[1] = childLists[1]; drawSplitNode = node; Draw_Scene(DrawAll); } #endif #if defined(DEBUG_SPLITS) if((node->compileFlags & C_DETAIL) && isstruct) Sys_FPrintf(SYS_ERR, "I am detail, my child is structural, this is a wtf1\n", node->has_structural_children); #endif for(i = 0; i < 2; i++) { BuildFaceTree_r(node->children[i], childLists[i]); node->has_structural_children |= node->children[i]->has_structural_children; } #if defined(DEBUG_SPLITS) if((node->compileFlags & C_DETAIL) && !(node->children[0]->compileFlags & C_DETAIL) && node->children[0]->planenum != PLANENUM_LEAF) Sys_FPrintf(SYS_ERR, "I am detail, my child is structural\n", node->has_structural_children); if((node->compileFlags & C_DETAIL) && isstruct) Sys_FPrintf(SYS_ERR, "I am detail, my child is structural, this is a wtf2\n", node->has_structural_children); #endif }
/* ================ SplitBrush Generates two new brushes, leaving the original unchanged ================ */ void SplitBrush (bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back) { bspbrush_t *b[2]; int i, j; winding_t *w, *cw[2], *midwinding; plane_t *plane, *plane2; side_t *s, *cs; float d, d_front, d_back; *front = *back = NULL; plane = &mapplanes[planenum]; // check all points d_front = d_back = 0; for (i=0 ; i<brush->numsides ; i++) { w = brush->sides[i].winding; if (!w) continue; for (j=0 ; j<w->numpoints ; j++) { d = DotProduct (w->p[j], plane->normal) - plane->dist; if (d > 0 && d > d_front) d_front = d; if (d < 0 && d < d_back) d_back = d; } } if (d_front < 0.1) // PLANESIDE_EPSILON) { // only on back *back = CopyBrush (brush); return; } if (d_back > -0.1) // PLANESIDE_EPSILON) { // only on front *front = CopyBrush (brush); return; } // create a new winding from the split plane w = BaseWindingForPlane (plane->normal, plane->dist); for (i=0 ; i<brush->numsides && w ; i++) { plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); } if (!w || WindingIsTiny (w) ) { // the brush isn't really split int side; side = BrushMostlyOnSide (brush, plane); if (side == PSIDE_FRONT) *front = CopyBrush (brush); if (side == PSIDE_BACK) *back = CopyBrush (brush); return; } if (WindingIsHuge (w)) { qprintf ("WARNING: huge winding\n"); } midwinding = w; // split it for real for (i=0 ; i<2 ; i++) { b[i] = AllocBrush (brush->numsides+1); b[i]->original = brush->original; } // split all the current windings for (i=0 ; i<brush->numsides ; i++) { s = &brush->sides[i]; w = s->winding; if (!w) continue; ClipWindingEpsilon (w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); for (j=0 ; j<2 ; j++) { if (!cw[j]) continue; #if 0 if (WindingIsTiny (cw[j])) { FreeWinding (cw[j]); continue; } #endif cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; // cs->planenum = s->planenum; // cs->texinfo = s->texinfo; // cs->visible = s->visible; // cs->original = s->original; cs->winding = cw[j]; cs->tested = false; } } // see if we have valid polygons on both sides for (i=0 ; i<2 ; i++) { BoundBrush (b[i]); for (j=0 ; j<3 ; j++) { if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096) { qprintf ("bogus brush after clip\n"); break; } } if (b[i]->numsides < 3 || j < 3) { FreeBrush (b[i]); b[i] = NULL; } } if ( !(b[0] && b[1]) ) { if (!b[0] && !b[1]) qprintf ("split removed brush\n"); else qprintf ("split not on both sides\n"); if (b[0]) { FreeBrush (b[0]); *front = CopyBrush (brush); } if (b[1]) { FreeBrush (b[1]); *back = CopyBrush (brush); } return; } // add the midwinding to both sides for (i=0 ; i<2 ; i++) { cs = &b[i]->sides[b[i]->numsides]; b[i]->numsides++; cs->planenum = planenum^i^1; cs->texinfo = TEXINFO_NODE; cs->visible = false; cs->tested = false; if (i==0) cs->winding = CopyWinding (midwinding); else cs->winding = midwinding; } { vec_t v1; int i; for (i=0 ; i<2 ; i++) { v1 = BrushVolume (b[i]); if (v1 < 1.0) { FreeBrush (b[i]); b[i] = NULL; // qprintf ("tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }
void BuildFaceTree_r( node_t *node, face_t *list ){ face_t *split; face_t *next; int side; plane_t *plane; face_t *newFace; face_t *childLists[2]; winding_t *frontWinding, *backWinding; int i; int splitPlaneNum, compileFlags; /* count faces left */ i = CountFaceList( list ); /* select the best split plane */ SelectSplitPlaneNum( node, list, &splitPlaneNum, &compileFlags ); /* if we don't have any more faces, this is a node */ if ( splitPlaneNum == -1 ) { node->planenum = PLANENUM_LEAF; c_faceLeafs++; return; } /* partition the list */ node->planenum = splitPlaneNum; node->compileFlags = compileFlags; plane = &mapplanes[ splitPlaneNum ]; childLists[0] = NULL; childLists[1] = NULL; for ( split = list; split; split = next ) { /* set next */ next = split->next; /* don't split by identical plane */ if ( split->planenum == node->planenum ) { FreeBspFace( split ); continue; } /* determine which side the face falls on */ side = WindingOnPlaneSide( split->w, plane->normal, plane->dist ); /* switch on side */ if ( side == SIDE_CROSS ) { ClipWindingEpsilon( split->w, plane->normal, plane->dist, CLIP_EPSILON * 2, &frontWinding, &backWinding ); if ( frontWinding ) { newFace = AllocBspFace(); newFace->w = frontWinding; newFace->next = childLists[0]; newFace->planenum = split->planenum; newFace->priority = split->priority; newFace->compileFlags = split->compileFlags; childLists[0] = newFace; } if ( backWinding ) { newFace = AllocBspFace(); newFace->w = backWinding; newFace->next = childLists[1]; newFace->planenum = split->planenum; newFace->priority = split->priority; newFace->compileFlags = split->compileFlags; childLists[1] = newFace; } FreeBspFace( split ); } else if ( side == SIDE_FRONT ) { split->next = childLists[0]; childLists[0] = split; } else if ( side == SIDE_BACK ) { split->next = childLists[1]; childLists[1] = split; } } // recursively process children for ( i = 0 ; i < 2 ; i++ ) { node->children[i] = AllocNode(); node->children[i]->parent = node; VectorCopy( node->mins, node->children[i]->mins ); VectorCopy( node->maxs, node->children[i]->maxs ); } for ( i = 0 ; i < 3 ; i++ ) { if ( plane->normal[i] == 1 ) { node->children[0]->mins[i] = plane->dist; node->children[1]->maxs[i] = plane->dist; break; } } for ( i = 0 ; i < 2 ; i++ ) { BuildFaceTree_r( node->children[i], childLists[i] ); } }
/* ============== SplitNodePortals Move or split the portals that bound node so that the node's children have portals instead of node. ============== */ void SplitNodePortals (node_t *node) { portal_t *p, *next_portal, *new_portal; node_t *f, *b, *other_node; int side = 0; plane_t *plane; winding_t *frontwinding, *backwinding; plane = &mapplanes[node->planenum]; f = node->children[0]; b = node->children[1]; for (p = node->portals ; p ; p = next_portal) { if (p->nodes[0] == node) side = 0; else if (p->nodes[1] == node) side = 1; else Error ("CutNodePortals_r: mislinked portal"); next_portal = p->next[side]; other_node = p->nodes[!side]; RemovePortalFromNode (p, p->nodes[0]); RemovePortalFromNode (p, p->nodes[1]); // // cut the portal into two portals, one on each side of the cut plane // ClipWindingEpsilon (p->winding, plane->normal, plane->dist, SPLIT_WINDING_EPSILON, &frontwinding, &backwinding); if (frontwinding && WindingIsTiny(frontwinding)) { FreeWinding (frontwinding); frontwinding = NULL; c_tinyportals++; } if (backwinding && WindingIsTiny(backwinding)) { FreeWinding (backwinding); backwinding = NULL; c_tinyportals++; } if (!frontwinding && !backwinding) { // tiny windings on both sides continue; } if (!frontwinding) { FreeWinding (backwinding); if (side == 0) AddPortalToNodes (p, b, other_node); else AddPortalToNodes (p, other_node, b); continue; } if (!backwinding) { FreeWinding (frontwinding); if (side == 0) AddPortalToNodes (p, f, other_node); else AddPortalToNodes (p, other_node, f); continue; } // the winding is split new_portal = AllocPortal (); *new_portal = *p; new_portal->winding = backwinding; FreeWinding (p->winding); p->winding = frontwinding; if (side == 0) { AddPortalToNodes (p, f, other_node); AddPortalToNodes (new_portal, b, other_node); } else { AddPortalToNodes (p, other_node, f); AddPortalToNodes (new_portal, other_node, b); } } node->portals = NULL; }
qboolean ChopFaceSurfaceByBrush(entity_t * e, mapDrawSurface_t * ds, brush_t * b) { int i, j; side_t *s; plane_t *plane; winding_t *w; winding_t *front, *back; winding_t *outside[MAX_BRUSH_SIDES]; int numOutside; mapDrawSurface_t *newds; /* dummy check */ if(ds->sideRef == NULL || ds->sideRef->side == NULL) return qfalse; /* initial setup */ w = WindingFromDrawSurf(ds); numOutside = 0; /* chop by each brush side */ for(i = 0; i < b->numsides; i++) { /* get brush side and plane */ s = &b->sides[i]; plane = &mapplanes[s->planenum]; /* handle coplanar outfacing (don't fog) */ if(ds->sideRef->side->planenum == s->planenum) return qfalse; /* handle coplanar infacing (keep inside) */ if((ds->sideRef->side->planenum ^ 1) == s->planenum) continue; /* general case */ ClipWindingEpsilon(w, plane->normal, plane->dist, ON_EPSILON, &front, &back); FreeWinding(w); if(back == NULL) { /* nothing actually contained inside */ for(j = 0; j < numOutside; j++) FreeWinding(outside[j]); return qfalse; } if(front != NULL) { if(numOutside == MAX_BRUSH_SIDES) Error("MAX_BRUSH_SIDES"); outside[numOutside] = front; numOutside++; } w = back; } /* fixme: celshaded surface fragment errata */ /* all of outside fragments become seperate drawsurfs */ numFogFragments += numOutside; s = ds->sideRef->side; for(i = 0; i < numOutside; i++) { newds = DrawSurfaceForSide(e, ds->mapBrush, s, outside[i]); newds->fogNum = ds->fogNum; FreeWinding(outside[i]); } /* ydnar: the old code neglected to snap to 0.125 for the fragment inside the fog brush, leading to sparklies. this new code does the right thing and uses the original surface's brush side */ /* build a drawsurf for it */ newds = DrawSurfaceForSide(e, ds->mapBrush, s, w); if(newds == NULL) return qfalse; /* copy new to original */ ClearSurface(ds); memcpy(ds, newds, sizeof(mapDrawSurface_t)); /* didn't really add a new drawsurface... :) */ numMapDrawSurfs--; /* return ok */ return qtrue; }
/* ================ BuildFaceTree_r ================ */ void BuildFaceTree_r(node_t * node, bspFace_t * list) { bspFace_t *split; bspFace_t *next; int side; plane_t *plane; bspFace_t *newFace; bspFace_t *childLists[2]; winding_t *frontWinding, *backWinding; int i; int splitPlaneNum; int hintSplit; i = CountFaceList(list); SelectSplitPlaneNum(node, list, &splitPlaneNum, &hintSplit); // if we don't have any more faces, this is a node if(splitPlaneNum == -1) { node->planenum = PLANENUM_LEAF; c_faceLeafs++; return; } // partition the list node->planenum = splitPlaneNum; node->hint = hintSplit; plane = &mapPlanes[splitPlaneNum]; childLists[0] = NULL; childLists[1] = NULL; for(split = list; split; split = next) { next = split->next; if(split->planenum == node->planenum) { FreeBspFace(split); continue; } side = WindingOnPlaneSide(split->w, plane->normal, plane->dist); if(side == SIDE_CROSS) { ClipWindingEpsilon(split->w, plane->normal, plane->dist, CLIP_EPSILON * 2, &frontWinding, &backWinding); if(frontWinding) { newFace = AllocBspFace(); newFace->w = frontWinding; newFace->next = childLists[0]; newFace->planenum = split->planenum; newFace->priority = split->priority; newFace->hint = split->hint; childLists[0] = newFace; } if(backWinding) { newFace = AllocBspFace(); newFace->w = backWinding; newFace->next = childLists[1]; newFace->planenum = split->planenum; newFace->priority = split->priority; newFace->hint = split->hint; childLists[1] = newFace; } FreeBspFace(split); } else if(side == SIDE_FRONT) { split->next = childLists[0]; childLists[0] = split; } else if(side == SIDE_BACK) { split->next = childLists[1]; childLists[1] = split; } } // recursively process children for(i = 0; i < 2; i++) { node->children[i] = AllocNode(); node->children[i]->parent = node; VectorCopy(node->mins, node->children[i]->mins); VectorCopy(node->maxs, node->children[i]->maxs); } for(i = 0; i < 3; i++) { if(plane->normal[i] == 1) { node->children[0]->mins[i] = plane->dist; node->children[1]->maxs[i] = plane->dist; break; } } for(i = 0; i < 2; i++) { BuildFaceTree_r(node->children[i], childLists[i]); } }
/* ============= SubdividePatch Chops the patch only if its local bounds exceed the max size ============= */ void SubdividePatch( patch_t *patch ){ winding_t *w, *o1, *o2; vec3_t mins, maxs, total; vec3_t split; vec_t dist; int i, j; vec_t v; patch_t *newp; w = patch->winding; mins[0] = mins[1] = mins[2] = 99999; maxs[0] = maxs[1] = maxs[2] = -99999; for ( i = 0 ; i < w->numpoints ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { v = w->p[i][j]; if ( v < mins[j] ) { mins[j] = v; } if ( v > maxs[j] ) { maxs[j] = v; } } } VectorSubtract( maxs, mins, total ); for ( i = 0 ; i < 3 ; i++ ) if ( total[i] > ( subdiv + 1 ) ) { break; } if ( i == 3 ) { // no splitting needed return; } // // split the winding // VectorCopy( vec3_origin, split ); split[i] = 1; dist = ( mins[i] + maxs[i] ) * 0.5; ClipWindingEpsilon( w, split, dist, ON_EPSILON, &o1, &o2 ); // // create a new patch // if ( num_patches == MAX_PATCHES ) { Error( "MAX_PATCHES" ); } newp = &patches[num_patches]; num_patches++; newp->next = patch->next; patch->next = newp; patch->winding = o1; newp->winding = o2; FinishSplit( patch, newp ); SubdividePatch( patch ); SubdividePatch( newp ); }
/* ============ ExpandBrush ============= */ void ExpandBrush (int hullnum) { int i, x, s; vec3_t corner; winding_t *w; plane_t plane; int j, k, numwindings; vec_t r; winding_t **windings; plane_t clipplane, faceplane; mface_t *mf; vec3_t point; vec3_t mins, maxs; if (!numbrushfaces) return; num_hull_points = 0; num_hull_edges = 0; ClearBounds( mins, maxs ); // generate windings and bounds data numwindings = 0; windings = calloc(numbrushfaces, sizeof(*windings)); for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; windings[i] = NULL; faceplane = mf->plane; w = BaseWindingForPlane (&faceplane); for (j = 0;j < numbrushfaces && w;j++) { clipplane = faces[j].plane; if( j == i ) continue; // flip the plane, because we want to keep the back side VectorNegate(clipplane.normal, clipplane.normal); clipplane.dist *= -1; w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true); } if (!w) continue; // overcontrained plane for (j = 0;j < w->numpoints;j++) { for (k = 0;k < 3;k++) { point[k] = w->points[j][k]; r = Q_rint( point[k] ); if ( fabs( point[k] - r ) < ZERO_EPSILON) w->points[j][k] = r; else w->points[j][k] = point[k]; // check for incomplete brushes if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE ) return; } AddPointToBounds( w->points[j], mins, maxs ); } windings[i] = w; } // add all of the corner offsets for (i = 0;i < numwindings;i++) { w = windings[i]; for (j = 0;j < w->numpoints;j++) AddHullPoint(w->points[j], hullnum); } // expand the face planes for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; for (x=0 ; x<3 ; x++) { if (mf->plane.normal[x] > 0) corner[x] = -hullinfo.hullsizes[hullnum][0][x]; else if (mf->plane.normal[x] < 0) corner[x] = -hullinfo.hullsizes[hullnum][1][x]; } mf->plane.dist += DotProduct (corner, mf->plane.normal); } // add any axis planes not contained in the brush to bevel off corners for (x=0 ; x<3 ; x++) for (s=-1 ; s<=1 ; s+=2) { // add the plane VectorClear (plane.normal); plane.normal[x] = s; if (s == -1) plane.dist = -mins[x] + hullinfo.hullsizes[hullnum][1][x]; else plane.dist = maxs[x] + -hullinfo.hullsizes[hullnum][0][x]; AddBrushPlane (&plane); } // add all of the edge bevels for (i = 0;i < numwindings;i++) { w = windings[i]; for (j = 0;j < w->numpoints;j++) AddHullEdge(w->points[j], w->points[(j+1)%w->numpoints], hullnum); } // free the windings as we no longer need them for (i = 0;i < numwindings;i++) if (windings[i]) FreeWinding(windings[i]); free(windings); }
/** * @brief Generates two new brushes, leaving the original unchanged */ void SplitBrush(brush_t *brush, int32_t plane_num, brush_t **front, brush_t **back) { brush_t *b[2]; int32_t i, j; winding_t *w, *cw[2], *midwinding; map_plane_t *plane, *plane2; side_t *s, *cs; vec_t d, d_front, d_back; *front = *back = NULL; plane = &map_planes[plane_num]; // check all points d_front = d_back = 0; for (i = 0; i < brush->num_sides; i++) { w = brush->sides[i].winding; if (!w) { continue; } for (j = 0; j < w->num_points; j++) { d = DotProduct(w->points[j], plane->normal) - plane->dist; if (d > 0 && d > d_front) { d_front = d; } if (d < 0 && d < d_back) { d_back = d; } } } if (d_front < 0.1) { // PLANESIDE_EPSILON) // only on back *back = CopyBrush(brush); return; } if (d_back > -0.1) { // PLANESIDE_EPSILON) // only on front *front = CopyBrush(brush); return; } // create a new winding from the split plane w = WindingForPlane(plane->normal, plane->dist); for (i = 0; i < brush->num_sides && w; i++) { plane2 = &map_planes[brush->sides[i].plane_num ^ 1]; ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); } if (!w || WindingIsTiny(w)) { // the brush isn't really split int32_t side; side = BrushMostlyOnSide(brush, plane); if (side == SIDE_FRONT) { *front = CopyBrush(brush); } if (side == SIDE_BACK) { *back = CopyBrush(brush); } return; } if (WindingIsHuge(w)) { Mon_SendWinding(ERROR_WARN, (const vec3_t *) w->points, w->num_points, "Large winding"); } midwinding = w; // split it for real for (i = 0; i < 2; i++) { b[i] = AllocBrush(brush->num_sides + 1); b[i]->original = brush->original; } // split all the current windings for (i = 0; i < brush->num_sides; i++) { s = &brush->sides[i]; w = s->winding; if (!w) { continue; } ClipWindingEpsilon(w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON */, &cw[0], &cw[1]); for (j = 0; j < 2; j++) { if (!cw[j]) { continue; } cs = &b[j]->sides[b[j]->num_sides]; b[j]->num_sides++; *cs = *s; cs->winding = cw[j]; cs->tested = false; } } // see if we have valid polygons on both sides for (i = 0; i < 2; i++) { BoundBrush(b[i]); for (j = 0; j < 3; j++) { if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD) { Com_Debug(DEBUG_ALL, "bogus brush after clip\n"); break; } } if (b[i]->num_sides < 3 || j < 3) { FreeBrush(b[i]); b[i] = NULL; } } if (!(b[0] && b[1])) { if (!b[0] && !b[1]) { Com_Debug(DEBUG_ALL, "split removed brush\n"); } else { Com_Debug(DEBUG_ALL, "split not on both sides\n"); } if (b[0]) { FreeBrush(b[0]); *front = CopyBrush(brush); } if (b[1]) { FreeBrush(b[1]); *back = CopyBrush(brush); } return; } // add the midwinding to both sides for (i = 0; i < 2; i++) { cs = &b[i]->sides[b[i]->num_sides]; b[i]->num_sides++; cs->plane_num = plane_num ^ i ^ 1; cs->texinfo = TEXINFO_NODE; cs->visible = false; cs->tested = false; if (i == 0) { cs->winding = CopyWinding(midwinding); } else { cs->winding = midwinding; } } { vec_t v1; for (i = 0; i < 2; i++) { v1 = BrushVolume(b[i]); if (v1 < 1.0) { FreeBrush(b[i]); b[i] = NULL; Com_Debug(DEBUG_ALL, "tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }
/* ================= CreateBrushFaces ================= */ void CreateBrushFaces (void) { int i,j, k; vec_t r; face_t *f, *next; winding_t *w; plane_t clipplane, faceplane; mface_t *mf; vec3_t offset, point; offset[0] = offset[1] = offset[2] = 0; ClearBounds( brush_mins, brush_maxs ); brush_faces = NULL; if (!strncmp(ValueForKey(CurrentEntity, "classname"), "rotate_", 7)) { entity_t *FoundEntity; char *searchstring; char text[20]; searchstring = ValueForKey (CurrentEntity, "target"); FoundEntity = FindTargetEntity(searchstring); if (FoundEntity) GetVectorForKey(FoundEntity, "origin", offset); sprintf(text, "%g %g %g", offset[0], offset[1], offset[2]); SetKeyValue(CurrentEntity, "origin", text); } GetVectorForKey(CurrentEntity, "origin", offset); //printf("%i brushfaces at offset %f %f %f\n", numbrushfaces, offset[0], offset[1], offset[2]); for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; //printf("plane %f %f %f %f\n", mf->plane.normal[0], mf->plane.normal[1], mf->plane.normal[2], mf->plane.dist); faceplane = mf->plane; w = BaseWindingForPlane (&faceplane); //VectorNegate( faceplane.normal, point ); for (j = 0;j < numbrushfaces && w;j++) { clipplane = faces[j].plane; if( j == i/* || VectorCompare( clipplane.normal, point )*/ ) continue; // flip the plane, because we want to keep the back side VectorNegate(clipplane.normal, clipplane.normal); clipplane.dist *= -1; w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true); } if (!w) { //printf("----- skipped plane -----\n"); continue; // overcontrained plane } // this face is a keeper f = AllocFace (); f->winding = w; for (j = 0;j < w->numpoints;j++) { for (k = 0;k < 3;k++) { point[k] = w->points[j][k] - offset[k]; r = Q_rint( point[k] ); if ( fabs( point[k] - r ) < ZERO_EPSILON) w->points[j][k] = r; else w->points[j][k] = point[k]; // check for incomplete brushes if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE ) break; } // remove this brush if (k < 3) { FreeFace (f); for (f = brush_faces; f; f = next) { next = f->next; FreeFace (f); } brush_faces = NULL; //printf("----- skipped brush -----\n"); return; } AddPointToBounds( w->points[j], brush_mins, brush_maxs ); } CheckWinding( w ); faceplane.dist -= DotProduct(faceplane.normal, offset); f->texturenum = mf->texinfo; f->planenum = FindPlane (&faceplane, &f->planeside); f->next = brush_faces; brush_faces = f; } // Rotatable objects have to have a bounding box big enough // to account for all its rotations. if (DotProduct(offset, offset)) { vec_t delta; delta = RadiusFromBounds( brush_mins, brush_maxs ); for (k = 0;k < 3;k++) { brush_mins[k] = -delta; brush_maxs[k] = delta; } } //printf("%i : %f %f %f : %f %f %f\n", numbrushfaces, brush_mins[0], brush_mins[1], brush_mins[2], brush_maxs[0], brush_maxs[1], brush_maxs[2]); }
/* ==================== ChopFaceByBrush There may be a fragment contained in the brush ==================== */ qboolean ChopFaceByBrush(drawSurface_t * ds, bspBrush_t * b) { int i, j; side_t *s; plane_t *plane; winding_t *w; winding_t *front, *back; winding_t *outside[MAX_BRUSH_SIDES]; int numOutside; drawSurface_t *newds; drawVert_t *dv; shaderInfo_t *si; float mins[2]; // brush primitive : // axis base vec3_t texX, texY; vec_t x, y; w = WindingFromDrawSurf(ds); numOutside = 0; for(i = 0; i < b->numsides; i++) { s = &b->sides[i]; if(s->backSide) { continue; } plane = &mapPlanes[s->planenum]; // handle coplanar outfacing (don't fog) if(ds->side->planenum == s->planenum) { return qfalse; } // handle coplanar infacing (keep inside) if((ds->side->planenum ^ 1) == s->planenum) { continue; } // general case ClipWindingEpsilon(w, plane->normal, plane->dist, ON_EPSILON, &front, &back); FreeWinding(w); if(!back) { // nothing actually contained inside for(j = 0; j < numOutside; j++) { FreeWinding(outside[j]); } return qfalse; } if(front) { if(numOutside == MAX_BRUSH_SIDES) { Error("MAX_BRUSH_SIDES"); } outside[numOutside] = front; numOutside++; } w = back; } // all of outside fragments become seperate drawsurfs // linked to the same side c_fogFragment += numOutside; s = ds->side; for(i = 0; i < numOutside; i++) { newds = DrawSurfaceForSide(ds->mapBrush, s, outside[i]); FreeWinding(outside[i]); } // replace ds->verts with the verts for w ds->numVerts = w->numpoints; free(ds->verts); ds->verts = malloc(ds->numVerts * sizeof(*ds->verts)); memset(ds->verts, 0, ds->numVerts * sizeof(*ds->verts)); si = s->shaderInfo; mins[0] = 9999; mins[1] = 9999; // compute s/t coordinates from brush primitive texture matrix // compute axis base ComputeAxisBase(mapPlanes[s->planenum].normal, texX, texY); for(j = 0; j < w->numpoints; j++) { dv = ds->verts + j; VectorCopy(w->p[j], dv->xyz); if(g_bBrushPrimit == BPRIMIT_OLDBRUSHES) { // calculate texture s/t dv->st[0] = s->vecs[0][3] + DotProduct(s->vecs[0], dv->xyz); dv->st[1] = s->vecs[1][3] + DotProduct(s->vecs[1], dv->xyz); dv->st[0] /= si->width; dv->st[1] /= si->height; } else { // calculate texture s/t from brush primitive texture matrix x = DotProduct(dv->xyz, texX); y = DotProduct(dv->xyz, texY); dv->st[0] = s->texMat[0][0] * x + s->texMat[0][1] * y + s->texMat[0][2]; dv->st[1] = s->texMat[1][0] * x + s->texMat[1][1] * y + s->texMat[1][2]; } if(dv->st[0] < mins[0]) { mins[0] = dv->st[0]; } if(dv->st[1] < mins[1]) { mins[1] = dv->st[1]; } // copy normal VectorCopy(mapPlanes[s->planenum].normal, dv->normal); } // adjust the texture coordinates to be as close to 0 as possible if(!si->globalTexture) { mins[0] = floor(mins[0]); mins[1] = floor(mins[1]); for(i = 0; i < w->numpoints; i++) { dv = ds->verts + i; dv->st[0] -= mins[0]; dv->st[1] -= mins[1]; } } return qtrue; }
static void ProjectDecalOntoWinding( decalProjector_t *dp, mapDrawSurface_t *ds, winding_t *w ) { int i, j; float d, d2, alpha; winding_t *front, *back; mapDrawSurface_t *ds2; bspDrawVert_t *dv; vec4_t plane; /* dummy check */ if( w->numpoints < 3 ) { FreeWinding( w ); return; } /* offset by entity origin */ for( i = 0; i < w->numpoints; i++ ) VectorAdd( w->p[ i ], entityOrigin, w->p[ i ] ); /* make a plane from the winding */ if( !PlaneFromPoints( plane, w->p[ 0 ], w->p[ 1 ], w->p[ 2 ] ) ) { FreeWinding( w ); return; } /* backface check */ d = DotProduct( dp->planes[ 0 ], plane ); if( d < -0.0001f ) { FreeWinding( w ); return; } /* walk list of planes */ for( i = 0; i < dp->numPlanes; i++ ) { /* chop winding by the plane */ ClipWindingEpsilon( w, dp->planes[ i ], dp->planes[ i ][ 3 ], 0.0625f, &front, &back ); FreeWinding( w ); /* lose the front fragment */ if( front != NULL ) FreeWinding( front ); /* if nothing left in back, then bail */ if( back == NULL ) return; /* reset winding */ w = back; } /* nothing left? */ if( w == NULL || w->numpoints < 3 ) return; /* add to counts */ numDecalSurfaces++; /* make a new surface */ ds2 = AllocDrawSurface( SURFACE_DECAL ); /* set it up */ ds2->entityNum = ds->entityNum; ds2->castShadows = ds->castShadows; ds2->recvShadows = ds->recvShadows; ds2->shaderInfo = dp->si; ds2->fogNum = ds->fogNum; /* why was this -1? */ ds2->lightmapScale = ds->lightmapScale; ds2->numVerts = w->numpoints; ds2->verts = Malloc( ds2->numVerts * sizeof( *ds2->verts ) ); Mem_Set( ds2->verts, 0, ds2->numVerts * sizeof( *ds2->verts ) ); /* set vertexes */ for( i = 0; i < ds2->numVerts; i++ ) { /* get vertex */ dv = &ds2->verts[ i ]; /* set alpha */ d = DotProduct( w->p[ i ], dp->planes[ 0 ] ) - dp->planes[ 0 ][ 3 ]; d2 = DotProduct( w->p[ i ], dp->planes[ 1 ] ) - dp->planes[ 1 ][ 3 ]; alpha = 255.0f * d2 / (d + d2); if( alpha > 255 ) alpha = 255; else if( alpha < 0 ) alpha = 0; /* set misc */ VectorSubtract( w->p[ i ], entityOrigin, dv->xyz ); VectorCopy( plane, dv->normal ); dv->st[ 0 ] = DotProduct( dv->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ]; dv->st[ 1 ] = DotProduct( dv->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ]; /* set color */ for( j = 0; j < MAX_LIGHTMAPS; j++ ) { dv->color[ j ][ 0 ] = 255; dv->color[ j ][ 1 ] = 255; dv->color[ j ][ 2 ] = 255; dv->color[ j ][ 3 ] = alpha; } } }
//=========================================================================== // Generates two new brushes, leaving the original // unchanged // // modified for Half-Life because there are quite a lot of tiny node leaves // in the Half-Life bsps // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q1_SplitBrush(bspbrush_t *brush, int planenum, int nodenum, bspbrush_t **front, bspbrush_t **back) { bspbrush_t *b[2]; int i, j; winding_t *w, *cw[2], *midwinding; plane_t *plane, *plane2; side_t *s, *cs; float d, d_front, d_back; *front = *back = NULL; plane = &mapplanes[planenum]; // check all points d_front = d_back = 0; for (i=0 ; i<brush->numsides ; i++) { w = brush->sides[i].winding; if (!w) continue; for (j=0 ; j<w->numpoints ; j++) { d = DotProduct (w->p[j], plane->normal) - plane->dist; if (d > 0 && d > d_front) d_front = d; if (d < 0 && d < d_back) d_back = d; } //end for } //end for if (d_front < 0.1) // PLANESIDE_EPSILON) { // only on back *back = CopyBrush (brush); Log_Print("Q1_SplitBrush: only on back\n"); return; } //end if if (d_back > -0.1) // PLANESIDE_EPSILON) { // only on front *front = CopyBrush (brush); Log_Print("Q1_SplitBrush: only on front\n"); return; } //end if // create a new winding from the split plane w = BaseWindingForPlane (plane->normal, plane->dist); for (i = 0; i < brush->numsides && w; i++) { plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); } //end for if (!w || WindingIsTiny(w)) { // the brush isn't really split int side; Log_Print("Q1_SplitBrush: no split winding\n"); side = BrushMostlyOnSide (brush, plane); if (side == PSIDE_FRONT) *front = CopyBrush (brush); if (side == PSIDE_BACK) *back = CopyBrush (brush); return; } if (WindingIsHuge(w)) { Log_Print("Q1_SplitBrush: WARNING huge split winding\n"); } //end of midwinding = w; // split it for real for (i = 0; i < 2; i++) { b[i] = AllocBrush (brush->numsides+1); b[i]->original = brush->original; } //end for // split all the current windings for (i=0 ; i<brush->numsides ; i++) { s = &brush->sides[i]; w = s->winding; if (!w) continue; ClipWindingEpsilon (w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); for (j=0 ; j<2 ; j++) { if (!cw[j]) continue; #if 0 if (WindingIsTiny (cw[j])) { FreeWinding (cw[j]); continue; } #endif cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; // cs->planenum = s->planenum; // cs->texinfo = s->texinfo; // cs->visible = s->visible; // cs->original = s->original; cs->winding = cw[j]; cs->flags &= ~SFL_TESTED; } //end for } //end for // see if we have valid polygons on both sides for (i=0 ; i<2 ; i++) { BoundBrush (b[i]); for (j=0 ; j<3 ; j++) { if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096) { Log_Print("Q1_SplitBrush: bogus brush after clip\n"); break; } //end if } //end for if (b[i]->numsides < 3 || j < 3) { FreeBrush (b[i]); b[i] = NULL; Log_Print("Q1_SplitBrush: numsides < 3\n"); } //end if } //end for if ( !(b[0] && b[1]) ) { if (!b[0] && !b[1]) Log_Print("Q1_SplitBrush: split removed brush\n"); else Log_Print("Q1_SplitBrush: split not on both sides\n"); if (b[0]) { FreeBrush (b[0]); *front = CopyBrush (brush); } //end if if (b[1]) { FreeBrush (b[1]); *back = CopyBrush (brush); } //end if return; } //end if // add the midwinding to both sides for (i = 0; i < 2; i++) { cs = &b[i]->sides[b[i]->numsides]; b[i]->numsides++; cs->planenum = planenum^i^1; cs->texinfo = 0; //store the node number in the surf to find the texinfo later on cs->surf = nodenum; // cs->flags &= ~SFL_VISIBLE; cs->flags &= ~SFL_TESTED; cs->flags &= ~SFL_TEXTURED; if (i==0) cs->winding = CopyWinding (midwinding); else cs->winding = midwinding; } //end for { vec_t v1; int i; for (i=0 ; i<2 ; i++) { v1 = BrushVolume (b[i]); if (v1 < 1) { FreeBrush (b[i]); b[i] = NULL; Log_Print("Q1_SplitBrush: tiny volume after clip\n"); } //end if } //end for } //*/ *front = b[0]; *back = b[1]; } //end of the function Q1_SplitBrush
void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back ){ brush_t *b[2]; int i, j; winding_t *w, *cw[2], *midwinding; plane_t *plane, *plane2; side_t *s, *cs; float d, d_front, d_back; *front = NULL; *back = NULL; plane = &mapplanes[planenum]; // check all points d_front = d_back = 0; for ( i = 0 ; i < brush->numsides ; i++ ) { w = brush->sides[i].winding; if ( !w ) { continue; } for ( j = 0 ; j < w->numpoints ; j++ ) { d = DotProduct( w->p[j], plane->normal ) - plane->dist; if ( d > 0 && d > d_front ) { d_front = d; } if ( d < 0 && d < d_back ) { d_back = d; } } } if ( d_front < 0.1 ) { // PLANESIDE_EPSILON) // only on back *back = CopyBrush( brush ); return; } if ( d_back > -0.1 ) { // PLANESIDE_EPSILON) // only on front *front = CopyBrush( brush ); return; } // create a new winding from the split plane w = BaseWindingForPlane( plane->normal, plane->dist ); for ( i = 0 ; i < brush->numsides && w ; i++ ) { plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace( &w, plane2->normal, plane2->dist, 0 ); // PLANESIDE_EPSILON); } if ( !w || WindingIsTiny( w ) ) { // the brush isn't really split int side; side = BrushMostlyOnSide( brush, plane ); if ( side == PSIDE_FRONT ) { *front = CopyBrush( brush ); } if ( side == PSIDE_BACK ) { *back = CopyBrush( brush ); } return; } if ( WindingIsHuge( w ) ) { Sys_FPrintf( SYS_VRB,"WARNING: huge winding\n" ); } midwinding = w; // split it for real for ( i = 0 ; i < 2 ; i++ ) { b[i] = AllocBrush( brush->numsides + 1 ); memcpy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) ); b[i]->numsides = 0; b[i]->next = NULL; b[i]->original = brush->original; } // split all the current windings for ( i = 0 ; i < brush->numsides ; i++ ) { s = &brush->sides[i]; w = s->winding; if ( !w ) { continue; } ClipWindingEpsilon( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] ); for ( j = 0 ; j < 2 ; j++ ) { if ( !cw[j] ) { continue; } cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; cs->winding = cw[j]; } } // see if we have valid polygons on both sides for ( i = 0 ; i < 2 ; i++ ) { if ( b[i]->numsides < 3 || !BoundBrush( b[i] ) ) { if ( b[i]->numsides >= 3 ) { Sys_FPrintf( SYS_VRB,"bogus brush after clip\n" ); } FreeBrush( b[i] ); b[i] = NULL; } } if ( !( b[0] && b[1] ) ) { if ( !b[0] && !b[1] ) { Sys_FPrintf( SYS_VRB,"split removed brush\n" ); } else{ Sys_FPrintf( SYS_VRB,"split not on both sides\n" ); } if ( b[0] ) { FreeBrush( b[0] ); *front = CopyBrush( brush ); } if ( b[1] ) { FreeBrush( b[1] ); *back = CopyBrush( brush ); } return; } // add the midwinding to both sides for ( i = 0 ; i < 2 ; i++ ) { cs = &b[i]->sides[b[i]->numsides]; b[i]->numsides++; cs->planenum = planenum ^ i ^ 1; cs->shaderInfo = NULL; if ( i == 0 ) { cs->winding = CopyWinding( midwinding ); } else{ cs->winding = midwinding; } } { vec_t v1; int i; for ( i = 0 ; i < 2 ; i++ ) { v1 = BrushVolume( b[i] ); if ( v1 < 1.0 ) { FreeBrush( b[i] ); b[i] = NULL; // Sys_FPrintf (SYS_VRB,"tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }
/** * @brief Generates two new brushes, leaving the original unchanged */ void SplitBrush (const bspbrush_t* brush, uint16_t planenum, bspbrush_t** front, bspbrush_t** back) { bspbrush_t* b[2]; int i, j; winding_t* w, *cw[2], *midwinding; plane_t* plane; float d_front, d_back; *front = *back = nullptr; plane = &mapplanes[planenum]; /* check all points */ d_front = d_back = 0; for (i = 0; i < brush->numsides; i++) { w = brush->sides[i].winding; if (!w) continue; for (j = 0; j < w->numpoints; j++) { const float d = DotProduct(w->p[j], plane->normal) - plane->dist; if (d > 0 && d > d_front) d_front = d; else if (d < 0 && d < d_back) d_back = d; } } if (d_front < 0.1) { /* PLANESIDE_EPSILON) */ /* only on back */ *back = CopyBrush(brush); return; } if (d_back > -0.1) { /* PLANESIDE_EPSILON) */ /* only on front */ *front = CopyBrush(brush); return; } /* create a new winding from the split plane */ w = BaseWindingForPlane(plane->normal, plane->dist); for (i = 0; i < brush->numsides && w; i++) { plane_t* plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); /* PLANESIDE_EPSILON); */ } /* the brush isn't really split */ if (!w || WindingIsTiny(w)) { const int side = BrushMostlyOnSide(brush, plane); if (side == PSIDE_FRONT) *front = CopyBrush(brush); else if (side == PSIDE_BACK) *back = CopyBrush(brush); return; } if (WindingIsHuge(w)) { /** @todo Print brush and entnum either of the brush that was splitted * or the plane that was used as splitplane */ Com_Printf("WARNING: Large winding\n"); } midwinding = w; /* split it for real */ for (i = 0; i < 2; i++) { b[i] = AllocBrush(brush->numsides + 1); b[i]->original = brush->original; } /* split all the current windings */ for (i = 0; i < brush->numsides; i++) { const side_t* s = &brush->sides[i]; w = s->winding; if (!w) continue; ClipWindingEpsilon(w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); for (j = 0; j < 2; j++) { side_t* cs; if (!cw[j]) continue; cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; cs->winding = cw[j]; cs->tested = false; } } /* see if we have valid polygons on both sides */ for (i = 0; i < 2; i++) { BoundBrush(b[i]); for (j = 0; j < 3; j++) { if (b[i]->mins[j] < -MAX_WORLD_WIDTH || b[i]->maxs[j] > MAX_WORLD_WIDTH) { /** @todo Print brush and entnum either of the brush that was split * or the plane that was used as splitplane */ Verb_Printf(VERB_EXTRA, "bogus brush after clip\n"); break; } } if (b[i]->numsides < 3 || j < 3) { FreeBrush(b[i]); b[i] = nullptr; } } if (!(b[0] && b[1])) { /** @todo Print brush and entnum either of the brush that was splitted * or the plane that was used as splitplane */ if (!b[0] && !b[1]) Verb_Printf(VERB_EXTRA, "split removed brush\n"); else Verb_Printf(VERB_EXTRA, "split not on both sides\n"); if (b[0]) { FreeBrush(b[0]); *front = CopyBrush(brush); } if (b[1]) { FreeBrush(b[1]); *back = CopyBrush(brush); } return; } /* add the midwinding to both sides */ for (i = 0; i < 2; i++) { side_t* cs = &b[i]->sides[b[i]->numsides]; b[i]->numsides++; cs->planenum = planenum ^ i ^ 1; cs->texinfo = TEXINFO_NODE; cs->visible = false; cs->tested = false; if (i == 0) cs->winding = CopyWinding(midwinding); else cs->winding = midwinding; } for (i = 0; i < 2; i++) { const vec_t v1 = BrushVolume(b[i]); if (v1 < 1.0) { FreeBrush(b[i]); b[i] = nullptr; /** @todo Print brush and entnum either of the brush that was splitted * or the plane that was used as splitplane */ Verb_Printf(VERB_EXTRA, "tiny volume after clip\n"); } } *front = b[0]; *back = b[1]; }
/* ================== ClipWinding ================== */ winding_t *ClipWinding( winding_t *in, plane_t *split, qboolean keepon ) { return ClipWindingEpsilon( in, split, WINDING_EPSILON, keepon ); }