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.

624 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements a decal helper. The decal attaches itself to nearby
  4. // solids, dynamically creating decal faces as necessary.
  5. //
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "ClipCode.h"
  9. #include "MapDoc.h"
  10. #include "MapDecal.h"
  11. #include "MapFace.h"
  12. #include "MapSolid.h"
  13. #include "MapWorld.h"
  14. #include "Render3D.h"
  15. #include "TextureSystem.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include <tier0/memdbgon.h>
  18. IMPLEMENT_MAPCLASS(CMapDecal)
  19. //-----------------------------------------------------------------------------
  20. // Purpose: Factory function. Used for creating a CMapDecal from a set
  21. // of string parameters from the FGD file.
  22. // Input : pInfo - Pointer to helper info class which gives us information
  23. // about how to create the class.
  24. // Output : Returns a pointer to the class, NULL if an error occurs.
  25. //-----------------------------------------------------------------------------
  26. CMapClass *CMapDecal::CreateMapDecal(CHelperInfo *pHelperInfo, CMapEntity *pParent)
  27. {
  28. CMapDecal *pDecal = new CMapDecal;
  29. return(pDecal);
  30. }
  31. //-----------------------------------------------------------------------------
  32. // Purpose: Constructor. Initializes data members.
  33. //-----------------------------------------------------------------------------
  34. CMapDecal::CMapDecal(void)
  35. {
  36. m_pTexture = NULL;
  37. }
  38. //-----------------------------------------------------------------------------
  39. // Purpose: Destructor. Frees allocated memory.
  40. //-----------------------------------------------------------------------------
  41. CMapDecal::~CMapDecal(void)
  42. {
  43. // Delete our list of faces and each face in the list.
  44. FOR_EACH_OBJ( m_Faces, pos )
  45. {
  46. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  47. delete pDecalFace->pFace;
  48. delete pDecalFace;
  49. }
  50. }
  51. //-----------------------------------------------------------------------------
  52. // Purpose:
  53. // Input : pSolid -
  54. //-----------------------------------------------------------------------------
  55. void CMapDecal::AddSolid(CMapSolid *pSolid)
  56. {
  57. if ( m_Solids.Find(pSolid) == -1 )
  58. {
  59. UpdateDependency(NULL, pSolid);
  60. m_Solids.AddToTail(pSolid);
  61. }
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose:
  65. // Input : bFullUpdate -
  66. //-----------------------------------------------------------------------------
  67. void CMapDecal::CalcBounds(BOOL bFullUpdate)
  68. {
  69. CMapClass::CalcBounds(bFullUpdate);
  70. //
  71. // Calculate the 2D render box.
  72. //
  73. Vector Mins = m_Origin - Vector(2, 2, 2);
  74. Vector Maxs = m_Origin + Vector(2, 2, 2);
  75. m_Render2DBox.UpdateBounds(Mins, Maxs);
  76. //
  77. // Calculate the 3D culling bounds.
  78. //
  79. m_CullBox.ResetBounds();
  80. if (m_Faces.Count() > 0)
  81. {
  82. Vector MinsFace;
  83. Vector MaxsFace;
  84. FOR_EACH_OBJ( m_Faces, pos )
  85. {
  86. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  87. if ((pDecalFace != NULL) && (pDecalFace->pFace != NULL))
  88. {
  89. pDecalFace->pFace->GetFaceBounds( MinsFace, MaxsFace );
  90. m_CullBox.UpdateBounds( MinsFace, MaxsFace );
  91. }
  92. }
  93. //
  94. // Insure that the 3D bounds are at least 1 unit in all dimensions.
  95. //
  96. for (int nDim = 0; nDim < 3; nDim++)
  97. {
  98. if ((m_CullBox.bmaxs[nDim] - m_CullBox.bmins[nDim]) == 0)
  99. {
  100. m_CullBox.bmins[nDim] -= 0.5;
  101. m_CullBox.bmaxs[nDim] += 0.5;
  102. }
  103. }
  104. }
  105. else
  106. {
  107. m_CullBox.UpdateBounds(Mins, Maxs);
  108. }
  109. m_BoundingBox = m_CullBox;
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Determines whether we can attach to this solid by looking at the
  113. // normal distance to the solid face. We still may not lie within the
  114. // face; that is determined by the clipping code.
  115. // Input : pSolid - Solid to check.
  116. // ppFaces - Returns with pointers to the faces that are eligible.
  117. // Output : Returns the number of faces that were eligible.
  118. //-----------------------------------------------------------------------------
  119. int CMapDecal::CanDecalSolid(CMapSolid *pSolid, CMapFace **ppFaces)
  120. {
  121. //
  122. // Check distance from our origin to each face along the face's normal.
  123. // If the distance is very very small, add the face.
  124. //
  125. int nDecalFaces = 0;
  126. int nFaces = pSolid->GetFaceCount();
  127. Assert(nFaces <= MAPSOLID_MAX_FACES);
  128. for (int i = 0; i < nFaces; i++)
  129. {
  130. CMapFace *pFace = pSolid->GetFace(i);
  131. float fDistance = pFace->GetNormalDistance(m_Origin);
  132. if ((fDistance <= 16.0f) && (fDistance >= -0.0001))
  133. {
  134. if (ppFaces != NULL)
  135. {
  136. ppFaces[nDecalFaces] = pFace;
  137. }
  138. nDecalFaces++;
  139. }
  140. }
  141. return(nDecalFaces);
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. // Output : CMapClass
  146. //-----------------------------------------------------------------------------
  147. CMapClass *CMapDecal::Copy(bool bUpdateDependencies)
  148. {
  149. CMapDecal *pCopy = new CMapDecal;
  150. if (pCopy != NULL)
  151. {
  152. pCopy->CopyFrom(this, bUpdateDependencies);
  153. }
  154. return(pCopy);
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose: Makes this object identical to the given object.
  158. // Input : pObject - Object to copy.
  159. // bUpdateDependencies -
  160. //-----------------------------------------------------------------------------
  161. CMapClass *CMapDecal::CopyFrom(CMapClass *pObject, bool bUpdateDependencies)
  162. {
  163. Assert(pObject->IsMapClass(MAPCLASS_TYPE(CMapDecal)));
  164. CMapDecal *pFrom = (CMapDecal *)pObject;
  165. CMapClass::CopyFrom(pObject, bUpdateDependencies);
  166. m_pTexture = pFrom->m_pTexture;
  167. //
  168. // Copy our list of solids to which we are attached.
  169. //
  170. m_Solids.RemoveAll();
  171. m_Solids.AddVectorToTail(pFrom->m_Solids);
  172. //
  173. // Copy our decal faces. We don't copy the pointers because we don't do
  174. // reference counting yet.
  175. //
  176. m_Faces.RemoveAll();
  177. FOR_EACH_OBJ( pFrom->m_Faces, pos )
  178. {
  179. DecalFace_t *pDecalFace = new DecalFace_t;
  180. if (pDecalFace != NULL)
  181. {
  182. pDecalFace->pFace = new CMapFace;
  183. if (pDecalFace->pFace != NULL)
  184. {
  185. DecalFace_t *pFromDecalFace = pFrom->m_Faces.Element(pos);
  186. pDecalFace->pFace->CopyFrom(pFromDecalFace->pFace);
  187. pDecalFace->pFace->SetParent(this);
  188. pDecalFace->pSolid = pFromDecalFace->pSolid;
  189. m_Faces.AddToTail(pDecalFace);
  190. }
  191. }
  192. }
  193. return(this);
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. // Input : pSolid -
  198. // org -
  199. // piFacesRvl -
  200. // Output : int
  201. //-----------------------------------------------------------------------------
  202. int CMapDecal::DecalSolid(CMapSolid *pSolid)
  203. {
  204. if (m_pTexture == NULL)
  205. {
  206. return(0);
  207. }
  208. //
  209. // Determine how many, if any, faces will accept the decal.
  210. //
  211. CMapFace *ppFaces[MAPSOLID_MAX_FACES];
  212. int nDecalFaces = 0;
  213. int nTestFaces = CanDecalSolid(pSolid, ppFaces);
  214. if (nTestFaces != 0)
  215. {
  216. //
  217. // Apply the decal to each face that will accept it.
  218. //
  219. for (int nFace = 0; nFace < nTestFaces; nFace++)
  220. {
  221. CMapFace *pFace = ppFaces[nFace];
  222. //
  223. // Create the polygon, clipping it to this face.
  224. //
  225. vec5_t ClipPoints[MAX_CLIPVERT];
  226. int nPointCount = CreateClippedPoly(pFace, m_pTexture, m_Origin, ClipPoints, sizeof(ClipPoints) / sizeof(ClipPoints[0]));
  227. if (nPointCount != 0)
  228. {
  229. nDecalFaces++;
  230. Vector CreatePoints[64];
  231. for (int nPoint = 0; nPoint < nPointCount; nPoint++)
  232. {
  233. CreatePoints[nPoint][0] = ClipPoints[nPoint][0];
  234. CreatePoints[nPoint][1] = ClipPoints[nPoint][1];
  235. CreatePoints[nPoint][2] = ClipPoints[nPoint][2];
  236. }
  237. //
  238. // Create the decal face from the polygon.
  239. //
  240. DecalFace_t *pDecalFace = new DecalFace_t;
  241. pDecalFace->pFace = new CMapFace;
  242. pDecalFace->pFace->CreateFace(CreatePoints, nPointCount);
  243. pDecalFace->pFace->SetRenderColor(255, 255, 255);
  244. pDecalFace->pFace->SetParent(this);
  245. //
  246. // Associate this decal face with the solid.
  247. //
  248. pDecalFace->pSolid = pSolid;
  249. //
  250. // Set the texture in the decal face.
  251. //
  252. pDecalFace->pFace->SetTexture(m_pTexture);
  253. pDecalFace->pFace->CalcTextureCoords();
  254. for (int nPoint = 0; nPoint < nPointCount; nPoint++)
  255. {
  256. pDecalFace->pFace->SetTextureCoords(nPoint, ClipPoints[nPoint][3], ClipPoints[nPoint][4]);
  257. }
  258. m_Faces.AddToTail(pDecalFace);
  259. break;
  260. }
  261. }
  262. }
  263. return(nDecalFaces);
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose: Notifies that this object's parent entity has had a key value change.
  267. // Input : szKey - The key that changed.
  268. // szValue - The new value of the key.
  269. //-----------------------------------------------------------------------------
  270. void CMapDecal::OnParentKeyChanged(const char* szKey, const char* szValue)
  271. {
  272. //
  273. // The decal texture has changed.
  274. //
  275. if (!stricmp(szKey, "texture"))
  276. {
  277. IEditorTexture *pTexNew = g_Textures.FindActiveTexture(szValue);
  278. if (pTexNew != NULL)
  279. {
  280. m_pTexture = pTexNew;
  281. //
  282. // Rebuild all the decal faces with the new texture by pretending
  283. // that all the solids we are attached to have changed.
  284. //
  285. FOR_EACH_OBJ( m_Solids, pos )
  286. {
  287. CMapSolid *pSolid = (CMapSolid *)m_Solids.Element(pos);
  288. if (pSolid != NULL)
  289. {
  290. OnNotifyDependent(pSolid, Notify_Changed);
  291. }
  292. }
  293. }
  294. }
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose: This function enumerates all children of the world and tries to
  298. // apply the decal to them.
  299. //-----------------------------------------------------------------------------
  300. void CMapDecal::DecalAllSolids(CMapWorld *pWorld)
  301. {
  302. Assert(pWorld != NULL);
  303. if (pWorld != NULL)
  304. {
  305. //
  306. // Try to apply the decal to every solid in the world.
  307. //
  308. EnumChildrenPos_t pos;
  309. CMapClass *pChild = pWorld->GetFirstDescendent(pos);
  310. while (pChild != NULL)
  311. {
  312. CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pChild);
  313. if ((pSolid != NULL) && (DecalSolid(pSolid) != 0))
  314. {
  315. AddSolid(pSolid);
  316. }
  317. pChild = pWorld->GetNextDescendent(pos);
  318. }
  319. PostUpdate(Notify_Changed);
  320. }
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Notifys this decal of a change to a solid that it is attached to.
  324. // Input : pSolid - The solid that is changing.
  325. // bSolidDeleted - whether the solid is being deleted.
  326. // Output : Returns true if the decal is still attached to the solid, false if not.
  327. //-----------------------------------------------------------------------------
  328. void CMapDecal::OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType)
  329. {
  330. CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pObject);
  331. if (pSolid != NULL)
  332. {
  333. //
  334. // Delete any decal faces that are attached to this solid. They will be
  335. // rebuilt if we can still decal the solid.
  336. //
  337. for( int pos = m_Faces.Count()-1; pos>=0; pos-- )
  338. {
  339. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  340. if ((pDecalFace != NULL) && (pDecalFace->pSolid == pSolid))
  341. {
  342. delete pDecalFace->pFace;
  343. delete pDecalFace;
  344. m_Faces.Remove(pos);
  345. }
  346. }
  347. //
  348. // Attempt to re-attach to the solid.
  349. //
  350. if (eNotifyType != Notify_Removed)
  351. {
  352. if (DecalSolid(pSolid) != 0)
  353. {
  354. CalcBounds();
  355. return;
  356. }
  357. }
  358. //
  359. // We could not re-attach to the solid because it was moved out of range or deleted. If we are
  360. // no longer attached to any solids, remove our entity from the world.
  361. //
  362. int index = m_Solids.Find(pSolid);
  363. if (index != -1)
  364. {
  365. m_Solids.Remove(index);
  366. UpdateDependency(pSolid, NULL);
  367. }
  368. }
  369. CalcBounds();
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Called after the entire map has been loaded. This allows the object
  373. // to perform any linking with other map objects or to do other operations
  374. // that require all world objects to be present.
  375. // Input : pWorld - The world that we are in.
  376. //-----------------------------------------------------------------------------
  377. void CMapDecal::PostloadWorld(CMapWorld *pWorld)
  378. {
  379. CMapClass::PostloadWorld(pWorld);
  380. // Apply ourselves to all solids now that the map is loaded.
  381. DecalAllSolids(pWorld);
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. //-----------------------------------------------------------------------------
  386. void CMapDecal::RebuildDecalFaces(void)
  387. {
  388. //
  389. // Delete all current decal faces. They will be rebuilt below.
  390. //
  391. FOR_EACH_OBJ( m_Faces, pos )
  392. {
  393. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  394. if (pDecalFace != NULL)
  395. {
  396. delete pDecalFace->pFace;
  397. delete pDecalFace;
  398. }
  399. }
  400. m_Faces.RemoveAll();
  401. //
  402. // Attach to all eligible solids in the world.
  403. //
  404. CMapWorld *pWorld = (CMapWorld *)GetWorldObject(this);
  405. DecalAllSolids(pWorld);
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose:
  409. // Input : pRender -
  410. //-----------------------------------------------------------------------------
  411. void CMapDecal::Render3D(CRender3D *pRender)
  412. {
  413. //
  414. // Determine whether we need to render in one or two passes. If we are selected,
  415. // and rendering in flat or textured mode, we need to render using two passes.
  416. //
  417. int nPasses = 1;
  418. int nStart = 1;
  419. SelectionState_t eSelectionState = GetSelectionState();
  420. EditorRenderMode_t eDefaultRenderMode = pRender->GetDefaultRenderMode();
  421. if ((eSelectionState != SELECT_NONE) && (eDefaultRenderMode != RENDER_MODE_WIREFRAME))
  422. {
  423. nPasses = 2;
  424. }
  425. if ( eSelectionState == SELECT_MODIFY )
  426. {
  427. nStart = 2;
  428. }
  429. pRender->RenderEnable( RENDER_POLYGON_OFFSET_FILL, true );
  430. for (int nPass = nStart; nPass <= nPasses; nPass++)
  431. {
  432. //
  433. // Render the second pass in wireframe.
  434. //
  435. if ( nPass == 1 )
  436. {
  437. // use the texture instead of the lightmap coord for decals
  438. if (eDefaultRenderMode == RENDER_MODE_LIGHTMAP_GRID)
  439. pRender->PushRenderMode( RENDER_MODE_TEXTURED );
  440. else
  441. pRender->PushRenderMode( RENDER_MODE_CURRENT );
  442. }
  443. else
  444. {
  445. pRender->PushRenderMode(RENDER_MODE_WIREFRAME);
  446. }
  447. FOR_EACH_OBJ( m_Faces, pos )
  448. {
  449. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  450. if ((pDecalFace != NULL) && (pDecalFace->pFace != NULL))
  451. {
  452. pDecalFace->pFace->Render3D(pRender);
  453. }
  454. }
  455. pRender->PopRenderMode();
  456. }
  457. pRender->RenderEnable( RENDER_POLYGON_OFFSET_FILL, false );
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose:
  461. // Input : File -
  462. // bRMF -
  463. // Output : int
  464. //-----------------------------------------------------------------------------
  465. int CMapDecal::SerializeRMF(std::fstream &File, BOOL bRMF)
  466. {
  467. return(0);
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Purpose:
  471. // Input : File -
  472. // bRMF -
  473. // Output : int
  474. //-----------------------------------------------------------------------------
  475. int CMapDecal::SerializeMAP(std::fstream &File, BOOL bRMF)
  476. {
  477. return(0);
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose: Notifies us that a copy of ourselves was pasted.
  481. //-----------------------------------------------------------------------------
  482. void CMapDecal::OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  483. {
  484. CMapClass::OnPaste(pCopy, pSourceWorld, pDestWorld, OriginalList, NewList);
  485. //
  486. // Apply the copy to all solids in the destination world.
  487. //
  488. ((CMapDecal *)pCopy)->DecalAllSolids(pDestWorld);
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose: Called just after this object has been removed from the world so
  492. // that it can unlink itself from other objects in the world.
  493. // Input : pWorld - The world that we were just removed from.
  494. // bNotifyChildren - Whether we should forward notification to our children.
  495. //-----------------------------------------------------------------------------
  496. void CMapDecal::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren)
  497. {
  498. CMapClass::OnRemoveFromWorld(pWorld, bNotifyChildren);
  499. //
  500. // We're going away. Unlink ourselves from any solids that we are attached to.
  501. //
  502. FOR_EACH_OBJ( m_Solids, pos )
  503. {
  504. CMapSolid *pSolid = (CMapSolid *)m_Solids.Element(pos);
  505. UpdateDependency(pSolid, NULL);
  506. }
  507. m_Solids.RemoveAll();
  508. }
  509. //-----------------------------------------------------------------------------
  510. // Purpose:
  511. // Input : pTransBox -
  512. //-----------------------------------------------------------------------------
  513. void CMapDecal::DoTransform(const VMatrix &matrix)
  514. {
  515. BaseClass::DoTransform(matrix);
  516. RebuildDecalFaces();
  517. }