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.

688 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements a helper that manages a single keyvalue of type "side"
  4. // or "sidelist" for the entity that is its parent.
  5. //
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "fgdlib/HelperInfo.h"
  9. #include "materialsystem/imesh.h"
  10. #include "MapClass.h"
  11. #include "MapSolid.h"
  12. #include "MapWorld.h" // For the world's face ID functions.
  13. #include "MapSideList.h"
  14. #include "Material.h"
  15. #include "Render3D.h"
  16. #include "mapdoc.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include <tier0/memdbgon.h>
  19. IMPLEMENT_MAPCLASS(CMapSideList);
  20. //-----------------------------------------------------------------------------
  21. // Purpose: Factory function. Used for creating a CMapSideList from a set
  22. // of string parameters from the FGD file.
  23. // Input : pInfo - Pointer to helper info class which gives us information
  24. // about how to create the class.
  25. // Output : Returns a pointer to the class, NULL if an error occurs.
  26. //-----------------------------------------------------------------------------
  27. CMapClass *CMapSideList::CreateMapSideList(CHelperInfo *pHelperInfo, CMapEntity *pParent)
  28. {
  29. CMapSideList *pSideList = NULL;
  30. const char *pszParam = pHelperInfo->GetParameter(0);
  31. if (pszParam != NULL)
  32. {
  33. pSideList = new CMapSideList(pszParam);
  34. }
  35. else
  36. {
  37. pSideList = new CMapSideList("sides");
  38. }
  39. return(pSideList);
  40. }
  41. //-----------------------------------------------------------------------------
  42. // Purpose: Constructor.
  43. //-----------------------------------------------------------------------------
  44. CMapSideList::CMapSideList(void)
  45. {
  46. m_szKeyName[0] = '\0';
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose: Constructor with key name.
  50. //-----------------------------------------------------------------------------
  51. CMapSideList::CMapSideList(char const *pszKeyName)
  52. {
  53. strcpy( m_szKeyName, pszKeyName );
  54. }
  55. //-----------------------------------------------------------------------------
  56. // Purpose: Destructor.
  57. //-----------------------------------------------------------------------------
  58. CMapSideList::~CMapSideList(void)
  59. {
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Gets the keyvalue from our parent entity and rebuilds the face list from that.
  63. //-----------------------------------------------------------------------------
  64. void CMapSideList::RebuildFaceList()
  65. {
  66. CMapWorld *pWorld = GetWorldObject(this);
  67. if (pWorld == NULL)
  68. {
  69. return;
  70. }
  71. CMapEntity *pParent = dynamic_cast <CMapEntity *>(GetParent());
  72. const char *pszValue = pParent->GetKeyValue(m_szKeyName);
  73. if (pszValue != NULL)
  74. {
  75. BuildFaceListForValue(pszValue, pWorld);
  76. }
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose:
  80. // Input : pszValue -
  81. // pWorld - The world object that we are contained in.
  82. //-----------------------------------------------------------------------------
  83. void CMapSideList::BuildFaceListForValue(char const *pszValue, CMapWorld *pWorld)
  84. {
  85. CMapFaceList NewFaces;
  86. pWorld->FaceID_StringToFaceLists(&NewFaces, NULL, pszValue);
  87. //
  88. // Detach from the faces that are not in the new list. Go
  89. // in reverse order since we are removing items as we go.
  90. //
  91. if (m_Faces.Count() > 0)
  92. {
  93. for (int i = m_Faces.Count() - 1; i >= 0; i--)
  94. {
  95. CMapFace *pFace = m_Faces.Element(i);
  96. Assert(pFace != NULL);
  97. if ((pFace != NULL) && (NewFaces.Find(pFace) == -1))
  98. {
  99. CMapSolid *pSolid = (CMapSolid *)pFace->GetParent();
  100. UpdateDependency(pSolid, NULL);
  101. m_Faces.FastRemove(i);
  102. }
  103. }
  104. }
  105. //
  106. // Attach to the faces that are not in the old list.
  107. //
  108. for (int i = 0; i < NewFaces.Count(); i++)
  109. {
  110. CMapFace *pFace = NewFaces.Element(i);
  111. Assert(pFace != NULL);
  112. if ((pFace != NULL) && (m_Faces.Find(pFace) == -1))
  113. {
  114. CMapSolid *pSolid = (CMapSolid *)pFace->GetParent();
  115. UpdateDependency(NULL, pSolid);
  116. m_Faces.AddToTail(pFace);
  117. }
  118. }
  119. CalcBounds();
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Calculates our bounds.
  123. // Input : bFullUpdate -
  124. //-----------------------------------------------------------------------------
  125. void CMapSideList::CalcBounds(BOOL bFullUpdate)
  126. {
  127. //
  128. // We're just a point in the 2D view because we don't render there.
  129. //
  130. m_Render2DBox.ResetBounds();
  131. m_Render2DBox.UpdateBounds(m_Origin);
  132. //
  133. // Our culling bounds includes the endpoints of all the lines we draw when
  134. // our parent entity is selected.
  135. //
  136. m_CullBox.ResetBounds();
  137. m_CullBox.UpdateBounds(m_Origin);
  138. for (int i = 0; i < m_Faces.Count(); i++)
  139. {
  140. CMapFace *pFace = m_Faces.Element(i);
  141. Vector Center;
  142. pFace->GetCenter(Center);
  143. m_CullBox.UpdateBounds(Center);
  144. }
  145. m_BoundingBox = m_CullBox;
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose: Returns a copy of this object.
  149. // Input : bUpdateDependencies - Whether the new object should link to any
  150. // other objects in the world when it copies pointers.
  151. //-----------------------------------------------------------------------------
  152. CMapClass *CMapSideList::Copy(bool bUpdateDependencies)
  153. {
  154. CMapSideList *pCopy = new CMapSideList;
  155. if (pCopy != NULL)
  156. {
  157. pCopy->CopyFrom(this, bUpdateDependencies);
  158. }
  159. return(pCopy);
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose: Turns us into an exact copy of the given object.
  163. // Input : pFrom - Object to copy.
  164. // Input : bUpdateDependencies - Whether we should link to any other objects
  165. // in the world when we copy pointers.
  166. //-----------------------------------------------------------------------------
  167. CMapClass *CMapSideList::CopyFrom(CMapClass *pOther, bool bUpdateDependencies)
  168. {
  169. CMapSideList *pFrom = dynamic_cast <CMapSideList *>(pOther);
  170. Assert(pFrom != NULL);
  171. CMapClass::CopyFrom(pOther, bUpdateDependencies);
  172. strcpy(m_szKeyName, pFrom->m_szKeyName);
  173. m_Faces = pFrom->m_Faces;
  174. if (bUpdateDependencies)
  175. {
  176. for (int i = 0; i < m_Faces.Count(); i++)
  177. {
  178. CMapFace *pFace = m_Faces.Element(i);
  179. CMapSolid *pSolid = (CMapSolid *)pFace->GetParent();
  180. UpdateDependency(pSolid, NULL);
  181. }
  182. }
  183. return(this);
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose: Searches for a face with the given unique ID in the list of objects.
  187. // FIXME: should this be in CMapObjectList?
  188. // Input : nFaceID - Face ID to search for.
  189. // List - List of objects to search.
  190. // Output : Returns the face with the given ID if it was found, NULL if not.
  191. //-----------------------------------------------------------------------------
  192. CMapFace *CMapSideList::FindFaceIDInList(int nFaceID, const CMapObjectList &List)
  193. {
  194. FOR_EACH_OBJ( List, pos )
  195. {
  196. //
  197. // If this object is a solid, look for the face there.
  198. //
  199. CMapClass *pObject = List.Element(pos);
  200. CMapSolid *pSolid = dynamic_cast <CMapSolid *>(pObject);
  201. if (pSolid != NULL)
  202. {
  203. CMapFace *pFace = pSolid->FindFaceID(nFaceID);
  204. if (pFace != NULL)
  205. {
  206. return(pFace);
  207. }
  208. }
  209. //
  210. // Check all of this object's solid children.
  211. //
  212. EnumChildrenPos_t pos2;
  213. CMapClass *pChild = pObject->GetFirstDescendent(pos2);
  214. while (pChild != NULL)
  215. {
  216. pSolid = dynamic_cast <CMapSolid *>(pChild);
  217. if (pSolid != NULL)
  218. {
  219. CMapFace *pFace = pSolid->FindFaceID(nFaceID);
  220. if (pFace != NULL)
  221. {
  222. return(pFace);
  223. }
  224. }
  225. pChild = pObject->GetNextDescendent(pos2);
  226. }
  227. }
  228. return(NULL);
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Returns the total approximate memory consumed by this object.
  232. //-----------------------------------------------------------------------------
  233. size_t CMapSideList::GetSize(void)
  234. {
  235. return(sizeof(this) + m_Faces.Count() * sizeof(CMapFace *));
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose: Notification that we have just been cloned. Fix up our clone's
  239. // face list based on the the objects that were cloned with it.
  240. // Input : pClone -
  241. // OriginalList -
  242. // NewList -
  243. //-----------------------------------------------------------------------------
  244. void CMapSideList::OnClone(CMapClass *pCloneObject, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  245. {
  246. ReplaceFacesInCopy((CMapSideList *)pCloneObject, OriginalList, NewList);
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose:
  250. // Input : pCopy -
  251. // pSourceWorld -
  252. // pDestWorld -
  253. // OriginalList -
  254. // NewList -
  255. //-----------------------------------------------------------------------------
  256. void CMapSideList::OnPaste(CMapClass *pCopyObject, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  257. {
  258. CMapSideList *pCopy = (CMapSideList *)pCopyObject;
  259. //
  260. // HACK: This is kinda nasty. Build a list of face IDs from our parent keyvalue so
  261. // we can remap them to faces in the clipboard.
  262. //
  263. CMapEntity *pParent = dynamic_cast <CMapEntity *>(pCopy->GetParent());
  264. const char *pszValue = pParent->GetKeyValue(m_szKeyName);
  265. if (pszValue != NULL)
  266. {
  267. char szVal[KEYVALUE_MAX_VALUE_LENGTH];
  268. strcpy(szVal, pszValue);
  269. char *psz = strtok(szVal, " ");
  270. while (psz != NULL)
  271. {
  272. //
  273. // The substring should now be a single face ID. Get the corresponding
  274. // face and add it to the list.
  275. //
  276. int nFaceID = atoi(psz);
  277. CMapFace *pFace = FindFaceIDInList(nFaceID, OriginalList);
  278. if (pFace != NULL)
  279. {
  280. pCopy->m_Faces.AddToTail(pFace);
  281. }
  282. //
  283. // Get the next substring.
  284. //
  285. psz = strtok(NULL, " ");
  286. }
  287. }
  288. //
  289. // Now replace all faces with their new counterparts, as in Clone.
  290. //
  291. ReplaceFacesInCopy(pCopy, OriginalList, NewList);
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose:
  295. // Input : pObject -
  296. // eNotifyType -
  297. //-----------------------------------------------------------------------------
  298. void CMapSideList::OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType)
  299. {
  300. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  301. if ( pDoc->IsLoading() )
  302. return;
  303. if ( eNotifyType == Notify_Changed )
  304. {
  305. // Remove? Purge?
  306. m_Faces.RemoveAll();
  307. // Rebuild the face list
  308. RebuildFaceList();
  309. }
  310. if (eNotifyType == Notify_Removed || eNotifyType == Notify_Clipped)
  311. {
  312. //
  313. // Check for a solid that we refer to via face ID going away.
  314. //
  315. CMapSolid *pSolid = dynamic_cast<CMapSolid *>(pObject);
  316. if ((pSolid != NULL) && (m_Faces.Count() > 0))
  317. {
  318. //
  319. // Remove faces from our list that are in this solid.
  320. // Do it backwards so we can remove them as we go. Also, add
  321. // the face IDs to our list of lost IDs so that we can reacquire
  322. // the face in our list if the solid comes back later.
  323. //
  324. for (int i = m_Faces.Count() - 1; i >= 0; i--)
  325. {
  326. CMapFace *pFace = m_Faces.Element(i);
  327. if (pFace != NULL)
  328. {
  329. CMapSolid *pParent = (CMapSolid *)pFace->GetParent();
  330. if (pParent == pSolid)
  331. {
  332. m_LostFaceIDs.AddToTail(pFace->GetFaceID());
  333. m_Faces.FastRemove(i);
  334. }
  335. }
  336. }
  337. //
  338. // Submit the updated face list to our parent entity.
  339. //
  340. UpdateParentKey();
  341. }
  342. }
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose:
  346. // Input : key -
  347. // value -
  348. //-----------------------------------------------------------------------------
  349. void CMapSideList::OnParentKeyChanged(char const *pszKey, char const *pszValue)
  350. {
  351. CMapWorld *pWorld = GetWorldObject(this);
  352. if (pWorld == NULL)
  353. {
  354. // We're probably being copied into the clipboard.
  355. return;
  356. }
  357. //
  358. // Update our face list if the key we care about is changing.
  359. //
  360. if (!stricmp(pszKey, m_szKeyName))
  361. {
  362. BuildFaceListForValue(pszValue, pWorld);
  363. }
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose: Called just after this object has been removed from the world so
  367. // that it can unlink itself from other objects in the world.
  368. // Input : pWorld - The world that we were just removed from.
  369. // bNotifyChildren - Whether we should forward notification to our children.
  370. //-----------------------------------------------------------------------------
  371. void CMapSideList::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren)
  372. {
  373. CMapClass::OnRemoveFromWorld(pWorld, bNotifyChildren);
  374. for (int i = 0; i < m_Faces.Count(); i++)
  375. {
  376. CMapFace *pFace = m_Faces.Element(i);
  377. CMapSolid *pSolid = (CMapSolid *)pFace->GetParent();
  378. UpdateDependency(pSolid, NULL);
  379. }
  380. m_Faces.RemoveAll();
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose:
  384. // Input : List -
  385. //-----------------------------------------------------------------------------
  386. void CMapSideList::RemoveFacesNotInList(const CMapObjectList &List)
  387. {
  388. if (m_Faces.Count() > 0)
  389. {
  390. for (int i = m_Faces.Count() - 1; i >= 0; i--)
  391. {
  392. CMapFace *pFace = m_Faces.Element(i);
  393. if (FindFaceIDInList(pFace->GetFaceID(), List) == NULL)
  394. {
  395. CMapSolid *pSolid = (CMapSolid *)pFace->GetParent();
  396. UpdateDependency(pSolid, NULL);
  397. m_Faces.FastRemove(i);
  398. }
  399. }
  400. }
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Purpose: Called from OnClone and OnPaste. Replaces references (in the
  404. // cloned object) to faces in the original list of objects with references
  405. // to corresponding faces in the new list of objects.
  406. // Input : pClone -
  407. // OriginalList -
  408. // NewList -
  409. //-----------------------------------------------------------------------------
  410. void CMapSideList::ReplaceFacesInCopy(CMapSideList *pCopy, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  411. {
  412. Assert( OriginalList.Count() == NewList.Count() );
  413. FOR_EACH_OBJ( OriginalList, pos )
  414. {
  415. CMapClass *pOriginal = OriginalList.Element(pos);
  416. CMapClass *pNew = NewList.Element(pos);
  417. if (pOriginal != this)
  418. {
  419. //
  420. // Check to see if these two objects are solids.
  421. //
  422. CMapSolid *pOrigSolid = dynamic_cast <CMapSolid *>(pOriginal);
  423. if (pOrigSolid != NULL)
  424. {
  425. CMapSolid *pNewSolid = dynamic_cast <CMapSolid *>(pNew);
  426. Assert(pNewSolid != NULL);
  427. if (pNewSolid != NULL)
  428. {
  429. pCopy->ReplaceSolidFaces(pOrigSolid, pNewSolid);
  430. }
  431. }
  432. //
  433. // Check all of these objects' children.
  434. //
  435. EnumChildrenPos_t e1;
  436. EnumChildrenPos_t e2;
  437. CMapClass *pOrigChild = pOriginal->GetFirstDescendent(e1);
  438. CMapClass *pNewChild = pNew->GetFirstDescendent(e2);
  439. while (pOrigChild != NULL)
  440. {
  441. Assert(pNewChild != NULL);
  442. pOrigSolid = dynamic_cast <CMapSolid *>(pOrigChild);
  443. if (pOrigSolid != NULL)
  444. {
  445. CMapSolid *pNewSolid = dynamic_cast <CMapSolid *>(pNewChild);
  446. Assert(pNewSolid != NULL);
  447. if (pNewSolid != NULL)
  448. {
  449. pCopy->ReplaceSolidFaces(pOrigSolid, pNewSolid);
  450. }
  451. }
  452. pOrigChild = pOriginal->GetNextDescendent(e1);
  453. pNewChild = pNew->GetNextDescendent(e2);
  454. }
  455. }
  456. }
  457. //
  458. // Update the keyvalue in the copy's parent entity.
  459. //
  460. pCopy->UpdateParentKey();
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Purpose:
  464. // Input : pRender - Interface to use for rendering.
  465. //-----------------------------------------------------------------------------
  466. void CMapSideList::Render2D(CRender2D *pRender)
  467. {
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Purpose: Renders us in the 3D view.
  471. // Input : pRender - Interface to use for rendering.
  472. //-----------------------------------------------------------------------------
  473. void CMapSideList::Render3D(CRender3D *pRender)
  474. {
  475. if ( !m_pParent->IsSelected() )
  476. return;
  477. //
  478. // Draw lines from us to the center of all faces in the list.
  479. //
  480. pRender->PushRenderMode(RENDER_MODE_WIREFRAME);
  481. CMeshBuilder meshBuilder;
  482. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  483. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  484. meshBuilder.Begin(pMesh, MATERIAL_LINES, m_Faces.Count());
  485. for (int i = 0; i < m_Faces.Count(); i++)
  486. {
  487. CMapFace *pFace = m_Faces.Element(i);
  488. Vector Center;
  489. pFace->GetCenter(Center);
  490. unsigned char color[3];
  491. color[0] = SELECT_EDGE_RED;
  492. color[1] = SELECT_EDGE_GREEN;
  493. color[2] = SELECT_EDGE_BLUE;
  494. meshBuilder.Color3ubv( color );
  495. meshBuilder.Position3f(m_Origin.x, m_Origin.y, m_Origin.z);
  496. meshBuilder.AdvanceVertex();
  497. meshBuilder.Color3ubv( color );
  498. meshBuilder.Position3f(Center.x, Center.y, Center.z);
  499. meshBuilder.AdvanceVertex();
  500. }
  501. meshBuilder.End();
  502. pMesh->Draw();
  503. pRender->PopRenderMode();
  504. }
  505. //-----------------------------------------------------------------------------
  506. // Purpose: Called from OnClone and OnPaste, updates references to face IDs
  507. // in the one solid with references to corresponding face IDs in
  508. // another solid.
  509. // Input : pOrigSolid - Solid with faces to find.
  510. // pNewSolid - Solid with faces to replace with.
  511. // Output : Returns true if it replaced at least one face.
  512. //-----------------------------------------------------------------------------
  513. bool CMapSideList::ReplaceSolidFaces(CMapSolid *pOrigSolid, CMapSolid *pNewSolid)
  514. {
  515. bool bDidSomething = false;
  516. for (int i = 0; i < pOrigSolid->GetFaceCount(); i++)
  517. {
  518. CMapFace *pFace = pOrigSolid->GetFace(i);
  519. int nIndex = m_Faces.FindFaceID(pFace->GetFaceID());
  520. if (nIndex != -1)
  521. {
  522. //
  523. // Replace the element in our face list and unlink
  524. // us from the original solid, relinking us to the new solid.
  525. //
  526. m_Faces.Element(nIndex) = pNewSolid->GetFace(i);
  527. UpdateDependency(pOrigSolid, pNewSolid);
  528. bDidSomething = true;
  529. }
  530. }
  531. return(bDidSomething);
  532. }
  533. //-----------------------------------------------------------------------------
  534. // Purpose: Something happened in the world that requires us to refresh our
  535. // dependencies. Try to reacquire face IDs in our deleted faces list.
  536. // Input : pWorld -
  537. //-----------------------------------------------------------------------------
  538. void CMapSideList::UpdateDependencies(CMapWorld *pWorld, CMapClass *pObject)
  539. {
  540. CMapClass::UpdateDependencies(pWorld, pObject);
  541. //
  542. // See if it is a solid that holds faces in our lost faces list.
  543. //
  544. CMapSolid *pSolid = dynamic_cast <CMapSolid *>(pObject);
  545. if ((pSolid != NULL) && (m_LostFaceIDs.Count() > 0))
  546. {
  547. //
  548. // Walk the list backwards so we can remove as we go.
  549. //
  550. for (int i = m_LostFaceIDs.Count() - 1; i >= 0; i--)
  551. {
  552. int nFaceID = m_LostFaceIDs.Element(i);
  553. CMapFace *pFace = pSolid->FindFaceID(nFaceID);
  554. if (pFace != NULL)
  555. {
  556. if (m_Faces.Find(pFace) == -1)
  557. {
  558. m_Faces.AddToTail(pFace);
  559. }
  560. //
  561. // We've reacquired the face, so it's no longer lost.
  562. //
  563. m_LostFaceIDs.FastRemove(i);
  564. }
  565. }
  566. UpdateParentKey();
  567. }
  568. }
  569. //-----------------------------------------------------------------------------
  570. // Purpose: Builds a new value string for our parent's facelist key from our
  571. // current list of faces and submits the keyvalue to our parent for
  572. // storage.
  573. //-----------------------------------------------------------------------------
  574. void CMapSideList::UpdateParentKey(void)
  575. {
  576. char szValue[KEYVALUE_MAX_VALUE_LENGTH];
  577. CMapWorld::FaceID_FaceListsToString(szValue, sizeof(szValue), &m_Faces, NULL);
  578. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(m_pParent);
  579. if (pEntity != NULL)
  580. {
  581. pEntity->NotifyChildKeyChanged(this, m_szKeyName, szValue);
  582. }
  583. }