diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/mapdecal.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'hammer/mapdecal.cpp')
| -rw-r--r-- | hammer/mapdecal.cpp | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/hammer/mapdecal.cpp b/hammer/mapdecal.cpp new file mode 100644 index 0000000..3cc0a43 --- /dev/null +++ b/hammer/mapdecal.cpp @@ -0,0 +1,624 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements a decal helper. The decal attaches itself to nearby +// solids, dynamically creating decal faces as necessary. +// +//=============================================================================// + +#include "stdafx.h" +#include "ClipCode.h" +#include "MapDoc.h" +#include "MapDecal.h" +#include "MapFace.h" +#include "MapSolid.h" +#include "MapWorld.h" +#include "Render3D.h" +#include "TextureSystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +IMPLEMENT_MAPCLASS(CMapDecal) + + +//----------------------------------------------------------------------------- +// Purpose: Factory function. Used for creating a CMapDecal from a set +// of string parameters from the FGD file. +// Input : pInfo - Pointer to helper info class which gives us information +// about how to create the class. +// Output : Returns a pointer to the class, NULL if an error occurs. +//----------------------------------------------------------------------------- +CMapClass *CMapDecal::CreateMapDecal(CHelperInfo *pHelperInfo, CMapEntity *pParent) +{ + CMapDecal *pDecal = new CMapDecal; + return(pDecal); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. Initializes data members. +//----------------------------------------------------------------------------- +CMapDecal::CMapDecal(void) +{ + m_pTexture = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. Frees allocated memory. +//----------------------------------------------------------------------------- +CMapDecal::~CMapDecal(void) +{ + // Delete our list of faces and each face in the list. + FOR_EACH_OBJ( m_Faces, pos ) + { + DecalFace_t *pDecalFace = m_Faces.Element(pos); + delete pDecalFace->pFace; + delete pDecalFace; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pSolid - +//----------------------------------------------------------------------------- +void CMapDecal::AddSolid(CMapSolid *pSolid) +{ + if ( m_Solids.Find(pSolid) == -1 ) + { + UpdateDependency(NULL, pSolid); + m_Solids.AddToTail(pSolid); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bFullUpdate - +//----------------------------------------------------------------------------- +void CMapDecal::CalcBounds(BOOL bFullUpdate) +{ + CMapClass::CalcBounds(bFullUpdate); + + // + // Calculate the 2D render box. + // + Vector Mins = m_Origin - Vector(2, 2, 2); + Vector Maxs = m_Origin + Vector(2, 2, 2); + + m_Render2DBox.UpdateBounds(Mins, Maxs); + + // + // Calculate the 3D culling bounds. + // + m_CullBox.ResetBounds(); + + if (m_Faces.Count() > 0) + { + Vector MinsFace; + Vector MaxsFace; + + FOR_EACH_OBJ( m_Faces, pos ) + { + DecalFace_t *pDecalFace = m_Faces.Element(pos); + + if ((pDecalFace != NULL) && (pDecalFace->pFace != NULL)) + { + pDecalFace->pFace->GetFaceBounds( MinsFace, MaxsFace ); + + m_CullBox.UpdateBounds( MinsFace, MaxsFace ); + } + } + + // + // Insure that the 3D bounds are at least 1 unit in all dimensions. + // + for (int nDim = 0; nDim < 3; nDim++) + { + if ((m_CullBox.bmaxs[nDim] - m_CullBox.bmins[nDim]) == 0) + { + m_CullBox.bmins[nDim] -= 0.5; + m_CullBox.bmaxs[nDim] += 0.5; + } + } + } + else + { + m_CullBox.UpdateBounds(Mins, Maxs); + } + + m_BoundingBox = m_CullBox; +} + + +//----------------------------------------------------------------------------- +// Purpose: Determines whether we can attach to this solid by looking at the +// normal distance to the solid face. We still may not lie within the +// face; that is determined by the clipping code. +// Input : pSolid - Solid to check. +// ppFaces - Returns with pointers to the faces that are eligible. +// Output : Returns the number of faces that were eligible. +//----------------------------------------------------------------------------- +int CMapDecal::CanDecalSolid(CMapSolid *pSolid, CMapFace **ppFaces) +{ + // + // Check distance from our origin to each face along the face's normal. + // If the distance is very very small, add the face. + // + int nDecalFaces = 0; + + int nFaces = pSolid->GetFaceCount(); + Assert(nFaces <= MAPSOLID_MAX_FACES); + for (int i = 0; i < nFaces; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + float fDistance = pFace->GetNormalDistance(m_Origin); + + if ((fDistance <= 16.0f) && (fDistance >= -0.0001)) + { + if (ppFaces != NULL) + { + ppFaces[nDecalFaces] = pFace; + } + + nDecalFaces++; + } + } + + return(nDecalFaces); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CMapClass +//----------------------------------------------------------------------------- +CMapClass *CMapDecal::Copy(bool bUpdateDependencies) +{ + CMapDecal *pCopy = new CMapDecal; + + if (pCopy != NULL) + { + pCopy->CopyFrom(this, bUpdateDependencies); + } + + return(pCopy); +} + + +//----------------------------------------------------------------------------- +// Purpose: Makes this object identical to the given object. +// Input : pObject - Object to copy. +// bUpdateDependencies - +//----------------------------------------------------------------------------- +CMapClass *CMapDecal::CopyFrom(CMapClass *pObject, bool bUpdateDependencies) +{ + Assert(pObject->IsMapClass(MAPCLASS_TYPE(CMapDecal))); + CMapDecal *pFrom = (CMapDecal *)pObject; + + CMapClass::CopyFrom(pObject, bUpdateDependencies); + + m_pTexture = pFrom->m_pTexture; + + // + // Copy our list of solids to which we are attached. + // + m_Solids.RemoveAll(); + m_Solids.AddVectorToTail(pFrom->m_Solids); + + // + // Copy our decal faces. We don't copy the pointers because we don't do + // reference counting yet. + // + m_Faces.RemoveAll(); + + FOR_EACH_OBJ( pFrom->m_Faces, pos ) + { + DecalFace_t *pDecalFace = new DecalFace_t; + if (pDecalFace != NULL) + { + pDecalFace->pFace = new CMapFace; + if (pDecalFace->pFace != NULL) + { + DecalFace_t *pFromDecalFace = pFrom->m_Faces.Element(pos); + + pDecalFace->pFace->CopyFrom(pFromDecalFace->pFace); + pDecalFace->pFace->SetParent(this); + + pDecalFace->pSolid = pFromDecalFace->pSolid; + + m_Faces.AddToTail(pDecalFace); + } + } + } + + return(this); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pSolid - +// org - +// piFacesRvl - +// Output : int +//----------------------------------------------------------------------------- +int CMapDecal::DecalSolid(CMapSolid *pSolid) +{ + if (m_pTexture == NULL) + { + return(0); + } + + // + // Determine how many, if any, faces will accept the decal. + // + CMapFace *ppFaces[MAPSOLID_MAX_FACES]; + int nDecalFaces = 0; + int nTestFaces = CanDecalSolid(pSolid, ppFaces); + if (nTestFaces != 0) + { + // + // Apply the decal to each face that will accept it. + // + for (int nFace = 0; nFace < nTestFaces; nFace++) + { + CMapFace *pFace = ppFaces[nFace]; + + // + // Create the polygon, clipping it to this face. + // + vec5_t ClipPoints[MAX_CLIPVERT]; + int nPointCount = CreateClippedPoly(pFace, m_pTexture, m_Origin, ClipPoints, sizeof(ClipPoints) / sizeof(ClipPoints[0])); + + if (nPointCount != 0) + { + nDecalFaces++; + + Vector CreatePoints[64]; + for (int nPoint = 0; nPoint < nPointCount; nPoint++) + { + CreatePoints[nPoint][0] = ClipPoints[nPoint][0]; + CreatePoints[nPoint][1] = ClipPoints[nPoint][1]; + CreatePoints[nPoint][2] = ClipPoints[nPoint][2]; + } + + // + // Create the decal face from the polygon. + // + DecalFace_t *pDecalFace = new DecalFace_t; + pDecalFace->pFace = new CMapFace; + + pDecalFace->pFace->CreateFace(CreatePoints, nPointCount); + + pDecalFace->pFace->SetRenderColor(255, 255, 255); + pDecalFace->pFace->SetParent(this); + + // + // Associate this decal face with the solid. + // + pDecalFace->pSolid = pSolid; + + // + // Set the texture in the decal face. + // + pDecalFace->pFace->SetTexture(m_pTexture); + pDecalFace->pFace->CalcTextureCoords(); + + for (int nPoint = 0; nPoint < nPointCount; nPoint++) + { + pDecalFace->pFace->SetTextureCoords(nPoint, ClipPoints[nPoint][3], ClipPoints[nPoint][4]); + } + + m_Faces.AddToTail(pDecalFace); + break; + } + } + } + + return(nDecalFaces); +} + + +//----------------------------------------------------------------------------- +// Purpose: Notifies that this object's parent entity has had a key value change. +// Input : szKey - The key that changed. +// szValue - The new value of the key. +//----------------------------------------------------------------------------- +void CMapDecal::OnParentKeyChanged(const char* szKey, const char* szValue) +{ + // + // The decal texture has changed. + // + if (!stricmp(szKey, "texture")) + { + IEditorTexture *pTexNew = g_Textures.FindActiveTexture(szValue); + + if (pTexNew != NULL) + { + m_pTexture = pTexNew; + + // + // Rebuild all the decal faces with the new texture by pretending + // that all the solids we are attached to have changed. + // + FOR_EACH_OBJ( m_Solids, pos ) + { + CMapSolid *pSolid = (CMapSolid *)m_Solids.Element(pos); + if (pSolid != NULL) + { + OnNotifyDependent(pSolid, Notify_Changed); + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: This function enumerates all children of the world and tries to +// apply the decal to them. +//----------------------------------------------------------------------------- +void CMapDecal::DecalAllSolids(CMapWorld *pWorld) +{ + Assert(pWorld != NULL); + + if (pWorld != NULL) + { + // + // Try to apply the decal to every solid in the world. + // + EnumChildrenPos_t pos; + CMapClass *pChild = pWorld->GetFirstDescendent(pos); + while (pChild != NULL) + { + CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pChild); + if ((pSolid != NULL) && (DecalSolid(pSolid) != 0)) + { + AddSolid(pSolid); + } + + pChild = pWorld->GetNextDescendent(pos); + } + + PostUpdate(Notify_Changed); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Notifys this decal of a change to a solid that it is attached to. +// Input : pSolid - The solid that is changing. +// bSolidDeleted - whether the solid is being deleted. +// Output : Returns true if the decal is still attached to the solid, false if not. +//----------------------------------------------------------------------------- +void CMapDecal::OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType) +{ + CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pObject); + + if (pSolid != NULL) + { + // + // Delete any decal faces that are attached to this solid. They will be + // rebuilt if we can still decal the solid. + // + + for( int pos = m_Faces.Count()-1; pos>=0; pos-- ) + { + DecalFace_t *pDecalFace = m_Faces.Element(pos); + if ((pDecalFace != NULL) && (pDecalFace->pSolid == pSolid)) + { + delete pDecalFace->pFace; + delete pDecalFace; + m_Faces.Remove(pos); + } + } + + // + // Attempt to re-attach to the solid. + // + if (eNotifyType != Notify_Removed) + { + if (DecalSolid(pSolid) != 0) + { + CalcBounds(); + return; + } + } + + // + // We could not re-attach to the solid because it was moved out of range or deleted. If we are + // no longer attached to any solids, remove our entity from the world. + // + int index = m_Solids.Find(pSolid); + if (index != -1) + { + m_Solids.Remove(index); + UpdateDependency(pSolid, NULL); + } + } + + CalcBounds(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called after the entire map has been loaded. This allows the object +// to perform any linking with other map objects or to do other operations +// that require all world objects to be present. +// Input : pWorld - The world that we are in. +//----------------------------------------------------------------------------- +void CMapDecal::PostloadWorld(CMapWorld *pWorld) +{ + CMapClass::PostloadWorld(pWorld); + + // Apply ourselves to all solids now that the map is loaded. + DecalAllSolids(pWorld); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapDecal::RebuildDecalFaces(void) +{ + // + // Delete all current decal faces. They will be rebuilt below. + // + FOR_EACH_OBJ( m_Faces, pos ) + { + DecalFace_t *pDecalFace = m_Faces.Element(pos); + if (pDecalFace != NULL) + { + delete pDecalFace->pFace; + delete pDecalFace; + } + } + + m_Faces.RemoveAll(); + + // + // Attach to all eligible solids in the world. + // + CMapWorld *pWorld = (CMapWorld *)GetWorldObject(this); + DecalAllSolids(pWorld); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pRender - +//----------------------------------------------------------------------------- +void CMapDecal::Render3D(CRender3D *pRender) +{ + // + // Determine whether we need to render in one or two passes. If we are selected, + // and rendering in flat or textured mode, we need to render using two passes. + // + int nPasses = 1; + int nStart = 1; + SelectionState_t eSelectionState = GetSelectionState(); + EditorRenderMode_t eDefaultRenderMode = pRender->GetDefaultRenderMode(); + if ((eSelectionState != SELECT_NONE) && (eDefaultRenderMode != RENDER_MODE_WIREFRAME)) + { + nPasses = 2; + } + + + if ( eSelectionState == SELECT_MODIFY ) + { + nStart = 2; + } + + pRender->RenderEnable( RENDER_POLYGON_OFFSET_FILL, true ); + + for (int nPass = nStart; nPass <= nPasses; nPass++) + { + // + // Render the second pass in wireframe. + // + + if ( nPass == 1 ) + { + // use the texture instead of the lightmap coord for decals + if (eDefaultRenderMode == RENDER_MODE_LIGHTMAP_GRID) + pRender->PushRenderMode( RENDER_MODE_TEXTURED ); + else + pRender->PushRenderMode( RENDER_MODE_CURRENT ); + } + else + { + pRender->PushRenderMode(RENDER_MODE_WIREFRAME); + } + + FOR_EACH_OBJ( m_Faces, pos ) + { + DecalFace_t *pDecalFace = m_Faces.Element(pos); + + if ((pDecalFace != NULL) && (pDecalFace->pFace != NULL)) + { + pDecalFace->pFace->Render3D(pRender); + } + } + + pRender->PopRenderMode(); + } + + pRender->RenderEnable( RENDER_POLYGON_OFFSET_FILL, false ); + + +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : File - +// bRMF - +// Output : int +//----------------------------------------------------------------------------- +int CMapDecal::SerializeRMF(std::fstream &File, BOOL bRMF) +{ + return(0); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : File - +// bRMF - +// Output : int +//----------------------------------------------------------------------------- +int CMapDecal::SerializeMAP(std::fstream &File, BOOL bRMF) +{ + return(0); +} + + +//----------------------------------------------------------------------------- +// Purpose: Notifies us that a copy of ourselves was pasted. +//----------------------------------------------------------------------------- +void CMapDecal::OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList) +{ + CMapClass::OnPaste(pCopy, pSourceWorld, pDestWorld, OriginalList, NewList); + + // + // Apply the copy to all solids in the destination world. + // + ((CMapDecal *)pCopy)->DecalAllSolids(pDestWorld); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called just after this object has been removed from the world so +// that it can unlink itself from other objects in the world. +// Input : pWorld - The world that we were just removed from. +// bNotifyChildren - Whether we should forward notification to our children. +//----------------------------------------------------------------------------- +void CMapDecal::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren) +{ + CMapClass::OnRemoveFromWorld(pWorld, bNotifyChildren); + + // + // We're going away. Unlink ourselves from any solids that we are attached to. + // + FOR_EACH_OBJ( m_Solids, pos ) + { + CMapSolid *pSolid = (CMapSolid *)m_Solids.Element(pos); + UpdateDependency(pSolid, NULL); + } + + m_Solids.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pTransBox - +//----------------------------------------------------------------------------- +void CMapDecal::DoTransform(const VMatrix &matrix) +{ + BaseClass::DoTransform(matrix); + RebuildDecalFaces(); +} |