brush.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. #include "csg.h"
  2. plane_t g_mapplanes[MAX_INTERNAL_MAP_PLANES];
  3. int g_nummapplanes;
  4. #define DIST_EPSILON 0.01
  5. #if !defined HLCSG_FASTFIND
  6. /*
  7. * =============
  8. * FindIntPlane
  9. *
  10. * Returns which plane number to use for a given integer defined plane.
  11. *
  12. * =============
  13. */
  14. int FindIntPlane(const vec_t* const normal, const vec_t* const origin)
  15. {
  16. int i, j;
  17. plane_t* p;
  18. plane_t temp;
  19. vec_t t;
  20. bool locked;
  21. p = g_mapplanes;
  22. locked = false;
  23. i = 0;
  24. while (1)
  25. {
  26. if (i == g_nummapplanes)
  27. {
  28. if (!locked)
  29. {
  30. locked = true;
  31. ThreadLock(); // make sure we don't race
  32. }
  33. if (i == g_nummapplanes)
  34. {
  35. break; // we didn't race
  36. }
  37. }
  38. t = 0; // Unrolled loop
  39. t += (origin[0] - p->origin[0]) * normal[0];
  40. t += (origin[1] - p->origin[1]) * normal[1];
  41. t += (origin[2] - p->origin[2]) * normal[2];
  42. if (fabs(t) < DIST_EPSILON)
  43. { // on plane
  44. // see if the normal is forward, backwards, or off
  45. for (j = 0; j < 3; j++)
  46. {
  47. if (fabs(normal[j] - p->normal[j]) > NORMAL_EPSILON)
  48. {
  49. break;
  50. }
  51. }
  52. if (j == 3)
  53. {
  54. if (locked)
  55. {
  56. ThreadUnlock();
  57. }
  58. return i;
  59. }
  60. }
  61. i++;
  62. p++;
  63. }
  64. hlassert(locked);
  65. // create a new plane
  66. p->origin[0] = origin[0];
  67. p->origin[1] = origin[1];
  68. p->origin[2] = origin[2];
  69. (p + 1)->origin[0] = origin[0];
  70. (p + 1)->origin[1] = origin[1];
  71. (p + 1)->origin[2] = origin[2];
  72. p->normal[0] = normal[0];
  73. p->normal[1] = normal[1];
  74. p->normal[2] = normal[2];
  75. (p + 1)->normal[0] = -normal[0];
  76. (p + 1)->normal[1] = -normal[1];
  77. (p + 1)->normal[2] = -normal[2];
  78. hlassume(g_nummapplanes < MAX_INTERNAL_MAP_PLANES, assume_MAX_INTERNAL_MAP_PLANES);
  79. VectorNormalize(p->normal);
  80. p->type = (p + 1)->type = PlaneTypeForNormal(p->normal);
  81. p->dist = DotProduct(origin, p->normal);
  82. VectorSubtract(vec3_origin, p->normal, (p + 1)->normal);
  83. (p + 1)->dist = -p->dist;
  84. // always put axial planes facing positive first
  85. if (p->type <= last_axial)
  86. {
  87. if (normal[0] < 0 || normal[1] < 0 || normal[2] < 0)
  88. {
  89. // flip order
  90. temp = *p;
  91. *p = *(p + 1);
  92. *(p + 1) = temp;
  93. g_nummapplanes += 2;
  94. ThreadUnlock();
  95. return i + 1;
  96. }
  97. }
  98. g_nummapplanes += 2;
  99. ThreadUnlock();
  100. return i;
  101. }
  102. #else //ifdef HLCSG_FASTFIND
  103. // =====================================================================================
  104. // FindIntPlane, fast version (replacement by KGP)
  105. // This process could be optimized by placing the planes in a (non hash-) set and using
  106. // half of the inner loop check below as the comparator; I'd expect the speed gain to be
  107. // very large given the change from O(N^2) to O(NlogN) to build the set of planes.
  108. // =====================================================================================
  109. int FindIntPlane(const vec_t* const normal, const vec_t* const origin)
  110. {
  111. int returnval;
  112. plane_t* p;
  113. plane_t temp;
  114. vec_t t;
  115. returnval = 0;
  116. find_plane:
  117. for( ; returnval < g_nummapplanes; returnval++)
  118. {
  119. if( -NORMAL_EPSILON < (t = normal[0] - g_mapplanes[returnval].normal[0]) && t < NORMAL_EPSILON &&
  120. -NORMAL_EPSILON < (t = normal[1] - g_mapplanes[returnval].normal[1]) && t < NORMAL_EPSILON &&
  121. -NORMAL_EPSILON < (t = normal[2] - g_mapplanes[returnval].normal[2]) && t < NORMAL_EPSILON )
  122. {
  123. //t = (origin - plane_origin) dot (normal), unrolled
  124. t = (origin[0] - g_mapplanes[returnval].origin[0]) * normal[0]
  125. + (origin[1] - g_mapplanes[returnval].origin[1]) * normal[1]
  126. + (origin[2] - g_mapplanes[returnval].origin[2]) * normal[2];
  127. if (-DIST_EPSILON < t && t < DIST_EPSILON) // on plane
  128. { return returnval; }
  129. }
  130. }
  131. ThreadLock();
  132. if(returnval != g_nummapplanes) // make sure we don't race
  133. {
  134. ThreadUnlock();
  135. goto find_plane; //check to see if other thread added plane we need
  136. }
  137. // create new planes - double check that we have room for 2 planes
  138. hlassume(g_nummapplanes+1 < MAX_INTERNAL_MAP_PLANES, assume_MAX_INTERNAL_MAP_PLANES);
  139. p = &g_mapplanes[g_nummapplanes];
  140. VectorCopy(origin,p->origin);
  141. VectorCopy(normal,p->normal);
  142. VectorNormalize(p->normal);
  143. p->type = PlaneTypeForNormal(p->normal);
  144. p->dist = DotProduct(origin, p->normal);
  145. VectorCopy(origin,(p+1)->origin);
  146. VectorSubtract(vec3_origin,p->normal,(p+1)->normal);
  147. (p+1)->type = p->type;
  148. (p+1)->dist = -p->dist;
  149. // always put axial planes facing positive first
  150. if (p->type <= last_axial && (normal[0] < 0 || normal[1] < 0 || normal[2] < 0)) // flip order
  151. {
  152. temp = *p;
  153. *p = *(p+1);
  154. *(p+1) = temp;
  155. returnval = g_nummapplanes+1;
  156. }
  157. else
  158. { returnval = g_nummapplanes; }
  159. g_nummapplanes += 2;
  160. ThreadUnlock();
  161. return returnval;
  162. }
  163. #endif //HLCSG_FASTFIND
  164. int PlaneFromPoints(const vec_t* const p0, const vec_t* const p1, const vec_t* const p2)
  165. {
  166. vec3_t v1, v2;
  167. vec3_t normal;
  168. VectorSubtract(p0, p1, v1);
  169. VectorSubtract(p2, p1, v2);
  170. CrossProduct(v1, v2, normal);
  171. if (VectorNormalize(normal))
  172. {
  173. return FindIntPlane(normal, p0);
  174. }
  175. return -1;
  176. }
  177. #ifdef HLCSG_PRECISIONCLIP
  178. const char ClipTypeStrings[5][11] = {{"smallest"},{"normalized"},{"simple"},{"precise"},{"legacy"}};
  179. const char* GetClipTypeString(cliptype ct)
  180. {
  181. return ClipTypeStrings[ct];
  182. }
  183. // =====================================================================================
  184. // AddHullPlane (subroutine for replacement of ExpandBrush, KGP)
  185. // Called to add any and all clip hull planes by the new ExpandBrush.
  186. // =====================================================================================
  187. void AddHullPlane(brushhull_t* hull, const vec_t* const normal, const vec_t* const origin, const bool check_planenum)
  188. {
  189. int planenum = FindIntPlane(normal,origin);
  190. //check to see if this plane is already in the brush (optional to speed
  191. //up cases where we know the plane hasn't been added yet, like axial case)
  192. if(check_planenum)
  193. {
  194. if(g_mapplanes[planenum].type <= last_axial) //we know axial planes are added in last step
  195. { return; }
  196. bface_t* current_face;
  197. for(current_face = hull->faces; current_face; current_face = current_face->next)
  198. {
  199. if(current_face->planenum == planenum)
  200. { return; } //don't add a plane twice
  201. }
  202. }
  203. bface_t* new_face = (bface_t*)Alloc(sizeof(bface_t)); // TODO: This leaks
  204. new_face->planenum = planenum;
  205. new_face->plane = &g_mapplanes[new_face->planenum];
  206. new_face->next = hull->faces;
  207. new_face->contents = CONTENTS_EMPTY;
  208. hull->faces = new_face;
  209. new_face->texinfo = 0;
  210. }
  211. // =====================================================================================
  212. // ExpandBrush (replacement by KGP)
  213. // Since the six bounding box planes were always added anyway, they've been moved to
  214. // an explicit separate step eliminating the need to check for duplicate planes (which
  215. // should be using plane numbers instead of the full definition anyway).
  216. //
  217. // The core of the new function adds additional bevels to brushes containing faces that
  218. // have 3 nonzero normal components -- this is necessary to finish the beveling process,
  219. // but is turned off by default for backward compatability and because the number of
  220. // clipnodes and faces will go up with the extra beveling. The advantage of the extra
  221. // precision comes from the absense of "sticky" outside corners on ackward geometry.
  222. //
  223. // Another source of "sticky" walls has been the inconsistant offset along each axis
  224. // (variant with plane normal in the old code). The normal component of the offset has
  225. // been scrapped (it made a ~29% difference in the worst case of 45 degrees, or about 10
  226. // height units for a standard half-life player hull). The new offsets generate fewer
  227. // clipping nodes and won't cause players to stick when moving across 2 brushes that flip
  228. // sign along an axis (this wasn't noticible on floors because the engine took care of the
  229. // invisible 0-3 unit steps it generated, but was noticible with walls).
  230. //
  231. // To prevent players from floating 10 units above the floor, the "precise" hull generation
  232. // option still uses the plane normal when the Z component is high enough for the plane to
  233. // be considered a floor. The "simple" hull generation option always uses the full hull
  234. // distance, resulting in lower clipnode counts.
  235. //
  236. // Bevel planes might be added twice (once from each side of the edge), so a planenum
  237. // based check is used to see if each has been added before.
  238. // =====================================================================================
  239. void ExpandBrush(brush_t* brush, const int hullnum)
  240. {
  241. //for looping through the faces and constructing the hull
  242. bface_t* current_face;
  243. plane_t* current_plane;
  244. brushhull_t* hull;
  245. vec3_t origin, normal;
  246. //for non-axial bevel testing
  247. Winding* winding;
  248. bface_t* other_face;
  249. plane_t* other_plane;
  250. Winding* other_winding;
  251. vec3_t edge_start, edge_end, edge, bevel_edge;
  252. unsigned int counter, counter2, dir;
  253. bool start_found,end_found;
  254. bool axialbevel[last_axial+1][2] = { {false,false}, {false,false}, {false,false} };
  255. bool warned = false;
  256. hull = &brush->hulls[hullnum];
  257. for(current_face = brush->hulls[0].faces; current_face; current_face = current_face->next)
  258. {
  259. current_plane = current_face->plane;
  260. //don't bother adding axial planes,
  261. //they're defined by adding the bounding box anyway
  262. if(current_plane->type <= last_axial)
  263. {
  264. //flag case where bounding box shouldn't expand
  265. if((g_texinfo[current_face->texinfo].flags & TEX_BEVEL))
  266. {
  267. switch(current_plane->type)
  268. {
  269. case plane_x:
  270. axialbevel[plane_x][(current_plane->normal[0] > 0 ? 1 : 0)] = true;
  271. break;
  272. case plane_y:
  273. axialbevel[plane_y][(current_plane->normal[1] > 0 ? 1 : 0)] = true;
  274. break;
  275. case plane_z:
  276. axialbevel[plane_z][(current_plane->normal[2] > 0 ? 1 : 0)] = true;
  277. break;
  278. }
  279. }
  280. continue;
  281. }
  282. //add the offset non-axial plane to the expanded hull
  283. VectorCopy(current_plane->origin, origin);
  284. VectorCopy(current_plane->normal, normal);
  285. //old code multiplied offset by normal -- this led to post-csg "sticky" walls where a
  286. //slope met an axial plane from the next brush since the offset from the slope would be less
  287. //than the full offset for the axial plane -- the discontinuity also contributes to increased
  288. //clipnodes. If the normal is zero along an axis, shifting the origin in that direction won't
  289. //change the plane number, so I don't explicitly test that case. The old method is still used if
  290. //preciseclip is turned off to allow backward compatability -- some of the improperly beveled edges
  291. //grow using the new origins, and might cause additional problems.
  292. if((g_texinfo[current_face->texinfo].flags & TEX_BEVEL))
  293. {
  294. //don't adjust origin - we'll correct g_texinfo's flags in a later step
  295. }
  296. else if(g_cliptype == clip_legacy || (g_cliptype == clip_precise && (normal[2] > FLOOR_Z)) || g_cliptype == clip_normalized)
  297. {
  298. if(normal[0])
  299. { origin[0] += normal[0] * (normal[0] > 0 ? g_hull_size[hullnum][1][0] : -g_hull_size[hullnum][0][0]); }
  300. if(normal[1])
  301. { origin[1] += normal[1] * (normal[1] > 0 ? g_hull_size[hullnum][1][1] : -g_hull_size[hullnum][0][1]); }
  302. if(normal[2])
  303. { origin[2] += normal[2] * (normal[2] > 0 ? g_hull_size[hullnum][1][2] : -g_hull_size[hullnum][0][2]); }
  304. }
  305. else
  306. {
  307. origin[0] += g_hull_size[hullnum][(normal[0] > 0 ? 1 : 0)][0];
  308. origin[1] += g_hull_size[hullnum][(normal[1] > 0 ? 1 : 0)][1];
  309. origin[2] += g_hull_size[hullnum][(normal[2] > 0 ? 1 : 0)][2];
  310. }
  311. AddHullPlane(hull,normal,origin,false);
  312. } //end for loop over all faces
  313. //split bevel check into a second pass so we don't have to check for duplicate planes when adding offset planes
  314. //in step above -- otherwise a bevel plane might duplicate an offset plane, causing problems later on.
  315. //only executes if cliptype is simple, normalized or precise
  316. if(g_cliptype == clip_simple || g_cliptype == clip_precise || g_cliptype == clip_normalized)
  317. {
  318. for(current_face = brush->hulls[0].faces; current_face; current_face = current_face->next)
  319. {
  320. current_plane = current_face->plane;
  321. if(current_plane->type <= last_axial || !current_plane->normal[0] || !current_plane->normal[1] || !current_plane->normal[2])
  322. { continue; } //only add bevels to completely non-axial planes
  323. //test to see if the plane is completely non-axial (if it is, need to add bevels to any
  324. //existing "inflection edges" where there's a sign change with a neighboring plane's normal for
  325. //a given axis)
  326. //move along winding and find plane on other side of each edge. If normals change sign,
  327. //add a new plane by offsetting the points of the winding to bevel the edge in that direction.
  328. //It's possible to have inflection in multiple directions -- in this case, a new plane
  329. //must be added for each sign change in the edge.
  330. winding = current_face->w;
  331. for(counter = 0; counter < (winding->m_NumPoints); counter++) //for each edge
  332. {
  333. VectorCopy(winding->m_Points[counter],edge_start);
  334. VectorCopy(winding->m_Points[(counter+1)%winding->m_NumPoints],edge_end);
  335. //grab the edge (find relative length)
  336. VectorSubtract(edge_end,edge_start,edge);
  337. //brute force - need to check every other winding for common points -- if the points match, the
  338. //other face is the one we need to look at.
  339. for(other_face = brush->hulls[0].faces; other_face; other_face = other_face->next)
  340. {
  341. if(other_face == current_face)
  342. { continue; }
  343. start_found = false;
  344. end_found = false;
  345. other_winding = other_face->w;
  346. for(counter2 = 0; counter2 < other_winding->m_NumPoints; counter2++)
  347. {
  348. if(!start_found && VectorCompare(other_winding->m_Points[counter2],edge_start))
  349. { start_found = true; }
  350. if(!end_found && VectorCompare(other_winding->m_Points[counter2],edge_end))
  351. { end_found = true; }
  352. if(start_found && end_found)
  353. { break; } //we've found the face we want, move on to planar comparison
  354. } // for each point in other winding
  355. if(start_found && end_found)
  356. { break; } //we've found the face we want, move on to planar comparison
  357. } // for each face
  358. if(!other_face)
  359. {
  360. if(hullnum == 1 && !warned)
  361. {
  362. Warning("Illegal Brush (edge without opposite face): Entity %i, Brush %i\n",brush->entitynum, brush->brushnum);
  363. warned = true;
  364. }
  365. continue;
  366. }
  367. other_plane = other_face->plane;
  368. //check each direction for sign change in normal -- zero can be safely ignored
  369. for(dir = 0; dir < 3; dir++)
  370. {
  371. if(current_plane->normal[dir]*other_plane->normal[dir] < 0) //sign changed, add bevel
  372. {
  373. //pick direction of bevel edge by looking at normal of existing planes
  374. VectorClear(bevel_edge);
  375. bevel_edge[dir] = (current_plane->normal[dir] > 0) ? -1 : 1;
  376. //find normal by taking normalized cross of the edge vector and the bevel edge
  377. CrossProduct(edge,bevel_edge,normal);
  378. //normalize to length 1
  379. VectorNormalize(normal);
  380. //get the origin
  381. VectorCopy(edge_start,origin);
  382. //unrolled loop - legacy never hits this point, so don't test for it
  383. if((g_cliptype == clip_precise && (normal[2] > FLOOR_Z)) || g_cliptype == clip_normalized)
  384. {
  385. if(normal[0])
  386. { origin[0] += normal[0] * (normal[0] > 0 ? g_hull_size[hullnum][1][0] : -g_hull_size[hullnum][0][0]); }
  387. if(normal[1])
  388. { origin[1] += normal[1] * (normal[1] > 0 ? g_hull_size[hullnum][1][1] : -g_hull_size[hullnum][0][1]); }
  389. if(normal[2])
  390. { origin[2] += normal[2] * (normal[2] > 0 ? g_hull_size[hullnum][1][2] : -g_hull_size[hullnum][0][2]); }
  391. }
  392. else //simple or precise for non-floors
  393. {
  394. //note: if normal == 0 in direction indicated, shifting origin doesn't change plane #
  395. origin[0] += g_hull_size[hullnum][(normal[0] > 0 ? 1 : 0)][0];
  396. origin[1] += g_hull_size[hullnum][(normal[1] > 0 ? 1 : 0)][1];
  397. origin[2] += g_hull_size[hullnum][(normal[2] > 0 ? 1 : 0)][2];
  398. }
  399. //add the bevel plane to the expanded hull
  400. AddHullPlane(hull,normal,origin,true); //double check that this edge hasn't been added yet
  401. }
  402. } //end for loop (check for each direction)
  403. } //end for loop (over all edges in face)
  404. } //end for loop (over all faces in hull 0)
  405. } //end if completely non-axial
  406. //add the bounding box to the expanded hull -- for a
  407. //completely axial brush, this is the only necessary step
  408. //add mins
  409. VectorAdd(brush->hulls[0].bounds.m_Mins, g_hull_size[hullnum][0], origin);
  410. normal[0] = -1;
  411. normal[1] = 0;
  412. normal[2] = 0;
  413. AddHullPlane(hull,normal,(axialbevel[plane_x][0] ? brush->hulls[0].bounds.m_Mins : origin),false);
  414. normal[0] = 0;
  415. normal[1] = -1;
  416. AddHullPlane(hull,normal,(axialbevel[plane_y][0] ? brush->hulls[0].bounds.m_Mins : origin),false);
  417. normal[1] = 0;
  418. normal[2] = -1;
  419. AddHullPlane(hull,normal,(axialbevel[plane_z][0] ? brush->hulls[0].bounds.m_Mins : origin),false);
  420. normal[2] = 0;
  421. //add maxes
  422. VectorAdd(brush->hulls[0].bounds.m_Maxs, g_hull_size[hullnum][1], origin);
  423. normal[0] = 1;
  424. AddHullPlane(hull,normal,(axialbevel[plane_x][1] ? brush->hulls[0].bounds.m_Maxs : origin),false);
  425. normal[0] = 0;
  426. normal[1] = 1;
  427. AddHullPlane(hull,normal,(axialbevel[plane_y][1] ? brush->hulls[0].bounds.m_Maxs : origin),false);
  428. normal[1] = 0;
  429. normal[2] = 1;
  430. AddHullPlane(hull,normal,(axialbevel[plane_z][1] ? brush->hulls[0].bounds.m_Maxs : origin),false);
  431. /*
  432. bface_t* hull_face; //sanity check
  433. for(hull_face = hull->faces; hull_face; hull_face = hull_face->next)
  434. {
  435. for(current_face = brush->hulls[0].faces; current_face; current_face = current_face->next)
  436. {
  437. if(current_face->w->m_NumPoints < 3)
  438. { continue; }
  439. for(counter = 0; counter < current_face->w->m_NumPoints; counter++)
  440. {
  441. if(DotProduct(hull_face->plane->normal,hull_face->plane->origin) < DotProduct(hull_face->plane->normal,current_face->w->m_Points[counter]))
  442. {
  443. Warning("Illegal Brush (clip hull [%i] has backward face): Entity %i, Brush %i\n",hullnum,brush->entitynum, brush->brushnum);
  444. break;
  445. }
  446. }
  447. }
  448. }
  449. */
  450. }
  451. #else //!HLCSG_PRECISIONCLIP
  452. #define MAX_HULL_POINTS 32
  453. #define MAX_HULL_EDGES 64
  454. typedef struct
  455. {
  456. brush_t* b;
  457. int hullnum;
  458. int num_hull_points;
  459. vec3_t hull_points[MAX_HULL_POINTS];
  460. vec3_t hull_corners[MAX_HULL_POINTS * 8];
  461. int num_hull_edges;
  462. int hull_edges[MAX_HULL_EDGES][2];
  463. } expand_t;
  464. /*
  465. * =============
  466. * IPlaneEquiv
  467. *
  468. * =============
  469. */
  470. bool IPlaneEquiv(const plane_t* const p1, const plane_t* const p2)
  471. {
  472. vec_t t;
  473. int j;
  474. // see if origin is on plane
  475. t = 0;
  476. for (j = 0; j < 3; j++)
  477. {
  478. t += (p2->origin[j] - p1->origin[j]) * p2->normal[j];
  479. }
  480. if (fabs(t) > DIST_EPSILON)
  481. {
  482. return false;
  483. }
  484. // see if the normal is forward, backwards, or off
  485. for (j = 0; j < 3; j++)
  486. {
  487. if (fabs(p2->normal[j] - p1->normal[j]) > NORMAL_EPSILON)
  488. {
  489. break;
  490. }
  491. }
  492. if (j == 3)
  493. {
  494. return true;
  495. }
  496. for (j = 0; j < 3; j++)
  497. {
  498. if (fabs(p2->normal[j] - p1->normal[j]) > NORMAL_EPSILON)
  499. {
  500. break;
  501. }
  502. }
  503. if (j == 3)
  504. {
  505. return true;
  506. }
  507. return false;
  508. }
  509. /*
  510. * ============
  511. * AddBrushPlane
  512. * =============
  513. */
  514. void AddBrushPlane(const expand_t* const ex, const plane_t* const plane)
  515. {
  516. plane_t* pl;
  517. bface_t* f;
  518. bface_t* nf;
  519. brushhull_t* h;
  520. h = &ex->b->hulls[ex->hullnum];
  521. // see if the plane has allready been added
  522. for (f = h->faces; f; f = f->next)
  523. {
  524. pl = f->plane;
  525. if (IPlaneEquiv(plane, pl))
  526. {
  527. return;
  528. }
  529. }
  530. nf = (bface_t*)Alloc(sizeof(*nf)); // TODO: This leaks
  531. nf->planenum = FindIntPlane(plane->normal, plane->origin);
  532. nf->plane = &g_mapplanes[nf->planenum];
  533. nf->next = h->faces;
  534. nf->contents = CONTENTS_EMPTY;
  535. h->faces = nf;
  536. nf->texinfo = 0; // all clip hulls have same texture
  537. }
  538. // =====================================================================================
  539. // ExpandBrush
  540. // =====================================================================================
  541. void ExpandBrush(brush_t* b, const int hullnum)
  542. {
  543. int x;
  544. int s;
  545. int corner;
  546. bface_t* brush_faces;
  547. bface_t* f;
  548. bface_t* nf;
  549. plane_t* p;
  550. plane_t plane;
  551. vec3_t origin;
  552. vec3_t normal;
  553. expand_t ex;
  554. brushhull_t* h;
  555. bool axial;
  556. brush_faces = b->hulls[0].faces;
  557. h = &b->hulls[hullnum];
  558. ex.b = b;
  559. ex.hullnum = hullnum;
  560. ex.num_hull_points = 0;
  561. ex.num_hull_edges = 0;
  562. // expand all of the planes
  563. axial = true;
  564. // for each of this brushes faces
  565. for (f = brush_faces; f; f = f->next)
  566. {
  567. p = f->plane;
  568. if (p->type > last_axial) // ajm: last_axial == (planetypes enum)plane_z == (2)
  569. {
  570. axial = false; // not an xyz axial plane
  571. }
  572. VectorCopy(p->origin, origin);
  573. VectorCopy(p->normal, normal);
  574. for (x = 0; x < 3; x++)
  575. {
  576. if (p->normal[x] > 0)
  577. {
  578. corner = g_hull_size[hullnum][1][x];
  579. }
  580. else if (p->normal[x] < 0)
  581. {
  582. corner = -g_hull_size[hullnum][0][x];
  583. }
  584. else
  585. {
  586. corner = 0;
  587. }
  588. origin[x] += p->normal[x] * corner;
  589. }
  590. nf = (bface_t*)Alloc(sizeof(*nf)); // TODO: This leaks
  591. nf->planenum = FindIntPlane(normal, origin);
  592. nf->plane = &g_mapplanes[nf->planenum];
  593. nf->next = h->faces;
  594. nf->contents = CONTENTS_EMPTY;
  595. h->faces = nf;
  596. nf->texinfo = 0; // all clip hulls have same texture
  597. // nf->texinfo = f->texinfo; // Hack to view clipping hull with textures (might crash halflife)
  598. }
  599. // if this was an axial brush, we are done
  600. if (axial)
  601. {
  602. return;
  603. }
  604. // add any axis planes not contained in the brush to bevel off corners
  605. for (x = 0; x < 3; x++)
  606. {
  607. for (s = -1; s <= 1; s += 2)
  608. {
  609. // add the plane
  610. VectorCopy(vec3_origin, plane.normal);
  611. plane.normal[x] = s;
  612. if (s == -1)
  613. {
  614. VectorAdd(b->hulls[0].bounds.m_Mins, g_hull_size[hullnum][0], plane.origin);
  615. }
  616. else
  617. {
  618. VectorAdd(b->hulls[0].bounds.m_Maxs, g_hull_size[hullnum][1], plane.origin);
  619. }
  620. AddBrushPlane(&ex, &plane);
  621. }
  622. }
  623. }
  624. #endif //HLCSG_PRECISECLIP
  625. // =====================================================================================
  626. // MakeHullFaces
  627. // =====================================================================================
  628. void MakeHullFaces(const brush_t* const b, brushhull_t *h)
  629. {
  630. bface_t* f;
  631. bface_t* f2;
  632. #ifdef HLCSG_PRECISECLIP
  633. bool warned = false;
  634. #endif
  635. restart:
  636. h->bounds.reset();
  637. // for each face in this brushes hull
  638. for (f = h->faces; f; f = f->next)
  639. {
  640. Winding* w = new Winding(f->plane->normal, f->plane->dist);
  641. for (f2 = h->faces; f2; f2 = f2->next)
  642. {
  643. if (f == f2)
  644. {
  645. continue;
  646. }
  647. const plane_t* p = &g_mapplanes[f2->planenum ^ 1];
  648. if (!w->Chop(p->normal, p->dist)) // Nothing left to chop (getArea will return 0 for us in this case for below)
  649. {
  650. break;
  651. }
  652. }
  653. if (w->getArea() < 0.1)
  654. {
  655. #ifdef HLCSG_PRECISECLIP
  656. if(w->getArea() == 0 && !warned) //warn user when there's a bad brush (face not contributing)
  657. {
  658. Warning("Illegal Brush (plane doesn't contribute to final shape): Entity %i, Brush %i\n",b->entitynum, b->brushnum);
  659. warned = true;
  660. }
  661. #endif
  662. delete w;
  663. if (h->faces == f)
  664. {
  665. h->faces = f->next;
  666. }
  667. else
  668. {
  669. for (f2 = h->faces; f2->next != f; f2 = f2->next)
  670. {
  671. ;
  672. }
  673. f2->next = f->next;
  674. }
  675. goto restart;
  676. }
  677. else
  678. {
  679. f->w = w;
  680. f->contents = CONTENTS_EMPTY;
  681. unsigned int i;
  682. for (i = 0; i < w->m_NumPoints; i++)
  683. {
  684. h->bounds.add(w->m_Points[i]);
  685. }
  686. }
  687. }
  688. unsigned int i;
  689. for (i = 0; i < 3; i++)
  690. {
  691. if (h->bounds.m_Mins[i] < -BOGUS_RANGE / 2 || h->bounds.m_Maxs[i] > BOGUS_RANGE / 2)
  692. {
  693. Fatal(assume_BRUSH_OUTSIDE_WORLD, "Entity %i, Brush %i: outside world(+/-%d): (%.0f,%.0f,%.0f)-(%.0f,%.0f,%.0f)",
  694. b->entitynum, b->brushnum,
  695. BOGUS_RANGE / 2,
  696. h->bounds.m_Mins[0], h->bounds.m_Mins[1], h->bounds.m_Mins[2],
  697. h->bounds.m_Maxs[0], h->bounds.m_Maxs[1], h->bounds.m_Maxs[2]);
  698. }
  699. }
  700. }
  701. // =====================================================================================
  702. // MakeBrushPlanes
  703. // =====================================================================================
  704. bool MakeBrushPlanes(brush_t* b)
  705. {
  706. int i;
  707. int j;
  708. int planenum;
  709. side_t* s;
  710. bface_t* f;
  711. vec3_t origin;
  712. //
  713. // if the origin key is set (by an origin brush), offset all of the values
  714. //
  715. GetVectorForKey(&g_entities[b->entitynum], "origin", origin);
  716. //
  717. // convert to mapplanes
  718. //
  719. // for each side in this brush
  720. for (i = 0; i < b->numsides; i++)
  721. {
  722. s = &g_brushsides[b->firstside + i];
  723. for (j = 0; j < 3; j++)
  724. {
  725. VectorSubtract(s->planepts[j], origin, s->planepts[j]);
  726. }
  727. planenum = PlaneFromPoints(s->planepts[0], s->planepts[1], s->planepts[2]);
  728. if (planenum == -1)
  729. {
  730. Fatal(assume_PLANE_WITH_NO_NORMAL, "Entity %i, Brush %i, Side %i: plane with no normal", b->entitynum, b->brushnum, i);
  731. }
  732. //
  733. // see if the plane has been used already
  734. //
  735. for (f = b->hulls[0].faces; f; f = f->next)
  736. {
  737. if (f->planenum == planenum || f->planenum == (planenum ^ 1))
  738. {
  739. Fatal(assume_BRUSH_WITH_COPLANAR_FACES, "Entity %i, Brush %i, Side %i: has a coplanar plane at (%.0f, %.0f, %.0f), texture %s",
  740. b->entitynum, b->brushnum, i, s->planepts[0][0] + origin[0], s->planepts[0][1] + origin[1],
  741. s->planepts[0][2] + origin[2], s->td.name);
  742. }
  743. }
  744. f = (bface_t*)Alloc(sizeof(*f)); // TODO: This leaks
  745. f->planenum = planenum;
  746. f->plane = &g_mapplanes[planenum];
  747. f->next = b->hulls[0].faces;
  748. b->hulls[0].faces = f;
  749. f->texinfo = g_onlyents ? 0 : TexinfoForBrushTexture(f->plane, &s->td, origin);
  750. }
  751. return true;
  752. }
  753. // =====================================================================================
  754. // TextureContents
  755. // =====================================================================================
  756. static contents_t TextureContents(const char* const name)
  757. {
  758. if (!strncasecmp(name, "sky", 3))
  759. return CONTENTS_SKY;
  760. // =====================================================================================
  761. //Cpt_Andrew - Env_Sky Check
  762. // =====================================================================================
  763. if (!strncasecmp(name, "env_sky", 3))
  764. return CONTENTS_SKY;
  765. // =====================================================================================
  766. if (!strncasecmp(name + 1, "!lava", 5))
  767. return CONTENTS_LAVA;
  768. if (!strncasecmp(name + 1, "!slime", 6))
  769. return CONTENTS_SLIME;
  770. if (name[0] == '!') //optimized -- don't check for current unless it's liquid (KGP)
  771. {
  772. if (!strncasecmp(name, "!cur_90", 7))
  773. return CONTENTS_CURRENT_90;
  774. if (!strncasecmp(name, "!cur_0", 6))
  775. return CONTENTS_CURRENT_0;
  776. if (!strncasecmp(name, "!cur_270", 8))
  777. return CONTENTS_CURRENT_270;
  778. if (!strncasecmp(name, "!cur_180", 8))
  779. return CONTENTS_CURRENT_180;
  780. if (!strncasecmp(name, "!cur_up", 7))
  781. return CONTENTS_CURRENT_UP;
  782. if (!strncasecmp(name, "!cur_dwn", 8))
  783. return CONTENTS_CURRENT_DOWN;
  784. return CONTENTS_WATER; //default for liquids
  785. }
  786. if (!strncasecmp(name, "origin", 6))
  787. return CONTENTS_ORIGIN;
  788. if (!strncasecmp(name, "clip", 4))
  789. return CONTENTS_CLIP;
  790. if (!strncasecmp(name, "hint", 4))
  791. return CONTENTS_HINT;
  792. if (!strncasecmp(name, "skip", 4))
  793. return CONTENTS_HINT;
  794. if (!strncasecmp(name, "translucent", 11))
  795. return CONTENTS_TRANSLUCENT;
  796. if (name[0] == '@')
  797. return CONTENTS_TRANSLUCENT;
  798. #ifdef ZHLT_NULLTEX // AJM:
  799. if (!strncasecmp(name, "null", 4))
  800. return CONTENTS_NULL;
  801. #ifdef HLCSG_PRECISIONCLIP // KGP
  802. if(!strncasecmp(name,"bevel",5))
  803. return CONTENTS_NULL;
  804. #endif //precisionclip
  805. #endif //nulltex
  806. return CONTENTS_SOLID;
  807. }
  808. // =====================================================================================
  809. // ContentsToString
  810. // =====================================================================================
  811. const char* ContentsToString(const contents_t type)
  812. {
  813. switch (type)
  814. {
  815. case CONTENTS_EMPTY:
  816. return "EMPTY";
  817. case CONTENTS_SOLID:
  818. return "SOLID";
  819. case CONTENTS_WATER:
  820. return "WATER";
  821. case CONTENTS_SLIME:
  822. return "SLIME";
  823. case CONTENTS_LAVA:
  824. return "LAVA";
  825. case CONTENTS_SKY:
  826. return "SKY";
  827. case CONTENTS_ORIGIN:
  828. return "ORIGIN";
  829. case CONTENTS_CLIP:
  830. return "CLIP";
  831. case CONTENTS_CURRENT_0:
  832. return "CURRENT_0";
  833. case CONTENTS_CURRENT_90:
  834. return "CURRENT_90";
  835. case CONTENTS_CURRENT_180:
  836. return "CURRENT_180";
  837. case CONTENTS_CURRENT_270:
  838. return "CURRENT_270";
  839. case CONTENTS_CURRENT_UP:
  840. return "CURRENT_UP";
  841. case CONTENTS_CURRENT_DOWN:
  842. return "CURRENT_DOWN";
  843. case CONTENTS_TRANSLUCENT:
  844. return "TRANSLUCENT";
  845. case CONTENTS_HINT:
  846. return "HINT";
  847. #ifdef ZHLT_NULLTEX // AJM
  848. case CONTENTS_NULL:
  849. return "NULL";
  850. #endif
  851. #ifdef ZHLT_DETAIL // AJM
  852. case CONTENTS_DETAIL:
  853. return "DETAIL";
  854. #endif
  855. default:
  856. return "UNKNOWN";
  857. }
  858. }
  859. // =====================================================================================
  860. // CheckBrushContents
  861. // Perfoms abitrary checking on brush surfaces and states to try and catch errors
  862. // =====================================================================================
  863. contents_t CheckBrushContents(const brush_t* const b)
  864. {
  865. contents_t best_contents;
  866. contents_t contents;
  867. side_t* s;
  868. int i;
  869. s = &g_brushsides[b->firstside];
  870. // cycle though the sides of the brush and attempt to get our best side contents for
  871. // determining overall brush contents
  872. best_contents = TextureContents(s->td.name);
  873. s++;
  874. for (i = 1; i < b->numsides; i++, s++)
  875. {
  876. contents_t contents_consider = TextureContents(s->td.name);
  877. if (contents_consider > best_contents)
  878. {
  879. // if our current surface contents is better (larger) than our best, make it our best.
  880. best_contents = contents_consider;
  881. }
  882. }
  883. contents = best_contents;
  884. // attempt to pick up on mixed_face_contents errors
  885. s = &g_brushsides[b->firstside];
  886. s++;
  887. for (i = 1; i < b->numsides; i++, s++)
  888. {
  889. contents_t contents2 = TextureContents(s->td.name);
  890. // AJM: sky and null types are not to cause mixed face contents
  891. if (contents2 == CONTENTS_SKY)
  892. continue;
  893. #ifdef ZHLT_NULLTEX
  894. if (contents2 == CONTENTS_NULL)
  895. continue;
  896. #endif
  897. if (contents2 != best_contents)
  898. {
  899. Fatal(assume_MIXED_FACE_CONTENTS, "Entity %i, Brush %i: mixed face contents\n Texture %s and %s",
  900. b->entitynum, b->brushnum, g_brushsides[b->firstside].td.name, s->td.name);
  901. }
  902. }
  903. // check to make sure we dont have an origin brush as part of worldspawn
  904. if ((b->entitynum == 0) || (strcmp("func_group", ValueForKey(&g_entities[b->entitynum], "classname"))==0))
  905. {
  906. if (contents == CONTENTS_ORIGIN)
  907. {
  908. 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));
  909. }
  910. }
  911. else
  912. {
  913. // otherwise its not worldspawn, therefore its an entity. check to make sure this brush is allowed
  914. // to be an entity.
  915. switch (contents)
  916. {
  917. case CONTENTS_SOLID:
  918. case CONTENTS_WATER:
  919. case CONTENTS_SLIME:
  920. case CONTENTS_LAVA:
  921. case CONTENTS_ORIGIN:
  922. case CONTENTS_CLIP:
  923. #ifdef ZHLT_NULLTEX // AJM
  924. case CONTENTS_NULL:
  925. break;
  926. #endif
  927. default:
  928. Fatal(assume_BRUSH_NOT_ALLOWED_IN_ENTITY, "Entity %i, Brush %i: %s brushes not allowed in entity", b->entitynum, b->brushnum, ContentsToString(contents));
  929. break;
  930. }
  931. }
  932. return contents;
  933. }
  934. // =====================================================================================
  935. // CreateBrush
  936. // makes a brush!
  937. // =====================================================================================
  938. void CreateBrush(const int brushnum)
  939. {
  940. brush_t* b;
  941. int contents;
  942. int h;
  943. b = &g_mapbrushes[brushnum];
  944. contents = b->contents;
  945. if (contents == CONTENTS_ORIGIN)
  946. return;
  947. // HULL 0
  948. MakeBrushPlanes(b);
  949. MakeHullFaces(b, &b->hulls[0]);
  950. // these brush types do not need to be represented in the clipping hull
  951. switch (contents)
  952. {
  953. case CONTENTS_LAVA:
  954. case CONTENTS_SLIME:
  955. case CONTENTS_WATER:
  956. case CONTENTS_TRANSLUCENT:
  957. case CONTENTS_HINT:
  958. return;
  959. }
  960. #ifdef HLCSG_CLIPECONOMY // AJM
  961. if (b->noclip)
  962. return;
  963. #endif
  964. // HULLS 1-3
  965. if (!g_noclip)
  966. {
  967. for (h = 1; h < NUM_HULLS; h++)
  968. {
  969. ExpandBrush(b, h);
  970. MakeHullFaces(b, &b->hulls[h]);
  971. }
  972. }
  973. // clip brushes don't stay in the drawing hull
  974. if (contents == CONTENTS_CLIP)
  975. {
  976. b->hulls[0].faces = NULL;
  977. b->contents = CONTENTS_SOLID;
  978. }
  979. }