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.

626 lines
16 KiB

  1. //========= Copyright � 1996-2005, 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 Mins;
  83. Vector Maxs;
  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(Mins, Maxs);
  90. m_CullBox.UpdateBounds(Mins, Maxs);
  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. CMapClass *pMapClass = (CUtlReference< CMapClass >)m_Solids.Element(pos);
  288. CMapSolid *pSolid = (CMapSolid *)pMapClass;
  289. if (pSolid != NULL)
  290. {
  291. OnNotifyDependent(pSolid, Notify_Changed);
  292. }
  293. }
  294. }
  295. }
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose: This function enumerates all children of the world and tries to
  299. // apply the decal to them.
  300. //-----------------------------------------------------------------------------
  301. void CMapDecal::DecalAllSolids(CMapWorld *pWorld)
  302. {
  303. Assert(pWorld != NULL);
  304. if (pWorld != NULL)
  305. {
  306. //
  307. // Try to apply the decal to every solid in the world.
  308. //
  309. EnumChildrenPos_t pos;
  310. CMapClass *pChild = pWorld->GetFirstDescendent(pos);
  311. while (pChild != NULL)
  312. {
  313. CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pChild);
  314. if ((pSolid != NULL) && (DecalSolid(pSolid) != 0))
  315. {
  316. AddSolid(pSolid);
  317. }
  318. pChild = pWorld->GetNextDescendent(pos);
  319. }
  320. PostUpdate(Notify_Changed);
  321. }
  322. }
  323. //-----------------------------------------------------------------------------
  324. // Purpose: Notifys this decal of a change to a solid that it is attached to.
  325. // Input : pSolid - The solid that is changing.
  326. // bSolidDeleted - whether the solid is being deleted.
  327. // Output : Returns true if the decal is still attached to the solid, false if not.
  328. //-----------------------------------------------------------------------------
  329. void CMapDecal::OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType)
  330. {
  331. CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pObject);
  332. if (pSolid != NULL)
  333. {
  334. //
  335. // Delete any decal faces that are attached to this solid. They will be
  336. // rebuilt if we can still decal the solid.
  337. //
  338. for( int pos = m_Faces.Count()-1; pos>=0; pos-- )
  339. {
  340. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  341. if ((pDecalFace != NULL) && (pDecalFace->pSolid == pSolid))
  342. {
  343. delete pDecalFace->pFace;
  344. delete pDecalFace;
  345. m_Faces.Remove(pos);
  346. }
  347. }
  348. //
  349. // Attempt to re-attach to the solid.
  350. //
  351. if (eNotifyType != Notify_Removed)
  352. {
  353. if (DecalSolid(pSolid) != 0)
  354. {
  355. CalcBounds();
  356. return;
  357. }
  358. }
  359. //
  360. // We could not re-attach to the solid because it was moved out of range or deleted. If we are
  361. // no longer attached to any solids, remove our entity from the world.
  362. //
  363. int index = m_Solids.Find(pSolid);
  364. if (index != -1)
  365. {
  366. m_Solids.FastRemove(index);
  367. UpdateDependency(pSolid, NULL);
  368. }
  369. }
  370. CalcBounds();
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose: Called after the entire map has been loaded. This allows the object
  374. // to perform any linking with other map objects or to do other operations
  375. // that require all world objects to be present.
  376. // Input : pWorld - The world that we are in.
  377. //-----------------------------------------------------------------------------
  378. void CMapDecal::PostloadWorld(CMapWorld *pWorld)
  379. {
  380. CMapClass::PostloadWorld(pWorld);
  381. // Apply ourselves to all solids now that the map is loaded.
  382. DecalAllSolids(pWorld);
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose:
  386. //-----------------------------------------------------------------------------
  387. void CMapDecal::RebuildDecalFaces(void)
  388. {
  389. //
  390. // Delete all current decal faces. They will be rebuilt below.
  391. //
  392. FOR_EACH_OBJ( m_Faces, pos )
  393. {
  394. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  395. if (pDecalFace != NULL)
  396. {
  397. delete pDecalFace->pFace;
  398. delete pDecalFace;
  399. }
  400. }
  401. m_Faces.RemoveAll();
  402. //
  403. // Attach to all eligible solids in the world.
  404. //
  405. CMapWorld *pWorld = (CMapWorld *)GetWorldObject(this);
  406. DecalAllSolids(pWorld);
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Purpose:
  410. // Input : pRender -
  411. //-----------------------------------------------------------------------------
  412. void CMapDecal::Render3D(CRender3D *pRender)
  413. {
  414. //
  415. // Determine whether we need to render in one or two passes. If we are selected,
  416. // and rendering in flat or textured mode, we need to render using two passes.
  417. //
  418. int nPasses = 1;
  419. int nStart = 1;
  420. SelectionState_t eSelectionState = GetSelectionState();
  421. EditorRenderMode_t eDefaultRenderMode = pRender->GetDefaultRenderMode();
  422. if ((eSelectionState != SELECT_NONE) && (eDefaultRenderMode != RENDER_MODE_WIREFRAME))
  423. {
  424. nPasses = 2;
  425. }
  426. if ( eSelectionState == SELECT_MODIFY )
  427. {
  428. nStart = 2;
  429. }
  430. pRender->RenderEnable( RENDER_POLYGON_OFFSET_FILL, true );
  431. for (int nPass = nStart; nPass <= nPasses; nPass++)
  432. {
  433. //
  434. // Render the second pass in wireframe.
  435. //
  436. if ( nPass == 1 )
  437. {
  438. // use the texture instead of the lightmap coord for decals
  439. if (eDefaultRenderMode == RENDER_MODE_LIGHTMAP_GRID)
  440. pRender->PushRenderMode( RENDER_MODE_TEXTURED );
  441. else
  442. pRender->PushRenderMode( RENDER_MODE_CURRENT );
  443. }
  444. else
  445. {
  446. pRender->PushRenderMode(RENDER_MODE_WIREFRAME);
  447. }
  448. FOR_EACH_OBJ( m_Faces, pos )
  449. {
  450. DecalFace_t *pDecalFace = m_Faces.Element(pos);
  451. if ((pDecalFace != NULL) && (pDecalFace->pFace != NULL))
  452. {
  453. pDecalFace->pFace->Render3D(pRender);
  454. }
  455. }
  456. pRender->PopRenderMode();
  457. }
  458. pRender->RenderEnable( RENDER_POLYGON_OFFSET_FILL, false );
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Purpose:
  462. // Input : File -
  463. // bRMF -
  464. // Output : int
  465. //-----------------------------------------------------------------------------
  466. int CMapDecal::SerializeRMF(std::fstream &File, BOOL bRMF)
  467. {
  468. return(0);
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Purpose:
  472. // Input : File -
  473. // bRMF -
  474. // Output : int
  475. //-----------------------------------------------------------------------------
  476. int CMapDecal::SerializeMAP(std::fstream &File, BOOL bRMF)
  477. {
  478. return(0);
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Purpose: Notifies us that a copy of ourselves was pasted.
  482. //-----------------------------------------------------------------------------
  483. void CMapDecal::OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  484. {
  485. CMapClass::OnPaste(pCopy, pSourceWorld, pDestWorld, OriginalList, NewList);
  486. //
  487. // Apply the copy to all solids in the destination world.
  488. //
  489. ((CMapDecal *)pCopy)->DecalAllSolids(pDestWorld);
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose: Called just after this object has been removed from the world so
  493. // that it can unlink itself from other objects in the world.
  494. // Input : pWorld - The world that we were just removed from.
  495. // bNotifyChildren - Whether we should forward notification to our children.
  496. //-----------------------------------------------------------------------------
  497. void CMapDecal::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren)
  498. {
  499. CMapClass::OnRemoveFromWorld(pWorld, bNotifyChildren);
  500. //
  501. // We're going away. Unlink ourselves from any solids that we are attached to.
  502. //
  503. FOR_EACH_OBJ( m_Solids, pos )
  504. {
  505. CMapClass *pMapClass = (CUtlReference< CMapClass >)m_Solids.Element(pos);
  506. CMapSolid *pSolid = (CMapSolid *)pMapClass;
  507. UpdateDependency(pSolid, NULL);
  508. }
  509. m_Solids.RemoveAll();
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Purpose:
  513. // Input : pTransBox -
  514. //-----------------------------------------------------------------------------
  515. void CMapDecal::DoTransform(const VMatrix &matrix)
  516. {
  517. BaseClass::DoTransform(matrix);
  518. RebuildDecalFaces();
  519. }