12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127 |
- #include "csg.h"
- plane_t g_mapplanes[MAX_INTERNAL_MAP_PLANES];
- int g_nummapplanes;
- #define DIST_EPSILON 0.01
- #if !defined HLCSG_FASTFIND
- /*
- * =============
- * FindIntPlane
- *
- * Returns which plane number to use for a given integer defined plane.
- *
- * =============
- */
- int FindIntPlane(const vec_t* const normal, const vec_t* const origin)
- {
- int i, j;
- plane_t* p;
- plane_t temp;
- vec_t t;
- bool locked;
- p = g_mapplanes;
- locked = false;
- i = 0;
- while (1)
- {
- if (i == g_nummapplanes)
- {
- if (!locked)
- {
- locked = true;
- ThreadLock(); // make sure we don't race
- }
- if (i == g_nummapplanes)
- {
- break; // we didn't race
- }
- }
- t = 0; // Unrolled loop
- t += (origin[0] - p->origin[0]) * normal[0];
- t += (origin[1] - p->origin[1]) * normal[1];
- t += (origin[2] - p->origin[2]) * normal[2];
- if (fabs(t) < DIST_EPSILON)
- { // on plane
- // see if the normal is forward, backwards, or off
- for (j = 0; j < 3; j++)
- {
- if (fabs(normal[j] - p->normal[j]) > NORMAL_EPSILON)
- {
- break;
- }
- }
- if (j == 3)
- {
- if (locked)
- {
- ThreadUnlock();
- }
- return i;
- }
- }
- i++;
- p++;
- }
- hlassert(locked);
- // create a new plane
- p->origin[0] = origin[0];
- p->origin[1] = origin[1];
- p->origin[2] = origin[2];
- (p + 1)->origin[0] = origin[0];
- (p + 1)->origin[1] = origin[1];
- (p + 1)->origin[2] = origin[2];
- p->normal[0] = normal[0];
- p->normal[1] = normal[1];
- p->normal[2] = normal[2];
- (p + 1)->normal[0] = -normal[0];
- (p + 1)->normal[1] = -normal[1];
- (p + 1)->normal[2] = -normal[2];
- hlassume(g_nummapplanes < MAX_INTERNAL_MAP_PLANES, assume_MAX_INTERNAL_MAP_PLANES);
- VectorNormalize(p->normal);
- p->type = (p + 1)->type = PlaneTypeForNormal(p->normal);
- p->dist = DotProduct(origin, p->normal);
- VectorSubtract(vec3_origin, p->normal, (p + 1)->normal);
- (p + 1)->dist = -p->dist;
- // always put axial planes facing positive first
- if (p->type <= last_axial)
- {
- if (normal[0] < 0 || normal[1] < 0 || normal[2] < 0)
- {
- // flip order
- temp = *p;
- *p = *(p + 1);
- *(p + 1) = temp;
- g_nummapplanes += 2;
- ThreadUnlock();
- return i + 1;
- }
- }
- g_nummapplanes += 2;
- ThreadUnlock();
- return i;
- }
- #else //ifdef HLCSG_FASTFIND
- // =====================================================================================
- // FindIntPlane, fast version (replacement by KGP)
- // This process could be optimized by placing the planes in a (non hash-) set and using
- // half of the inner loop check below as the comparator; I'd expect the speed gain to be
- // very large given the change from O(N^2) to O(NlogN) to build the set of planes.
- // =====================================================================================
- int FindIntPlane(const vec_t* const normal, const vec_t* const origin)
- {
- int returnval;
- plane_t* p;
- plane_t temp;
- vec_t t;
- returnval = 0;
- find_plane:
- for( ; returnval < g_nummapplanes; returnval++)
- {
- if( -NORMAL_EPSILON < (t = normal[0] - g_mapplanes[returnval].normal[0]) && t < NORMAL_EPSILON &&
- -NORMAL_EPSILON < (t = normal[1] - g_mapplanes[returnval].normal[1]) && t < NORMAL_EPSILON &&
- -NORMAL_EPSILON < (t = normal[2] - g_mapplanes[returnval].normal[2]) && t < NORMAL_EPSILON )
- {
- //t = (origin - plane_origin) dot (normal), unrolled
- t = (origin[0] - g_mapplanes[returnval].origin[0]) * normal[0]
- + (origin[1] - g_mapplanes[returnval].origin[1]) * normal[1]
- + (origin[2] - g_mapplanes[returnval].origin[2]) * normal[2];
- if (-DIST_EPSILON < t && t < DIST_EPSILON) // on plane
- { return returnval; }
- }
- }
- ThreadLock();
- if(returnval != g_nummapplanes) // make sure we don't race
- {
- ThreadUnlock();
- goto find_plane; //check to see if other thread added plane we need
- }
- // create new planes - double check that we have room for 2 planes
- hlassume(g_nummapplanes+1 < MAX_INTERNAL_MAP_PLANES, assume_MAX_INTERNAL_MAP_PLANES);
- p = &g_mapplanes[g_nummapplanes];
- VectorCopy(origin,p->origin);
- VectorCopy(normal,p->normal);
- VectorNormalize(p->normal);
- p->type = PlaneTypeForNormal(p->normal);
- p->dist = DotProduct(origin, p->normal);
- VectorCopy(origin,(p+1)->origin);
- VectorSubtract(vec3_origin,p->normal,(p+1)->normal);
- (p+1)->type = p->type;
- (p+1)->dist = -p->dist;
- // always put axial planes facing positive first
- if (p->type <= last_axial && (normal[0] < 0 || normal[1] < 0 || normal[2] < 0)) // flip order
- {
- temp = *p;
- *p = *(p+1);
- *(p+1) = temp;
- returnval = g_nummapplanes+1;
- }
- else
- { returnval = g_nummapplanes; }
- g_nummapplanes += 2;
- ThreadUnlock();
- return returnval;
- }
- #endif //HLCSG_FASTFIND
- int PlaneFromPoints(const vec_t* const p0, const vec_t* const p1, const vec_t* const p2)
- {
- vec3_t v1, v2;
- vec3_t normal;
- VectorSubtract(p0, p1, v1);
- VectorSubtract(p2, p1, v2);
- CrossProduct(v1, v2, normal);
- if (VectorNormalize(normal))
- {
- return FindIntPlane(normal, p0);
- }
- return -1;
- }
- #ifdef HLCSG_PRECISIONCLIP
- const char ClipTypeStrings[5][11] = {{"smallest"},{"normalized"},{"simple"},{"precise"},{"legacy"}};
- const char* GetClipTypeString(cliptype ct)
- {
- return ClipTypeStrings[ct];
- }
- // =====================================================================================
- // AddHullPlane (subroutine for replacement of ExpandBrush, KGP)
- // Called to add any and all clip hull planes by the new ExpandBrush.
- // =====================================================================================
- void AddHullPlane(brushhull_t* hull, const vec_t* const normal, const vec_t* const origin, const bool check_planenum)
- {
- int planenum = FindIntPlane(normal,origin);
- //check to see if this plane is already in the brush (optional to speed
- //up cases where we know the plane hasn't been added yet, like axial case)
- if(check_planenum)
- {
- if(g_mapplanes[planenum].type <= last_axial) //we know axial planes are added in last step
- { return; }
- bface_t* current_face;
- for(current_face = hull->faces; current_face; current_face = current_face->next)
- {
- if(current_face->planenum == planenum)
- { return; } //don't add a plane twice
- }
- }
- bface_t* new_face = (bface_t*)Alloc(sizeof(bface_t)); // TODO: This leaks
- new_face->planenum = planenum;
- new_face->plane = &g_mapplanes[new_face->planenum];
- new_face->next = hull->faces;
- new_face->contents = CONTENTS_EMPTY;
- hull->faces = new_face;
- new_face->texinfo = 0;
- }
- // =====================================================================================
- // ExpandBrush (replacement by KGP)
- // Since the six bounding box planes were always added anyway, they've been moved to
- // an explicit separate step eliminating the need to check for duplicate planes (which
- // should be using plane numbers instead of the full definition anyway).
- //
- // The core of the new function adds additional bevels to brushes containing faces that
- // have 3 nonzero normal components -- this is necessary to finish the beveling process,
- // but is turned off by default for backward compatability and because the number of
- // clipnodes and faces will go up with the extra beveling. The advantage of the extra
- // precision comes from the absense of "sticky" outside corners on ackward geometry.
- //
- // Another source of "sticky" walls has been the inconsistant offset along each axis
- // (variant with plane normal in the old code). The normal component of the offset has
- // been scrapped (it made a ~29% difference in the worst case of 45 degrees, or about 10
- // height units for a standard half-life player hull). The new offsets generate fewer
- // clipping nodes and won't cause players to stick when moving across 2 brushes that flip
- // sign along an axis (this wasn't noticible on floors because the engine took care of the
- // invisible 0-3 unit steps it generated, but was noticible with walls).
- //
- // To prevent players from floating 10 units above the floor, the "precise" hull generation
- // option still uses the plane normal when the Z component is high enough for the plane to
- // be considered a floor. The "simple" hull generation option always uses the full hull
- // distance, resulting in lower clipnode counts.
- //
- // Bevel planes might be added twice (once from each side of the edge), so a planenum
- // based check is used to see if each has been added before.
- // =====================================================================================
- void ExpandBrush(brush_t* brush, const int hullnum)
- {
- //for looping through the faces and constructing the hull
- bface_t* current_face;
- plane_t* current_plane;
- brushhull_t* hull;
- vec3_t origin, normal;
- //for non-axial bevel testing
- Winding* winding;
- bface_t* other_face;
- plane_t* other_plane;
- Winding* other_winding;
- vec3_t edge_start, edge_end, edge, bevel_edge;
- unsigned int counter, counter2, dir;
- bool start_found,end_found;
- bool axialbevel[last_axial+1][2] = { {false,false}, {false,false}, {false,false} };
- bool warned = false;
- hull = &brush->hulls[hullnum];
- for(current_face = brush->hulls[0].faces; current_face; current_face = current_face->next)
- {
- current_plane = current_face->plane;
- //don't bother adding axial planes,
- //they're defined by adding the bounding box anyway
- if(current_plane->type <= last_axial)
- {
- //flag case where bounding box shouldn't expand
- if((g_texinfo[current_face->texinfo].flags & TEX_BEVEL))
- {
- switch(current_plane->type)
- {
- case plane_x:
- axialbevel[plane_x][(current_plane->normal[0] > 0 ? 1 : 0)] = true;
- break;
- case plane_y:
- axialbevel[plane_y][(current_plane->normal[1] > 0 ? 1 : 0)] = true;
- break;
- case plane_z:
- axialbevel[plane_z][(current_plane->normal[2] > 0 ? 1 : 0)] = true;
- break;
- }
- }
- continue;
- }
- //add the offset non-axial plane to the expanded hull
- VectorCopy(current_plane->origin, origin);
- VectorCopy(current_plane->normal, normal);
- //old code multiplied offset by normal -- this led to post-csg "sticky" walls where a
- //slope met an axial plane from the next brush since the offset from the slope would be less
- //than the full offset for the axial plane -- the discontinuity also contributes to increased
- //clipnodes. If the normal is zero along an axis, shifting the origin in that direction won't
- //change the plane number, so I don't explicitly test that case. The old method is still used if
- //preciseclip is turned off to allow backward compatability -- some of the improperly beveled edges
- //grow using the new origins, and might cause additional problems.
- if((g_texinfo[current_face->texinfo].flags & TEX_BEVEL))
- {
- //don't adjust origin - we'll correct g_texinfo's flags in a later step
- }
- else if(g_cliptype == clip_legacy || (g_cliptype == clip_precise && (normal[2] > FLOOR_Z)) || g_cliptype == clip_normalized)
- {
- if(normal[0])
- { origin[0] += normal[0] * (normal[0] > 0 ? g_hull_size[hullnum][1][0] : -g_hull_size[hullnum][0][0]); }
- if(normal[1])
- { origin[1] += normal[1] * (normal[1] > 0 ? g_hull_size[hullnum][1][1] : -g_hull_size[hullnum][0][1]); }
- if(normal[2])
- { origin[2] += normal[2] * (normal[2] > 0 ? g_hull_size[hullnum][1][2] : -g_hull_size[hullnum][0][2]); }
- }
- else
- {
- origin[0] += g_hull_size[hullnum][(normal[0] > 0 ? 1 : 0)][0];
- origin[1] += g_hull_size[hullnum][(normal[1] > 0 ? 1 : 0)][1];
- origin[2] += g_hull_size[hullnum][(normal[2] > 0 ? 1 : 0)][2];
- }
- AddHullPlane(hull,normal,origin,false);
- } //end for loop over all faces
- //split bevel check into a second pass so we don't have to check for duplicate planes when adding offset planes
- //in step above -- otherwise a bevel plane might duplicate an offset plane, causing problems later on.
- //only executes if cliptype is simple, normalized or precise
- if(g_cliptype == clip_simple || g_cliptype == clip_precise || g_cliptype == clip_normalized)
- {
- for(current_face = brush->hulls[0].faces; current_face; current_face = current_face->next)
- {
- current_plane = current_face->plane;
- if(current_plane->type <= last_axial || !current_plane->normal[0] || !current_plane->normal[1] || !current_plane->normal[2])
- { continue; } //only add bevels to completely non-axial planes
- //test to see if the plane is completely non-axial (if it is, need to add bevels to any
- //existing "inflection edges" where there's a sign change with a neighboring plane's normal for
- //a given axis)
- //move along winding and find plane on other side of each edge. If normals change sign,
- //add a new plane by offsetting the points of the winding to bevel the edge in that direction.
- //It's possible to have inflection in multiple directions -- in this case, a new plane
- //must be added for each sign change in the edge.
- winding = current_face->w;
- for(counter = 0; counter < (winding->m_NumPoints); counter++) //for each edge
- {
- VectorCopy(winding->m_Points[counter],edge_start);
- VectorCopy(winding->m_Points[(counter+1)%winding->m_NumPoints],edge_end);
- //grab the edge (find relative length)
- VectorSubtract(edge_end,edge_start,edge);
- //brute force - need to check every other winding for common points -- if the points match, the
- //other face is the one we need to look at.
- for(other_face = brush->hulls[0].faces; other_face; other_face = other_face->next)
- {
- if(other_face == current_face)
- { continue; }
- start_found = false;
- end_found = false;
- other_winding = other_face->w;
- for(counter2 = 0; counter2 < other_winding->m_NumPoints; counter2++)
- {
- if(!start_found && VectorCompare(other_winding->m_Points[counter2],edge_start))
- { start_found = true; }
- if(!end_found && VectorCompare(other_winding->m_Points[counter2],edge_end))
- { end_found = true; }
- if(start_found && end_found)
- { break; } //we've found the face we want, move on to planar comparison
- } // for each point in other winding
- if(start_found && end_found)
- { break; } //we've found the face we want, move on to planar comparison
- } // for each face
- if(!other_face)
- {
- if(hullnum == 1 && !warned)
- {
- Warning("Illegal Brush (edge without opposite face): Entity %i, Brush %i\n",brush->entitynum, brush->brushnum);
- warned = true;
- }
- continue;
- }
- other_plane = other_face->plane;
- //check each direction for sign change in normal -- zero can be safely ignored
- for(dir = 0; dir < 3; dir++)
- {
- if(current_plane->normal[dir]*other_plane->normal[dir] < 0) //sign changed, add bevel
- {
- //pick direction of bevel edge by looking at normal of existing planes
- VectorClear(bevel_edge);
- bevel_edge[dir] = (current_plane->normal[dir] > 0) ? -1 : 1;
- //find normal by taking normalized cross of the edge vector and the bevel edge
- CrossProduct(edge,bevel_edge,normal);
- //normalize to length 1
- VectorNormalize(normal);
- //get the origin
- VectorCopy(edge_start,origin);
- //unrolled loop - legacy never hits this point, so don't test for it
- if((g_cliptype == clip_precise && (normal[2] > FLOOR_Z)) || g_cliptype == clip_normalized)
- {
- if(normal[0])
- { origin[0] += normal[0] * (normal[0] > 0 ? g_hull_size[hullnum][1][0] : -g_hull_size[hullnum][0][0]); }
- if(normal[1])
- { origin[1] += normal[1] * (normal[1] > 0 ? g_hull_size[hullnum][1][1] : -g_hull_size[hullnum][0][1]); }
- if(normal[2])
- { origin[2] += normal[2] * (normal[2] > 0 ? g_hull_size[hullnum][1][2] : -g_hull_size[hullnum][0][2]); }
- }
- else //simple or precise for non-floors
- {
- //note: if normal == 0 in direction indicated, shifting origin doesn't change plane #
- origin[0] += g_hull_size[hullnum][(normal[0] > 0 ? 1 : 0)][0];
- origin[1] += g_hull_size[hullnum][(normal[1] > 0 ? 1 : 0)][1];
- origin[2] += g_hull_size[hullnum][(normal[2] > 0 ? 1 : 0)][2];
- }
- //add the bevel plane to the expanded hull
- AddHullPlane(hull,normal,origin,true); //double check that this edge hasn't been added yet
- }
- } //end for loop (check for each direction)
- } //end for loop (over all edges in face)
- } //end for loop (over all faces in hull 0)
- } //end if completely non-axial
- //add the bounding box to the expanded hull -- for a
- //completely axial brush, this is the only necessary step
- //add mins
- VectorAdd(brush->hulls[0].bounds.m_Mins, g_hull_size[hullnum][0], origin);
- normal[0] = -1;
- normal[1] = 0;
- normal[2] = 0;
- AddHullPlane(hull,normal,(axialbevel[plane_x][0] ? brush->hulls[0].bounds.m_Mins : origin),false);
- normal[0] = 0;
- normal[1] = -1;
- AddHullPlane(hull,normal,(axialbevel[plane_y][0] ? brush->hulls[0].bounds.m_Mins : origin),false);
- normal[1] = 0;
- normal[2] = -1;
- AddHullPlane(hull,normal,(axialbevel[plane_z][0] ? brush->hulls[0].bounds.m_Mins : origin),false);
- normal[2] = 0;
- //add maxes
- VectorAdd(brush->hulls[0].bounds.m_Maxs, g_hull_size[hullnum][1], origin);
- normal[0] = 1;
- AddHullPlane(hull,normal,(axialbevel[plane_x][1] ? brush->hulls[0].bounds.m_Maxs : origin),false);
- normal[0] = 0;
- normal[1] = 1;
- AddHullPlane(hull,normal,(axialbevel[plane_y][1] ? brush->hulls[0].bounds.m_Maxs : origin),false);
- normal[1] = 0;
- normal[2] = 1;
- AddHullPlane(hull,normal,(axialbevel[plane_z][1] ? brush->hulls[0].bounds.m_Maxs : origin),false);
- /*
- bface_t* hull_face; //sanity check
- for(hull_face = hull->faces; hull_face; hull_face = hull_face->next)
- {
- for(current_face = brush->hulls[0].faces; current_face; current_face = current_face->next)
- {
- if(current_face->w->m_NumPoints < 3)
- { continue; }
- for(counter = 0; counter < current_face->w->m_NumPoints; counter++)
- {
- if(DotProduct(hull_face->plane->normal,hull_face->plane->origin) < DotProduct(hull_face->plane->normal,current_face->w->m_Points[counter]))
- {
- Warning("Illegal Brush (clip hull [%i] has backward face): Entity %i, Brush %i\n",hullnum,brush->entitynum, brush->brushnum);
- break;
- }
- }
- }
- }
- */
- }
- #else //!HLCSG_PRECISIONCLIP
- #define MAX_HULL_POINTS 32
- #define MAX_HULL_EDGES 64
- typedef struct
- {
- brush_t* b;
- int hullnum;
- int num_hull_points;
- vec3_t hull_points[MAX_HULL_POINTS];
- vec3_t hull_corners[MAX_HULL_POINTS * 8];
- int num_hull_edges;
- int hull_edges[MAX_HULL_EDGES][2];
- } expand_t;
- /*
- * =============
- * IPlaneEquiv
- *
- * =============
- */
- bool IPlaneEquiv(const plane_t* const p1, const plane_t* const p2)
- {
- vec_t t;
- int j;
- // see if origin is on plane
- t = 0;
- for (j = 0; j < 3; j++)
- {
- t += (p2->origin[j] - p1->origin[j]) * p2->normal[j];
- }
- if (fabs(t) > DIST_EPSILON)
- {
- return false;
- }
- // see if the normal is forward, backwards, or off
- for (j = 0; j < 3; j++)
- {
- if (fabs(p2->normal[j] - p1->normal[j]) > NORMAL_EPSILON)
- {
- break;
- }
- }
- if (j == 3)
- {
- return true;
- }
- for (j = 0; j < 3; j++)
- {
- if (fabs(p2->normal[j] - p1->normal[j]) > NORMAL_EPSILON)
- {
- break;
- }
- }
- if (j == 3)
- {
- return true;
- }
- return false;
- }
- /*
- * ============
- * AddBrushPlane
- * =============
- */
- void AddBrushPlane(const expand_t* const ex, const plane_t* const plane)
- {
- plane_t* pl;
- bface_t* f;
- bface_t* nf;
- brushhull_t* h;
- h = &ex->b->hulls[ex->hullnum];
- // see if the plane has allready been added
- for (f = h->faces; f; f = f->next)
- {
- pl = f->plane;
- if (IPlaneEquiv(plane, pl))
- {
- return;
- }
- }
- nf = (bface_t*)Alloc(sizeof(*nf)); // TODO: This leaks
- nf->planenum = FindIntPlane(plane->normal, plane->origin);
- nf->plane = &g_mapplanes[nf->planenum];
- nf->next = h->faces;
- nf->contents = CONTENTS_EMPTY;
- h->faces = nf;
- nf->texinfo = 0; // all clip hulls have same texture
- }
- // =====================================================================================
- // ExpandBrush
- // =====================================================================================
- void ExpandBrush(brush_t* b, const int hullnum)
- {
- int x;
- int s;
- int corner;
- bface_t* brush_faces;
- bface_t* f;
- bface_t* nf;
- plane_t* p;
- plane_t plane;
- vec3_t origin;
- vec3_t normal;
- expand_t ex;
- brushhull_t* h;
- bool axial;
- brush_faces = b->hulls[0].faces;
- h = &b->hulls[hullnum];
- ex.b = b;
- ex.hullnum = hullnum;
- ex.num_hull_points = 0;
- ex.num_hull_edges = 0;
- // expand all of the planes
- axial = true;
- // for each of this brushes faces
- for (f = brush_faces; f; f = f->next)
- {
- p = f->plane;
- if (p->type > last_axial) // ajm: last_axial == (planetypes enum)plane_z == (2)
- {
- axial = false; // not an xyz axial plane
- }
- VectorCopy(p->origin, origin);
- VectorCopy(p->normal, normal);
- for (x = 0; x < 3; x++)
- {
- if (p->normal[x] > 0)
- {
- corner = g_hull_size[hullnum][1][x];
- }
- else if (p->normal[x] < 0)
- {
- corner = -g_hull_size[hullnum][0][x];
- }
- else
- {
- corner = 0;
- }
- origin[x] += p->normal[x] * corner;
- }
- nf = (bface_t*)Alloc(sizeof(*nf)); // TODO: This leaks
- nf->planenum = FindIntPlane(normal, origin);
- nf->plane = &g_mapplanes[nf->planenum];
- nf->next = h->faces;
- nf->contents = CONTENTS_EMPTY;
- h->faces = nf;
- nf->texinfo = 0; // all clip hulls have same texture
- // nf->texinfo = f->texinfo; // Hack to view clipping hull with textures (might crash halflife)
- }
- // if this was an axial brush, we are done
- if (axial)
- {
- return;
- }
- // 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
- VectorCopy(vec3_origin, plane.normal);
- plane.normal[x] = s;
- if (s == -1)
- {
- VectorAdd(b->hulls[0].bounds.m_Mins, g_hull_size[hullnum][0], plane.origin);
- }
- else
- {
- VectorAdd(b->hulls[0].bounds.m_Maxs, g_hull_size[hullnum][1], plane.origin);
- }
- AddBrushPlane(&ex, &plane);
- }
- }
- }
- #endif //HLCSG_PRECISECLIP
- // =====================================================================================
- // MakeHullFaces
- // =====================================================================================
- void MakeHullFaces(const brush_t* const b, brushhull_t *h)
- {
- bface_t* f;
- bface_t* f2;
- #ifdef HLCSG_PRECISECLIP
- bool warned = false;
- #endif
- restart:
- h->bounds.reset();
- // for each face in this brushes hull
- for (f = h->faces; f; f = f->next)
- {
- Winding* w = new Winding(f->plane->normal, f->plane->dist);
- for (f2 = h->faces; f2; f2 = f2->next)
- {
- if (f == f2)
- {
- continue;
- }
- const plane_t* p = &g_mapplanes[f2->planenum ^ 1];
- if (!w->Chop(p->normal, p->dist)) // Nothing left to chop (getArea will return 0 for us in this case for below)
- {
- break;
- }
- }
- if (w->getArea() < 0.1)
- {
- #ifdef HLCSG_PRECISECLIP
- if(w->getArea() == 0 && !warned) //warn user when there's a bad brush (face not contributing)
- {
- Warning("Illegal Brush (plane doesn't contribute to final shape): Entity %i, Brush %i\n",b->entitynum, b->brushnum);
- warned = true;
- }
- #endif
- delete w;
- if (h->faces == f)
- {
- h->faces = f->next;
- }
- else
- {
- for (f2 = h->faces; f2->next != f; f2 = f2->next)
- {
- ;
- }
- f2->next = f->next;
- }
- goto restart;
- }
- else
- {
- f->w = w;
- f->contents = CONTENTS_EMPTY;
- unsigned int i;
- for (i = 0; i < w->m_NumPoints; i++)
- {
- h->bounds.add(w->m_Points[i]);
- }
- }
- }
- unsigned int i;
- for (i = 0; i < 3; i++)
- {
- if (h->bounds.m_Mins[i] < -BOGUS_RANGE / 2 || h->bounds.m_Maxs[i] > BOGUS_RANGE / 2)
- {
- Fatal(assume_BRUSH_OUTSIDE_WORLD, "Entity %i, Brush %i: outside world(+/-%d): (%.0f,%.0f,%.0f)-(%.0f,%.0f,%.0f)",
- b->entitynum, b->brushnum,
- BOGUS_RANGE / 2,
- h->bounds.m_Mins[0], h->bounds.m_Mins[1], h->bounds.m_Mins[2],
- h->bounds.m_Maxs[0], h->bounds.m_Maxs[1], h->bounds.m_Maxs[2]);
- }
- }
- }
- // =====================================================================================
- // MakeBrushPlanes
- // =====================================================================================
- bool MakeBrushPlanes(brush_t* b)
- {
- int i;
- int j;
- int planenum;
- side_t* s;
- bface_t* f;
- vec3_t origin;
- //
- // if the origin key is set (by an origin brush), offset all of the values
- //
- GetVectorForKey(&g_entities[b->entitynum], "origin", origin);
- //
- // convert to mapplanes
- //
- // for each side in this brush
- for (i = 0; i < b->numsides; i++)
- {
- s = &g_brushsides[b->firstside + i];
- for (j = 0; j < 3; j++)
- {
- VectorSubtract(s->planepts[j], origin, s->planepts[j]);
- }
- planenum = PlaneFromPoints(s->planepts[0], s->planepts[1], s->planepts[2]);
- if (planenum == -1)
- {
- Fatal(assume_PLANE_WITH_NO_NORMAL, "Entity %i, Brush %i, Side %i: plane with no normal", b->entitynum, b->brushnum, i);
- }
- //
- // see if the plane has been used already
- //
- for (f = b->hulls[0].faces; f; f = f->next)
- {
- if (f->planenum == planenum || f->planenum == (planenum ^ 1))
- {
- Fatal(assume_BRUSH_WITH_COPLANAR_FACES, "Entity %i, Brush %i, Side %i: has a coplanar plane at (%.0f, %.0f, %.0f), texture %s",
- b->entitynum, b->brushnum, i, s->planepts[0][0] + origin[0], s->planepts[0][1] + origin[1],
- s->planepts[0][2] + origin[2], s->td.name);
- }
- }
- f = (bface_t*)Alloc(sizeof(*f)); // TODO: This leaks
- f->planenum = planenum;
- f->plane = &g_mapplanes[planenum];
- f->next = b->hulls[0].faces;
- b->hulls[0].faces = f;
- f->texinfo = g_onlyents ? 0 : TexinfoForBrushTexture(f->plane, &s->td, origin);
- }
- return true;
- }
- // =====================================================================================
- // TextureContents
- // =====================================================================================
- static contents_t TextureContents(const char* const name)
- {
- if (!strncasecmp(name, "sky", 3))
- return CONTENTS_SKY;
- // =====================================================================================
- //Cpt_Andrew - Env_Sky Check
- // =====================================================================================
- if (!strncasecmp(name, "env_sky", 3))
- return CONTENTS_SKY;
- // =====================================================================================
- if (!strncasecmp(name + 1, "!lava", 5))
- return CONTENTS_LAVA;
- if (!strncasecmp(name + 1, "!slime", 6))
- return CONTENTS_SLIME;
- if (name[0] == '!') //optimized -- don't check for current unless it's liquid (KGP)
- {
- if (!strncasecmp(name, "!cur_90", 7))
- return CONTENTS_CURRENT_90;
- if (!strncasecmp(name, "!cur_0", 6))
- return CONTENTS_CURRENT_0;
- if (!strncasecmp(name, "!cur_270", 8))
- return CONTENTS_CURRENT_270;
- if (!strncasecmp(name, "!cur_180", 8))
- return CONTENTS_CURRENT_180;
- if (!strncasecmp(name, "!cur_up", 7))
- return CONTENTS_CURRENT_UP;
- if (!strncasecmp(name, "!cur_dwn", 8))
- return CONTENTS_CURRENT_DOWN;
- return CONTENTS_WATER; //default for liquids
- }
- if (!strncasecmp(name, "origin", 6))
- return CONTENTS_ORIGIN;
- if (!strncasecmp(name, "clip", 4))
- return CONTENTS_CLIP;
- if (!strncasecmp(name, "hint", 4))
- return CONTENTS_HINT;
- if (!strncasecmp(name, "skip", 4))
- return CONTENTS_HINT;
- if (!strncasecmp(name, "translucent", 11))
- return CONTENTS_TRANSLUCENT;
- if (name[0] == '@')
- return CONTENTS_TRANSLUCENT;
- #ifdef ZHLT_NULLTEX // AJM:
- if (!strncasecmp(name, "null", 4))
- return CONTENTS_NULL;
- #ifdef HLCSG_PRECISIONCLIP // KGP
- if(!strncasecmp(name,"bevel",5))
- return CONTENTS_NULL;
- #endif //precisionclip
- #endif //nulltex
- return CONTENTS_SOLID;
- }
- // =====================================================================================
- // ContentsToString
- // =====================================================================================
- const char* ContentsToString(const contents_t type)
- {
- switch (type)
- {
- case CONTENTS_EMPTY:
- return "EMPTY";
- case CONTENTS_SOLID:
- return "SOLID";
- case CONTENTS_WATER:
- return "WATER";
- case CONTENTS_SLIME:
- return "SLIME";
- case CONTENTS_LAVA:
- return "LAVA";
- case CONTENTS_SKY:
- return "SKY";
- case CONTENTS_ORIGIN:
- return "ORIGIN";
- case CONTENTS_CLIP:
- return "CLIP";
- case CONTENTS_CURRENT_0:
- return "CURRENT_0";
- case CONTENTS_CURRENT_90:
- return "CURRENT_90";
- case CONTENTS_CURRENT_180:
- return "CURRENT_180";
- case CONTENTS_CURRENT_270:
- return "CURRENT_270";
- case CONTENTS_CURRENT_UP:
- return "CURRENT_UP";
- case CONTENTS_CURRENT_DOWN:
- return "CURRENT_DOWN";
- case CONTENTS_TRANSLUCENT:
- return "TRANSLUCENT";
- case CONTENTS_HINT:
- return "HINT";
- #ifdef ZHLT_NULLTEX // AJM
- case CONTENTS_NULL:
- return "NULL";
- #endif
- #ifdef ZHLT_DETAIL // AJM
- case CONTENTS_DETAIL:
- return "DETAIL";
- #endif
- default:
- return "UNKNOWN";
- }
- }
- // =====================================================================================
- // CheckBrushContents
- // Perfoms abitrary checking on brush surfaces and states to try and catch errors
- // =====================================================================================
- contents_t CheckBrushContents(const brush_t* const b)
- {
- contents_t best_contents;
- contents_t contents;
- side_t* s;
- int i;
- s = &g_brushsides[b->firstside];
- // cycle though the sides of the brush and attempt to get our best side contents for
- // determining overall brush contents
- best_contents = TextureContents(s->td.name);
- s++;
- for (i = 1; i < b->numsides; i++, s++)
- {
- contents_t contents_consider = TextureContents(s->td.name);
- if (contents_consider > best_contents)
- {
- // if our current surface contents is better (larger) than our best, make it our best.
- best_contents = contents_consider;
- }
- }
- contents = best_contents;
- // attempt to pick up on mixed_face_contents errors
- s = &g_brushsides[b->firstside];
- s++;
- for (i = 1; i < b->numsides; i++, s++)
- {
- contents_t contents2 = TextureContents(s->td.name);
- // AJM: sky and null types are not to cause mixed face contents
- if (contents2 == CONTENTS_SKY)
- continue;
- #ifdef ZHLT_NULLTEX
- if (contents2 == CONTENTS_NULL)
- continue;
- #endif
- if (contents2 != best_contents)
- {
- Fatal(assume_MIXED_FACE_CONTENTS, "Entity %i, Brush %i: mixed face contents\n Texture %s and %s",
- b->entitynum, b->brushnum, g_brushsides[b->firstside].td.name, s->td.name);
- }
- }
- // check to make sure we dont have an origin brush as part of worldspawn
- if ((b->entitynum == 0) || (strcmp("func_group", ValueForKey(&g_entities[b->entitynum], "classname"))==0))
- {
- if (contents == CONTENTS_ORIGIN)
- {
- Fatal(assume_BRUSH_NOT_ALLOWED_IN_WORLD, "Entity %i, Brush %i: %s brushes not allowed in world\n(did you forget to tie this origin brush to a rotating entity?)", b->entitynum, b->brushnum, ContentsToString(contents));
- }
- }
- else
- {
- // otherwise its not worldspawn, therefore its an entity. check to make sure this brush is allowed
- // to be an entity.
- switch (contents)
- {
- case CONTENTS_SOLID:
- case CONTENTS_WATER:
- case CONTENTS_SLIME:
- case CONTENTS_LAVA:
- case CONTENTS_ORIGIN:
- case CONTENTS_CLIP:
- #ifdef ZHLT_NULLTEX // AJM
- case CONTENTS_NULL:
- break;
- #endif
- default:
- Fatal(assume_BRUSH_NOT_ALLOWED_IN_ENTITY, "Entity %i, Brush %i: %s brushes not allowed in entity", b->entitynum, b->brushnum, ContentsToString(contents));
- break;
- }
- }
- return contents;
- }
- // =====================================================================================
- // CreateBrush
- // makes a brush!
- // =====================================================================================
- void CreateBrush(const int brushnum)
- {
- brush_t* b;
- int contents;
- int h;
- b = &g_mapbrushes[brushnum];
- contents = b->contents;
- if (contents == CONTENTS_ORIGIN)
- return;
- // HULL 0
- MakeBrushPlanes(b);
- MakeHullFaces(b, &b->hulls[0]);
- // these brush types do not need to be represented in the clipping hull
- switch (contents)
- {
- case CONTENTS_LAVA:
- case CONTENTS_SLIME:
- case CONTENTS_WATER:
- case CONTENTS_TRANSLUCENT:
- case CONTENTS_HINT:
- return;
- }
- #ifdef HLCSG_CLIPECONOMY // AJM
- if (b->noclip)
- return;
- #endif
- // HULLS 1-3
- if (!g_noclip)
- {
- for (h = 1; h < NUM_HULLS; h++)
- {
- ExpandBrush(b, h);
- MakeHullFaces(b, &b->hulls[h]);
- }
- }
- // clip brushes don't stay in the drawing hull
- if (contents == CONTENTS_CLIP)
- {
- b->hulls[0].faces = NULL;
- b->contents = CONTENTS_SOLID;
- }
- }
|