Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3304 lines
92 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "vbsp.h"
  8. #include "map_shared.h"
  9. #include "disp_vbsp.h"
  10. #include "tier1/strtools.h"
  11. #include "builddisp.h"
  12. #include "tier0/icommandline.h"
  13. #include "KeyValues.h"
  14. #include "materialsub.h"
  15. #include "fgdlib/fgdlib.h"
  16. #include "manifest.h"
  17. #ifdef VSVMFIO
  18. #include "VmfImport.h"
  19. #endif // VSVMFIO
  20. // undefine to make plane finding use linear sort
  21. #define USE_HASHING
  22. #define RENDER_NORMAL_EPSILON 0.00001
  23. #define RENDER_DIST_EPSILON 0.01f
  24. #define BRUSH_CLIP_EPSILON 0.01f // this should probably be the same
  25. // as clip epsilon, but it is 0.1f and I
  26. // currently don't know how that number was
  27. // come to (cab) - this is 0.01 of an inch
  28. // for clipping brush solids
  29. struct LoadSide_t
  30. {
  31. mapbrush_t *pBrush;
  32. side_t *pSide;
  33. int nSideIndex;
  34. int nBaseFlags;
  35. int nBaseContents;
  36. Vector planepts[3];
  37. brush_texture_t td;
  38. };
  39. extern qboolean onlyents;
  40. CUtlVector< CMapFile * > g_Maps;
  41. CMapFile *g_MainMap = NULL;
  42. CMapFile *g_LoadingMap = NULL;
  43. char CMapFile::m_InstancePath[ MAX_PATH ] = "";
  44. int CMapFile::m_InstanceCount = 0;
  45. int CMapFile::c_areaportals = 0;
  46. void CMapFile::Init( void )
  47. {
  48. entity_num = 0;
  49. num_entities = 0;
  50. nummapplanes = 0;
  51. memset( mapplanes, 0, sizeof( mapplanes ) );
  52. nummapbrushes = 0;
  53. memset( mapbrushes, 0, sizeof( mapbrushes ) );
  54. nummapbrushsides = 0;
  55. memset( brushsides, 0, sizeof( brushsides ) );
  56. memset( side_brushtextures, 0, sizeof( side_brushtextures ) );
  57. memset( planehash, 0, sizeof( planehash ) );
  58. m_ConnectionPairs = NULL;
  59. m_StartMapOverlays = g_aMapOverlays.Count();
  60. m_StartMapWaterOverlays = g_aMapWaterOverlays.Count();
  61. c_boxbevels = 0;
  62. c_edgebevels = 0;
  63. c_clipbrushes = 0;
  64. g_ClipTexinfo = -1;
  65. }
  66. // All the brush sides referenced by info_no_dynamic_shadow entities.
  67. CUtlVector<int> g_NoDynamicShadowSides;
  68. void TestExpandBrushes (void);
  69. ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
  70. ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
  71. ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo );
  72. ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
  73. ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
  74. ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
  75. ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
  76. ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
  77. ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
  78. ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
  79. ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
  80. ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
  81. #ifdef VSVMFIO
  82. ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
  83. ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
  84. #endif // VSVMFIO
  85. ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
  86. ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
  87. ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
  88. ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
  89. ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
  90. ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush);
  91. ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
  92. ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo);
  93. /*
  94. =============================================================================
  95. PLANE FINDING
  96. =============================================================================
  97. */
  98. /*
  99. =================
  100. PlaneTypeForNormal
  101. =================
  102. */
  103. int PlaneTypeForNormal (Vector& normal)
  104. {
  105. vec_t ax, ay, az;
  106. // NOTE: should these have an epsilon around 1.0?
  107. if (normal[0] == 1.0 || normal[0] == -1.0)
  108. return PLANE_X;
  109. if (normal[1] == 1.0 || normal[1] == -1.0)
  110. return PLANE_Y;
  111. if (normal[2] == 1.0 || normal[2] == -1.0)
  112. return PLANE_Z;
  113. ax = fabs(normal[0]);
  114. ay = fabs(normal[1]);
  115. az = fabs(normal[2]);
  116. if (ax >= ay && ax >= az)
  117. return PLANE_ANYX;
  118. if (ay >= ax && ay >= az)
  119. return PLANE_ANYY;
  120. return PLANE_ANYZ;
  121. }
  122. /*
  123. ================
  124. PlaneEqual
  125. ================
  126. */
  127. qboolean PlaneEqual (plane_t *p, Vector& normal, vec_t dist, float normalEpsilon, float distEpsilon)
  128. {
  129. #if 1
  130. if (
  131. fabs(p->normal[0] - normal[0]) < normalEpsilon
  132. && fabs(p->normal[1] - normal[1]) < normalEpsilon
  133. && fabs(p->normal[2] - normal[2]) < normalEpsilon
  134. && fabs(p->dist - dist) < distEpsilon )
  135. return true;
  136. #else
  137. if (p->normal[0] == normal[0]
  138. && p->normal[1] == normal[1]
  139. && p->normal[2] == normal[2]
  140. && p->dist == dist)
  141. return true;
  142. #endif
  143. return false;
  144. }
  145. /*
  146. ================
  147. AddPlaneToHash
  148. ================
  149. */
  150. void CMapFile::AddPlaneToHash (plane_t *p)
  151. {
  152. int hash;
  153. hash = (int)fabs(p->dist) / 8;
  154. hash &= (PLANE_HASHES-1);
  155. p->hash_chain = planehash[hash];
  156. planehash[hash] = p;
  157. }
  158. /*
  159. ================
  160. CreateNewFloatPlane
  161. ================
  162. */
  163. int CMapFile::CreateNewFloatPlane (Vector& normal, vec_t dist)
  164. {
  165. plane_t *p, temp;
  166. if (VectorLength(normal) < 0.5)
  167. g_MapError.ReportError ("FloatPlane: bad normal");
  168. // create a new plane
  169. if (nummapplanes+2 > MAX_MAP_PLANES)
  170. g_MapError.ReportError ("MAX_MAP_PLANES");
  171. p = &mapplanes[nummapplanes];
  172. VectorCopy (normal, p->normal);
  173. p->dist = dist;
  174. p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
  175. VectorSubtract (vec3_origin, normal, (p+1)->normal);
  176. (p+1)->dist = -dist;
  177. nummapplanes += 2;
  178. // allways put axial planes facing positive first
  179. if (p->type < 3)
  180. {
  181. if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
  182. {
  183. // flip order
  184. temp = *p;
  185. *p = *(p+1);
  186. *(p+1) = temp;
  187. AddPlaneToHash (p);
  188. AddPlaneToHash (p+1);
  189. return nummapplanes - 1;
  190. }
  191. }
  192. AddPlaneToHash (p);
  193. AddPlaneToHash (p+1);
  194. return nummapplanes - 2;
  195. }
  196. /*
  197. ==============
  198. SnapVector
  199. ==============
  200. */
  201. bool SnapVector (Vector& normal)
  202. {
  203. int i;
  204. for (i=0 ; i<3 ; i++)
  205. {
  206. if ( fabs(normal[i] - 1) < RENDER_NORMAL_EPSILON )
  207. {
  208. VectorClear (normal);
  209. normal[i] = 1;
  210. return true;
  211. }
  212. if ( fabs(normal[i] - -1) < RENDER_NORMAL_EPSILON )
  213. {
  214. VectorClear (normal);
  215. normal[i] = -1;
  216. return true;
  217. }
  218. }
  219. return false;
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
  223. // Rounds dist to integer if it is within an epsilon of integer.
  224. // Input : normal - Plane normal vector (assumed to be unit length).
  225. // dist - Plane constant.
  226. //-----------------------------------------------------------------------------
  227. void SnapPlane(Vector &normal, vec_t &dist)
  228. {
  229. SnapVector(normal);
  230. if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
  231. {
  232. dist = RoundInt(dist);
  233. }
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
  237. // Recalculates dist if the normal was snapped. Rounds dist to integer
  238. // if it is within an epsilon of integer.
  239. // Input : normal - Plane normal vector (assumed to be unit length).
  240. // dist - Plane constant.
  241. // p0, p1, p2 - Three points on the plane.
  242. //-----------------------------------------------------------------------------
  243. void SnapPlane(Vector &normal, vec_t &dist, const Vector &p0, const Vector &p1, const Vector &p2)
  244. {
  245. if (SnapVector(normal))
  246. {
  247. //
  248. // Calculate a new plane constant using the snapped normal. Use the
  249. // centroid of the three plane points to minimize error. This is like
  250. // rotating the plane around the centroid.
  251. //
  252. Vector p3 = (p0 + p1 + p2) / 3.0f;
  253. dist = normal.Dot(p3);
  254. if ( g_snapAxialPlanes )
  255. {
  256. dist = RoundInt(dist);
  257. }
  258. }
  259. if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
  260. {
  261. dist = RoundInt(dist);
  262. }
  263. }
  264. /*
  265. =============
  266. FindFloatPlane
  267. =============
  268. */
  269. #ifndef USE_HASHING
  270. int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
  271. {
  272. int i;
  273. plane_t *p;
  274. SnapPlane(normal, dist);
  275. for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
  276. {
  277. if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
  278. return i;
  279. }
  280. return CreateNewFloatPlane (normal, dist);
  281. }
  282. #else
  283. int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
  284. {
  285. int i;
  286. plane_t *p;
  287. int hash, h;
  288. SnapPlane(normal, dist);
  289. hash = (int)fabs(dist) / 8;
  290. hash &= (PLANE_HASHES-1);
  291. // search the border bins as well
  292. for (i=-1 ; i<=1 ; i++)
  293. {
  294. h = (hash+i)&(PLANE_HASHES-1);
  295. for (p = planehash[h] ; p ; p=p->hash_chain)
  296. {
  297. if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
  298. return p-mapplanes;
  299. }
  300. }
  301. return CreateNewFloatPlane (normal, dist);
  302. }
  303. #endif
  304. //-----------------------------------------------------------------------------
  305. // Purpose: Builds a plane normal and distance from three points on the plane.
  306. // If the normal is nearly axial, it will be snapped to be axial. Looks
  307. // up the plane in the unique planes.
  308. // Input : p0, p1, p2 - Three points on the plane.
  309. // Output : Returns the index of the plane in the planes list.
  310. //-----------------------------------------------------------------------------
  311. int CMapFile::PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2)
  312. {
  313. Vector t1, t2, normal;
  314. vec_t dist;
  315. VectorSubtract (p0, p1, t1);
  316. VectorSubtract (p2, p1, t2);
  317. CrossProduct (t1, t2, normal);
  318. VectorNormalize (normal);
  319. dist = DotProduct (p0, normal);
  320. SnapPlane(normal, dist, p0, p1, p2);
  321. return FindFloatPlane (normal, dist);
  322. }
  323. /*
  324. ===========
  325. BrushContents
  326. ===========
  327. */
  328. int BrushContents (mapbrush_t *b)
  329. {
  330. int contents;
  331. int unionContents = 0;
  332. side_t *s;
  333. int i;
  334. s = &b->original_sides[0];
  335. contents = s->contents;
  336. unionContents = contents;
  337. for (i=1 ; i<b->numsides ; i++, s++)
  338. {
  339. s = &b->original_sides[i];
  340. unionContents |= s->contents;
  341. #if 0
  342. if (s->contents != contents)
  343. {
  344. Msg("Brush %i: mixed face contents\n", b->id);
  345. break;
  346. }
  347. #endif
  348. }
  349. // NOTE: we're making slime translucent so that it doesn't block lighting on things floating on its surface
  350. int transparentContents = unionContents & (CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_WATER|CONTENTS_SLIME);
  351. if ( transparentContents )
  352. {
  353. contents |= transparentContents | CONTENTS_TRANSLUCENT;
  354. contents &= ~CONTENTS_SOLID;
  355. }
  356. return contents;
  357. }
  358. //============================================================================
  359. bool IsAreaPortal( char const *pClassName )
  360. {
  361. // If the class name starts with "func_areaportal", then it's considered an area portal.
  362. char const *pBaseName = "func_areaportal";
  363. char const *pCur = pBaseName;
  364. while( *pCur && *pClassName )
  365. {
  366. if( *pCur != *pClassName )
  367. break;
  368. ++pCur;
  369. ++pClassName;
  370. }
  371. return *pCur == 0;
  372. }
  373. /*
  374. =================
  375. AddBrushBevels
  376. Adds any additional planes necessary to allow the brush to be expanded
  377. against axial bounding boxes
  378. =================
  379. */
  380. void CMapFile::AddBrushBevels (mapbrush_t *b)
  381. {
  382. int axis, dir;
  383. int i, j, k, l, order;
  384. side_t sidetemp;
  385. brush_texture_t tdtemp;
  386. side_t *s, *s2;
  387. Vector normal;
  388. float dist;
  389. winding_t *w, *w2;
  390. Vector vec, vec2;
  391. float d;
  392. //
  393. // add the axial planes
  394. //
  395. order = 0;
  396. for (axis=0 ; axis <3 ; axis++)
  397. {
  398. for (dir=-1 ; dir <= 1 ; dir+=2, order++)
  399. {
  400. // see if the plane is allready present
  401. for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
  402. {
  403. if (mapplanes[s->planenum].normal[axis] == dir)
  404. break;
  405. }
  406. if (i == b->numsides)
  407. { // add a new side
  408. if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
  409. g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
  410. nummapbrushsides++;
  411. b->numsides++;
  412. VectorClear (normal);
  413. normal[axis] = dir;
  414. if (dir == 1)
  415. dist = b->maxs[axis];
  416. else
  417. dist = -b->mins[axis];
  418. s->planenum = FindFloatPlane (normal, dist);
  419. s->texinfo = b->original_sides[0].texinfo;
  420. s->contents = b->original_sides[0].contents;
  421. s->bevel = true;
  422. c_boxbevels++;
  423. }
  424. // if the plane is not in it canonical order, swap it
  425. if (i != order)
  426. {
  427. sidetemp = b->original_sides[order];
  428. b->original_sides[order] = b->original_sides[i];
  429. b->original_sides[i] = sidetemp;
  430. j = b->original_sides - brushsides;
  431. tdtemp = side_brushtextures[j+order];
  432. side_brushtextures[j+order] = side_brushtextures[j+i];
  433. side_brushtextures[j+i] = tdtemp;
  434. }
  435. }
  436. }
  437. //
  438. // add the edge bevels
  439. //
  440. if (b->numsides == 6)
  441. return; // pure axial
  442. // test the non-axial plane edges
  443. for (i=6 ; i<b->numsides ; i++)
  444. {
  445. s = b->original_sides + i;
  446. w = s->winding;
  447. if (!w)
  448. continue;
  449. for (j=0 ; j<w->numpoints ; j++)
  450. {
  451. k = (j+1)%w->numpoints;
  452. VectorSubtract (w->p[j], w->p[k], vec);
  453. if (VectorNormalize (vec) < 0.5)
  454. continue;
  455. SnapVector (vec);
  456. for (k=0 ; k<3 ; k++)
  457. if ( vec[k] == -1 || vec[k] == 1)
  458. break; // axial
  459. if (k != 3)
  460. continue; // only test non-axial edges
  461. // try the six possible slanted axials from this edge
  462. for (axis=0 ; axis <3 ; axis++)
  463. {
  464. for (dir=-1 ; dir <= 1 ; dir+=2)
  465. {
  466. // construct a plane
  467. VectorClear (vec2);
  468. vec2[axis] = dir;
  469. CrossProduct (vec, vec2, normal);
  470. if (VectorNormalize (normal) < 0.5)
  471. continue;
  472. dist = DotProduct (w->p[j], normal);
  473. // if all the points on all the sides are
  474. // behind this plane, it is a proper edge bevel
  475. for (k=0 ; k<b->numsides ; k++)
  476. {
  477. // if this plane has allready been used, skip it
  478. // NOTE: Use a larger tolerance for collision planes than for rendering planes
  479. if ( PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist, 0.01f, 0.01f ) )
  480. break;
  481. w2 = b->original_sides[k].winding;
  482. if (!w2)
  483. continue;
  484. for (l=0 ; l<w2->numpoints ; l++)
  485. {
  486. d = DotProduct (w2->p[l], normal) - dist;
  487. if (d > 0.1)
  488. break; // point in front
  489. }
  490. if (l != w2->numpoints)
  491. break;
  492. }
  493. if (k != b->numsides)
  494. continue; // wasn't part of the outer hull
  495. // add this plane
  496. if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
  497. g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
  498. nummapbrushsides++;
  499. s2 = &b->original_sides[b->numsides];
  500. s2->planenum = FindFloatPlane (normal, dist);
  501. s2->texinfo = b->original_sides[0].texinfo;
  502. s2->contents = b->original_sides[0].contents;
  503. s2->bevel = true;
  504. c_edgebevels++;
  505. b->numsides++;
  506. }
  507. }
  508. }
  509. }
  510. }
  511. /*
  512. ================
  513. MakeBrushWindings
  514. makes basewindigs for sides and mins / maxs for the brush
  515. ================
  516. */
  517. qboolean CMapFile::MakeBrushWindings (mapbrush_t *ob)
  518. {
  519. int i, j;
  520. winding_t *w;
  521. side_t *side;
  522. plane_t *plane;
  523. ClearBounds (ob->mins, ob->maxs);
  524. for (i=0 ; i<ob->numsides ; i++)
  525. {
  526. plane = &mapplanes[ob->original_sides[i].planenum];
  527. w = BaseWindingForPlane (plane->normal, plane->dist);
  528. for (j=0 ; j<ob->numsides && w; j++)
  529. {
  530. if (i == j)
  531. continue;
  532. if (ob->original_sides[j].bevel)
  533. continue;
  534. plane = &mapplanes[ob->original_sides[j].planenum^1];
  535. // ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
  536. // adding an epsilon here, due to precision issues creating complex
  537. // displacement surfaces (cab)
  538. ChopWindingInPlace( &w, plane->normal, plane->dist, BRUSH_CLIP_EPSILON );
  539. }
  540. side = &ob->original_sides[i];
  541. side->winding = w;
  542. if (w)
  543. {
  544. side->visible = true;
  545. for (j=0 ; j<w->numpoints ; j++)
  546. AddPointToBounds (w->p[j], ob->mins, ob->maxs);
  547. }
  548. }
  549. for (i=0 ; i<3 ; i++)
  550. {
  551. if (ob->mins[i] < MIN_COORD_INTEGER || ob->maxs[i] > MAX_COORD_INTEGER)
  552. Msg("Brush %i: bounds out of range\n", ob->id);
  553. if (ob->mins[i] > MAX_COORD_INTEGER || ob->maxs[i] < MIN_COORD_INTEGER)
  554. Msg("Brush %i: no visible sides on brush\n", ob->id);
  555. }
  556. return true;
  557. }
  558. //-----------------------------------------------------------------------------
  559. // Purpose: Takes all of the brushes from the current entity and adds them to the
  560. // world's brush list. Used by func_detail and func_areaportal.
  561. // THIS ROUTINE MAY ONLY BE USED DURING ENTITY LOADING.
  562. // Input : mapent - Entity whose brushes are to be moved to the world.
  563. //-----------------------------------------------------------------------------
  564. void CMapFile::MoveBrushesToWorld( entity_t *mapent )
  565. {
  566. int newbrushes;
  567. int worldbrushes;
  568. mapbrush_t *temp;
  569. int i;
  570. // this is pretty gross, because the brushes are expected to be
  571. // in linear order for each entity
  572. newbrushes = mapent->numbrushes;
  573. worldbrushes = entities[0].numbrushes;
  574. temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
  575. memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
  576. #if 0 // let them keep their original brush numbers
  577. for (i=0 ; i<newbrushes ; i++)
  578. temp[i].entitynum = 0;
  579. #endif
  580. // make space to move the brushes (overlapped copy)
  581. memmove (mapbrushes + worldbrushes + newbrushes,
  582. mapbrushes + worldbrushes,
  583. sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
  584. // copy the new brushes down
  585. memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
  586. // fix up indexes
  587. entities[0].numbrushes += newbrushes;
  588. for (i=1 ; i<num_entities ; i++)
  589. entities[i].firstbrush += newbrushes;
  590. free (temp);
  591. mapent->numbrushes = 0;
  592. }
  593. //-----------------------------------------------------------------------------
  594. // Purpose: Takes all of the brushes from the current entity and adds them to the
  595. // world's brush list. Used by func_detail and func_areaportal.
  596. // Input : mapent - Entity whose brushes are to be moved to the world.
  597. //-----------------------------------------------------------------------------
  598. void CMapFile::MoveBrushesToWorldGeneral( entity_t *mapent )
  599. {
  600. int newbrushes;
  601. int worldbrushes;
  602. mapbrush_t *temp;
  603. int i;
  604. for( i = 0; i < nummapdispinfo; i++ )
  605. {
  606. if ( mapdispinfo[ i ].entitynum == ( mapent - entities ) )
  607. {
  608. mapdispinfo[ i ].entitynum = 0;
  609. }
  610. }
  611. // this is pretty gross, because the brushes are expected to be
  612. // in linear order for each entity
  613. newbrushes = mapent->numbrushes;
  614. worldbrushes = entities[0].numbrushes;
  615. temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
  616. memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
  617. #if 0 // let them keep their original brush numbers
  618. for (i=0 ; i<newbrushes ; i++)
  619. temp[i].entitynum = 0;
  620. #endif
  621. // make space to move the brushes (overlapped copy)
  622. memmove (mapbrushes + worldbrushes + newbrushes,
  623. mapbrushes + worldbrushes,
  624. sizeof(mapbrush_t) * (mapent->firstbrush - worldbrushes) );
  625. // wwwxxxmmyyy
  626. // copy the new brushes down
  627. memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
  628. // fix up indexes
  629. entities[0].numbrushes += newbrushes;
  630. for (i=1 ; i<num_entities ; i++)
  631. {
  632. if ( entities[ i ].firstbrush < mapent->firstbrush ) // if we use <=, then we'll remap the passed in ent, which we don't want to
  633. {
  634. entities[ i ].firstbrush += newbrushes;
  635. }
  636. }
  637. free (temp);
  638. mapent->numbrushes = 0;
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Purpose: Iterates the sides of brush and removed CONTENTS_DETAIL from each side
  642. // Input : *brush -
  643. //-----------------------------------------------------------------------------
  644. void RemoveContentsDetailFromBrush( mapbrush_t *brush )
  645. {
  646. // Only valid on non-world brushes
  647. Assert( brush->entitynum != 0 );
  648. side_t *s;
  649. int i;
  650. s = &brush->original_sides[0];
  651. for ( i=0 ; i<brush->numsides ; i++, s++ )
  652. {
  653. if ( s->contents & CONTENTS_DETAIL )
  654. {
  655. s->contents &= ~CONTENTS_DETAIL;
  656. }
  657. }
  658. }
  659. //-----------------------------------------------------------------------------
  660. // Purpose: Iterates all brushes in an entity and removes CONTENTS_DETAIL from all brushes
  661. // Input : *mapent -
  662. //-----------------------------------------------------------------------------
  663. void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent )
  664. {
  665. int i;
  666. for ( i = 0; i < mapent->numbrushes; i++ )
  667. {
  668. int brushnum = mapent->firstbrush + i;
  669. mapbrush_t *brush = &mapbrushes[ brushnum ];
  670. RemoveContentsDetailFromBrush( brush );
  671. }
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Purpose:
  675. // Input : *pFile -
  676. // *pDisp -
  677. // Output : ChunkFileResult_t
  678. //-----------------------------------------------------------------------------
  679. ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
  680. {
  681. return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pMapDispInfo));
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Purpose:
  685. // Input : szKey -
  686. // szValue -
  687. // pDisp -
  688. // Output : ChunkFileResult_t
  689. //-----------------------------------------------------------------------------
  690. ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
  691. {
  692. if (!strnicmp(szKey, "row", 3))
  693. {
  694. char szBuf[MAX_KEYVALUE_LEN];
  695. strcpy(szBuf, szValue);
  696. int nCols = (1 << pMapDispInfo->power) + 1;
  697. int nRow = atoi(&szKey[3]);
  698. char *pszNext = strtok(szBuf, " ");
  699. int nIndex = nRow * nCols;
  700. while (pszNext != NULL)
  701. {
  702. pMapDispInfo->dispDists[nIndex] = (float)atof(pszNext);
  703. pszNext = strtok(NULL, " ");
  704. nIndex++;
  705. }
  706. }
  707. return(ChunkFile_Ok);
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Purpose: load in the displacement info "chunk" from the .map file into the
  711. // vbsp map displacement info data structure
  712. // Output : return the index of the map displacement info
  713. //-----------------------------------------------------------------------------
  714. ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo )
  715. {
  716. //
  717. // check to see if we exceeded the maximum displacement info list size
  718. //
  719. if (nummapdispinfo > MAX_MAP_DISPINFO)
  720. {
  721. g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO" );
  722. }
  723. // get a pointer to the next available displacement info slot
  724. mapdispinfo_t *pMapDispInfo = &mapdispinfo[nummapdispinfo];
  725. nummapdispinfo++;
  726. //
  727. // Set up handlers for the subchunks that we are interested in.
  728. //
  729. CChunkHandlerMap Handlers;
  730. Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, pMapDispInfo);
  731. Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, pMapDispInfo);
  732. Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, pMapDispInfo);
  733. Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, pMapDispInfo);
  734. Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, pMapDispInfo);
  735. #ifdef VSVMFIO
  736. Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, pMapDispInfo);
  737. #endif // VSVMFIO
  738. //
  739. // Read the displacement chunk.
  740. //
  741. pFile->PushHandlers(&Handlers);
  742. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispInfoKeyCallback, pMapDispInfo);
  743. pFile->PopHandlers();
  744. if (eResult == ChunkFile_Ok)
  745. {
  746. // return a pointer to the displacement info
  747. *ppMapDispInfo = pMapDispInfo;
  748. }
  749. return(eResult);
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose:
  753. // Input : *szKey -
  754. // *szValue -
  755. // *mapent -
  756. // Output : ChunkFileResult_t
  757. //-----------------------------------------------------------------------------
  758. ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
  759. {
  760. if (!stricmp(szKey, "power"))
  761. {
  762. CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->power);
  763. }
  764. #ifdef VSVMFIO
  765. else if (!stricmp(szKey, "elevation"))
  766. {
  767. CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->m_elevation);
  768. }
  769. #endif // VSVMFIO
  770. else if (!stricmp(szKey, "uaxis"))
  771. {
  772. CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->uAxis);
  773. }
  774. else if (!stricmp(szKey, "vaxis"))
  775. {
  776. CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->vAxis);
  777. }
  778. else if( !stricmp( szKey, "startposition" ) )
  779. {
  780. CChunkFile::ReadKeyValueVector3( szValue, pMapDispInfo->startPosition );
  781. }
  782. else if( !stricmp( szKey, "flags" ) )
  783. {
  784. CChunkFile::ReadKeyValueInt( szValue, pMapDispInfo->flags );
  785. }
  786. #if 0 // old data
  787. else if (!stricmp( szKey, "alpha" ) )
  788. {
  789. CChunkFile::ReadKeyValueVector4( szValue, pMapDispInfo->alphaValues );
  790. }
  791. #endif
  792. else if (!stricmp(szKey, "mintess"))
  793. {
  794. CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->minTess);
  795. }
  796. else if (!stricmp(szKey, "smooth"))
  797. {
  798. CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->smoothingAngle);
  799. }
  800. return(ChunkFile_Ok);
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Purpose:
  804. // Input : *pFile -
  805. // *pDisp -
  806. // Output : ChunkFileResult_t
  807. //-----------------------------------------------------------------------------
  808. ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
  809. {
  810. return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pMapDispInfo));
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Purpose:
  814. // Input : *szKey -
  815. // *szValue -
  816. // *pDisp -
  817. // Output : ChunkFileResult_t
  818. //-----------------------------------------------------------------------------
  819. ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
  820. {
  821. if (!strnicmp(szKey, "row", 3))
  822. {
  823. char szBuf[MAX_KEYVALUE_LEN];
  824. strcpy(szBuf, szValue);
  825. int nCols = (1 << pMapDispInfo->power) + 1;
  826. int nRow = atoi(&szKey[3]);
  827. char *pszNext0 = strtok(szBuf, " ");
  828. char *pszNext1 = strtok(NULL, " ");
  829. char *pszNext2 = strtok(NULL, " ");
  830. int nIndex = nRow * nCols;
  831. while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
  832. {
  833. pMapDispInfo->vectorDisps[nIndex][0] = (float)atof(pszNext0);
  834. pMapDispInfo->vectorDisps[nIndex][1] = (float)atof(pszNext1);
  835. pMapDispInfo->vectorDisps[nIndex][2] = (float)atof(pszNext2);
  836. pszNext0 = strtok(NULL, " ");
  837. pszNext1 = strtok(NULL, " ");
  838. pszNext2 = strtok(NULL, " ");
  839. nIndex++;
  840. }
  841. }
  842. return(ChunkFile_Ok);
  843. }
  844. //-----------------------------------------------------------------------------
  845. // Purpose:
  846. // Input : *szKey -
  847. // *szValue -
  848. // *pDisp -
  849. // Output : ChunkFileResult_t
  850. //-----------------------------------------------------------------------------
  851. ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
  852. {
  853. return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pMapDispInfo));
  854. }
  855. //-----------------------------------------------------------------------------
  856. // Purpose:
  857. // Input : *szKey -
  858. // *szValue -
  859. // *pDisp -
  860. // Output : ChunkFileResult_t
  861. //-----------------------------------------------------------------------------
  862. ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
  863. {
  864. if (!strnicmp(szKey, "row", 3))
  865. {
  866. char szBuf[MAX_KEYVALUE_LEN];
  867. strcpy(szBuf, szValue);
  868. int nCols = (1 << pMapDispInfo->power) + 1;
  869. int nRow = atoi(&szKey[3]);
  870. char *pszNext0 = strtok(szBuf, " ");
  871. char *pszNext1 = strtok(NULL, " ");
  872. char *pszNext2 = strtok(NULL, " ");
  873. int nIndex = nRow * nCols;
  874. while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
  875. {
  876. pMapDispInfo->vectorOffsets[nIndex][0] = (float)atof(pszNext0);
  877. pMapDispInfo->vectorOffsets[nIndex][1] = (float)atof(pszNext1);
  878. pMapDispInfo->vectorOffsets[nIndex][2] = (float)atof(pszNext2);
  879. pszNext0 = strtok(NULL, " ");
  880. pszNext1 = strtok(NULL, " ");
  881. pszNext2 = strtok(NULL, " ");
  882. nIndex++;
  883. }
  884. }
  885. return(ChunkFile_Ok);
  886. }
  887. #ifdef VSVMFIO
  888. ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
  889. {
  890. return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pMapDispInfo));
  891. }
  892. ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
  893. {
  894. if (!strnicmp(szKey, "row", 3))
  895. {
  896. char szBuf[MAX_KEYVALUE_LEN];
  897. strcpy(szBuf, szValue);
  898. int nCols = (1 << pMapDispInfo->power) + 1;
  899. int nRow = atoi(&szKey[3]);
  900. char *pszNext0 = strtok(szBuf, " ");
  901. char *pszNext1 = strtok(NULL, " ");
  902. char *pszNext2 = strtok(NULL, " ");
  903. int nIndex = nRow * nCols;
  904. while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
  905. {
  906. pMapDispInfo->m_offsetNormals[nIndex][0] = (float)atof(pszNext0);
  907. pMapDispInfo->m_offsetNormals[nIndex][1] = (float)atof(pszNext1);
  908. pMapDispInfo->m_offsetNormals[nIndex][2] = (float)atof(pszNext2);
  909. pszNext0 = strtok(NULL, " ");
  910. pszNext1 = strtok(NULL, " ");
  911. pszNext2 = strtok(NULL, " ");
  912. nIndex++;
  913. }
  914. }
  915. return(ChunkFile_Ok);
  916. }
  917. #endif // VSVMFIO
  918. //-----------------------------------------------------------------------------
  919. // Purpose:
  920. // Input : *szKey -
  921. // *szValue -
  922. // *pDisp -
  923. // Output : ChunkFileResult_t
  924. //-----------------------------------------------------------------------------
  925. ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
  926. {
  927. return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pMapDispInfo));
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Purpose:
  931. // Input : *szKey -
  932. // *szValue -
  933. // *pDisp -
  934. // Output : ChunkFileResult_t
  935. //-----------------------------------------------------------------------------
  936. ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
  937. {
  938. if (!strnicmp(szKey, "row", 3))
  939. {
  940. char szBuf[MAX_KEYVALUE_LEN];
  941. strcpy(szBuf, szValue);
  942. int nCols = (1 << pMapDispInfo->power) + 1;
  943. int nRow = atoi(&szKey[3]);
  944. char *pszNext0 = strtok(szBuf, " ");
  945. int nIndex = nRow * nCols;
  946. while (pszNext0 != NULL)
  947. {
  948. pMapDispInfo->alphaValues[nIndex] = (float)atof(pszNext0);
  949. pszNext0 = strtok(NULL, " ");
  950. nIndex++;
  951. }
  952. }
  953. return(ChunkFile_Ok);
  954. }
  955. //-----------------------------------------------------------------------------
  956. //-----------------------------------------------------------------------------
  957. ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
  958. {
  959. return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pMapDispInfo));
  960. }
  961. //-----------------------------------------------------------------------------
  962. //-----------------------------------------------------------------------------
  963. ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
  964. {
  965. if ( !strnicmp( szKey, "row", 3 ) )
  966. {
  967. char szBuf[MAX_KEYVALUE_LEN];
  968. strcpy( szBuf, szValue );
  969. int nCols = ( 1 << pMapDispInfo->power );
  970. int nRow = atoi( &szKey[3] );
  971. char *pszNext = strtok( szBuf, " " );
  972. int nIndex = nRow * nCols;
  973. int iTri = nIndex * 2;
  974. while ( pszNext != NULL )
  975. {
  976. // Collapse the tags here!
  977. unsigned short nTriTags = ( unsigned short )atoi( pszNext );
  978. // Walkable
  979. bool bWalkable = ( ( nTriTags & COREDISPTRI_TAG_WALKABLE ) != 0 );
  980. if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) != 0 ) )
  981. {
  982. bWalkable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_VAL ) != 0 );
  983. }
  984. // Buildable
  985. bool bBuildable = ( ( nTriTags & COREDISPTRI_TAG_BUILDABLE ) != 0 );
  986. if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) != 0 ) )
  987. {
  988. bBuildable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ) != 0 );
  989. }
  990. nTriTags = 0;
  991. if ( bWalkable )
  992. {
  993. nTriTags |= DISPTRI_TAG_WALKABLE;
  994. }
  995. if ( bBuildable )
  996. {
  997. nTriTags |= DISPTRI_TAG_BUILDABLE;
  998. }
  999. pMapDispInfo->triTags[iTri] = nTriTags;
  1000. pszNext = strtok( NULL, " " );
  1001. iTri++;
  1002. }
  1003. }
  1004. return( ChunkFile_Ok );
  1005. }
  1006. //-----------------------------------------------------------------------------
  1007. // Purpose:
  1008. // Input : brushSideID -
  1009. // Output : int
  1010. //-----------------------------------------------------------------------------
  1011. int CMapFile::SideIDToIndex( int brushSideID )
  1012. {
  1013. int i;
  1014. for ( i = 0; i < nummapbrushsides; i++ )
  1015. {
  1016. if ( brushsides[i].id == brushSideID )
  1017. {
  1018. return i;
  1019. }
  1020. }
  1021. Assert( 0 );
  1022. return -1;
  1023. }
  1024. //-----------------------------------------------------------------------------
  1025. // Purpose:
  1026. // Input : *mapent -
  1027. // *key -
  1028. //-----------------------------------------------------------------------------
  1029. void ConvertSideList( entity_t *mapent, char *key )
  1030. {
  1031. char *pszSideList = ValueForKey( mapent, key );
  1032. if (pszSideList)
  1033. {
  1034. char *pszTmpList = ( char* )_alloca( strlen( pszSideList ) + 1 );
  1035. strcpy( pszTmpList, pszSideList );
  1036. bool bFirst = true;
  1037. char szNewValue[1024];
  1038. szNewValue[0] = '\0';
  1039. const char *pszScan = strtok( pszTmpList, " " );
  1040. if ( !pszScan )
  1041. return;
  1042. do
  1043. {
  1044. int nSideID;
  1045. if ( sscanf( pszScan, "%d", &nSideID ) == 1 )
  1046. {
  1047. int nIndex = g_LoadingMap->SideIDToIndex(nSideID);
  1048. if (nIndex != -1)
  1049. {
  1050. if (!bFirst)
  1051. {
  1052. strcat( szNewValue, " " );
  1053. }
  1054. else
  1055. {
  1056. bFirst = false;
  1057. }
  1058. char szIndex[15];
  1059. itoa( nIndex, szIndex, 10 );
  1060. strcat( szNewValue, szIndex );
  1061. }
  1062. }
  1063. } while ( ( pszScan = strtok( NULL, " " ) ) );
  1064. SetKeyValue( mapent, key, szNewValue );
  1065. }
  1066. }
  1067. // Add all the sides referenced by info_no_dynamic_shadows entities to g_NoDynamicShadowSides.
  1068. ChunkFileResult_t HandleNoDynamicShadowsEnt( entity_t *pMapEnt )
  1069. {
  1070. // Get the list of the sides.
  1071. char *pSideList = ValueForKey( pMapEnt, "sides" );
  1072. // Parse the side list.
  1073. char *pScan = strtok( pSideList, " " );
  1074. if( pScan )
  1075. {
  1076. do
  1077. {
  1078. int brushSideID;
  1079. if( sscanf( pScan, "%d", &brushSideID ) == 1 )
  1080. {
  1081. if ( g_NoDynamicShadowSides.Find( brushSideID ) == -1 )
  1082. g_NoDynamicShadowSides.AddToTail( brushSideID );
  1083. }
  1084. } while( ( pScan = strtok( NULL, " " ) ) );
  1085. }
  1086. // Clear out this entity.
  1087. pMapEnt->epairs = NULL;
  1088. return ( ChunkFile_Ok );
  1089. }
  1090. static ChunkFileResult_t LoadOverlayDataTransitionKeyCallback( const char *szKey, const char *szValue, mapoverlay_t *pOverlay )
  1091. {
  1092. if ( !stricmp( szKey, "material" ) )
  1093. {
  1094. // Get the material name.
  1095. const char *pMaterialName = szValue;
  1096. if( g_ReplaceMaterials )
  1097. {
  1098. pMaterialName = ReplaceMaterialName( szValue );
  1099. }
  1100. Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
  1101. if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
  1102. {
  1103. Error( "Overlay Material Name (%s) > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
  1104. return ChunkFile_Fail;
  1105. }
  1106. strcpy( pOverlay->szMaterialName, pMaterialName );
  1107. }
  1108. else if ( !stricmp( szKey, "StartU") )
  1109. {
  1110. CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[0] );
  1111. }
  1112. else if ( !stricmp( szKey, "EndU" ) )
  1113. {
  1114. CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[1] );
  1115. }
  1116. else if ( !stricmp( szKey, "StartV" ) )
  1117. {
  1118. CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[0] );
  1119. }
  1120. else if ( !stricmp( szKey, "EndV" ) )
  1121. {
  1122. CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[1] );
  1123. }
  1124. else if ( !stricmp( szKey, "BasisOrigin" ) )
  1125. {
  1126. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecOrigin );
  1127. }
  1128. else if ( !stricmp( szKey, "BasisU" ) )
  1129. {
  1130. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[0] );
  1131. }
  1132. else if ( !stricmp( szKey, "BasisV" ) )
  1133. {
  1134. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[1] );
  1135. }
  1136. else if ( !stricmp( szKey, "BasisNormal" ) )
  1137. {
  1138. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[2] );
  1139. }
  1140. else if ( !stricmp( szKey, "uv0" ) )
  1141. {
  1142. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[0] );
  1143. }
  1144. else if ( !stricmp( szKey, "uv1" ) )
  1145. {
  1146. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[1] );
  1147. }
  1148. else if ( !stricmp( szKey, "uv2" ) )
  1149. {
  1150. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[2] );
  1151. }
  1152. else if ( !stricmp( szKey, "uv3" ) )
  1153. {
  1154. CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[3] );
  1155. }
  1156. else if ( !stricmp( szKey, "sides" ) )
  1157. {
  1158. const char *pSideList = szValue;
  1159. char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
  1160. strcpy( pTmpList, pSideList );
  1161. const char *pScan = strtok( pTmpList, " " );
  1162. if ( !pScan )
  1163. return ChunkFile_Fail;
  1164. pOverlay->aSideList.Purge();
  1165. pOverlay->aFaceList.Purge();
  1166. do
  1167. {
  1168. int nSideId;
  1169. if ( sscanf( pScan, "%d", &nSideId ) == 1 )
  1170. {
  1171. pOverlay->aSideList.AddToTail( nSideId );
  1172. }
  1173. } while ( ( pScan = strtok( NULL, " " ) ) );
  1174. }
  1175. return ChunkFile_Ok;
  1176. }
  1177. static ChunkFileResult_t LoadOverlayDataTransitionCallback( CChunkFile *pFile, int nParam )
  1178. {
  1179. int iOverlay = g_aMapWaterOverlays.AddToTail();
  1180. mapoverlay_t *pOverlay = &g_aMapWaterOverlays[iOverlay];
  1181. if ( !pOverlay )
  1182. return ChunkFile_Fail;
  1183. pOverlay->nId = ( MAX_MAP_OVERLAYS + 1 ) + g_aMapWaterOverlays.Count() - 1;
  1184. pOverlay->m_nRenderOrder = 0;
  1185. ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadOverlayDataTransitionKeyCallback, pOverlay );
  1186. return eResult;
  1187. }
  1188. static ChunkFileResult_t LoadOverlayTransitionCallback( CChunkFile *pFile, int nParam )
  1189. {
  1190. CChunkHandlerMap Handlers;
  1191. Handlers.AddHandler( "overlaydata", ( ChunkHandler_t )LoadOverlayDataTransitionCallback, 0 );
  1192. pFile->PushHandlers( &Handlers );
  1193. ChunkFileResult_t eResult = pFile->ReadChunk( NULL, NULL );
  1194. pFile->PopHandlers();
  1195. return eResult;
  1196. }
  1197. //-----------------------------------------------------------------------------
  1198. // Purpose: Iterates all brushes in a ladder entity, generates its mins and maxs.
  1199. // These are stored in the object, since the brushes are going to go away.
  1200. // Input : *mapent -
  1201. //-----------------------------------------------------------------------------
  1202. void CMapFile::AddLadderKeys( entity_t *mapent )
  1203. {
  1204. Vector mins, maxs;
  1205. ClearBounds( mins, maxs );
  1206. int i;
  1207. for ( i = 0; i < mapent->numbrushes; i++ )
  1208. {
  1209. int brushnum = mapent->firstbrush + i;
  1210. mapbrush_t *brush = &mapbrushes[ brushnum ];
  1211. AddPointToBounds( brush->mins, mins, maxs );
  1212. AddPointToBounds( brush->maxs, mins, maxs );
  1213. }
  1214. char buf[16];
  1215. Q_snprintf( buf, sizeof(buf), "%2.2f", mins.x );
  1216. SetKeyValue( mapent, "mins.x", buf );
  1217. Q_snprintf( buf, sizeof(buf), "%2.2f", mins.y );
  1218. SetKeyValue( mapent, "mins.y", buf );
  1219. Q_snprintf( buf, sizeof(buf), "%2.2f", mins.z );
  1220. SetKeyValue( mapent, "mins.z", buf );
  1221. Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.x );
  1222. SetKeyValue( mapent, "maxs.x", buf );
  1223. Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.y );
  1224. SetKeyValue( mapent, "maxs.y", buf );
  1225. Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.z );
  1226. SetKeyValue( mapent, "maxs.z", buf );
  1227. }
  1228. ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam)
  1229. {
  1230. return g_LoadingMap->LoadEntityCallback( pFile, nParam );
  1231. }
  1232. //-----------------------------------------------------------------------------
  1233. // Purpose:
  1234. // Input : *pFile -
  1235. // ulParam -
  1236. // Output : ChunkFileResult_t
  1237. //-----------------------------------------------------------------------------
  1238. ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam)
  1239. {
  1240. if (num_entities == MAX_MAP_ENTITIES)
  1241. {
  1242. // Exits.
  1243. g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES");
  1244. }
  1245. entity_t *mapent = &entities[num_entities];
  1246. num_entities++;
  1247. memset(mapent, 0, sizeof(*mapent));
  1248. mapent->firstbrush = nummapbrushes;
  1249. mapent->numbrushes = 0;
  1250. //mapent->portalareas[0] = -1;
  1251. //mapent->portalareas[1] = -1;
  1252. LoadEntity_t LoadEntity;
  1253. LoadEntity.pEntity = mapent;
  1254. // No default flags/contents
  1255. LoadEntity.nBaseFlags = 0;
  1256. LoadEntity.nBaseContents = 0;
  1257. //
  1258. // Set up handlers for the subchunks that we are interested in.
  1259. //
  1260. CChunkHandlerMap Handlers;
  1261. Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
  1262. Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, &LoadEntity);
  1263. Handlers.AddHandler( "overlaytransition", ( ChunkHandler_t )LoadOverlayTransitionCallback, 0 );
  1264. //
  1265. // Read the entity chunk.
  1266. //
  1267. pFile->PushHandlers(&Handlers);
  1268. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity);
  1269. pFile->PopHandlers();
  1270. if (eResult == ChunkFile_Ok)
  1271. {
  1272. GetVectorForKey (mapent, "origin", mapent->origin);
  1273. const char *pMinDXLevelStr = ValueForKey( mapent, "mindxlevel" );
  1274. const char *pMaxDXLevelStr = ValueForKey( mapent, "maxdxlevel" );
  1275. if( *pMinDXLevelStr != '\0' || *pMaxDXLevelStr != '\0' )
  1276. {
  1277. int min = 0;
  1278. int max = 0;
  1279. if( *pMinDXLevelStr )
  1280. {
  1281. min = atoi( pMinDXLevelStr );
  1282. }
  1283. if( *pMaxDXLevelStr )
  1284. {
  1285. max = atoi( pMaxDXLevelStr );
  1286. }
  1287. // Set min and max to default values.
  1288. if( min == 0 )
  1289. {
  1290. min = g_nDXLevel;
  1291. }
  1292. if( max == 0 )
  1293. {
  1294. max = g_nDXLevel;
  1295. }
  1296. if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < min || g_nDXLevel > max ) )
  1297. {
  1298. mapent->numbrushes = 0;
  1299. mapent->epairs = NULL;
  1300. return(ChunkFile_Ok);
  1301. }
  1302. }
  1303. // offset all of the planes and texinfo
  1304. if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] )
  1305. {
  1306. for (int i=0 ; i<mapent->numbrushes ; i++)
  1307. {
  1308. mapbrush_t *b = &mapbrushes[mapent->firstbrush + i];
  1309. for (int j=0 ; j<b->numsides ; j++)
  1310. {
  1311. side_t *s = &b->original_sides[j];
  1312. vec_t newdist = mapplanes[s->planenum].dist - DotProduct (mapplanes[s->planenum].normal, mapent->origin);
  1313. s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
  1314. if ( !onlyents )
  1315. {
  1316. s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], &side_brushtextures[s-brushsides], mapent->origin);
  1317. }
  1318. }
  1319. MakeBrushWindings (b);
  1320. }
  1321. }
  1322. //
  1323. // func_detail brushes are moved into the world entity. The CONTENTS_DETAIL flag was set by the loader.
  1324. //
  1325. const char *pClassName = ValueForKey( mapent, "classname" );
  1326. if ( !strcmp( "func_detail", pClassName ) )
  1327. {
  1328. MoveBrushesToWorld (mapent);
  1329. mapent->numbrushes = 0;
  1330. // clear out this entity
  1331. mapent->epairs = NULL;
  1332. return(ChunkFile_Ok);
  1333. }
  1334. // these get added to a list for processing the portal file
  1335. // but aren't necessary to emit to the BSP
  1336. if ( !strcmp( "func_viscluster", pClassName ) )
  1337. {
  1338. AddVisCluster(mapent);
  1339. return(ChunkFile_Ok);
  1340. }
  1341. //
  1342. // func_ladder brushes are moved into the world entity. We convert the func_ladder to an info_ladder
  1343. // that holds the ladder's mins and maxs, and leave the entity. This helps the bots figure out ladders.
  1344. //
  1345. if ( !strcmp( "func_ladder", pClassName ) )
  1346. {
  1347. AddLadderKeys( mapent );
  1348. MoveBrushesToWorld (mapent);
  1349. // Convert to info_ladder entity
  1350. SetKeyValue( mapent, "classname", "info_ladder" );
  1351. return(ChunkFile_Ok);
  1352. }
  1353. if( !strcmp( "env_cubemap", pClassName ) )
  1354. {
  1355. if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
  1356. {
  1357. const char *pSideListStr = ValueForKey( mapent, "sides" );
  1358. int size;
  1359. size = IntForKey( mapent, "cubemapsize" );
  1360. Cubemap_InsertSample( mapent->origin, size );
  1361. Cubemap_SaveBrushSides( pSideListStr );
  1362. }
  1363. // clear out this entity
  1364. mapent->epairs = NULL;
  1365. return(ChunkFile_Ok);
  1366. }
  1367. if ( !strcmp( "test_sidelist", pClassName ) )
  1368. {
  1369. ConvertSideList(mapent, "sides");
  1370. return ChunkFile_Ok;
  1371. }
  1372. if ( !strcmp( "info_overlay", pClassName ) )
  1373. {
  1374. int iAccessorID = Overlay_GetFromEntity( mapent );
  1375. if ( iAccessorID < 0 )
  1376. {
  1377. // Clear out this entity.
  1378. mapent->epairs = NULL;
  1379. }
  1380. else
  1381. {
  1382. // Convert to info_overlay_accessor entity
  1383. SetKeyValue( mapent, "classname", "info_overlay_accessor" );
  1384. // Remember the id for accessing the overlay
  1385. char buf[16];
  1386. Q_snprintf( buf, sizeof(buf), "%i", iAccessorID );
  1387. SetKeyValue( mapent, "OverlayID", buf );
  1388. }
  1389. return ( ChunkFile_Ok );
  1390. }
  1391. if ( !strcmp( "info_overlay_transition", pClassName ) )
  1392. {
  1393. // Clear out this entity.
  1394. mapent->epairs = NULL;
  1395. return ( ChunkFile_Ok );
  1396. }
  1397. if ( Q_stricmp( pClassName, "info_no_dynamic_shadow" ) == 0 )
  1398. {
  1399. return HandleNoDynamicShadowsEnt( mapent );
  1400. }
  1401. if ( Q_stricmp( pClassName, "func_instance_parms" ) == 0 )
  1402. {
  1403. // Clear out this entity.
  1404. mapent->epairs = NULL;
  1405. return ( ChunkFile_Ok );
  1406. }
  1407. // areaportal entities move their brushes, but don't eliminate
  1408. // the entity
  1409. if( IsAreaPortal( pClassName ) )
  1410. {
  1411. char str[128];
  1412. if (mapent->numbrushes != 1)
  1413. {
  1414. Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
  1415. }
  1416. mapbrush_t *b = &mapbrushes[nummapbrushes-1];
  1417. b->contents = CONTENTS_AREAPORTAL;
  1418. c_areaportals++;
  1419. mapent->areaportalnum = c_areaportals;
  1420. // set the portal number as "portalnumber"
  1421. sprintf (str, "%i", c_areaportals);
  1422. SetKeyValue (mapent, "portalnumber", str);
  1423. MoveBrushesToWorld (mapent);
  1424. return(ChunkFile_Ok);
  1425. }
  1426. #ifdef VSVMFIO
  1427. if ( !Q_stricmp( pClassName, "light" ) )
  1428. {
  1429. CVmfImport::GetVmfImporter()->ImportLightCallback(
  1430. ValueForKey( mapent, "hammerid" ),
  1431. ValueForKey( mapent, "origin" ),
  1432. ValueForKey( mapent, "_light" ),
  1433. ValueForKey( mapent, "_lightHDR" ),
  1434. ValueForKey( mapent, "_lightscaleHDR" ),
  1435. ValueForKey( mapent, "_quadratic_attn" ) );
  1436. }
  1437. if ( !Q_stricmp( pClassName, "light_spot" ) )
  1438. {
  1439. CVmfImport::GetVmfImporter()->ImportLightSpotCallback(
  1440. ValueForKey( mapent, "hammerid" ),
  1441. ValueForKey( mapent, "origin" ),
  1442. ValueForKey( mapent, "angles" ),
  1443. ValueForKey( mapent, "pitch" ),
  1444. ValueForKey( mapent, "_light" ),
  1445. ValueForKey( mapent, "_lightHDR" ),
  1446. ValueForKey( mapent, "_lightscaleHDR" ),
  1447. ValueForKey( mapent, "_quadratic_attn" ),
  1448. ValueForKey( mapent, "_inner_cone" ),
  1449. ValueForKey( mapent, "_cone" ),
  1450. ValueForKey( mapent, "_exponent" ) );
  1451. }
  1452. if ( !Q_stricmp( pClassName, "light_dynamic" ) )
  1453. {
  1454. CVmfImport::GetVmfImporter()->ImportLightDynamicCallback(
  1455. ValueForKey( mapent, "hammerid" ),
  1456. ValueForKey( mapent, "origin" ),
  1457. ValueForKey( mapent, "angles" ),
  1458. ValueForKey( mapent, "pitch" ),
  1459. ValueForKey( mapent, "_light" ),
  1460. ValueForKey( mapent, "_quadratic_attn" ),
  1461. ValueForKey( mapent, "_inner_cone" ),
  1462. ValueForKey( mapent, "_cone" ),
  1463. ValueForKey( mapent, "brightness" ),
  1464. ValueForKey( mapent, "distance" ),
  1465. ValueForKey( mapent, "spotlight_radius" ) );
  1466. }
  1467. if ( !Q_stricmp( pClassName, "light_environment" ) )
  1468. {
  1469. CVmfImport::GetVmfImporter()->ImportLightEnvironmentCallback(
  1470. ValueForKey( mapent, "hammerid" ),
  1471. ValueForKey( mapent, "origin" ),
  1472. ValueForKey( mapent, "angles" ),
  1473. ValueForKey( mapent, "pitch" ),
  1474. ValueForKey( mapent, "_light" ),
  1475. ValueForKey( mapent, "_lightHDR" ),
  1476. ValueForKey( mapent, "_lightscaleHDR" ),
  1477. ValueForKey( mapent, "_ambient" ),
  1478. ValueForKey( mapent, "_ambientHDR" ),
  1479. ValueForKey( mapent, "_AmbientScaleHDR" ),
  1480. ValueForKey( mapent, "SunSpreadAngle" ) );
  1481. }
  1482. const char *pModel = ValueForKey( mapent, "model" );
  1483. if ( pModel && Q_strlen( pModel ) )
  1484. {
  1485. CVmfImport::GetVmfImporter()->ImportModelCallback(
  1486. pModel,
  1487. ValueForKey( mapent, "hammerid" ),
  1488. ValueForKey( mapent, "angles" ),
  1489. ValueForKey( mapent, "origin" ),
  1490. MDagPath() );
  1491. }
  1492. #endif // VSVMFIO
  1493. // If it's not in the world at this point, unmark CONTENTS_DETAIL from all sides...
  1494. if ( mapent != &entities[ 0 ] )
  1495. {
  1496. RemoveContentsDetailFromEntity( mapent );
  1497. }
  1498. return(ChunkFile_Ok);
  1499. }
  1500. return(eResult);
  1501. }
  1502. entity_t* EntityByName( char const *pTestName )
  1503. {
  1504. if( !pTestName )
  1505. return 0;
  1506. for( int i=0; i < g_MainMap->num_entities; i++ )
  1507. {
  1508. entity_t *e = &g_MainMap->entities[i];
  1509. const char *pName = ValueForKey( e, "targetname" );
  1510. if( stricmp( pName, pTestName ) == 0 )
  1511. return e;
  1512. }
  1513. return 0;
  1514. }
  1515. void CMapFile::ForceFuncAreaPortalWindowContents()
  1516. {
  1517. // Now go through all areaportal entities and force CONTENTS_WINDOW
  1518. // on the brushes of the bmodels they point at.
  1519. char *targets[] = {"target", "BackgroundBModel"};
  1520. int nTargets = sizeof(targets) / sizeof(targets[0]);
  1521. for( int i=0; i < num_entities; i++ )
  1522. {
  1523. entity_t *e = &entities[i];
  1524. const char *pClassName = ValueForKey( e, "classname" );
  1525. // Don't do this on "normal" func_areaportal entities. Those are tied to doors
  1526. // and should be opaque when closed. But areaportal windows (and any other
  1527. // distance-based areaportals) should be windows because they are normally open/transparent
  1528. if( !IsAreaPortal( pClassName ) || !Q_stricmp( pClassName, "func_areaportal" ) )
  1529. continue;
  1530. // const char *pTestEntName = ValueForKey( e, "targetname" );
  1531. for( int iTarget=0; iTarget < nTargets; iTarget++ )
  1532. {
  1533. char const *pEntName = ValueForKey( e, targets[iTarget] );
  1534. if( !pEntName[0] )
  1535. continue;
  1536. entity_t *pBrushEnt = EntityByName( pEntName );
  1537. if( !pBrushEnt )
  1538. continue;
  1539. for( int iBrush=0; iBrush < pBrushEnt->numbrushes; iBrush++ )
  1540. {
  1541. mapbrushes[pBrushEnt->firstbrush + iBrush].contents &= ~CONTENTS_SOLID;
  1542. mapbrushes[pBrushEnt->firstbrush + iBrush].contents |= CONTENTS_TRANSLUCENT | CONTENTS_WINDOW;
  1543. }
  1544. }
  1545. }
  1546. }
  1547. // ============ Instancing ============
  1548. // #define MERGE_INSTANCE_DEBUG_INFO 1
  1549. #define INSTANCE_VARIABLE_KEY "replace"
  1550. static GameData GD;
  1551. //-----------------------------------------------------------------------------
  1552. // Purpose: this function will read in a standard key / value file
  1553. // Input : pFilename - the absolute name of the file to read
  1554. // Output : returns the KeyValues of the file, NULL if the file could not be read.
  1555. //-----------------------------------------------------------------------------
  1556. static KeyValues *ReadKeyValuesFile( const char *pFilename )
  1557. {
  1558. // Read in the gameinfo.txt file and null-terminate it.
  1559. FILE *fp = fopen( pFilename, "rb" );
  1560. if ( !fp )
  1561. return NULL;
  1562. CUtlVector<char> buf;
  1563. fseek( fp, 0, SEEK_END );
  1564. buf.SetSize( ftell( fp ) + 1 );
  1565. fseek( fp, 0, SEEK_SET );
  1566. fread( buf.Base(), 1, buf.Count()-1, fp );
  1567. fclose( fp );
  1568. buf[buf.Count()-1] = 0;
  1569. KeyValues *kv = new KeyValues( "" );
  1570. if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
  1571. {
  1572. kv->deleteThis();
  1573. return NULL;
  1574. }
  1575. return kv;
  1576. }
  1577. //-----------------------------------------------------------------------------
  1578. // Purpose: this function will set a secondary lookup path for instances.
  1579. // Input : pszInstancePath - the secondary lookup path
  1580. //-----------------------------------------------------------------------------
  1581. void CMapFile::SetInstancePath( const char *pszInstancePath )
  1582. {
  1583. strcpy( m_InstancePath, pszInstancePath );
  1584. V_strlower( m_InstancePath );
  1585. V_FixSlashes( m_InstancePath );
  1586. }
  1587. //-----------------------------------------------------------------------------
  1588. // Purpose: This function will attempt to find a full path given the base and relative names.
  1589. // Input : pszBaseFileName - the base file that referenced this instance
  1590. // pszInstanceFileName - the relative file name of this instance
  1591. // Output : Returns true if it was able to locate the file
  1592. // pszOutFileName - the full path to the file name if located
  1593. //-----------------------------------------------------------------------------
  1594. bool CMapFile::DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName )
  1595. {
  1596. char szInstanceFileNameFixed[ MAX_PATH ];
  1597. const char *pszMapPath = "\\maps\\";
  1598. strcpy( szInstanceFileNameFixed, pszInstanceFileName );
  1599. V_SetExtension( szInstanceFileNameFixed, ".vmf", sizeof( szInstanceFileNameFixed ) );
  1600. V_FixSlashes( szInstanceFileNameFixed );
  1601. // first, try to find a relative location based upon the Base file name
  1602. strcpy( pszOutFileName, pszBaseFileName );
  1603. V_StripFilename( pszOutFileName );
  1604. strcat( pszOutFileName, "\\" );
  1605. strcat( pszOutFileName, szInstanceFileNameFixed );
  1606. if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
  1607. {
  1608. return true;
  1609. }
  1610. // second, try to find the master 'maps' directory and make it relative from that
  1611. strcpy( pszOutFileName, pszBaseFileName );
  1612. V_StripFilename( pszOutFileName );
  1613. V_RemoveDotSlashes( pszOutFileName );
  1614. V_FixDoubleSlashes( pszOutFileName );
  1615. V_strlower( pszOutFileName );
  1616. strcat( pszOutFileName, "\\" );
  1617. char *pos = strstr( pszOutFileName, pszMapPath );
  1618. if ( pos )
  1619. {
  1620. pos += strlen( pszMapPath );
  1621. *pos = 0;
  1622. strcat( pszOutFileName, szInstanceFileNameFixed );
  1623. if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
  1624. {
  1625. return true;
  1626. }
  1627. }
  1628. if ( m_InstancePath[ 0 ] != 0 )
  1629. {
  1630. sprintf( szInstanceFileNameFixed, "%s%s", m_InstancePath, pszInstanceFileName );
  1631. if ( g_pFullFileSystem->FileExists( szInstanceFileNameFixed, "GAME" ) )
  1632. {
  1633. char FullPath[ MAX_PATH ];
  1634. g_pFullFileSystem->RelativePathToFullPath( szInstanceFileNameFixed, "GAME", FullPath, sizeof( FullPath ) );
  1635. strcpy( pszOutFileName, FullPath );
  1636. return true;
  1637. }
  1638. }
  1639. pszOutFileName[ 0 ] = 0;
  1640. return false;
  1641. }
  1642. //-----------------------------------------------------------------------------
  1643. // Purpose: this function will check the main map for any func_instances. It will
  1644. // also attempt to load in the gamedata file for instancing remapping help.
  1645. // Input : none
  1646. // Output : none
  1647. //-----------------------------------------------------------------------------
  1648. void CMapFile::CheckForInstances( const char *pszFileName )
  1649. {
  1650. if ( this != g_MainMap )
  1651. { // all sub-instances will be appended to the main map master list as they are read in
  1652. // so the main loop below will naturally get to the appended ones.
  1653. return;
  1654. }
  1655. char GameInfoPath[ MAX_PATH ];
  1656. g_pFullFileSystem->RelativePathToFullPath( "gameinfo.txt", "MOD", GameInfoPath, sizeof( GameInfoPath ) );
  1657. KeyValues *GameInfoKV = ReadKeyValuesFile( GameInfoPath );
  1658. if ( !GameInfoKV )
  1659. {
  1660. Msg( "Could not locate gameinfo.txt for Instance Remapping at %s\n", GameInfoPath );
  1661. return;
  1662. }
  1663. const char *InstancePath = GameInfoKV->GetString( "InstancePath", NULL );
  1664. if ( InstancePath )
  1665. {
  1666. CMapFile::SetInstancePath( InstancePath );
  1667. }
  1668. const char *GameDataFile = GameInfoKV->GetString( "GameData", NULL );
  1669. if ( !GameDataFile )
  1670. {
  1671. Msg( "Could not locate 'GameData' key in %s\n", GameInfoPath );
  1672. return;
  1673. }
  1674. char FDGPath[ MAX_PATH ];
  1675. if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) )
  1676. {
  1677. if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, NULL, FDGPath, sizeof( FDGPath ) ) )
  1678. {
  1679. Msg( "Could not locate GameData file %s\n", GameDataFile );
  1680. }
  1681. }
  1682. GD.Load( FDGPath );
  1683. // this list will grow as instances are merged onto it. sub-instances are merged and
  1684. // automatically done in this processing.
  1685. for ( int i = 0; i < num_entities; i++ )
  1686. {
  1687. char *pEntity = ValueForKey( &entities[ i ], "classname" );
  1688. if ( !strcmp( pEntity, "func_instance" ) )
  1689. {
  1690. char *pInstanceFile = ValueForKey( &entities[ i ], "file" );
  1691. if ( pInstanceFile[ 0 ] )
  1692. {
  1693. char InstancePath[ MAX_PATH ];
  1694. bool bLoaded = false;
  1695. if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) )
  1696. {
  1697. if ( LoadMapFile( InstancePath ) )
  1698. {
  1699. MergeInstance( &entities[ i ], g_LoadingMap );
  1700. delete g_LoadingMap;
  1701. bLoaded = true;
  1702. }
  1703. }
  1704. if ( bLoaded == false )
  1705. {
  1706. Color red( 255, 0, 0, 255 );
  1707. ColorSpewMessage( SPEW_ERROR, &red, "Could not open instance file %s\n", pInstanceFile );
  1708. }
  1709. }
  1710. entities[ i ].numbrushes = 0;
  1711. entities[ i ].epairs = NULL;
  1712. }
  1713. }
  1714. g_LoadingMap = this;
  1715. }
  1716. //-----------------------------------------------------------------------------
  1717. // Purpose: this function will do all of the necessary work to merge the instance
  1718. // into the main map.
  1719. // Input : pInstanceEntity - the entity of the func_instance
  1720. // Instance - the map file of the instance
  1721. // Output : none
  1722. //-----------------------------------------------------------------------------
  1723. void CMapFile::MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance )
  1724. {
  1725. matrix3x4_t mat;
  1726. QAngle angles;
  1727. Vector OriginOffset = pInstanceEntity->origin;
  1728. m_InstanceCount++;
  1729. GetAnglesForKey( pInstanceEntity, "angles", angles );
  1730. AngleMatrix( angles, OriginOffset, mat );
  1731. #ifdef MERGE_INSTANCE_DEBUG_INFO
  1732. Msg( "Instance Remapping: O:( %g, %g, %g ) A:( %g, %g, %g )\n", OriginOffset.x, OriginOffset.y, OriginOffset.z, angles.x, angles.y, angles.z );
  1733. #endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
  1734. MergePlanes( pInstanceEntity, Instance, OriginOffset, angles, mat );
  1735. MergeBrushes( pInstanceEntity, Instance, OriginOffset, angles, mat );
  1736. MergeBrushSides( pInstanceEntity, Instance, OriginOffset, angles, mat );
  1737. MergeEntities( pInstanceEntity, Instance, OriginOffset, angles, mat );
  1738. MergeOverlays( pInstanceEntity, Instance, OriginOffset, angles, mat );
  1739. }
  1740. //-----------------------------------------------------------------------------
  1741. // Purpose: this function will merge in the map planes from the instance into
  1742. // the main map.
  1743. // Input : pInstanceEntity - the entity of the func_instance
  1744. // Instance - the map file of the instance
  1745. // InstanceOrigin - the translation of the instance
  1746. // InstanceAngle - the rotation of the instance
  1747. // InstanceMatrix - the translation / rotation matrix of the instance
  1748. // Output : none
  1749. //-----------------------------------------------------------------------------
  1750. void CMapFile::MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
  1751. {
  1752. // Each pair of planes needs to be added to the main map
  1753. for ( int i = 0; i < Instance->nummapplanes; i += 2 )
  1754. {
  1755. FindFloatPlane( Instance->mapplanes[i].normal, Instance->mapplanes[i].dist );
  1756. }
  1757. }
  1758. //-----------------------------------------------------------------------------
  1759. // Purpose: this function will merge in the map brushes from the instance into
  1760. // the main map.
  1761. // Input : pInstanceEntity - the entity of the func_instance
  1762. // Instance - the map file of the instance
  1763. // InstanceOrigin - the translation of the instance
  1764. // InstanceAngle - the rotation of the instance
  1765. // InstanceMatrix - the translation / rotation matrix of the instance
  1766. // Output : none
  1767. //-----------------------------------------------------------------------------
  1768. void CMapFile::MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
  1769. {
  1770. int max_brush_id = 0;
  1771. for( int i = 0; i < nummapbrushes; i++ )
  1772. {
  1773. if ( mapbrushes[ i ].id > max_brush_id )
  1774. {
  1775. max_brush_id = mapbrushes[ i ].id;
  1776. }
  1777. }
  1778. for( int i = 0; i < Instance->nummapbrushes; i++ )
  1779. {
  1780. mapbrushes[ nummapbrushes + i ] = Instance->mapbrushes[ i ];
  1781. mapbrush_t *brush = &mapbrushes[ nummapbrushes + i ];
  1782. brush->entitynum += num_entities;
  1783. brush->brushnum += nummapbrushes;
  1784. if ( i < Instance->entities[ 0 ].numbrushes || ( brush->contents & CONTENTS_LADDER ) != 0 )
  1785. { // world spawn brushes as well as ladders we physically move
  1786. Vector minsIn = brush->mins;
  1787. Vector maxsIn = brush->maxs;
  1788. TransformAABB( InstanceMatrix, minsIn, maxsIn, brush->mins, brush->maxs );
  1789. }
  1790. else
  1791. {
  1792. }
  1793. brush->id += max_brush_id;
  1794. int index = brush->original_sides - Instance->brushsides;
  1795. brush->original_sides = &brushsides[ nummapbrushsides + index ];
  1796. }
  1797. nummapbrushes += Instance->nummapbrushes;
  1798. }
  1799. //-----------------------------------------------------------------------------
  1800. // Purpose: this function will merge in the map sides from the instance into
  1801. // the main map.
  1802. // Input : pInstanceEntity - the entity of the func_instance
  1803. // Instance - the map file of the instance
  1804. // InstanceOrigin - the translation of the instance
  1805. // InstanceAngle - the rotation of the instance
  1806. // InstanceMatrix - the translation / rotation matrix of the instance
  1807. // Output : none
  1808. //-----------------------------------------------------------------------------
  1809. void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
  1810. {
  1811. int max_side_id = 0;
  1812. for( int i = 0; i < nummapbrushsides; i++ )
  1813. {
  1814. if ( brushsides[ i ].id > max_side_id )
  1815. {
  1816. max_side_id = brushsides[ i ].id;
  1817. }
  1818. }
  1819. for( int i = 0; i < Instance->nummapbrushsides; i++ )
  1820. {
  1821. brushsides[ nummapbrushsides + i ] = Instance->brushsides[ i ];
  1822. side_t *side = &brushsides[ nummapbrushsides + i ];
  1823. // The planes got merged & remapped. So you need to search for the output plane index on each side
  1824. // NOTE: You could optimize this by saving off an index map in MergePlanes
  1825. side->planenum = FindFloatPlane( Instance->mapplanes[side->planenum].normal, Instance->mapplanes[side->planenum].dist );
  1826. side->id += max_side_id;
  1827. // this could be pre-processed into a list for quicker checking
  1828. bool bNeedsTranslation = ( side->pMapDisp && side->pMapDisp->entitynum == 0 );
  1829. if ( !bNeedsTranslation )
  1830. { // check for sides that are part of the world spawn - those need translating
  1831. for( int j = 0; j < Instance->entities[ 0 ].numbrushes; j++ )
  1832. {
  1833. int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
  1834. if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) )
  1835. {
  1836. bNeedsTranslation = true;
  1837. break;
  1838. }
  1839. }
  1840. }
  1841. if ( !bNeedsTranslation )
  1842. { // sides for ladders are outside of the world spawn, but also need translating
  1843. for( int j = Instance->entities[ 0 ].numbrushes; j < Instance->nummapbrushes; j++ )
  1844. {
  1845. int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
  1846. if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) && ( Instance->mapbrushes[ j ].contents & CONTENTS_LADDER ) != 0 )
  1847. {
  1848. bNeedsTranslation = true;
  1849. break;
  1850. }
  1851. }
  1852. }
  1853. if ( bNeedsTranslation )
  1854. { // we only want to do the adjustment on world spawn brushes, not entity brushes
  1855. if ( side->winding )
  1856. {
  1857. for( int point = 0; point < side->winding->numpoints; point++ )
  1858. {
  1859. Vector inPoint = side->winding->p[ point ];
  1860. VectorTransform( inPoint, InstanceMatrix, side->winding->p[ point ] );
  1861. }
  1862. }
  1863. int planenum = side->planenum;
  1864. cplane_t inPlane, outPlane;
  1865. inPlane.normal = mapplanes[ planenum ].normal;
  1866. inPlane.dist = mapplanes[ planenum ].dist;
  1867. MatrixTransformPlane( InstanceMatrix, inPlane, outPlane );
  1868. planenum = FindFloatPlane( outPlane.normal, outPlane.dist );
  1869. side->planenum = planenum;
  1870. brush_texture_t bt = Instance->side_brushtextures[ i ];
  1871. VectorRotate( Instance->side_brushtextures[ i ].UAxis, InstanceMatrix, bt.UAxis );
  1872. VectorRotate( Instance->side_brushtextures[ i ].VAxis, InstanceMatrix, bt.VAxis );
  1873. bt.shift[ 0 ] -= InstanceOrigin.Dot( bt.UAxis ) / bt.textureWorldUnitsPerTexel[ 0 ];
  1874. bt.shift[ 1 ] -= InstanceOrigin.Dot( bt.VAxis ) / bt.textureWorldUnitsPerTexel[ 1 ];
  1875. if ( !onlyents )
  1876. {
  1877. side->texinfo = TexinfoForBrushTexture ( &mapplanes[ side->planenum ], &bt, vec3_origin );
  1878. }
  1879. }
  1880. if ( side->pMapDisp )
  1881. {
  1882. mapdispinfo_t *disp = side->pMapDisp;
  1883. disp->brushSideID = side->id;
  1884. Vector inPoint = disp->startPosition;
  1885. VectorTransform( inPoint, InstanceMatrix, disp->startPosition );
  1886. disp->face.originalface = side;
  1887. disp->face.texinfo = side->texinfo;
  1888. disp->face.planenum = side->planenum;
  1889. disp->entitynum += num_entities;
  1890. for( int point = 0; point < disp->face.w->numpoints; point++ )
  1891. {
  1892. Vector inPoint = disp->face.w->p[ point ];
  1893. VectorTransform( inPoint, InstanceMatrix, disp->face.w->p[ point ] );
  1894. }
  1895. }
  1896. }
  1897. nummapbrushsides += Instance->nummapbrushsides;
  1898. }
  1899. //-----------------------------------------------------------------------------
  1900. // Purpose: this function will look for replace parameters in the function instance
  1901. // to see if there is anything in the epair that should be replaced.
  1902. // Input : pPair - the epair with the value
  1903. // pInstanceEntity - the func_instance that may ahve replace keywords
  1904. // Output : pPair - the value field may be updated
  1905. //-----------------------------------------------------------------------------
  1906. void CMapFile::ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity )
  1907. {
  1908. char Value[ MAX_KEYVALUE_LEN ], NewValue[ MAX_KEYVALUE_LEN ];
  1909. bool Overwritten = false;
  1910. strcpy( NewValue, pPair->value );
  1911. for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next )
  1912. {
  1913. if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 )
  1914. {
  1915. char InstanceVariable[ MAX_KEYVALUE_LEN ];
  1916. strcpy( InstanceVariable, epInstance->value );
  1917. char *ValuePos = strchr( InstanceVariable, ' ' );
  1918. if ( !ValuePos )
  1919. {
  1920. continue;
  1921. }
  1922. *ValuePos = 0;
  1923. ValuePos++;
  1924. strcpy( Value, NewValue );
  1925. if ( !V_StrSubst( Value, InstanceVariable, ValuePos, NewValue, sizeof( NewValue ), false ) )
  1926. {
  1927. Overwritten = true;
  1928. break;
  1929. }
  1930. }
  1931. }
  1932. if ( !Overwritten && strcmp( pPair->value, NewValue ) != 0 )
  1933. {
  1934. free( pPair->value );
  1935. pPair->value = copystring( NewValue );
  1936. }
  1937. }
  1938. //-----------------------------------------------------------------------------
  1939. // Purpose: this function will merge in the entities from the instance into
  1940. // the main map.
  1941. // Input : pInstanceEntity - the entity of the func_instance
  1942. // Instance - the map file of the instance
  1943. // InstanceOrigin - the translation of the instance
  1944. // InstanceAngle - the rotation of the instance
  1945. // InstanceMatrix - the translation / rotation matrix of the instance
  1946. // Output : none
  1947. //-----------------------------------------------------------------------------
  1948. void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
  1949. {
  1950. int max_entity_id = 0;
  1951. char temp[ 2048 ];
  1952. char NameFixup[ 128 ];
  1953. entity_t *WorldspawnEnt = NULL;
  1954. GameData::TNameFixup FixupStyle;
  1955. char *pTargetName = ValueForKey( pInstanceEntity, "targetname" );
  1956. char *pName = ValueForKey( pInstanceEntity, "name" );
  1957. if ( pTargetName[ 0 ] )
  1958. {
  1959. sprintf( NameFixup, "%s", pTargetName );
  1960. }
  1961. else if ( pName[ 0 ] )
  1962. {
  1963. sprintf( NameFixup, "%s", pName );
  1964. }
  1965. else
  1966. {
  1967. sprintf( NameFixup, "InstanceAuto%d", m_InstanceCount );
  1968. }
  1969. for( int i = 0; i < num_entities; i++ )
  1970. {
  1971. char *pID = ValueForKey( &entities[ i ], "hammerid" );
  1972. if ( pID[ 0 ] )
  1973. {
  1974. int value = atoi( pID );
  1975. if ( value > max_entity_id )
  1976. {
  1977. max_entity_id = value;
  1978. }
  1979. }
  1980. }
  1981. FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) );
  1982. for( int i = 0; i < Instance->num_entities; i++ )
  1983. {
  1984. entities[ num_entities + i ] = Instance->entities[ i ];
  1985. entity_t *entity = &entities[ num_entities + i ];
  1986. entity->firstbrush += ( nummapbrushes - Instance->nummapbrushes );
  1987. char *pID = ValueForKey( entity, "hammerid" );
  1988. if ( pID[ 0 ] )
  1989. {
  1990. int value = atoi( pID );
  1991. value += max_entity_id;
  1992. sprintf( temp, "%d", value );
  1993. SetKeyValue( entity, "hammerid", temp );
  1994. }
  1995. char *pEntity = ValueForKey( entity, "classname" );
  1996. if ( strcmpi( pEntity, "worldspawn" ) == 0 )
  1997. {
  1998. WorldspawnEnt = entity;
  1999. }
  2000. else
  2001. {
  2002. Vector inOrigin = entity->origin;
  2003. VectorTransform( inOrigin, InstanceMatrix, entity->origin );
  2004. // search for variables coming from the func_instance to replace inside of the instance
  2005. // this is done before entity fixup, so fixup may occur on the replaced value. Not sure if this is a desired order of operation yet.
  2006. for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next )
  2007. {
  2008. ReplaceInstancePair( ep, pInstanceEntity );
  2009. }
  2010. #ifdef MERGE_INSTANCE_DEBUG_INFO
  2011. Msg( "Remapping class %s\n", pEntity );
  2012. #endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
  2013. GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle );
  2014. if ( EntClass )
  2015. {
  2016. for( int i = 0; i < EntClass->GetVariableCount(); i++ )
  2017. {
  2018. GDinputvariable *EntVar = EntClass->GetVariableAt( i );
  2019. char *pValue = ValueForKey( entity, ( char * )EntVar->GetName() );
  2020. if ( GD.RemapKeyValue( EntVar->GetName(), pValue, temp, FixupStyle ) )
  2021. {
  2022. #ifdef MERGE_INSTANCE_DEBUG_INFO
  2023. Msg( " %d. Remapped %s: from %s to %s\n", i, EntVar->GetName(), pValue, temp );
  2024. #endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
  2025. SetKeyValue( entity, EntVar->GetName(), temp );
  2026. }
  2027. else
  2028. {
  2029. #ifdef MERGE_INSTANCE_DEBUG_INFO
  2030. Msg( " %d. Ignored %s: %s\n", i, EntVar->GetName(), pValue );
  2031. #endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
  2032. }
  2033. }
  2034. }
  2035. if ( strcmpi( pEntity, "func_simpleladder" ) == 0 )
  2036. { // hate having to do this, but the key values are so screwed up
  2037. AddLadderKeys( entity );
  2038. /* Vector vInNormal, vOutNormal;
  2039. vInNormal.x = FloatForKey( entity, "normal.x" );
  2040. vInNormal.y = FloatForKey( entity, "normal.y" );
  2041. vInNormal.z = FloatForKey( entity, "normal.z" );
  2042. VectorRotate( vInNormal, InstanceMatrix, vOutNormal );
  2043. Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.x );
  2044. SetKeyValue( entity, "normal.x", temp );
  2045. Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.y );
  2046. SetKeyValue( entity, "normal.y", temp );
  2047. Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z );
  2048. SetKeyValue( entity, "normal.z", temp );*/
  2049. }
  2050. }
  2051. #ifdef MERGE_INSTANCE_DEBUG_INFO
  2052. Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i );
  2053. Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush );
  2054. Msg( " KV Pairs:\n" );
  2055. for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next )
  2056. {
  2057. Msg( " %s %s\n", ep->key, ep->value );
  2058. }
  2059. #endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
  2060. }
  2061. // search for variables coming from the func_instance to replace inside of the instance
  2062. // this is done before connection fix up, so fix up may occur on the replaced value. Not sure if this is a desired order of operation yet.
  2063. for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
  2064. {
  2065. ReplaceInstancePair( Connection->m_Pair, pInstanceEntity );
  2066. }
  2067. for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
  2068. {
  2069. char *newValue, *oldValue;
  2070. char origValue[ 4096 ];
  2071. int extraLen = 0;
  2072. oldValue = Connection->m_Pair->value;
  2073. strcpy( origValue, oldValue );
  2074. char *pos = strchr( origValue, ',' );
  2075. if ( pos )
  2076. { // null terminate the first field
  2077. *pos = NULL;
  2078. extraLen = strlen( pos + 1) + 1; // for the comma we just null'd
  2079. }
  2080. if ( GD.RemapNameField( origValue, temp, FixupStyle ) )
  2081. {
  2082. newValue = new char [ strlen( temp ) + extraLen + 1 ];
  2083. strcpy( newValue, temp );
  2084. if ( pos )
  2085. {
  2086. strcat( newValue, "," );
  2087. strcat( newValue, pos + 1 );
  2088. }
  2089. Connection->m_Pair->value = newValue;
  2090. delete oldValue;
  2091. }
  2092. }
  2093. num_entities += Instance->num_entities;
  2094. MoveBrushesToWorldGeneral( WorldspawnEnt );
  2095. WorldspawnEnt->numbrushes = 0;
  2096. WorldspawnEnt->epairs = NULL;
  2097. }
  2098. //-----------------------------------------------------------------------------
  2099. // Purpose: this function will translate overlays from the instance into
  2100. // the main map.
  2101. // Input : InstanceEntityNum - the entity number of the func_instance
  2102. // Instance - the map file of the instance
  2103. // InstanceOrigin - the translation of the instance
  2104. // InstanceAngle - the rotation of the instance
  2105. // InstanceMatrix - the translation / rotation matrix of the instance
  2106. // Output : none
  2107. //-----------------------------------------------------------------------------
  2108. void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
  2109. {
  2110. for( int i = Instance->m_StartMapOverlays; i < g_aMapOverlays.Count(); i++ )
  2111. {
  2112. Overlay_Translate( &g_aMapOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
  2113. }
  2114. for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ )
  2115. {
  2116. Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
  2117. }
  2118. }
  2119. //-----------------------------------------------------------------------------
  2120. // Purpose: Loads a VMF or MAP file. If the file has a .MAP extension, the MAP
  2121. // loader is used, otherwise the file is assumed to be in VMF format.
  2122. // Input : pszFileName - Full path of the map file to load.
  2123. //-----------------------------------------------------------------------------
  2124. bool LoadMapFile( const char *pszFileName )
  2125. {
  2126. bool bLoadingManifest = false;
  2127. CManifest *pMainManifest = NULL;
  2128. ChunkFileResult_t eResult;
  2129. //
  2130. // Dummy this up for the texture handling. This can be removed when old .MAP file
  2131. // support is removed.
  2132. //
  2133. g_nMapFileVersion = 400;
  2134. const char *pszExtension =V_GetFileExtension( pszFileName );
  2135. if ( pszExtension && strcmpi( pszExtension, "vmm" ) == 0 )
  2136. {
  2137. pMainManifest = new CManifest();
  2138. if ( pMainManifest->LoadVMFManifest( pszFileName ) )
  2139. {
  2140. eResult = ChunkFile_Ok;
  2141. pszFileName = pMainManifest->GetInstancePath();
  2142. }
  2143. else
  2144. {
  2145. eResult = ChunkFile_Fail;
  2146. }
  2147. bLoadingManifest = true;
  2148. }
  2149. else
  2150. {
  2151. //
  2152. // Open the file.
  2153. //
  2154. CChunkFile File;
  2155. eResult = File.Open(pszFileName, ChunkFile_Read);
  2156. //
  2157. // Read the file.
  2158. //
  2159. if (eResult == ChunkFile_Ok)
  2160. {
  2161. int index = g_Maps.AddToTail( new CMapFile() );
  2162. g_LoadingMap = g_Maps[ index ];
  2163. if ( g_MainMap == NULL )
  2164. {
  2165. g_MainMap = g_LoadingMap;
  2166. }
  2167. if ( g_MainMap == g_LoadingMap || verbose )
  2168. {
  2169. Msg( "Loading %s\n", pszFileName );
  2170. }
  2171. // reset the displacement info count
  2172. // nummapdispinfo = 0;
  2173. //
  2174. // Set up handlers for the subchunks that we are interested in.
  2175. //
  2176. CChunkHandlerMap Handlers;
  2177. Handlers.AddHandler("world", (ChunkHandler_t)LoadEntityCallback, 0);
  2178. Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0);
  2179. File.PushHandlers(&Handlers);
  2180. //
  2181. // Read the sub-chunks. We ignore keys in the root of the file.
  2182. //
  2183. while (eResult == ChunkFile_Ok)
  2184. {
  2185. eResult = File.ReadChunk();
  2186. }
  2187. File.PopHandlers();
  2188. }
  2189. else
  2190. {
  2191. Error("Error opening %s: %s.\n", pszFileName, File.GetErrorText(eResult));
  2192. }
  2193. }
  2194. if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF))
  2195. {
  2196. // Update the overlay/side list(s).
  2197. Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays );
  2198. OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays );
  2199. g_LoadingMap->CheckForInstances( pszFileName );
  2200. if ( pMainManifest )
  2201. {
  2202. pMainManifest->CordonWorld();
  2203. }
  2204. ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
  2205. for (int i=0 ; i<g_MainMap->entities[0].numbrushes ; i++)
  2206. {
  2207. // HLTOOLS: Raise map limits
  2208. if (g_LoadingMap->mapbrushes[i].mins[0] > MAX_COORD_INTEGER)
  2209. {
  2210. continue; // no valid points
  2211. }
  2212. AddPointToBounds (g_LoadingMap->mapbrushes[i].mins, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
  2213. AddPointToBounds (g_LoadingMap->mapbrushes[i].maxs, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
  2214. }
  2215. qprintf ("%5i brushes\n", g_LoadingMap->nummapbrushes);
  2216. qprintf ("%5i clipbrushes\n", g_LoadingMap->c_clipbrushes);
  2217. qprintf ("%5i total sides\n", g_LoadingMap->nummapbrushsides);
  2218. qprintf ("%5i boxbevels\n", g_LoadingMap->c_boxbevels);
  2219. qprintf ("%5i edgebevels\n", g_LoadingMap->c_edgebevels);
  2220. qprintf ("%5i entities\n", g_LoadingMap->num_entities);
  2221. qprintf ("%5i planes\n", g_LoadingMap->nummapplanes);
  2222. qprintf ("%5i areaportals\n", g_LoadingMap->c_areaportals);
  2223. qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", g_LoadingMap->map_mins[0],g_LoadingMap->map_mins[1],g_LoadingMap->map_mins[2],
  2224. g_LoadingMap->map_maxs[0],g_LoadingMap->map_maxs[1],g_LoadingMap->map_maxs[2]);
  2225. //TestExpandBrushes();
  2226. // Clear the error reporting
  2227. g_MapError.ClearState();
  2228. }
  2229. if ( g_MainMap == g_LoadingMap )
  2230. {
  2231. num_entities = g_MainMap->num_entities;
  2232. memcpy( entities, g_MainMap->entities, sizeof( g_MainMap->entities ) );
  2233. }
  2234. g_LoadingMap->ForceFuncAreaPortalWindowContents();
  2235. return ( ( eResult == ChunkFile_Ok ) || ( eResult == ChunkFile_EOF ) );
  2236. }
  2237. ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
  2238. {
  2239. return g_LoadingMap->LoadSideCallback( pFile, pSideInfo );
  2240. }
  2241. //-----------------------------------------------------------------------------
  2242. // Purpose:
  2243. // Input : pFile -
  2244. // pParent -
  2245. // Output : ChunkFileResult_t
  2246. //-----------------------------------------------------------------------------
  2247. ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
  2248. {
  2249. if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
  2250. {
  2251. g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
  2252. }
  2253. pSideInfo->pSide = &brushsides[nummapbrushsides];
  2254. side_t *side = pSideInfo->pSide;
  2255. mapbrush_t *b = pSideInfo->pBrush;
  2256. g_MapError.BrushSide( pSideInfo->nSideIndex++ );
  2257. // initialize the displacement info
  2258. pSideInfo->pSide->pMapDisp = NULL;
  2259. //
  2260. // Set up handlers for the subchunks that we are interested in.
  2261. //
  2262. CChunkHandlerMap Handlers;
  2263. Handlers.AddHandler( "dispinfo", ( ChunkHandler_t )LoadDispInfoCallback, &side->pMapDisp );
  2264. //
  2265. // Read the side chunk.
  2266. //
  2267. pFile->PushHandlers(&Handlers);
  2268. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSideKeyCallback, pSideInfo);
  2269. pFile->PopHandlers();
  2270. if (eResult == ChunkFile_Ok)
  2271. {
  2272. side->contents |= pSideInfo->nBaseContents;
  2273. side->surf |= pSideInfo->nBaseFlags;
  2274. pSideInfo->td.flags |= pSideInfo->nBaseFlags;
  2275. if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
  2276. {
  2277. side->contents |= CONTENTS_DETAIL;
  2278. }
  2279. if (fulldetail )
  2280. {
  2281. side->contents &= ~CONTENTS_DETAIL;
  2282. }
  2283. if (!(side->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
  2284. {
  2285. side->contents |= CONTENTS_SOLID;
  2286. }
  2287. // hints and skips are never detail, and have no content
  2288. if (side->surf & (SURF_HINT|SURF_SKIP) )
  2289. {
  2290. side->contents = 0;
  2291. }
  2292. //
  2293. // find the plane number
  2294. //
  2295. int planenum = PlaneFromPoints(pSideInfo->planepts[0], pSideInfo->planepts[1], pSideInfo->planepts[2]);
  2296. if (planenum != -1)
  2297. {
  2298. //
  2299. // See if the plane has been used already.
  2300. //
  2301. int k;
  2302. for ( k = 0; k < b->numsides; k++)
  2303. {
  2304. side_t *s2 = b->original_sides + k;
  2305. if (s2->planenum == planenum)
  2306. {
  2307. g_MapError.ReportWarning("duplicate plane");
  2308. break;
  2309. }
  2310. if ( s2->planenum == (planenum^1) )
  2311. {
  2312. g_MapError.ReportWarning("mirrored plane");
  2313. break;
  2314. }
  2315. }
  2316. //
  2317. // If the plane hasn't been used already, keep this side.
  2318. //
  2319. if (k == b->numsides)
  2320. {
  2321. side = b->original_sides + b->numsides;
  2322. side->planenum = planenum;
  2323. if ( !onlyents )
  2324. {
  2325. side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &pSideInfo->td, vec3_origin);
  2326. }
  2327. // save the td off in case there is an origin brush and we
  2328. // have to recalculate the texinfo
  2329. if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
  2330. g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
  2331. side_brushtextures[nummapbrushsides] = pSideInfo->td;
  2332. nummapbrushsides++;
  2333. b->numsides++;
  2334. #ifdef VSVMFIO
  2335. // Tell Maya We Have Another Side
  2336. if ( CVmfImport::GetVmfImporter() )
  2337. {
  2338. CVmfImport::GetVmfImporter()->AddSideCallback(
  2339. b, side, pSideInfo->td,
  2340. pSideInfo->planepts[ 0 ], pSideInfo->planepts[ 1 ], pSideInfo->planepts[ 2 ] );
  2341. }
  2342. #endif // VSVMFIO
  2343. }
  2344. }
  2345. else
  2346. {
  2347. g_MapError.ReportWarning("plane with no normal");
  2348. }
  2349. }
  2350. return(eResult);
  2351. }
  2352. //-----------------------------------------------------------------------------
  2353. // Purpose:
  2354. // Input : szKey -
  2355. // szValue -
  2356. // pSideInfo -
  2357. // Output :
  2358. //-----------------------------------------------------------------------------
  2359. ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo)
  2360. {
  2361. if (!stricmp(szKey, "plane"))
  2362. {
  2363. int nRead = sscanf(szValue, "(%f %f %f) (%f %f %f) (%f %f %f)",
  2364. &pSideInfo->planepts[0][0], &pSideInfo->planepts[0][1], &pSideInfo->planepts[0][2],
  2365. &pSideInfo->planepts[1][0], &pSideInfo->planepts[1][1], &pSideInfo->planepts[1][2],
  2366. &pSideInfo->planepts[2][0], &pSideInfo->planepts[2][1], &pSideInfo->planepts[2][2]);
  2367. if (nRead != 9)
  2368. {
  2369. g_MapError.ReportError("parsing plane definition");
  2370. }
  2371. }
  2372. else if (!stricmp(szKey, "material"))
  2373. {
  2374. // Get the material name.
  2375. if( g_ReplaceMaterials )
  2376. {
  2377. szValue = ReplaceMaterialName( szValue );
  2378. }
  2379. strcpy(pSideInfo->td.name, szValue);
  2380. g_MapError.TextureState(szValue);
  2381. // Find default flags and values for this material.
  2382. int mt = FindMiptex(pSideInfo->td.name);
  2383. pSideInfo->td.flags = textureref[mt].flags;
  2384. pSideInfo->td.lightmapWorldUnitsPerLuxel = textureref[mt].lightmapWorldUnitsPerLuxel;
  2385. pSideInfo->pSide->contents = textureref[mt].contents;
  2386. pSideInfo->pSide->surf = pSideInfo->td.flags;
  2387. }
  2388. else if (!stricmp(szKey, "uaxis"))
  2389. {
  2390. int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.UAxis[0], &pSideInfo->td.UAxis[1], &pSideInfo->td.UAxis[2], &pSideInfo->td.shift[0], &pSideInfo->td.textureWorldUnitsPerTexel[0]);
  2391. if (nRead != 5)
  2392. {
  2393. g_MapError.ReportError("parsing U axis definition");
  2394. }
  2395. }
  2396. else if (!stricmp(szKey, "vaxis"))
  2397. {
  2398. int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.VAxis[0], &pSideInfo->td.VAxis[1], &pSideInfo->td.VAxis[2], &pSideInfo->td.shift[1], &pSideInfo->td.textureWorldUnitsPerTexel[1]);
  2399. if (nRead != 5)
  2400. {
  2401. g_MapError.ReportError("parsing V axis definition");
  2402. }
  2403. }
  2404. else if (!stricmp(szKey, "lightmapscale"))
  2405. {
  2406. pSideInfo->td.lightmapWorldUnitsPerLuxel = atoi(szValue);
  2407. if (pSideInfo->td.lightmapWorldUnitsPerLuxel == 0.0f)
  2408. {
  2409. g_MapError.ReportWarning("luxel size of 0");
  2410. pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize;
  2411. }
  2412. pSideInfo->td.lightmapWorldUnitsPerLuxel *= g_luxelScale;
  2413. if (pSideInfo->td.lightmapWorldUnitsPerLuxel < g_minLuxelScale)
  2414. {
  2415. pSideInfo->td.lightmapWorldUnitsPerLuxel = g_minLuxelScale;
  2416. }
  2417. }
  2418. else if (!stricmp(szKey, "contents"))
  2419. {
  2420. pSideInfo->pSide->contents |= atoi(szValue);
  2421. }
  2422. else if (!stricmp(szKey, "flags"))
  2423. {
  2424. pSideInfo->td.flags |= atoi(szValue);
  2425. pSideInfo->pSide->surf = pSideInfo->td.flags;
  2426. }
  2427. else if (!stricmp(szKey, "id"))
  2428. {
  2429. pSideInfo->pSide->id = atoi( szValue );
  2430. }
  2431. else if (!stricmp(szKey, "smoothing_groups"))
  2432. {
  2433. pSideInfo->pSide->smoothingGroups = atoi( szValue );
  2434. }
  2435. return(ChunkFile_Ok);
  2436. }
  2437. //-----------------------------------------------------------------------------
  2438. // Purpose: Reads the connections chunk of the entity.
  2439. // Input : pFile - Chunk file to load from.
  2440. // pLoadEntity - Structure to receive loaded entity information.
  2441. // Output : ChunkFileResult_t
  2442. //-----------------------------------------------------------------------------
  2443. ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
  2444. {
  2445. return(pFile->ReadChunk((KeyHandler_t)LoadConnectionsKeyCallback, pLoadEntity));
  2446. }
  2447. //-----------------------------------------------------------------------------
  2448. // Purpose: Parses a key/value pair from the entity connections chunk.
  2449. // Input : szKey - Key indicating the name of the entity output.
  2450. // szValue - Comma delimited fields in the following format:
  2451. // <target>,<input>,<parameter>,<delay>,<times to fire>
  2452. // pLoadEntity - Structure to receive loaded entity information.
  2453. // Output : ChunkFileResult_t
  2454. //-----------------------------------------------------------------------------
  2455. ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
  2456. {
  2457. return g_LoadingMap->LoadConnectionsKeyCallback( szKey, szValue, pLoadEntity );
  2458. }
  2459. ChunkFileResult_t CMapFile::LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
  2460. {
  2461. //
  2462. // Create new input and fill it out.
  2463. //
  2464. epair_t *pOutput = new epair_t;
  2465. pOutput->key = new char [strlen(szKey) + 1];
  2466. pOutput->value = new char [strlen(szValue) + 1];
  2467. strcpy(pOutput->key, szKey);
  2468. strcpy(pOutput->value, szValue);
  2469. m_ConnectionPairs = new CConnectionPairs( pOutput, m_ConnectionPairs );
  2470. //
  2471. // Append it to the end of epairs list.
  2472. //
  2473. pOutput->next = NULL;
  2474. if (!pLoadEntity->pEntity->epairs)
  2475. {
  2476. pLoadEntity->pEntity->epairs = pOutput;
  2477. }
  2478. else
  2479. {
  2480. epair_t *ep;
  2481. for ( ep = pLoadEntity->pEntity->epairs; ep->next != NULL; ep = ep->next )
  2482. {
  2483. }
  2484. ep->next = pOutput;
  2485. }
  2486. return(ChunkFile_Ok);
  2487. }
  2488. ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
  2489. {
  2490. return g_LoadingMap->LoadSolidCallback( pFile, pLoadEntity );
  2491. };
  2492. //-----------------------------------------------------------------------------
  2493. // Purpose:
  2494. // Input : pFile -
  2495. // pParent -
  2496. // Output : ChunkFileResult_t
  2497. //-----------------------------------------------------------------------------
  2498. ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
  2499. {
  2500. if (nummapbrushes == MAX_MAP_BRUSHES)
  2501. {
  2502. g_MapError.ReportError ("nummapbrushes == MAX_MAP_BRUSHES");
  2503. }
  2504. mapbrush_t *b = &mapbrushes[nummapbrushes];
  2505. b->original_sides = &brushsides[nummapbrushsides];
  2506. b->entitynum = num_entities-1;
  2507. b->brushnum = nummapbrushes - pLoadEntity->pEntity->firstbrush;
  2508. LoadSide_t SideInfo;
  2509. SideInfo.pBrush = b;
  2510. SideInfo.nSideIndex = 0;
  2511. SideInfo.nBaseContents = pLoadEntity->nBaseContents;
  2512. SideInfo.nBaseFlags = pLoadEntity->nBaseFlags;
  2513. //
  2514. // Set up handlers for the subchunks that we are interested in.
  2515. //
  2516. CChunkHandlerMap Handlers;
  2517. Handlers.AddHandler("side", (ChunkHandler_t)::LoadSideCallback, &SideInfo);
  2518. //
  2519. // Read the solid chunk.
  2520. //
  2521. pFile->PushHandlers(&Handlers);
  2522. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSolidKeyCallback, b);
  2523. pFile->PopHandlers();
  2524. if (eResult == ChunkFile_Ok)
  2525. {
  2526. // get the content for the entire brush
  2527. b->contents = BrushContents (b);
  2528. // allow detail brushes to be removed
  2529. if (nodetail && (b->contents & CONTENTS_DETAIL) && !HasDispInfo( b ) )
  2530. {
  2531. b->numsides = 0;
  2532. return(ChunkFile_Ok);
  2533. }
  2534. // allow water brushes to be removed
  2535. if (nowater && (b->contents & MASK_WATER) )
  2536. {
  2537. b->numsides = 0;
  2538. return(ChunkFile_Ok);
  2539. }
  2540. // create windings for sides and bounds for brush
  2541. MakeBrushWindings (b);
  2542. //
  2543. // brushes that will not be visible at all will never be
  2544. // used as bsp splitters
  2545. //
  2546. // only do this on the world entity
  2547. //
  2548. if ( b->entitynum == 0 )
  2549. {
  2550. if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
  2551. {
  2552. if ( g_ClipTexinfo < 0 )
  2553. {
  2554. g_ClipTexinfo = b->original_sides[0].texinfo;
  2555. }
  2556. c_clipbrushes++;
  2557. for (int i=0 ; i<b->numsides ; i++)
  2558. {
  2559. b->original_sides[i].texinfo = TEXINFO_NODE;
  2560. }
  2561. }
  2562. }
  2563. //
  2564. // origin brushes are removed, but they set
  2565. // the rotation origin for the rest of the brushes
  2566. // in the entity. After the entire entity is parsed,
  2567. // the planenums and texinfos will be adjusted for
  2568. // the origin brush
  2569. //
  2570. if (b->contents & CONTENTS_ORIGIN)
  2571. {
  2572. char string[32];
  2573. Vector origin;
  2574. if (num_entities == 1)
  2575. {
  2576. Error("Brush %i: origin brushes not allowed in world", b->id);
  2577. }
  2578. VectorAdd (b->mins, b->maxs, origin);
  2579. VectorScale (origin, 0.5, origin);
  2580. sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
  2581. SetKeyValue (&entities[b->entitynum], "origin", string);
  2582. VectorCopy (origin, entities[b->entitynum].origin);
  2583. // don't keep this brush
  2584. b->numsides = 0;
  2585. return(ChunkFile_Ok);
  2586. }
  2587. #ifdef VSVMFIO
  2588. if ( CVmfImport::GetVmfImporter() )
  2589. {
  2590. CVmfImport::GetVmfImporter()->MapBrushToMayaCallback( b );
  2591. }
  2592. #endif // VSVMFIO
  2593. //
  2594. // find a map brushes with displacement surfaces and remove them from the "world"
  2595. //
  2596. if( HasDispInfo( b ) )
  2597. {
  2598. // add the base face data to the displacement surface
  2599. DispGetFaceInfo( b );
  2600. // don't keep this brush
  2601. b->numsides = 0;
  2602. return( ChunkFile_Ok );
  2603. }
  2604. AddBrushBevels (b);
  2605. nummapbrushes++;
  2606. pLoadEntity->pEntity->numbrushes++;
  2607. }
  2608. else
  2609. {
  2610. return eResult;
  2611. }
  2612. return(ChunkFile_Ok);
  2613. }
  2614. //-----------------------------------------------------------------------------
  2615. // Purpose:
  2616. // Input : pFile -
  2617. // parent -
  2618. // Output : ChunkFileResult_t
  2619. //-----------------------------------------------------------------------------
  2620. ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush)
  2621. {
  2622. if (!stricmp(szKey, "id"))
  2623. {
  2624. pLoadBrush->id = atoi(szValue);
  2625. g_MapError.BrushState(pLoadBrush->id);
  2626. }
  2627. return ChunkFile_Ok;
  2628. }
  2629. /*
  2630. ================
  2631. TestExpandBrushes
  2632. Expands all the brush planes and saves a new map out
  2633. ================
  2634. */
  2635. void CMapFile::TestExpandBrushes (void)
  2636. {
  2637. FILE *f;
  2638. side_t *s;
  2639. int i, j, bn;
  2640. winding_t *w;
  2641. char *name = "expanded.map";
  2642. mapbrush_t *brush;
  2643. vec_t dist;
  2644. Msg ("writing %s\n", name);
  2645. f = fopen (name, "wb");
  2646. if (!f)
  2647. Error ("Can't write %s\b", name);
  2648. fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
  2649. fprintf( f, "\"mapversion\" \"220\"\n\"sounds\" \"1\"\n\"MaxRange\" \"4096\"\n\"mapversion\" \"220\"\n\"wad\" \"vert.wad;dev.wad;generic.wad;spire.wad;urb.wad;cit.wad;water.wad\"\n" );
  2650. for (bn=0 ; bn<nummapbrushes ; bn++)
  2651. {
  2652. brush = &mapbrushes[bn];
  2653. fprintf (f, "{\n");
  2654. for (i=0 ; i<brush->numsides ; i++)
  2655. {
  2656. s = brush->original_sides + i;
  2657. dist = mapplanes[s->planenum].dist;
  2658. for (j=0 ; j<3 ; j++)
  2659. dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
  2660. w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist);
  2661. fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
  2662. fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
  2663. fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
  2664. fprintf (f, "%s [ 0 0 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n",
  2665. TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
  2666. FreeWinding (w);
  2667. }
  2668. fprintf (f, "}\n");
  2669. }
  2670. fprintf (f, "}\n");
  2671. fclose (f);
  2672. Error ("can't proceed after expanding brushes");
  2673. }
  2674. //-----------------------------------------------------------------------------
  2675. // Purpose: load in the displacement info "chunk" from the .map file into the
  2676. // vbsp map displacement info data structure
  2677. // Output: return the pointer to the displacement map
  2678. //-----------------------------------------------------------------------------
  2679. mapdispinfo_t *ParseDispInfoChunk( void )
  2680. {
  2681. int i, j;
  2682. int vertCount;
  2683. mapdispinfo_t *pMapDispInfo;
  2684. //
  2685. // check to see if we exceeded the maximum displacement info list size
  2686. //
  2687. if( nummapdispinfo > MAX_MAP_DISPINFO )
  2688. g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO");
  2689. // get a pointer to the next available displacement info slot
  2690. pMapDispInfo = &mapdispinfo[nummapdispinfo];
  2691. nummapdispinfo++;
  2692. //
  2693. // get the chunk opener - "{"
  2694. //
  2695. GetToken( false );
  2696. if( strcmp( token, "{" ) )
  2697. g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - {" );
  2698. //
  2699. //
  2700. // get the displacement info attribs
  2701. //
  2702. //
  2703. // power
  2704. GetToken( true );
  2705. pMapDispInfo->power = atoi( token );
  2706. // u and v mapping axes
  2707. for( i = 0; i < 2; i++ )
  2708. {
  2709. GetToken( false );
  2710. if( strcmp( token, "[" ) )
  2711. g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - [" );
  2712. for( j = 0; j < 3; j++ )
  2713. {
  2714. GetToken( false );
  2715. if( i == 0 )
  2716. {
  2717. pMapDispInfo->uAxis[j] = atof( token );
  2718. }
  2719. else
  2720. {
  2721. pMapDispInfo->vAxis[j] = atof( token );
  2722. }
  2723. }
  2724. GetToken( false );
  2725. if( strcmp( token, "]" ) )
  2726. g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - ]" );
  2727. }
  2728. // max displacement value
  2729. if( g_nMapFileVersion < 350 )
  2730. {
  2731. GetToken( false );
  2732. pMapDispInfo->maxDispDist = atof( token );
  2733. }
  2734. // minimum tesselation value
  2735. GetToken( false );
  2736. pMapDispInfo->minTess = atoi( token );
  2737. // light smoothing angle
  2738. GetToken( false );
  2739. pMapDispInfo->smoothingAngle = atof( token );
  2740. //
  2741. // get the displacement info displacement normals
  2742. //
  2743. GetToken( true );
  2744. pMapDispInfo->vectorDisps[0][0] = atof( token );
  2745. GetToken( false );
  2746. pMapDispInfo->vectorDisps[0][1] = atof( token );
  2747. GetToken( false );
  2748. pMapDispInfo->vectorDisps[0][2] = atof( token );
  2749. vertCount = ( ( ( 1 << pMapDispInfo->power ) + 1 ) * ( ( 1 << pMapDispInfo->power ) + 1 ) );
  2750. for( i = 1; i < vertCount; i++ )
  2751. {
  2752. GetToken( false );
  2753. pMapDispInfo->vectorDisps[i][0] = atof( token );
  2754. GetToken( false );
  2755. pMapDispInfo->vectorDisps[i][1] = atof( token );
  2756. GetToken( false );
  2757. pMapDispInfo->vectorDisps[i][2] = atof( token );
  2758. }
  2759. //
  2760. // get the displacement info displacement values
  2761. //
  2762. GetToken( true );
  2763. pMapDispInfo->dispDists[0] = atof( token );
  2764. for( i = 1; i < vertCount; i++ )
  2765. {
  2766. GetToken( false );
  2767. pMapDispInfo->dispDists[i] = atof( token );
  2768. }
  2769. //
  2770. // get the chunk closer - "}"
  2771. //
  2772. GetToken( true );
  2773. if( strcmp( token, "}" ) )
  2774. g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - }" );
  2775. // return the index of the displacement info slot
  2776. return pMapDispInfo;
  2777. }