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

2010 lines
50 KiB

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