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.

2044 lines
52 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "stdafx.h"
  7. #include "Box3D.h"
  8. #include "BrushOps.h"
  9. #include "GlobalFunctions.h"
  10. #include "MapDefs.h" // dvs: For COORD_NOTINIT
  11. #include "MapView2D.h" // dvs FIXME: For HitTest2D implementation
  12. #include "MapWorld.h"
  13. #include "MapSolid.h"
  14. #include "Options.h"
  15. #include "Render2D.h"
  16. #include "Render3D.h"
  17. #include "SaveInfo.h"
  18. #include "MapDoc.h"
  19. #include "MapDisp.h"
  20. #include "camera.h"
  21. #include "ssolid.h"
  22. #include "utlvector.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include <tier0/memdbgon.h>
  25. IMPLEMENT_MAPCLASS(CMapSolid)
  26. #define CENTER_HANDLE_RADIUS 3
  27. int CMapSolid::g_nBadSolidCount = 0;
  28. int CMapSolid::g_nRecordedBadSolidCount = 0;
  29. int CMapSolid::g_nRecordedBadSolidIds[CMapSolid::MAX_RECORDED_BAD_SOLIDS];
  30. //-----------------------------------------------------------------------------
  31. // Purpose: Constructor. Sets this solid's color to a random blue-green color.
  32. // Input : Parent0 - The parent of this solid. Typically this is the world.
  33. //-----------------------------------------------------------------------------
  34. CMapSolid::CMapSolid(CMapClass *Parent0)
  35. : m_bValid(FALSE) // well, no faces
  36. {
  37. m_pParent = Parent0;
  38. m_eSolidType = btSolid;
  39. m_bIsCordonBrush = false;
  40. PickRandomColor();
  41. }
  42. //-----------------------------------------------------------------------------
  43. // Purpose: Destructor.
  44. //-----------------------------------------------------------------------------
  45. CMapSolid::~CMapSolid(void)
  46. {
  47. Faces.SetCount(0);
  48. }
  49. //-----------------------------------------------------------------------------
  50. // Purpose: Adds the plane to the given solid.
  51. // Input : pSolid - Solid to which the plane is being added.
  52. // p - Plane to add to the solid.
  53. // Output : Returns true if the solid is still valid after the addition of the
  54. // plane, false if it was entirely behind the plane.
  55. //-----------------------------------------------------------------------------
  56. bool CMapSolid::AddPlane(const CMapFace *p)
  57. {
  58. CMapFace NewFace;
  59. //
  60. // Copy the info from the carving face, including the plane, but not the points.
  61. //
  62. NewFace.CopyFrom(p, COPY_FACE_PLANE);
  63. //
  64. // Use texture from our first face - this function adds a plane
  65. // from the subtraction brush itself.
  66. //
  67. const CMapFace *pSolidFace = GetFace(0);
  68. NewFace.SetTexture(pSolidFace->texture.texture);
  69. NewFace.texture.q2contents = pSolidFace->texture.q2contents;
  70. NewFace.texture.q2surface = pSolidFace->texture.q2surface;
  71. NewFace.texture.nLightmapScale = pSolidFace->texture.nLightmapScale;
  72. //
  73. // Set up the texture axes for the new face.
  74. //
  75. NewFace.InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_ALL | INIT_TEXTURE_FORCE);
  76. //
  77. // Add the face to the solid and rebuild the solid.
  78. //
  79. AddFace(&NewFace);
  80. CreateFromPlanes();
  81. SetValid(TRUE);
  82. PostUpdate(Notify_Changed);
  83. RemoveEmptyFaces();
  84. return(GetFaceCount() >= 4);
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose: Carves this solid using another.
  88. // Input : Inside - Receives the pieces of this solid that were inside the carver.
  89. // Outside - Receives the pieces of this solid that were outside the carver.
  90. // pCarver - The solid that is being subtracted from us.
  91. //-----------------------------------------------------------------------------
  92. bool CMapSolid::Carve(CMapObjectList *pInside, CMapObjectList *pOutside, CMapSolid *pCarver)
  93. {
  94. int i;
  95. CMapSolid *front = NULL;
  96. CMapSolid *back = NULL;
  97. Vector bmins, bmaxs;
  98. Vector carvemins, carvemaxs;
  99. GetRender2DBox(bmins, bmaxs);
  100. pCarver->GetRender2DBox(carvemins, carvemaxs);
  101. //
  102. // If this solid doesn't intersect the carving solid at all, add a copy
  103. // to the outside list and exit.
  104. //
  105. for (i=0 ; i<3 ; i++)
  106. {
  107. if ((bmins[i] >= carvemaxs[i]) || (bmaxs[i] <= carvemins[i]))
  108. {
  109. if (pOutside != NULL)
  110. {
  111. CMapSolid *pCopy = (CMapSolid *)Copy(false);
  112. pOutside->AddToTail(pCopy);
  113. }
  114. return(false);
  115. }
  116. }
  117. //
  118. // Build a duplicate of this solid to carve from.
  119. //
  120. CMapSolid CarveFrom;
  121. CarveFrom.CopyFrom(this, false);
  122. //
  123. // Carve the solid by the faces in the carving solid.
  124. //
  125. for (i = 0; i < pCarver->GetFaceCount(); i++)
  126. {
  127. const CMapFace *pFace = pCarver->GetFace(i);
  128. //
  129. // Split the solid by this face, into a front and a back piece.
  130. //
  131. CarveFrom.ClipByFace(pFace, pOutside != NULL ? &front : NULL, &back);
  132. //
  133. // If there was a front piece, add it to the outside list.
  134. //
  135. if ((front != NULL) && (pOutside != NULL))
  136. {
  137. pOutside->AddToTail(front);
  138. }
  139. else
  140. {
  141. delete front;
  142. }
  143. //
  144. // If there was no back piece, we have found a face the solid is completely in front of.
  145. // Per the separating axis theorem, the two solids cannot intersect, so we are done.
  146. //
  147. if (back == NULL)
  148. {
  149. return(false);
  150. }
  151. //
  152. // The next clip uses the back piece from this clip to prevent the carve results
  153. // from overlapping.
  154. //
  155. CarveFrom.CopyFrom(back, false);
  156. //
  157. // Add the back piece of the carved solid to the inside list.
  158. //
  159. if (pInside != NULL)
  160. {
  161. pInside->AddToTail(back);
  162. }
  163. else
  164. {
  165. delete back;
  166. }
  167. }
  168. return(true);
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose: Clips the given solid by the given face, returning the results.
  172. // Input : pSolid - Solid to clip.
  173. // fa - Face to use for the clipping operation.
  174. // f - Returns the part of the solid that was in front of the clipping
  175. // face (in the direction of the face normal).
  176. // b - Returns the part of the solid that was in back of the clipping
  177. // face (in the opposite direction of the face normal).
  178. //-----------------------------------------------------------------------------
  179. void CMapSolid::ClipByFace(const CMapFace *fa, CMapSolid **f, CMapSolid **b)
  180. {
  181. CMapSolid *front = new CMapSolid;
  182. CMapSolid *back = new CMapSolid;
  183. CMapFace fb;
  184. //
  185. // Build a back facing version of the clip face by reversing the plane points
  186. // and recalculate the plane normal and distance.
  187. //
  188. fb.CopyFrom(fa);
  189. Vector temp = fb.plane.planepts[0];
  190. fb.plane.planepts[0] = fb.plane.planepts[2];
  191. fb.plane.planepts[2] = temp;
  192. fb.CalcPlane();
  193. front->CopyFrom(this, false);
  194. front->SetParent(NULL);
  195. back->CopyFrom(this, false);
  196. back->SetParent(NULL);
  197. if (!back->AddPlane(fa))
  198. {
  199. delete back;
  200. back = NULL;
  201. }
  202. if (!front->AddPlane(&fb))
  203. {
  204. delete front;
  205. front = NULL;
  206. }
  207. if (f != NULL)
  208. {
  209. *f = front;
  210. }
  211. else
  212. {
  213. delete front;
  214. }
  215. if (b != NULL)
  216. {
  217. *b = back;
  218. }
  219. else
  220. {
  221. delete back;
  222. }
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Returns true if this solid contains a face with the given ID, false if not.
  226. // Input : nFaceID - unique face ID to look for.
  227. //-----------------------------------------------------------------------------
  228. CMapFace *CMapSolid::FindFaceID(int nFaceID)
  229. {
  230. int nFaceCount = GetFaceCount();
  231. for (int nFace = 0; nFace < nFaceCount; nFace++)
  232. {
  233. CMapFace *pFace = GetFace(nFace);
  234. if (pFace->GetFaceID() == nFaceID)
  235. {
  236. return(pFace);
  237. }
  238. }
  239. return(NULL);
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose:
  243. // Input : pWorld -
  244. //-----------------------------------------------------------------------------
  245. void CMapSolid::GenerateNewFaceIDs(CMapWorld *pWorld)
  246. {
  247. int nFaceCount = GetFaceCount();
  248. for (int nFace = 0; nFace < nFaceCount; nFace++)
  249. {
  250. CMapFace *pFace = GetFace(nFace);
  251. pFace->SetFaceID(pWorld->FaceID_GetNext());
  252. }
  253. }
  254. //-----------------------------------------------------------------------------
  255. // Purpose: Allows the solid to generate new face IDs before being added to the
  256. // world because of a clone.
  257. // Input : pClone - The clone of this object that is being added to the world.
  258. // pWorld - The world that the clone is being added to.
  259. // OriginalList - The list of objects that were cloned.
  260. // NewList - The list of clones.
  261. //-----------------------------------------------------------------------------
  262. void CMapSolid::OnPreClone(CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  263. {
  264. ((CMapSolid *)pClone)->GenerateNewFaceIDs(pWorld);
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Notifies the object that a copy of it is being pasted from the
  268. // clipboard before the copy is added to the world.
  269. // Input : pCopy - The copy of this object that is being added to the world.
  270. // pSourceWorld - The world that the originals were in.
  271. // pDestWorld - The world that the copies are being added to.
  272. // OriginalList - The list of original objects that were copied.
  273. // NewList - The list of copied.
  274. //-----------------------------------------------------------------------------
  275. void CMapSolid::OnPrePaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  276. {
  277. ((CMapSolid *)pCopy)->GenerateNewFaceIDs(pDestWorld);
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose: to split the solid by the given plane into frontside and backside
  281. // solids; memory is allocated in the function for the solids;
  282. // solids are only generated for (pointers to) CMapSolid pointers that
  283. // exist -- if a pointer is NULL that sidedness is ignored; the
  284. // function returns whether or not an actual split happened (TRUE/FALSE)
  285. // Input: pPlane - the plane to split the solid with
  286. // pFront - the front sided solid (if any)
  287. // pBack - the back sided solid (if any)
  288. // Output: 0 - on front side
  289. // 1 - on back side
  290. // 2 - split
  291. //-----------------------------------------------------------------------------
  292. int CMapSolid::Split( PLANE *pPlane, CMapSolid **pFront, CMapSolid **pBack )
  293. {
  294. const float SPLIT_DIST_EPSILON = 0.001f;
  295. CMapSolid *pFrontSolid = NULL;
  296. CMapSolid *pBackSolid = NULL;
  297. CMapFace face;
  298. //
  299. // The newly added face should get its texture from face zero of the solid.
  300. //
  301. CMapFace *pFirstFace = GetFace(0);
  302. if (pFirstFace != NULL)
  303. {
  304. face.SetTexture(pFirstFace->GetTexture());
  305. }
  306. //
  307. // check for plane intersection with solid
  308. //
  309. int frontCount = 0;
  310. int backCount = 0;
  311. int faceCount = GetFaceCount();
  312. for( int i = 0; i < faceCount; i++ )
  313. {
  314. CMapFace *pFace = GetFace( i );
  315. for( int j = 0; j < pFace->nPoints; j++ )
  316. {
  317. float dist = DotProduct( pFace->Points[j], pPlane->normal ) - pPlane->dist;
  318. if( dist > SPLIT_DIST_EPSILON )
  319. {
  320. frontCount++;
  321. }
  322. else if( dist < -SPLIT_DIST_EPSILON )
  323. {
  324. backCount++;
  325. }
  326. }
  327. }
  328. //
  329. // If we're all on one side of the splitting plane, copy ourselves into the appropriate
  330. // destination solid.
  331. //
  332. if ((frontCount == 0) || (backCount == 0))
  333. {
  334. CMapSolid **pReturn;
  335. if (frontCount == 0)
  336. {
  337. pReturn = pBack;
  338. }
  339. else
  340. {
  341. pReturn = pFront;
  342. }
  343. if (pReturn == NULL)
  344. {
  345. return -1;
  346. }
  347. //
  348. // The returned solid is just a copy of ourselves.
  349. //
  350. CMapSolid *pReturnSolid = new CMapSolid;
  351. pReturnSolid->CopyFrom(this, false);
  352. pReturnSolid->SetParent(NULL);
  353. pReturnSolid->SetTemporary(TRUE);
  354. //
  355. // Rebuild the solid because mappers are accustomed to using the clipper tool as a way of
  356. // verifying that geometry is valid.
  357. //
  358. if (pReturnSolid->CreateFromPlanes( CREATE_FROM_PLANES_CLIPPING ))
  359. {
  360. // Initialize the texture axes only of the newly created faces. Leave the others alone.
  361. pReturnSolid->InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_ALL);
  362. pReturnSolid->PostUpdate(Notify_Changed);
  363. }
  364. *pReturn = pReturnSolid;
  365. return 1;
  366. }
  367. //
  368. // split the solid and create the "front" solid
  369. //
  370. if( pFront )
  371. {
  372. //
  373. // copy the original solid info
  374. //
  375. pFrontSolid = new CMapSolid;
  376. pFrontSolid->CopyFrom(this, false);
  377. pFrontSolid->SetParent(NULL);
  378. pFrontSolid->SetTemporary( TRUE );
  379. face.plane.normal = pPlane->normal;
  380. VectorNegate( face.plane.normal );
  381. face.plane.dist = -pPlane->dist;
  382. pFrontSolid->AddFace( &face );
  383. //
  384. // The new face doesn't have plane points, only a normal and a distance, so we tell CreateFromPlanes
  385. // to generate new plane points for us after creation.
  386. //
  387. if (pFrontSolid->CreateFromPlanes(CREATE_BUILD_PLANE_POINTS | CREATE_FROM_PLANES_CLIPPING))
  388. {
  389. // Initialize the texture axes only of the newly created faces. Leave the others alone.
  390. pFrontSolid->InitializeTextureAxes( Options.GetTextureAlignment(), INIT_TEXTURE_ALL );
  391. pFrontSolid->PostUpdate(Notify_Clipped_Intermediate);
  392. *pFront = pFrontSolid;
  393. }
  394. }
  395. //
  396. // split the solid and create the "back" solid
  397. //
  398. if( pBack )
  399. {
  400. //
  401. // copy the original solid info
  402. //
  403. pBackSolid = new CMapSolid;
  404. pBackSolid->CopyFrom(this, false);
  405. pBackSolid->SetParent(NULL);
  406. pBackSolid->SetTemporary( TRUE );
  407. face.plane.normal = pPlane->normal;
  408. face.plane.dist = pPlane->dist;
  409. pBackSolid->AddFace( &face );
  410. //
  411. // The new face doesn't have plane points, only a normal and a distance, so we tell CreateFromPlanes
  412. // to generate new plane points for us after creation.
  413. //
  414. if (pBackSolid->CreateFromPlanes(CREATE_BUILD_PLANE_POINTS | CREATE_FROM_PLANES_CLIPPING))
  415. {
  416. // Initialize the texture axes only of the newly created faces. Leave the others alone.
  417. pBackSolid->InitializeTextureAxes( Options.GetTextureAlignment(), INIT_TEXTURE_ALL );
  418. pBackSolid->PostUpdate(Notify_Clipped_Intermediate);
  419. *pBack = pBackSolid;
  420. }
  421. }
  422. return 2;
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Returns the index (you could use it with GetFace) or -1 if the face doesn't exist in this solid.
  426. //-----------------------------------------------------------------------------
  427. int CMapSolid::GetFaceIndex( CMapFace *pFace )
  428. {
  429. for ( int i=0; i < Faces.GetCount(); i++ )
  430. {
  431. if ( pFace == &Faces[i] )
  432. return i;
  433. }
  434. return -1;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose: Adds a face to this solid.
  438. // Input : pFace - The face to add. The face is copied into this solid's face
  439. // array, so it can be safely freed when AddFace returns.
  440. //-----------------------------------------------------------------------------
  441. void CMapSolid::AddFace(CMapFace *pFace)
  442. {
  443. int nFaces = Faces.GetCount();
  444. Faces.SetCount(nFaces + 1);
  445. CMapFace *pNewFace = &Faces[nFaces];
  446. pNewFace->CopyFrom(pFace, COPY_FACE_POINTS);
  447. pNewFace->SetRenderColor(r, g, b);
  448. pNewFace->SetCordonFace( m_bIsCordonBrush );
  449. pNewFace->SetParent(this);
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose:
  453. // Output : CMapClass
  454. //-----------------------------------------------------------------------------
  455. CMapClass *CMapSolid::Copy(bool bUpdateDependencies)
  456. {
  457. CMapSolid *pNew = new CMapSolid;
  458. pNew->CopyFrom(this, bUpdateDependencies);
  459. return pNew;
  460. }
  461. //-----------------------------------------------------------------------------
  462. // Purpose: Turns this solid into a duplicate of the given solid.
  463. // Input : pobj - Object to copy, must be a CMapSolid.
  464. // Output : Returns a pointer to this object.
  465. //-----------------------------------------------------------------------------
  466. CMapClass *CMapSolid::CopyFrom(CMapClass *pobj, bool bUpdateDependencies)
  467. {
  468. Assert(pobj->IsMapClass(MAPCLASS_TYPE(CMapSolid)));
  469. CMapSolid *pFrom = (CMapSolid *)pobj;
  470. CMapClass::CopyFrom(pobj, bUpdateDependencies);
  471. m_eSolidType = pFrom->GetHL1SolidType();
  472. m_bIsCordonBrush = pFrom->m_bIsCordonBrush;
  473. int nFaces = pFrom->Faces.GetCount();
  474. Faces.SetCount(nFaces);
  475. // copy faces
  476. CMapFace *pFromFace;
  477. CMapFace *pToFace;
  478. for (int i = nFaces - 1; i >= 0; i--)
  479. {
  480. pToFace = &Faces[i];
  481. pFromFace = &pFrom->Faces[i];
  482. if (!pToFace)
  483. {
  484. continue;
  485. }
  486. pToFace->SetParent(this);
  487. pToFace->CopyFrom(pFromFace, COPY_FACE_POINTS, bUpdateDependencies);
  488. Assert(pToFace->GetPointCount() != 0);
  489. }
  490. return(this);
  491. }
  492. //-----------------------------------------------------------------------------
  493. // Purpose: Walks the faces of a solid for debugging.
  494. //-----------------------------------------------------------------------------
  495. #ifdef _DEBUG
  496. #pragma warning (disable:4189)
  497. void CMapSolid::DebugSolid(void)
  498. {
  499. int nFaceCount = Faces.GetCount();
  500. for (int nFace = 0; nFace < nFaceCount; nFace++)
  501. {
  502. CMapFace *pFace = GetFace(nFace);
  503. }
  504. }
  505. #pragma warning (default:4189)
  506. #endif // _DEBUG
  507. //-----------------------------------------------------------------------------
  508. // Purpose:
  509. // Input : iIndex -
  510. //-----------------------------------------------------------------------------
  511. void CMapSolid::DeleteFace(int iIndex)
  512. {
  513. // shifts 'em down.
  514. int nFaces = Faces.GetCount();
  515. for(int j = iIndex; j < nFaces-1; j++)
  516. {
  517. Faces[j].CopyFrom(&Faces[j+1]);
  518. }
  519. Faces.SetCount(nFaces-1);
  520. }
  521. //-----------------------------------------------------------------------------
  522. //-----------------------------------------------------------------------------
  523. const char* CMapSolid::GetDescription(void)
  524. {
  525. static char szBuf[128];
  526. sprintf(szBuf, "solid with %d faces", Faces.GetCount());
  527. return szBuf;
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose: Calculates this solid's axis aligned bounding box.
  531. // Input : bFullUpdate - Whether to evaluate all children when calculating.
  532. //-----------------------------------------------------------------------------
  533. void CMapSolid::CalcBounds(BOOL bFullUpdate)
  534. {
  535. CMapClass::CalcBounds(bFullUpdate);
  536. //
  537. // Update mins/maxes based on our faces.
  538. //
  539. int nFaces = Faces.GetCount();
  540. for( int i = 0; i < nFaces; i++ )
  541. {
  542. // check for valid face
  543. if (!Faces[i].Points)
  544. continue;
  545. //
  546. // Get the 2d render bounds of this face and update the solid. 2D render bounds
  547. // can be different from 3D culling bounds because the 2D bounds do not consider
  548. // displacement faces.
  549. //
  550. Vector mins, maxs;
  551. bool result = Faces[i].GetRender2DBox( mins, maxs );
  552. if( result )
  553. {
  554. m_Render2DBox.UpdateBounds( mins, maxs );
  555. }
  556. //
  557. // Get the culling bounds and update the solid
  558. //
  559. result = Faces[i].GetCullBox( mins, maxs );
  560. if( result )
  561. {
  562. m_CullBox.UpdateBounds( mins, maxs );
  563. }
  564. }
  565. m_Render2DBox.GetBoundsCenter(m_Origin);
  566. m_BoundingBox = m_CullBox;
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose:
  570. // Input : t -
  571. //-----------------------------------------------------------------------------
  572. void CMapSolid::DoTransform(const VMatrix &matrix)
  573. {
  574. // get all points, transform them
  575. int nFaces = Faces.GetCount();
  576. for (int i = 0; i < nFaces; i++)
  577. {
  578. Faces[i].DoTransform( matrix );
  579. }
  580. BaseClass::DoTransform(matrix);
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose: Sets the render color of all of our faces when our render color is set.
  584. //-----------------------------------------------------------------------------
  585. void CMapSolid::SetRenderColor(color32 rgbColor)
  586. {
  587. CMapClass::SetRenderColor(rgbColor);
  588. int nFaces = Faces.GetCount();
  589. for (int i = 0; i < nFaces; i++)
  590. {
  591. Faces[i].SetRenderColor(rgbColor);
  592. }
  593. }
  594. //-----------------------------------------------------------------------------
  595. // Purpose: Sets the render color of all of our faces when our render color is set.
  596. //-----------------------------------------------------------------------------
  597. void CMapSolid::SetRenderColor(unsigned char uchRed, unsigned char uchGreen, unsigned char uchBlue)
  598. {
  599. CMapClass::SetRenderColor(uchRed, uchGreen, uchBlue);
  600. int nFaces = Faces.GetCount();
  601. for (int i = 0; i < nFaces; i++)
  602. {
  603. Faces[i].SetRenderColor(uchRed, uchGreen, uchBlue);
  604. }
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Purpose:
  608. // Input :
  609. // Output : size_t
  610. //-----------------------------------------------------------------------------
  611. size_t CMapSolid::GetSize(void)
  612. {
  613. size_t size = CMapClass::GetSize();
  614. size += sizeof *this;
  615. int nFaces = Faces.GetCount();
  616. for( int i = 0; i < nFaces; i++ )
  617. {
  618. size += Faces[i].GetDataSize();
  619. }
  620. return size;
  621. }
  622. //-----------------------------------------------------------------------------
  623. // Purpose: Sets the texture for a given face.
  624. // Input : pszTex - Texture name.
  625. // iFace - Index of face for which to set texture.
  626. //-----------------------------------------------------------------------------
  627. void CMapSolid::SetTexture(LPCTSTR pszTex, int iFace)
  628. {
  629. if(iFace == -1)
  630. {
  631. int nFaces = Faces.GetCount();
  632. for(int i = 0 ; i < nFaces; i++)
  633. {
  634. Faces[i].SetTexture(pszTex);
  635. }
  636. }
  637. else
  638. {
  639. Faces[iFace].SetTexture(pszTex);
  640. }
  641. CMapDoc *pMapDoc = CMapDoc::GetActiveMapDoc();
  642. pMapDoc->RemoveFromAutoVisGroups( this );
  643. pMapDoc->AddToAutoVisGroup( this );
  644. }
  645. //-----------------------------------------------------------------------------
  646. // Purpose: Returns the texture name of a given face.
  647. // Input : iFace - Index of face. If -1, returns the texture of face 0.
  648. // Output : Returns the texture name.
  649. //-----------------------------------------------------------------------------
  650. LPCTSTR CMapSolid::GetTexture(int iFace)
  651. {
  652. return Faces[iFace == -1 ? 0 : iFace].texture.texture;
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose: Creates the solid using the plane information from the solid's faces.
  656. //
  657. // ASSUMPTIONS: This solid's faces are assumed to have valid plane points.
  658. //
  659. // Input : dwFlags - Can be any or none of the following flags:
  660. //
  661. // CREATE_BUILD_PLANE_POINTS - if this flag is set, the 3-point
  662. // definition of each face plane will be regenerated based
  663. // on the face points after the solid is generated.
  664. //
  665. // Output : Returns TRUE if the solid is valid, FALSE if not.
  666. //
  667. // dvs: this should really use the public API of CMapSolid to add faces so that
  668. // parentage and render color are set automatically.
  669. //-----------------------------------------------------------------------------
  670. int CMapSolid::CreateFromPlanes( DWORD dwFlags )
  671. {
  672. int i, j, k;
  673. CUtlVector<bool> useplane;
  674. m_Render2DBox.SetBounds(Vector(COORD_NOTINIT, COORD_NOTINIT, COORD_NOTINIT),
  675. Vector(-COORD_NOTINIT, -COORD_NOTINIT, -COORD_NOTINIT));
  676. m_bValid = TRUE;
  677. //
  678. // Free all points from all faces and assign parentage.
  679. //
  680. int nFaces = GetFaceCount();
  681. Assert( nFaces > 0 );
  682. useplane.SetCount( nFaces );
  683. for (i = 0; i < nFaces; i++)
  684. {
  685. CMapFace *pFace = GetFace(i);
  686. pFace->AllocatePoints(0);
  687. pFace->SetParent(this);
  688. pFace->SetRenderColor(r, g, b);
  689. useplane[i] = false;
  690. }
  691. //
  692. // For every face that is not set to be ignored, check the plane and make sure
  693. // it is unique. We mark each plane that we intend to keep with a TRUE in the
  694. // 'useplane' array.
  695. //
  696. for (i = 0; i < nFaces; i++)
  697. {
  698. CMapFace *pFace = GetFace(i);
  699. PLANE *f = &pFace->plane;
  700. //
  701. // Don't use this plane if it has a zero-length normal.
  702. //
  703. if (VectorCompare(f->normal, vec3_origin))
  704. {
  705. useplane[i] = false;
  706. continue;
  707. }
  708. //
  709. // If the plane duplicates another plane, don't use it (assume it is a brush
  710. // being edited that will be fixed).
  711. //
  712. useplane[i] = true;
  713. for (j = 0; j < i; j++)
  714. {
  715. CMapFace *pFaceCheck = GetFace(j);
  716. Vector& f1 = f->normal;
  717. Vector& f2 = pFaceCheck->plane.normal;
  718. //
  719. // Check for duplicate plane within some tolerance.
  720. //
  721. if ((DotProduct(f1, f2) > 0.999) && (fabs(f->dist - pFaceCheck->plane.dist) < 0.01))
  722. {
  723. useplane[j] = false;
  724. break;
  725. }
  726. }
  727. }
  728. //
  729. // Now we have a set of planes, indicated by TRUE values in the 'useplanes' array,
  730. // from which we will build a solid.
  731. //
  732. BOOL bGotFaces = FALSE;
  733. for (i = 0; i < nFaces; i++)
  734. {
  735. CMapFace *pFace = GetFace(i);
  736. if (!useplane[i])
  737. continue;
  738. //
  739. // Create a huge winding from this face's plane, then clip it by all other
  740. // face planes.
  741. //
  742. winding_t *w = CreateWindingFromPlane(&pFace->plane);
  743. for (j = 0; j < nFaces && w; j++)
  744. {
  745. CMapFace *pFaceClip = GetFace(j);
  746. //
  747. // Flip the plane, because we want to keep the back side
  748. //
  749. if (j != i)
  750. {
  751. PLANE plane;
  752. VectorSubtract(vec3_origin, pFaceClip->plane.normal, plane.normal);
  753. plane.dist = -pFaceClip->plane.dist;
  754. w = ClipWinding(w, &plane);
  755. }
  756. }
  757. //
  758. // If we still have a winding after all that clipping, build a face from
  759. // the winding.
  760. //
  761. if (w != NULL)
  762. {
  763. //
  764. // Round all points in the winding that are within ROUND_VERTEX_EPSILON of
  765. // integer values.
  766. //
  767. for (j = 0; j < w->numpoints; j++)
  768. {
  769. for (k = 0; k < 3; k++)
  770. {
  771. float v = w->p[j][k];
  772. float v1 = rint(v);
  773. if ((v != v1) && (fabs(v - v1) < ROUND_VERTEX_EPSILON))
  774. {
  775. w->p[j][k] = v1;
  776. }
  777. }
  778. }
  779. //
  780. // The above rounding process may have created duplicate points. Eliminate them.
  781. //
  782. RemoveDuplicateWindingPoints(w, MIN_EDGE_LENGTH_EPSILON);
  783. bGotFaces = TRUE;
  784. //
  785. // Create a face from this winding. Leave the face plane
  786. // alone because we are still in the process of building our solid.
  787. //
  788. if ( dwFlags & CREATE_FROM_PLANES_CLIPPING )
  789. {
  790. pFace->CreateFace( w, CREATE_FACE_PRESERVE_PLANE | CREATE_FACE_CLIPPING );
  791. }
  792. else
  793. {
  794. pFace->CreateFace(w, CREATE_FACE_PRESERVE_PLANE);
  795. }
  796. //
  797. // Done with the winding, we can free it now.
  798. //
  799. FreeWinding(w);
  800. }
  801. }
  802. if (!bGotFaces)
  803. {
  804. m_bValid = FALSE;
  805. m_Render2DBox.SetBounds(vec3_origin, vec3_origin);
  806. }
  807. else
  808. {
  809. //
  810. // Remove faces that don't contribute to this solid.
  811. //
  812. int nFace = GetFaceCount();
  813. while (nFace > 0)
  814. {
  815. nFace--;
  816. CMapFace *pFace = GetFace(nFace);
  817. if ((!useplane[nFace]) || (pFace->GetPointCount() == 0))
  818. {
  819. DeleteFace(nFace);
  820. useplane.Remove( nFace );
  821. }
  822. }
  823. }
  824. //
  825. // Now that we have built the faces from the planes that we were given,
  826. // calculate the plane normals, distances, and texture coordinates.
  827. //
  828. nFaces = GetFaceCount();
  829. for (i = 0; i < nFaces; i++)
  830. {
  831. CMapFace *pFace = GetFace(i);
  832. if (dwFlags & CREATE_BUILD_PLANE_POINTS)
  833. {
  834. pFace->CalcPlaneFromFacePoints();
  835. }
  836. else
  837. {
  838. pFace->CalcPlane();
  839. }
  840. pFace->CalcTextureCoords();
  841. //
  842. // Make sure the face is valid.
  843. //
  844. if (!pFace->CheckFace())
  845. {
  846. m_bValid = FALSE;
  847. }
  848. }
  849. //
  850. // remove faces that do not contribute -- not just "unused or ignored" faces
  851. //
  852. int faceCount = Faces.GetCount();
  853. for( i = 0; i < faceCount; i++ )
  854. {
  855. if( Faces[i].nPoints == 0 )
  856. {
  857. DeleteFace( i );
  858. i--;
  859. faceCount--;
  860. }
  861. }
  862. return(m_bValid ? TRUE : FALSE);
  863. }
  864. //-----------------------------------------------------------------------------
  865. // Purpose: Initializes the texture axes for all faces in the solid.
  866. // Input : eAlignment - See CMapFace::InitializeTextureAxes
  867. // dwFlags - See CMapFace::InitializeTextureAxes
  868. //-----------------------------------------------------------------------------
  869. void CMapSolid::InitializeTextureAxes(TextureAlignment_t eAlignment, DWORD dwFlags)
  870. {
  871. int nFaces = Faces.GetCount();
  872. for (int i = 0; i < nFaces; i++)
  873. {
  874. Faces[i].InitializeTextureAxes(eAlignment, dwFlags);
  875. }
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose:
  879. // Input : *pLoadInfo -
  880. // *pSolid -
  881. // Output : ChunkFileResult_t
  882. //-----------------------------------------------------------------------------
  883. ChunkFileResult_t CMapSolid::LoadSideCallback(CChunkFile *pFile, CMapSolid *pSolid)
  884. {
  885. ChunkFileResult_t eResult = ChunkFile_Ok;
  886. //
  887. // this is hear in place of the AddFace -- may want to handle this better later!!!
  888. //
  889. int faceCount = pSolid->Faces.GetCount();
  890. pSolid->Faces.SetCount( faceCount + 1 );
  891. CMapFace *pFace = &pSolid->Faces[faceCount];
  892. eResult = pFace->LoadVMF(pFile);
  893. if (eResult == ChunkFile_Ok)
  894. {
  895. pFace->SetRenderColor( pSolid->r, pSolid->g, pSolid->b );
  896. pFace->SetParent( pSolid );
  897. }
  898. else
  899. {
  900. // UNDONE: need a better solution for user errors.
  901. AfxMessageBox("Out of memory loading solid.");
  902. eResult = ChunkFile_OutOfMemory;
  903. }
  904. return(eResult);
  905. }
  906. //-----------------------------------------------------------------------------
  907. // Purpose:
  908. // Input : pFile -
  909. // pData -
  910. // Output : ChunkFileResult_t
  911. //-----------------------------------------------------------------------------
  912. ChunkFileResult_t CMapSolid::LoadVMF(CChunkFile *pFile, bool &bValid)
  913. {
  914. //
  915. // Set up handlers for the subchunks that we are interested in.
  916. //
  917. CChunkHandlerMap Handlers;
  918. Handlers.AddHandler("side", (ChunkHandler_t)LoadSideCallback, this);
  919. Handlers.AddHandler("editor", (ChunkHandler_t)LoadEditorCallback, this);
  920. pFile->PushHandlers(&Handlers);
  921. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEditorKeyCallback, this);
  922. pFile->PopHandlers();
  923. bValid = false;
  924. if (eResult == ChunkFile_Ok)
  925. {
  926. //
  927. // Create the solid using the planes that were read from the MAP file.
  928. //
  929. if (CreateFromPlanes())
  930. {
  931. bValid = true;
  932. CalcBounds();
  933. //
  934. // Set solid type based on texture name.
  935. //
  936. m_eSolidType = HL1SolidTypeFromTextureName(Faces[0].texture.texture);
  937. //
  938. // create all of the displacement surfaces for faces with the displacement property
  939. //
  940. int faceCount = GetFaceCount();
  941. for( int i = 0; i < faceCount; i++ )
  942. {
  943. CMapFace *pFace = GetFace( i );
  944. if( !pFace->HasDisp() )
  945. continue;
  946. EditDispHandle_t handle = pFace->GetDisp();
  947. CMapDisp *pMapDisp = EditDispMgr()->GetDisp( handle );
  948. pMapDisp->InitDispSurfaceData( pFace, false );
  949. pMapDisp->Create();
  950. pMapDisp->PostLoad();
  951. }
  952. // There once was a bug that caused black solids. Fix it here.
  953. if ((r == 0) && (g == 0) || (b == 0))
  954. {
  955. PickRandomColor();
  956. }
  957. }
  958. else
  959. {
  960. g_nBadSolidCount++;
  961. if ( g_nRecordedBadSolidCount < MAX_RECORDED_BAD_SOLIDS )
  962. {
  963. g_nRecordedBadSolidIds[g_nRecordedBadSolidCount++] = m_nID;
  964. }
  965. }
  966. }
  967. return(eResult);
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Purpose: Picks a random shade of blue/green for this solid.
  971. //-----------------------------------------------------------------------------
  972. void CMapSolid::PickRandomColor()
  973. {
  974. SetRenderColor(0, 100 + (random() % 156), 100 + (random() % 156));
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Purpose: Called before loading a map file.
  978. //-----------------------------------------------------------------------------
  979. void CMapSolid::PreloadWorld(void)
  980. {
  981. g_nBadSolidCount = 0;
  982. g_nRecordedBadSolidCount = 0;
  983. }
  984. //-----------------------------------------------------------------------------
  985. // Purpose: Returns the number of solids that could not be loaded due to errors
  986. // in the VMF file. This should only occur after the first load of an
  987. // old RMF file.
  988. //-----------------------------------------------------------------------------
  989. int CMapSolid::GetBadSolidCount(void)
  990. {
  991. return(g_nBadSolidCount);
  992. }
  993. //-----------------------------------------------------------------------------
  994. // Purpose: Returns the number of recorded solids that could not be loaded due to errors
  995. // in the VMF file.
  996. //-----------------------------------------------------------------------------
  997. int CMapSolid::GetRecordedBadSolidCount(void)
  998. {
  999. return(g_nRecordedBadSolidCount);
  1000. }
  1001. //-----------------------------------------------------------------------------
  1002. // Purpose: Returns an ID for a bad recorded solid.
  1003. //-----------------------------------------------------------------------------
  1004. int CMapSolid::GetBadSolidId( int i )
  1005. {
  1006. if ( i < 0 || i >= g_nRecordedBadSolidCount ) return -1;
  1007. return g_nRecordedBadSolidIds[i];
  1008. }
  1009. //-----------------------------------------------------------------------------
  1010. // Purpose: Called after this object is added to the world.
  1011. //
  1012. // NOTE: This function is NOT called during serialization. Use PostloadWorld
  1013. // to do similar bookkeeping after map load.
  1014. //
  1015. // Input : pWorld - The world that we have been added to.
  1016. //-----------------------------------------------------------------------------
  1017. void CMapSolid::OnAddToWorld(CMapWorld *pWorld)
  1018. {
  1019. CMapClass::OnAddToWorld(pWorld);
  1020. //
  1021. // First, the common case: all our face IDs are zero. Assign new IDs to all faces
  1022. // with zero IDs. Add unhandled faces to a list. Those we will need to check against
  1023. // the world for uniqueness.
  1024. //
  1025. CMapFaceList CheckList;
  1026. int nFaceCount = GetFaceCount();
  1027. for (int i = 0; i < nFaceCount; i++)
  1028. {
  1029. CMapFace *pFace = GetFace(i);
  1030. if (pFace->GetFaceID() == 0)
  1031. {
  1032. pFace->SetFaceID(pWorld->FaceID_GetNext());
  1033. }
  1034. else
  1035. {
  1036. CheckList.AddToTail(pFace);
  1037. }
  1038. }
  1039. if (CheckList.Count() > 0)
  1040. {
  1041. //
  1042. // The less common case: make sure all our face IDs are unique in this world.
  1043. // We do it here instead of in CMapFace in order to save world tree traversals.
  1044. //
  1045. EnumChildrenPos_t pos;
  1046. CMapClass *pChild = pWorld->GetFirstDescendent(pos);
  1047. while (pChild != NULL)
  1048. {
  1049. CMapSolid *pSolid = dynamic_cast<CMapSolid *>(pChild);
  1050. if ( pSolid && pSolid != this )
  1051. {
  1052. CUtlRBTree<int,int> faceIDs;
  1053. SetDefLessFunc( faceIDs );
  1054. int nFaceCount = GetFaceCount();
  1055. for (int nFace = 0; nFace < nFaceCount; nFace++)
  1056. {
  1057. CMapFace *pFace = GetFace(nFace);
  1058. faceIDs.Insert( pFace->GetFaceID() );
  1059. }
  1060. for (int i = CheckList.Count() - 1; i >= 0; i--)
  1061. {
  1062. CMapFace *pFace = CheckList.Element(i);
  1063. // If this face ID is not unique, assign it a new unique face ID
  1064. // and remove it from our list.
  1065. if ( faceIDs.Find( pFace->GetFaceID() ) != faceIDs.InvalidIndex() )
  1066. {
  1067. pFace->SetFaceID(pWorld->FaceID_GetNext());
  1068. CheckList.FastRemove(i);
  1069. }
  1070. }
  1071. if (CheckList.Count() <= 0)
  1072. {
  1073. // We've handled all the faces in our list, early out.
  1074. break;
  1075. }
  1076. }
  1077. pChild = pWorld->GetNextDescendent(pos);
  1078. }
  1079. }
  1080. //
  1081. // Notify all faces that we are being added to the world.
  1082. //
  1083. for (int i = 0; i < nFaceCount; i++)
  1084. {
  1085. CMapFace *pFace = GetFace(i);
  1086. pFace->OnAddToWorld(pWorld);
  1087. }
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose: Called after the entire map has been loaded. This allows the object
  1091. // to perform any linking with other map objects or to do other operations
  1092. // that require all world objects to be present.
  1093. // Input : pWorld - The world that we are in.
  1094. //-----------------------------------------------------------------------------
  1095. void CMapSolid::PostloadWorld(CMapWorld *pWorld)
  1096. {
  1097. CMapClass::PostloadWorld(pWorld);
  1098. //
  1099. // Make sure all our faces have nonzero IDs. They might if the map was created
  1100. // before unique IDs were added.
  1101. //
  1102. int nFaces = GetFaceCount();
  1103. for (int i = 0; i < nFaces; i++)
  1104. {
  1105. CMapFace *pFace = GetFace(i);
  1106. if (pFace->GetFaceID() == 0)
  1107. {
  1108. pFace->SetFaceID(pWorld->FaceID_GetNext());
  1109. }
  1110. }
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // Purpose:
  1114. // Input : eSelectMode -
  1115. // Output : CMapClass
  1116. //-----------------------------------------------------------------------------
  1117. CMapClass *CMapSolid::PrepareSelection(SelectMode_t eSelectMode)
  1118. {
  1119. //
  1120. // If we have a parent who is not the world object, consider whether we should
  1121. // select it instead.
  1122. //
  1123. if ((eSelectMode != selectSolids) && (m_pParent != NULL) && !IsWorldObject(m_pParent) )
  1124. {
  1125. //
  1126. // If we are in group selection mode or our parent is an entity, select our
  1127. // parent.
  1128. //
  1129. if ( (eSelectMode == selectGroups) || (dynamic_cast <CMapEntity *>(m_pParent) != NULL))
  1130. {
  1131. return GetParent()->PrepareSelection(eSelectMode);
  1132. }
  1133. }
  1134. return this;
  1135. }
  1136. //-----------------------------------------------------------------------------
  1137. // Purpose: Called just after this object has been removed from the world so
  1138. // that it can unlink itself from other objects in the world.
  1139. // Input : pWorld - The world that we were just removed from.
  1140. // bNotifyChildren - Whether we should forward notification to our children.
  1141. //-----------------------------------------------------------------------------
  1142. void CMapSolid::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren)
  1143. {
  1144. CMapClass::OnRemoveFromWorld(pWorld, bNotifyChildren);
  1145. //
  1146. // Notify all faces that we are being removed from the world.
  1147. //
  1148. int nFaces = GetFaceCount();
  1149. for (int i = 0; i < nFaces; i++)
  1150. {
  1151. CMapFace *pFace = GetFace(i);
  1152. pFace->OnRemoveFromWorld();
  1153. }
  1154. }
  1155. //-----------------------------------------------------------------------------
  1156. // Purpose:
  1157. //-----------------------------------------------------------------------------
  1158. void CMapSolid::RemoveEmptyFaces(void)
  1159. {
  1160. int nFaces = GetFaceCount();
  1161. for (int i = 0; i < nFaces; i++)
  1162. {
  1163. //
  1164. // If this face has no points, delete it.
  1165. //
  1166. const CMapFace *pFace = GetFace(i);
  1167. if (pFace->Points == NULL)
  1168. {
  1169. DeleteFace(i);
  1170. i--;
  1171. nFaces--;
  1172. }
  1173. }
  1174. if (nFaces >= 4)
  1175. {
  1176. // dvs: test to verify that the SetFaceCount below is unnecessary
  1177. int nTest = GetFaceCount();
  1178. Assert(nTest == nFaces);
  1179. SetFaceCount(nFaces);
  1180. }
  1181. }
  1182. //-----------------------------------------------------------------------------
  1183. // for sorting
  1184. //-----------------------------------------------------------------------------
  1185. bool CMapSolid::ShouldRenderLast()
  1186. {
  1187. for (int nFace = 0; nFace < GetFaceCount(); nFace++)
  1188. {
  1189. CMapFace *pFace = GetFace(nFace);
  1190. if (pFace->ShouldRenderLast())
  1191. return true;
  1192. }
  1193. return false;
  1194. }
  1195. void CMapSolid::AddShadowingTriangles( CUtlVector<Vector> &tri_list )
  1196. {
  1197. for (int nFace = 0; nFace < GetFaceCount(); nFace++)
  1198. {
  1199. CMapFace *pFace = GetFace(nFace);
  1200. pFace->AddShadowingTriangles( tri_list );
  1201. if( pFace->HasDisp() )
  1202. {
  1203. EditDispHandle_t handle = pFace->GetDisp();
  1204. CMapDisp *pMapDisp = EditDispMgr()->GetDisp( handle );
  1205. pMapDisp->AddShadowingTriangles( tri_list );
  1206. }
  1207. }
  1208. }
  1209. //-----------------------------------------------------------------------------
  1210. // Purpose: Renders the solid using the default render mode. If the solid is
  1211. // currently selected, it will be rendered with a yellow wireframe
  1212. // in a second pass.
  1213. // Input : pRender - Rendering interface.
  1214. //-----------------------------------------------------------------------------
  1215. void CMapSolid::Render3D(CRender3D *pRender)
  1216. {
  1217. //
  1218. // determine whether or not this is a displacement solid - i.e. one of the faces
  1219. // on this solid is displaced
  1220. //
  1221. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1222. if( !pDoc )
  1223. return;
  1224. bool bMaskFaces = pDoc->IsDispSolidDrawMask() && HasDisp();
  1225. //
  1226. // Determine whether we need to render in one or two passes. If we are selected,
  1227. // and rendering in flat or textured mode, we need to render using two passes.
  1228. //
  1229. int nPasses = 1;
  1230. int iStartPass = 1;
  1231. SelectionState_t eSolidSelectionState = GetSelectionState();
  1232. EditorRenderMode_t eDefaultRenderMode = pRender->GetDefaultRenderMode();
  1233. if ((eSolidSelectionState != SELECT_NONE) && (eDefaultRenderMode != RENDER_MODE_WIREFRAME))
  1234. {
  1235. nPasses = 2;
  1236. }
  1237. if ( ( eSolidSelectionState == SELECT_MODIFY ) )
  1238. {
  1239. nPasses = 2;
  1240. iStartPass = 2;
  1241. }
  1242. for (int nPass = iStartPass; nPass <= nPasses; nPass++)
  1243. {
  1244. //
  1245. // Render the second pass in wireframe.
  1246. //
  1247. if (nPass == 1)
  1248. {
  1249. pRender->PushRenderMode(RENDER_MODE_CURRENT);
  1250. }
  1251. else
  1252. {
  1253. pRender->PushRenderMode(RENDER_MODE_WIREFRAME);
  1254. }
  1255. for (int nFace = 0; nFace < GetFaceCount(); nFace++)
  1256. {
  1257. CMapFace *pFace = GetFace(nFace);
  1258. // only render displaced faces on a displaced solid when the displacement
  1259. // solid render mask is set
  1260. if( bMaskFaces && !pFace->HasDisp() )
  1261. continue;
  1262. if( pRender->IsInLightingPreview() )
  1263. {
  1264. if( nPass == 1 )
  1265. {
  1266. if( pFace->GetSelectionState() != SELECT_NONE )
  1267. {
  1268. pRender->BeginRenderHitTarget(this, nFace);
  1269. pFace->Render3D( pRender );
  1270. pRender->EndRenderHitTarget();
  1271. }
  1272. }
  1273. else
  1274. {
  1275. pFace->Render3D( pRender );
  1276. }
  1277. }
  1278. else
  1279. {
  1280. pRender->BeginRenderHitTarget(this, nFace);
  1281. pFace->Render3D( pRender );
  1282. pRender->EndRenderHitTarget();
  1283. }
  1284. }
  1285. pRender->PopRenderMode();
  1286. }
  1287. }
  1288. //-----------------------------------------------------------------------------
  1289. //-----------------------------------------------------------------------------
  1290. bool CMapSolid::HasDisp( void )
  1291. {
  1292. for( int ndxFace = 0; ndxFace < GetFaceCount(); ndxFace++ )
  1293. {
  1294. CMapFace *pFace = GetFace( ndxFace );
  1295. if( pFace->HasDisp() )
  1296. return true;
  1297. }
  1298. return false;
  1299. }
  1300. //-----------------------------------------------------------------------------
  1301. // Purpose: Returns a solid type for the given texture name.
  1302. // Input : pszTexture -
  1303. //-----------------------------------------------------------------------------
  1304. HL1_SolidType_t CMapSolid::HL1SolidTypeFromTextureName(const char *pszTexture)
  1305. {
  1306. HL1_SolidType_t eSolidType;
  1307. if (pszTexture[0] == '*')
  1308. {
  1309. if (!strncmp(pszTexture + 1, "slime", 5))
  1310. {
  1311. eSolidType = btSlime;
  1312. }
  1313. else if (!strncmp(pszTexture + 1, "lava", 4))
  1314. {
  1315. eSolidType = btLava;
  1316. }
  1317. else
  1318. {
  1319. eSolidType = btWater;
  1320. }
  1321. }
  1322. else
  1323. {
  1324. eSolidType = btSolid;
  1325. }
  1326. return(eSolidType);
  1327. }
  1328. //-----------------------------------------------------------------------------
  1329. // Purpose:
  1330. // Input : *pFile -
  1331. // Output : ChunkFileResult_t
  1332. //-----------------------------------------------------------------------------
  1333. ChunkFileResult_t CMapSolid::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
  1334. {
  1335. //
  1336. // Check rules before saving this object.
  1337. //
  1338. if (!pSaveInfo->ShouldSaveObject(this))
  1339. {
  1340. return(ChunkFile_Ok);
  1341. }
  1342. ChunkFileResult_t eResult = ChunkFile_Ok;
  1343. //
  1344. // If we are hidden, place this object inside of a hidden chunk.
  1345. //
  1346. if (!IsVisible())
  1347. {
  1348. eResult = pFile->BeginChunk("hidden");
  1349. }
  1350. //
  1351. // Begin the solid chunk.
  1352. //
  1353. if (eResult == ChunkFile_Ok)
  1354. {
  1355. eResult = pFile->BeginChunk("solid");
  1356. }
  1357. if (eResult == ChunkFile_Ok)
  1358. {
  1359. //
  1360. // Save the solid's ID.
  1361. //
  1362. if (eResult == ChunkFile_Ok)
  1363. {
  1364. eResult = pFile->WriteKeyValueInt("id", GetID());
  1365. }
  1366. //
  1367. // Save all the brush faces.
  1368. //
  1369. int nFaceCount = GetFaceCount();
  1370. for (int nFace = 0; nFace < nFaceCount; nFace++)
  1371. {
  1372. CMapFace *pFace = GetFace(nFace);
  1373. eResult = pFace->SaveVMF(pFile, pSaveInfo);
  1374. if (eResult != ChunkFile_Ok)
  1375. {
  1376. break;
  1377. }
  1378. }
  1379. //
  1380. // Save our base class' information within our chunk.
  1381. //
  1382. if (eResult == ChunkFile_Ok)
  1383. {
  1384. eResult = CMapClass::SaveVMF(pFile, pSaveInfo);
  1385. }
  1386. if (eResult == ChunkFile_Ok)
  1387. {
  1388. eResult = pFile->EndChunk();
  1389. }
  1390. }
  1391. //
  1392. // End the hidden chunk if we began it.
  1393. //
  1394. if (!IsVisible())
  1395. {
  1396. eResult = pFile->EndChunk();
  1397. }
  1398. return(eResult);
  1399. }
  1400. bool CMapSolid::ShouldAppearInLightingPreview(void)
  1401. {
  1402. return true;
  1403. }
  1404. bool CMapSolid::ShouldAppearInRaytracedLightingPreview(void)
  1405. {
  1406. return true;
  1407. }
  1408. bool CMapSolid::ShouldAppearOverEngine(void)
  1409. {
  1410. return false;
  1411. }
  1412. //-----------------------------------------------------------------------------
  1413. // Purpose:
  1414. // Input : *pFile -
  1415. // Output : ChunkFileResult_t
  1416. //-----------------------------------------------------------------------------
  1417. ChunkFileResult_t CMapSolid::SaveEditorData(CChunkFile *pFile)
  1418. {
  1419. if (m_bIsCordonBrush)
  1420. {
  1421. return(pFile->WriteKeyValueBool("cordonsolid", true));
  1422. }
  1423. return(ChunkFile_Ok);
  1424. }
  1425. //-----------------------------------------------------------------------------
  1426. // Purpose: Sets whether this brush was created by the cordon tool. Brushes that
  1427. // were created by the cordon tool are not loaded.
  1428. // Input : bSet - true to set, false to clear.
  1429. //-----------------------------------------------------------------------------
  1430. void CMapSolid::SetCordonBrush(bool bSet)
  1431. {
  1432. m_bIsCordonBrush = bSet;
  1433. for ( int i = 0; i < GetFaceCount(); i++ )
  1434. {
  1435. CMapFace *pFace = GetFace( i );
  1436. pFace->SetCordonFace( bSet );
  1437. }
  1438. }
  1439. //-----------------------------------------------------------------------------
  1440. // Subtracts geometry from this solid.
  1441. // pSubtractWith - Solid (or group of solids) to subtract with.
  1442. // pInside - Unless NULL, receives the list of solids inside the subtraction (swallowed).
  1443. // pOutside - Unless NULL, receives the list of solids outside the subtraction (remainder).
  1444. // Returns true if the objects intersected (subtraction was performed),
  1445. // false if the objects did not intersect (no subtraction was performed).
  1446. //-----------------------------------------------------------------------------
  1447. bool CMapSolid::Subtract(CMapObjectList *pInside, CMapObjectList *pOutside, CMapClass *pSubtractWith)
  1448. {
  1449. //
  1450. // Build a list of solids to subtract with.
  1451. //
  1452. CMapObjectList SubList;
  1453. if (pSubtractWith->IsMapClass(MAPCLASS_TYPE(CMapSolid)))
  1454. {
  1455. SubList.AddToTail(pSubtractWith);
  1456. }
  1457. EnumChildrenPos_t pos;
  1458. CMapClass *pChild = pSubtractWith->GetFirstDescendent(pos);
  1459. while (pChild != NULL)
  1460. {
  1461. CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pChild);
  1462. if (pSolid != NULL)
  1463. {
  1464. SubList.AddToTail(pSolid);
  1465. }
  1466. pChild = pSubtractWith->GetNextDescendent(pos);
  1467. }
  1468. //
  1469. // For every solid that we are subtracting with...
  1470. //
  1471. bool bIntersected = false;
  1472. FOR_EACH_OBJ( SubList, p )
  1473. {
  1474. CMapClass *pMapClass = (CUtlReference< CMapClass >)SubList.Element(p);
  1475. CMapSolid *pCarver = (CMapSolid *)pMapClass;
  1476. //
  1477. // Subtract the 'with' solid from the 'from' solid, and place the
  1478. // results in the carve_in and carve_out lists.
  1479. //
  1480. CMapObjectList carve_in;
  1481. CMapObjectList carve_out;
  1482. CMapObjectList *pCarveIn = NULL;
  1483. CMapObjectList *pCarveOut = NULL;
  1484. if (pInside != NULL)
  1485. {
  1486. pCarveIn = &carve_in;
  1487. }
  1488. if (pOutside != NULL)
  1489. {
  1490. pCarveOut = &carve_out;
  1491. }
  1492. bIntersected |= Carve(pCarveIn, pCarveOut, pCarver);
  1493. if (pInside != NULL)
  1494. {
  1495. pInside->AddVectorToTail(carve_in);
  1496. carve_in.RemoveAll();
  1497. }
  1498. if (pOutside != NULL)
  1499. {
  1500. pOutside->AddVectorToTail(carve_out);
  1501. carve_out.RemoveAll();
  1502. }
  1503. }
  1504. return(bIntersected);
  1505. }
  1506. //-----------------------------------------------------------------------------
  1507. // Purpose:
  1508. //-----------------------------------------------------------------------------
  1509. color32 CMapSolid::GetLineColor( CRender2D *pRender )
  1510. {
  1511. //
  1512. // If the solid is not selected, determine the appropriate pen color.
  1513. //
  1514. if ( !IsSelected() )
  1515. {
  1516. //
  1517. // If this is a solid entity, use the entity pen color.
  1518. //
  1519. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(GetParent());
  1520. if (pEntity != NULL)
  1521. {
  1522. GDclass *pClass = pEntity->GetClass();
  1523. if (pClass)
  1524. {
  1525. return pClass->GetColor();
  1526. }
  1527. else
  1528. {
  1529. color32 clr;
  1530. clr.r = GetRValue(Options.colors.clrEntity);
  1531. clr.g = GetGValue(Options.colors.clrEntity);
  1532. clr.b = GetBValue(Options.colors.clrEntity);
  1533. clr.a = 255;
  1534. return clr;
  1535. }
  1536. }
  1537. //
  1538. // Otherwise, use the solid color.
  1539. //
  1540. else
  1541. {
  1542. if (Options.view2d.bUsegroupcolors)
  1543. {
  1544. return GetRenderColor();
  1545. }
  1546. else
  1547. {
  1548. color32 clr;
  1549. clr.r = GetRValue(Options.colors.clrBrush);
  1550. clr.g = GetGValue(Options.colors.clrBrush);
  1551. clr.b = GetBValue(Options.colors.clrBrush);
  1552. clr.a = 255;
  1553. return clr;
  1554. }
  1555. }
  1556. }
  1557. //
  1558. // The solid is selected, use the selected pen color.
  1559. //
  1560. else
  1561. {
  1562. color32 clr;
  1563. clr.r = GetRValue(Options.colors.clrSelection);
  1564. clr.g = GetGValue(Options.colors.clrSelection);
  1565. clr.b = GetBValue(Options.colors.clrSelection);
  1566. clr.a = 255;
  1567. return clr;
  1568. }
  1569. }
  1570. //-----------------------------------------------------------------------------
  1571. // Purpose:
  1572. // Input : pRender -
  1573. //-----------------------------------------------------------------------------
  1574. void CMapSolid::Render2D(CRender2D *pRender)
  1575. {
  1576. Vector vecMins, vecMaxs, vViewNormal;
  1577. GetRender2DBox(vecMins, vecMaxs);
  1578. pRender->GetCamera()->GetViewForward( vViewNormal );
  1579. Vector2D pt, pt2;
  1580. pRender->TransformPoint(pt, vecMins);
  1581. pRender->TransformPoint(pt2, vecMaxs);
  1582. int sizex = abs(pt2.x-pt.x)+1;
  1583. int sizey = abs(pt2.y-pt.y)+1;
  1584. color32 rgbLineColor = GetLineColor( pRender );
  1585. // check if we should draw handles & vertices
  1586. bool bIsSmall = sizex < (HANDLE_RADIUS*2) || sizey < (HANDLE_RADIUS*2);
  1587. bool bIsTiny = sizex < 2 || sizey < 2;
  1588. bool bDrawHandles = pRender->IsActiveView() && !bIsSmall && IsEditable();
  1589. bool bDrawVertices = Options.view2d.bDrawVertices && !bIsTiny;
  1590. pRender->SetDrawColor( rgbLineColor.r, rgbLineColor.g, rgbLineColor.b );
  1591. //
  1592. // Draw center handle if the solid is larger than the handle along either axis.
  1593. //
  1594. if ( bDrawHandles )
  1595. {
  1596. // draw center handle as cross
  1597. pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_CROSS );
  1598. pRender->SetHandleColor( rgbLineColor.r, rgbLineColor.g, rgbLineColor.b );
  1599. pRender->DrawHandle( (vecMins+vecMaxs)/2 );
  1600. }
  1601. if ( bDrawVertices )
  1602. {
  1603. // set handle style for upcoming vertex drawing
  1604. pRender->SetHandleStyle( 2, CRender::HANDLE_SQUARE );
  1605. pRender->SetHandleColor( GetRValue(Options.colors.clrVertex), GetGValue(Options.colors.clrVertex), GetBValue(Options.colors.clrVertex) );
  1606. }
  1607. // is solid projection is too small, draw simple line
  1608. if ( bIsTiny )
  1609. {
  1610. pRender->DrawLine( vecMins, vecMaxs );
  1611. }
  1612. else
  1613. {
  1614. int nFaces = GetFaceCount();
  1615. for ( int i = 0; i < nFaces; i++)
  1616. {
  1617. CMapFace *pFace = GetFace(i);
  1618. pFace->Render2D( pRender );
  1619. }
  1620. if ( bDrawVertices )
  1621. {
  1622. bool bPop = pRender->BeginClientSpace();
  1623. for ( int i = 0; i < nFaces; i++)
  1624. {
  1625. CMapFace *pFace = GetFace(i);
  1626. pFace->RenderVertices( pRender );
  1627. }
  1628. if ( bPop )
  1629. pRender->EndClientSpace();
  1630. }
  1631. }
  1632. }
  1633. //-----------------------------------------------------------------------------
  1634. // Purpose:
  1635. // Input : pView -
  1636. // vecPoint -
  1637. // nHitData -
  1638. // Output :
  1639. //-----------------------------------------------------------------------------
  1640. bool CMapSolid::HitTest2D(CMapView2D *pView, const Vector2D &point, HitInfo_t &HitData)
  1641. {
  1642. if (!IsVisible())
  1643. return false;
  1644. //
  1645. // First check center X.
  1646. //
  1647. Vector vecCenter, vecViewPoint;
  1648. GetBoundsCenter(vecCenter);
  1649. Vector2D vecClientCenter;
  1650. pView->WorldToClient(vecClientCenter, vecCenter);
  1651. pView->GetCamera()->GetViewPoint( vecViewPoint );
  1652. HitData.pObject = this;
  1653. HitData.nDepth = vecViewPoint[pView->axThird]-vecCenter[pView->axThird];
  1654. HitData.uData = 0;
  1655. if (pView->CheckDistance(point, vecClientCenter, HANDLE_RADIUS))
  1656. {
  1657. return true;
  1658. }
  1659. else if (!Options.view2d.bSelectbyhandles || !IsEditable() )
  1660. {
  1661. //
  1662. // See if any edges are within certain distance from the the point.
  1663. //
  1664. int iSelUnits = 2;
  1665. int x1 = point.x - iSelUnits;
  1666. int x2 = point.x + iSelUnits;
  1667. int y1 = point.y - iSelUnits;
  1668. int y2 = point.y + iSelUnits;
  1669. int nFaces = GetFaceCount();
  1670. for (int i = 0; i < nFaces; i++)
  1671. {
  1672. CMapFace *pFace = GetFace(i);
  1673. int nPoints = pFace->nPoints;
  1674. if (nPoints > 0)
  1675. {
  1676. Vector *pPoints = pFace->Points;
  1677. Vector2D vec1;
  1678. pView->WorldToClient(vec1, pPoints[0]);
  1679. for (int j = 1; j < nPoints; j++)
  1680. {
  1681. Vector2D vec2;
  1682. pView->WorldToClient(vec2, pPoints[j]);
  1683. if (IsLineInside(vec1, vec2, x1, y1, x2, y2))
  1684. {
  1685. return true;
  1686. }
  1687. else
  1688. {
  1689. vec1 = vec2;
  1690. }
  1691. }
  1692. }
  1693. }
  1694. }
  1695. HitData.pObject = NULL;
  1696. return false;
  1697. }
  1698. bool CMapSolid::SaveDXF(ExportDXFInfo_s *pInfo)
  1699. {
  1700. if (pInfo->bVisOnly)
  1701. {
  1702. if (!IsVisible())
  1703. {
  1704. return true;
  1705. }
  1706. }
  1707. CSSolid *pStrucSolid = new CSSolid;
  1708. pStrucSolid->Attach(this);
  1709. pStrucSolid->Convert( true, true );
  1710. pStrucSolid->SerializeDXF(pInfo->fp, pInfo->nObject++);
  1711. delete pStrucSolid;
  1712. // Serialize displacements
  1713. for (int i = 0; i < GetFaceCount(); ++i)
  1714. {
  1715. CMapFace *pMapFace = GetFace( i );
  1716. if (pMapFace->HasDisp())
  1717. {
  1718. EditDispHandle_t hDisp = pMapFace->GetDisp();
  1719. CMapDisp *pDisp = EditDispMgr()->GetDisp( hDisp );
  1720. if (!pDisp->SaveDXF( pInfo ))
  1721. return FALSE;
  1722. }
  1723. }
  1724. return TRUE;
  1725. }
  1726. //-----------------------------------------------------------------------------
  1727. // Called any time this object is modified by Undo or Redo.
  1728. //-----------------------------------------------------------------------------
  1729. void CMapSolid::OnUndoRedo()
  1730. {
  1731. int nFaces = GetFaceCount();
  1732. for (int i = 0; i < nFaces; i++)
  1733. {
  1734. CMapFace *pFace = GetFace(i);
  1735. pFace->OnUndoRedo();
  1736. }
  1737. }