Counter Strike : Global Offensive Source Code
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.

3932 lines
111 KiB

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