outside.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. #ifdef SYSTEM_WIN32
  2. #pragma warning(disable:4267 4996) // 'size_t' to 'unsigned int', possible loss of data
  3. #endif
  4. #include "bsp5.h"
  5. // PointInLeaf
  6. // PlaceOccupant
  7. // MarkLeakTrail
  8. // RecursiveFillOutside
  9. // ClearOutFaces_r
  10. // isClassnameAllowableOutside
  11. // FreeAllowableOutsideList
  12. // LoadAllowableOutsideList
  13. // FillOutside
  14. static int outleafs;
  15. static int valid;
  16. static int c_falsenodes;
  17. static int c_free_faces;
  18. static int c_keep_faces;
  19. // =====================================================================================
  20. // PointInLeaf
  21. // =====================================================================================
  22. static node_t* PointInLeaf(node_t* node, const vec3_t point)
  23. {
  24. vec_t d;
  25. if (node->contents)
  26. {
  27. //Log("PointInLeaf::node->contents == %i\n", node->contents);
  28. return node;
  29. }
  30. d = DotProduct(g_dplanes[node->planenum].normal, point) - g_dplanes[node->planenum].dist;
  31. if (d > 0)
  32. return PointInLeaf(node->children[0], point);
  33. return PointInLeaf(node->children[1], point);
  34. }
  35. // =====================================================================================
  36. // PlaceOccupant
  37. // =====================================================================================
  38. static bool PlaceOccupant(const int num, const vec3_t point, node_t* headnode)
  39. {
  40. node_t* n;
  41. n = PointInLeaf(headnode, point);
  42. if (n->contents == CONTENTS_SOLID)
  43. {
  44. return false;
  45. }
  46. //Log("PlaceOccupant::n->contents == %i\n", n->contents);
  47. n->occupied = num;
  48. return true;
  49. }
  50. // =====================================================================================
  51. // MarkLeakTrail
  52. // =====================================================================================
  53. static portal_t* prevleaknode;
  54. static FILE* pointfile;
  55. static FILE* linefile;
  56. static void MarkLeakTrail(portal_t* n2)
  57. {
  58. int i;
  59. vec3_t p1, p2, dir;
  60. float len;
  61. portal_t* n1;
  62. n1 = prevleaknode;
  63. prevleaknode = n2;
  64. if (!n1)
  65. {
  66. return;
  67. }
  68. n2->winding->getCenter(p1);
  69. n1->winding->getCenter(p2);
  70. // Linefile
  71. fprintf(linefile, "%f %f %f - %f %f %f\n", p1[0], p1[1], p1[2], p2[0], p2[1], p2[2]);
  72. // Pointfile
  73. fprintf(pointfile, "%f %f %f\n", p1[0], p1[1], p1[2]);
  74. VectorSubtract(p2, p1, dir);
  75. len = VectorLength(dir);
  76. VectorNormalize(dir);
  77. while (len > 2)
  78. {
  79. fprintf(pointfile, "%f %f %f\n", p1[0], p1[1], p1[2]);
  80. for (i = 0; i < 3; i++)
  81. p1[i] += dir[i] * 2;
  82. len -= 2;
  83. }
  84. }
  85. // =====================================================================================
  86. // RecursiveFillOutside
  87. // Returns true if an occupied leaf is reached
  88. // If fill is false, just check, don't fill
  89. // =====================================================================================
  90. static int hit_occupied;
  91. static int backdraw;
  92. static bool RecursiveFillOutside(node_t* l, const bool fill)
  93. {
  94. portal_t* p;
  95. int s;
  96. if ((l->contents == CONTENTS_SOLID) || (l->contents == CONTENTS_SKY)
  97. #ifdef ZHLT_DETAIL
  98. || (l->contents == CONTENTS_DETAIL)
  99. #endif
  100. )
  101. {
  102. /*if (l->contents != CONTENTS_SOLID)
  103. Log("RecursiveFillOutside::l->contents == %i \n", l->contents);*/
  104. return false;
  105. }
  106. if (l->valid == valid)
  107. {
  108. return false;
  109. }
  110. if (l->occupied)
  111. {
  112. hit_occupied = l->occupied;
  113. backdraw = 1000;
  114. return true;
  115. }
  116. l->valid = valid;
  117. // fill it and it's neighbors
  118. if (fill)
  119. {
  120. l->contents = CONTENTS_SOLID;
  121. l->planenum = -1;
  122. }
  123. outleafs++;
  124. for (p = l->portals; p;)
  125. {
  126. s = (p->nodes[0] == l);
  127. if (RecursiveFillOutside(p->nodes[s], fill))
  128. { // leaked, so stop filling
  129. if (backdraw-- > 0)
  130. {
  131. MarkLeakTrail(p);
  132. }
  133. return true;
  134. }
  135. p = p->next[!s];
  136. }
  137. return false;
  138. }
  139. // =====================================================================================
  140. // ClearOutFaces_r
  141. // Removes unused nodes
  142. // =====================================================================================
  143. static node_t* ClearOutFaces_r(node_t* node)
  144. {
  145. face_t* f;
  146. face_t* fnext;
  147. face_t** fp;
  148. portal_t* p;
  149. // mark the node and all it's faces, so they
  150. // can be removed if no children use them
  151. node->valid = 0; // will be set if any children touch it
  152. for (f = node->faces; f; f = f->next)
  153. {
  154. f->outputnumber = -1;
  155. }
  156. // go down the children
  157. if (node->planenum != -1)
  158. {
  159. //
  160. // decision node
  161. //
  162. node->children[0] = ClearOutFaces_r(node->children[0]);
  163. node->children[1] = ClearOutFaces_r(node->children[1]);
  164. // free any faces not in open child leafs
  165. f = node->faces;
  166. node->faces = NULL;
  167. for (; f; f = fnext)
  168. {
  169. fnext = f->next;
  170. if (f->outputnumber == -1)
  171. { // never referenced, so free it
  172. c_free_faces++;
  173. FreeFace(f);
  174. }
  175. else
  176. {
  177. c_keep_faces++;
  178. f->next = node->faces;
  179. node->faces = f;
  180. }
  181. }
  182. if (!node->valid)
  183. {
  184. // this node does not touch any interior leafs
  185. // if both children are solid, just make this node solid
  186. if (node->children[0]->contents == CONTENTS_SOLID && node->children[1]->contents == CONTENTS_SOLID)
  187. {
  188. node->contents = CONTENTS_SOLID;
  189. node->planenum = -1;
  190. return node;
  191. }
  192. // if one child is solid, shortcut down the other side
  193. if (node->children[0]->contents == CONTENTS_SOLID)
  194. {
  195. return node->children[1];
  196. }
  197. if (node->children[1]->contents == CONTENTS_SOLID)
  198. {
  199. return node->children[0];
  200. }
  201. c_falsenodes++;
  202. }
  203. return node;
  204. }
  205. //
  206. // leaf node
  207. //
  208. if (node->contents != CONTENTS_SOLID)
  209. {
  210. // this node is still inside
  211. // mark all the nodes used as portals
  212. for (p = node->portals; p;)
  213. {
  214. if (p->onnode)
  215. {
  216. p->onnode->valid = 1;
  217. }
  218. if (p->nodes[0] == node) // only write out from first leaf
  219. {
  220. p = p->next[0];
  221. }
  222. else
  223. {
  224. p = p->next[1];
  225. }
  226. }
  227. // mark all of the faces to be drawn
  228. for (fp = node->markfaces; *fp; fp++)
  229. {
  230. (*fp)->outputnumber = 0;
  231. }
  232. return node;
  233. }
  234. // this was a filled in node, so free the markfaces
  235. if (node->planenum != -1)
  236. {
  237. free(node->markfaces);
  238. }
  239. return node;
  240. }
  241. // =====================================================================================
  242. // isClassnameAllowableOutside
  243. // =====================================================================================
  244. #define MAX_ALLOWABLE_OUTSIDE_GROWTH_SIZE 64
  245. unsigned g_nAllowableOutside = 0;
  246. unsigned g_maxAllowableOutside = 0;
  247. char** g_strAllowableOutsideList;
  248. bool isClassnameAllowableOutside(const char* const classname)
  249. {
  250. if (g_strAllowableOutsideList)
  251. {
  252. unsigned x;
  253. char** list = g_strAllowableOutsideList;
  254. for (x = 0; x < g_nAllowableOutside; x++, list++)
  255. {
  256. if (list)
  257. {
  258. if (!strcasecmp(classname, *list))
  259. {
  260. return true;
  261. }
  262. }
  263. }
  264. }
  265. return false;
  266. }
  267. // =====================================================================================
  268. // FreeAllowableOutsideList
  269. // =====================================================================================
  270. void FreeAllowableOutsideList()
  271. {
  272. if (g_strAllowableOutsideList)
  273. {
  274. free(g_strAllowableOutsideList);
  275. g_strAllowableOutsideList = NULL;
  276. }
  277. }
  278. // =====================================================================================
  279. // LoadAllowableOutsideList
  280. // =====================================================================================
  281. void LoadAllowableOutsideList(const char* const filename)
  282. {
  283. char* fname;
  284. int i, x, y;
  285. char* pData;
  286. char* pszData;
  287. if (!filename)
  288. {
  289. return;
  290. }
  291. else
  292. {
  293. unsigned len = strlen(filename) + 5;
  294. fname = (char*)Alloc(len);
  295. safe_snprintf(fname, len, "%s", filename);
  296. }
  297. if (q_exists(fname))
  298. {
  299. if ((i = LoadFile(fname, &pData)))
  300. {
  301. Log("Reading allowable void entities from file '%s'\n", fname);
  302. g_nAllowableOutside = 0;
  303. for (pszData = pData, y = 0, x = 0; x < i; x++)
  304. {
  305. if ((pData[x] == '\n') || (pData[x] == '\r'))
  306. {
  307. pData[x] = 0;
  308. if (strlen(pszData))
  309. {
  310. if (g_nAllowableOutside == g_maxAllowableOutside)
  311. {
  312. g_maxAllowableOutside += MAX_ALLOWABLE_OUTSIDE_GROWTH_SIZE;
  313. g_strAllowableOutsideList =
  314. (char**)realloc(g_strAllowableOutsideList, sizeof(char*) * g_maxAllowableOutside);
  315. }
  316. g_strAllowableOutsideList[y++] = pszData;
  317. g_nAllowableOutside++;
  318. Verbose("Adding entity '%s' to the allowable void list\n", pszData);
  319. }
  320. pszData = pData + x + 1;
  321. }
  322. }
  323. }
  324. }
  325. }
  326. // =====================================================================================
  327. // FillOutside
  328. // =====================================================================================
  329. node_t* FillOutside(node_t* node, const bool leakfile, const unsigned hullnum)
  330. {
  331. int s;
  332. int i;
  333. bool inside;
  334. bool ret;
  335. vec3_t origin;
  336. const char* cl;
  337. Verbose("----- FillOutside ----\n");
  338. if (g_nofill)
  339. {
  340. Log("skipped\n");
  341. return node;
  342. }
  343. //
  344. // place markers for all entities so
  345. // we know if we leak inside
  346. //
  347. inside = false;
  348. for (i = 1; i < g_numentities; i++)
  349. {
  350. GetVectorForKey(&g_entities[i], "origin", origin);
  351. cl = ValueForKey(&g_entities[i], "classname");
  352. if (!isClassnameAllowableOutside(cl))
  353. {
  354. if (!VectorCompare(origin, vec3_origin))
  355. {
  356. origin[2] += 1; // so objects on floor are ok
  357. // nudge playerstart around if needed so clipping hulls allways
  358. // have a vlaid point
  359. if (!strcmp(cl, "info_player_start"))
  360. {
  361. int x, y;
  362. for (x = -16; x <= 16; x += 16)
  363. {
  364. for (y = -16; y <= 16; y += 16)
  365. {
  366. origin[0] += x;
  367. origin[1] += y;
  368. if (PlaceOccupant(i, origin, node))
  369. {
  370. inside = true;
  371. goto gotit;
  372. }
  373. origin[0] -= x;
  374. origin[1] -= y;
  375. }
  376. }
  377. gotit:;
  378. }
  379. else
  380. {
  381. if (PlaceOccupant(i, origin, node))
  382. inside = true;
  383. }
  384. }
  385. }
  386. }
  387. if (!inside)
  388. {
  389. Warning("No entities exist in hull %i, no filling performed for this hull", hullnum);
  390. return node;
  391. }
  392. if(!g_outside_node.portals)
  393. {
  394. Warning("No outside node portal found in hull %i, no filling performed for this hull",hullnum);
  395. return node;
  396. }
  397. s = !(g_outside_node.portals->nodes[1] == &g_outside_node);
  398. // first check to see if an occupied leaf is hit
  399. outleafs = 0;
  400. valid++;
  401. prevleaknode = NULL;
  402. if (leakfile)
  403. {
  404. pointfile = fopen(g_pointfilename, "w");
  405. if (!pointfile)
  406. {
  407. Error("Couldn't open pointfile %s\n", g_pointfilename);
  408. }
  409. linefile = fopen(g_linefilename, "w");
  410. if (!linefile)
  411. {
  412. Error("Couldn't open linefile %s\n", g_linefilename);
  413. }
  414. }
  415. ret = RecursiveFillOutside(g_outside_node.portals->nodes[s], false);
  416. if (leakfile)
  417. {
  418. fclose(pointfile);
  419. fclose(linefile);
  420. }
  421. if (ret)
  422. {
  423. GetVectorForKey(&g_entities[hit_occupied], "origin", origin);
  424. {
  425. Warning("=== LEAK in hull %i ===\nEntity %s @ (%4.0f,%4.0f,%4.0f)",
  426. hullnum, ValueForKey(&g_entities[hit_occupied], "classname"), origin[0], origin[1], origin[2]);
  427. PrintOnce(
  428. "\n A LEAK is a hole in the map, where the inside of it is exposed to the\n"
  429. "(unwanted) outside region. The entity listed in the error is just a helpful\n"
  430. "indication of where the beginning of the leak pointfile starts, so the\n"
  431. "beginning of the line can be quickly found and traced to until reaching the\n"
  432. "outside. Unless this entity is accidentally on the outside of the map, it\n"
  433. "probably should not be deleted. Some complex rotating objects entities need\n"
  434. "their origins outside the map. To deal with these, just enclose the origin\n"
  435. "brush with a solid world brush\n");
  436. }
  437. if (!g_bLeaked)
  438. {
  439. // First leak spits this out
  440. Log("Leak pointfile generated\n\n");
  441. }
  442. if (g_bLeakOnly)
  443. {
  444. Error("Stopped by leak.");
  445. }
  446. g_bLeaked = true;
  447. return node;
  448. }
  449. // now go back and fill things in
  450. valid++;
  451. RecursiveFillOutside(g_outside_node.portals->nodes[s], true);
  452. // remove faces and nodes from filled in leafs
  453. c_falsenodes = 0;
  454. c_free_faces = 0;
  455. c_keep_faces = 0;
  456. node = ClearOutFaces_r(node);
  457. Verbose("%5i outleafs\n", outleafs);
  458. Verbose("%5i freed faces\n", c_free_faces);
  459. Verbose("%5i keep faces\n", c_keep_faces);
  460. Verbose("%5i falsenodes\n", c_falsenodes);
  461. // save portal file for vis tracing
  462. if ((hullnum == 0) && leakfile)
  463. {
  464. WritePortalfile(node);
  465. }
  466. return node;
  467. }