brush.cpp 35 KB

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