summaryrefslogtreecommitdiff
path: root/hammer/maplightcone.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hammer/maplightcone.cpp')
-rw-r--r--hammer/maplightcone.cpp685
1 files changed, 685 insertions, 0 deletions
diff --git a/hammer/maplightcone.cpp b/hammer/maplightcone.cpp
new file mode 100644
index 0000000..a7fde10
--- /dev/null
+++ b/hammer/maplightcone.cpp
@@ -0,0 +1,685 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Renders a cone for spotlight entities. Only renders when the parent
+// entity is selected.
+//
+//=============================================================================//
+
+#include "stdafx.h"
+#include "Box3D.h"
+#include "fgdlib/HelperInfo.h"
+#include "MapDefs.h" // dvs: For COORD_NOTINIT
+#include "MapEntity.h"
+#include "MapLightCone.h"
+#include "Render3D.h"
+#include "Material.h"
+#include "materialsystem/imaterialsystem.h"
+#include "TextureSystem.h"
+#include "hammer.h"
+#include "Options.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#define NUM_LIGHTCONE_ZONES 5
+
+
+IMPLEMENT_MAPCLASS(CMapLightCone)
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Factory function. Used for creating a CMapLightCone helper 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 helper.
+// Output : Returns a pointer to the helper, NULL if an error occurs.
+//-----------------------------------------------------------------------------
+CMapClass *CMapLightCone::Create(CHelperInfo *pHelperInfo, CMapEntity *pParent)
+{
+ CMapLightCone *new1=new CMapLightCone;
+ if( new1 != NULL )
+ {
+ //
+ // The first parameter should be the inner fov key name. If it isn't
+ // there we assume "_inner_cone".
+ //
+ const char *pszKeyName = pHelperInfo->GetParameter(0);
+ if (pszKeyName != NULL)
+ {
+ strcpy(new1->m_szInnerConeKeyName, pszKeyName);
+ }
+ else
+ {
+ strcpy(new1->m_szInnerConeKeyName, "_inner_cone");
+ }
+
+ //
+ // The second parameter should be the outer fov key name. If it isn't
+ // there we assume "_cone".
+ //
+ pszKeyName = pHelperInfo->GetParameter(1);
+ if (pszKeyName != NULL)
+ {
+ strcpy(new1->m_szOuterConeKeyName, pszKeyName);
+ }
+ else
+ {
+ strcpy(new1->m_szOuterConeKeyName, "_cone");
+ }
+
+ //
+ // The third parameter should be the color of the light. If it isn't
+ // there we assume "_light".
+ //
+ pszKeyName = pHelperInfo->GetParameter(2);
+ if (pszKeyName != NULL)
+ {
+ strcpy(new1->m_szColorKeyName, pszKeyName);
+ }
+ else
+ {
+ strcpy(new1->m_szColorKeyName, "_light");
+ }
+
+ pszKeyName = pHelperInfo->GetParameter(3);
+ if (pszKeyName != NULL)
+ {
+ new1->m_flPitchScale = Q_atof( pszKeyName );
+ }
+ else
+ {
+ new1->m_flPitchScale = 1.0f;
+ }
+ }
+ return new1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CMapLightCone::CMapLightCone(void)
+{
+ m_fQuadraticAttn = 1;
+ m_fLinearAttn = 0;
+ m_fConstantAttn = 0;
+ m_bPitchSet = false;
+ m_fPitch = 0;
+ m_fFocus = 1;
+ m_flPitchScale = 1;
+
+ m_fBrightness = 100;
+ m_fInnerConeAngle = 0;
+ m_fOuterConeAngle = 45;
+
+ m_fFiftyPercentDistance = -1; // disabled - use attenuation
+ m_Angles.Init();
+ SignalUpdate( EVTYPE_LIGHTING_CHANGED );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor. Deletes faces allocated by BuildCone.
+//-----------------------------------------------------------------------------
+CMapLightCone::~CMapLightCone(void)
+{
+ for (int i = 0; i < m_Faces.Count(); i++)
+ {
+ CMapFace *pFace = m_Faces.Element(i);
+ delete pFace;
+ }
+ SignalUpdate( EVTYPE_LIGHTING_CHANGED );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds the light cone faces in local space. Does NOT call CalcBounds,
+// because that CalcBounds updates the parent, which causes problems
+// in the undo system.
+//-----------------------------------------------------------------------------
+void CMapLightCone::BuildCone(void)
+{
+ //
+ // Delete the current face list.
+ //
+ for (int i = 0; i < m_Faces.Count(); i++)
+ {
+ CMapFace *pFace = m_Faces.Element(i);
+ delete pFace;
+ }
+ m_Faces.RemoveAll();
+
+ //
+ // Make sure at least one of the lighting coefficients is nonzero.
+ //
+ if ((m_fQuadraticAttn == 0) && (m_fLinearAttn == 0) && (m_fConstantAttn == 0))
+ {
+ m_fConstantAttn = 1;
+ }
+
+ //
+ // Solve for the lighting scale factor by which the brightness will be multiplied.
+ //
+ float fScaleFactor = m_fQuadraticAttn * 10000 + m_fLinearAttn * 100 + m_fConstantAttn;
+ if (fScaleFactor == 0)
+ {
+ return;
+ }
+
+ //
+ // Calculate the distances from the light origin to the various zones.
+ //
+ float fOffsetDist = 0;
+ // Constant attenuation factor doesn't actually offset the cone yet. If it does, uncomment this:
+ //SolveQuadratic(fOffsetDist, 0, m_fQuadraticAttn, m_fLinearAttn, -m_fConstantAttn);
+
+ float fZoneDist[NUM_LIGHTCONE_ZONES];
+ memset( fZoneDist, 0, sizeof( fZoneDist ) );
+ fZoneDist[0] = 0;
+ SolveQuadratic(fZoneDist[1], 0.25 * fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
+ SolveQuadratic(fZoneDist[2], fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
+ SolveQuadratic(fZoneDist[3], 4 * fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
+ SolveQuadratic(fZoneDist[4], Options.view3d.fLightConeLength * fScaleFactor, m_fQuadraticAttn, m_fLinearAttn, m_fConstantAttn);
+
+ //
+ // there's no cone if it's greater then 90 degrees
+ //
+ if (m_fOuterConeAngle < 90)
+ {
+
+ //
+ // Calculate the cone radius at each zone.
+ //
+ float fZoneRadius[NUM_LIGHTCONE_ZONES];
+ for (int i = 0; i < NUM_LIGHTCONE_ZONES; i++)
+ {
+ fZoneRadius[i] = (fOffsetDist + fZoneDist[i]) * tan(DEG2RAD(m_fOuterConeAngle));
+ }
+
+ //
+ // Build the new face list using the new parameters.
+ //
+ float fStepSize = 360.0 / 15.0;
+ for (int nZone = 0; nZone < NUM_LIGHTCONE_ZONES - 1; nZone++)
+ {
+ float fSin0 = 0;
+ float fCos0 = 1;
+
+ float fTopDist = fZoneDist[nZone];
+ float fBottomDist = fZoneDist[nZone + 1];
+
+ float fTopRadius = fZoneRadius[nZone];
+ float fBottomRadius = fZoneRadius[nZone + 1];
+
+ for (int fAngle = fStepSize; fAngle <= 361; fAngle += fStepSize)
+ {
+ float fSin1 = sin(DEG2RAD(fAngle));
+ float fCos1 = cos(DEG2RAD(fAngle));
+
+ Vector Points[4];
+
+ Points[0][2] = fBottomRadius * fCos1;
+ Points[0][1] = fBottomRadius * fSin1;
+ Points[0][0] = fBottomDist;
+
+ Points[1][2] = fBottomRadius * fCos0;
+ Points[1][1] = fBottomRadius * fSin0;
+ Points[1][0] = fBottomDist;
+
+ Points[2][2] = fTopRadius * fCos0;
+ Points[2][1] = fTopRadius * fSin0;
+ Points[2][0] = fTopDist;
+
+ int nPoints = 3;
+ if (fTopRadius != 0)
+ {
+ Points[3][2] = fTopRadius * fCos1;
+ Points[3][1] = fTopRadius * fSin1;
+ Points[3][0] = fTopDist;
+ nPoints = 4;
+ }
+
+ CMapFace *pFace = new CMapFace;
+ pFace->SetRenderColor(r * (1 - nZone / (float)NUM_LIGHTCONE_ZONES), g * (1 - nZone / (float)NUM_LIGHTCONE_ZONES), b * (1 - nZone / (float)NUM_LIGHTCONE_ZONES));
+ pFace->SetRenderAlpha(180);
+ pFace->CreateFace(Points, nPoints);
+ pFace->RenderUnlit(true);
+ m_Faces.AddToTail(pFace);
+
+ fSin0 = fSin1;
+ fCos0 = fCos1;
+ }
+ }
+ }
+
+ //
+ // Lobe's aren't defined for > 90
+ //
+ if (m_fOuterConeAngle > 90)
+ return;
+
+ //
+ // Build the a face list that shows light-angle falloff
+ //
+ float fStepSize = 360.0 / 15.0;
+ float fPitchStepSize = 90.0 / 15.0;
+ float fFocusRadius0 = 0;
+ float fFocusDist0 = fZoneDist[1];
+ float fInnerDot = cos(DEG2RAD(m_fInnerConeAngle));
+ float fOuterDot = cos(DEG2RAD(m_fOuterConeAngle));
+
+ for (float fPitch = fPitchStepSize; fPitch < m_fOuterConeAngle + fPitchStepSize; fPitch += fPitchStepSize)
+ {
+ float fSin0 = 0;
+ float fCos0 = 1;
+
+ // clamp to edge of cone
+ if (fPitch > m_fOuterConeAngle)
+ fPitch = m_fOuterConeAngle;
+
+ float fIllumination = 0;
+ if (fPitch <= m_fInnerConeAngle)
+ {
+ fIllumination = 1.0;
+ }
+ else
+ {
+ float fPitchDot = cos(DEG2RAD(fPitch));
+
+ fIllumination = (fPitchDot - fOuterDot) / (fInnerDot - fOuterDot);
+
+ if ((m_fFocus != 1) && (m_fFocus != 0))
+ {
+ fIllumination = pow( fIllumination, m_fFocus );
+ }
+ }
+
+ // cosine falloff ^ exponent
+
+ // draw as lobe
+ float fFocusDist1 = cos(DEG2RAD(fPitch)) * fIllumination * fZoneDist[1];
+ float fFocusRadius1 = sin(DEG2RAD(fPitch)) * fIllumination * fZoneDist[1];
+
+ // draw as disk
+ // float fFocusDist1 = fZoneDist[1];
+ // float fFocusRadius1 = sin(DEG2RAD(fPitch)) * fZoneRadius[1] / sin(DEG_RAD * m_fConeAngle);
+
+ for (int fAngle = fStepSize; fAngle <= 361; fAngle += fStepSize)
+ {
+ float fSin1 = sin(DEG2RAD(fAngle));
+ float fCos1 = cos(DEG2RAD(fAngle));
+
+ Vector Points[4];
+
+ Points[0][2] = fFocusRadius1 * fCos0;
+ Points[0][1] = fFocusRadius1 * fSin0;
+ Points[0][0] = fFocusDist1;
+
+ Points[1][2] = fFocusRadius1 * fCos1;
+
+ Points[1][1] = fFocusRadius1 * fSin1;
+ Points[1][0] = fFocusDist1;
+
+ Points[2][2] = fFocusRadius0 * fCos1;
+ Points[2][1] = fFocusRadius0 * fSin1;
+ Points[2][0] = fFocusDist0;
+
+ int nPoints = 3;
+ if (fFocusRadius0 != 0)
+ {
+ Points[3][2] = fFocusRadius0 * fCos0;
+ Points[3][1] = fFocusRadius0 * fSin0;
+ Points[3][0] = fFocusDist0;
+ nPoints = 4;
+ }
+
+ CMapFace *pFace = new CMapFace;
+ pFace->SetRenderColor(r * fIllumination, g * fIllumination, b * fIllumination);
+ pFace->SetRenderAlpha(180);
+ pFace->CreateFace(Points, nPoints);
+ pFace->RenderUnlit(true);
+ m_Faces.AddToTail(pFace);
+
+ fSin0 = fSin1;
+ fCos0 = fCos1;
+ }
+ fFocusRadius0 = fFocusRadius1;
+ fFocusDist0 = fFocusDist1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bFullUpdate -
+//-----------------------------------------------------------------------------
+void CMapLightCone::CalcBounds(BOOL bFullUpdate)
+{
+ CMapClass::CalcBounds(bFullUpdate);
+
+ //
+ // HACK: Update our origin to stick to our parent.
+ //
+ if (m_pParent != NULL)
+ {
+ GetParent()->GetOrigin(m_Origin);
+ }
+
+ //
+ // Pretend to be very small for the 2D view. Won't be necessary when 2D
+ // rendering is done in the map classes.
+ //
+ m_Render2DBox.ResetBounds();
+ m_Render2DBox.UpdateBounds(m_Origin);
+
+ SetCullBoxFromFaceList( &m_Faces );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : Angles -
+//-----------------------------------------------------------------------------
+void CMapLightCone::GetAngles(QAngle &Angles)
+{
+ Angles = m_Angles;
+
+ if (m_bPitchSet)
+ {
+ Angles[PITCH] = m_fPitch;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CMapClass
+//-----------------------------------------------------------------------------
+CMapClass *CMapLightCone::Copy(bool bUpdateDependencies)
+{
+ CMapLightCone *pCopy = new CMapLightCone;
+
+ if (pCopy != NULL)
+ {
+ pCopy->CopyFrom(this, bUpdateDependencies);
+ }
+
+ return(pCopy);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pObject -
+// Output : CMapClass
+//-----------------------------------------------------------------------------
+CMapClass *CMapLightCone::CopyFrom(CMapClass *pObject, bool bUpdateDependencies)
+{
+ Assert(pObject->IsMapClass(MAPCLASS_TYPE(CMapLightCone)));
+ CMapLightCone *pFrom = (CMapLightCone *)pObject;
+
+ CMapClass::CopyFrom(pObject, bUpdateDependencies);
+
+ m_fBrightness = pFrom->m_fBrightness;
+
+ m_fQuadraticAttn = pFrom->m_fQuadraticAttn;
+ m_fLinearAttn = pFrom->m_fLinearAttn;
+ m_fConstantAttn = pFrom->m_fConstantAttn;
+ m_flPitchScale = pFrom->m_flPitchScale;
+
+ m_fInnerConeAngle = pFrom->m_fInnerConeAngle;
+ m_fOuterConeAngle = pFrom->m_fOuterConeAngle;
+
+ m_Angles = pFrom->m_Angles;
+
+ m_bPitchSet = pFrom->m_bPitchSet;
+ m_fPitch = pFrom->m_fPitch;
+
+ m_fFocus = pFrom->m_fFocus;
+
+ m_fFiftyPercentDistance = pFrom->m_fFiftyPercentDistance;
+ m_fZeroPercentDistance = pFrom->m_fZeroPercentDistance;
+ m_LightColor = pFrom->m_LightColor;
+
+ Q_strncpy( m_szColorKeyName, pFrom->m_szColorKeyName, sizeof( m_szColorKeyName ) );
+ Q_strncpy( m_szInnerConeKeyName, pFrom->m_szInnerConeKeyName, sizeof( m_szInnerConeKeyName ) );
+ Q_strncpy( m_szOuterConeKeyName, pFrom->m_szOuterConeKeyName, sizeof( m_szOuterConeKeyName ) );
+
+ BuildCone();
+
+ SignalUpdate( EVTYPE_LIGHTING_CHANGED );
+ return(this);
+}
+
+
+//-----------------------------------------------------------------------------
+// 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 CMapLightCone::OnParentKeyChanged(const char *szKey, const char *szValue)
+{
+ bool bRebuild = true;
+
+ if (!stricmp(szKey, "angles"))
+ {
+ sscanf(szValue, "%f %f %f", &m_Angles[PITCH], &m_Angles[YAW], &m_Angles[ROLL]);
+ }
+ else if (!stricmp(szKey, m_szColorKeyName))
+ {
+ int nRed;
+ int nGreen;
+ int nBlue;
+ int nBrightness;
+ sscanf(szValue, "%d %d %d %d", &nRed, &nGreen, &nBlue, &nBrightness);
+
+ r = m_LightColor.x = nRed;
+ g = m_LightColor.y = nGreen;
+ b = m_LightColor.z = nBlue;
+ m_fBrightness = nBrightness;
+ }
+ else if (!stricmp(szKey, "pitch"))
+ {
+ // Pitch
+ m_bPitchSet = true;
+ m_fPitch = atof(szValue);
+ }
+ else if (!stricmp(szKey, "_constant_attn"))
+ {
+ // Constant attenuation
+ m_fConstantAttn = atof(szValue);
+ }
+ else if (!stricmp(szKey, "_linear_attn"))
+ {
+ // Linear attenuation
+ m_fLinearAttn = atof(szValue);
+ }
+ else if (!stricmp(szKey, "_quadratic_attn"))
+ {
+ // Quadratic attenuation
+ m_fQuadraticAttn = atof(szValue);
+ }
+ else if (!stricmp(szKey, "_exponent"))
+ {
+ // Focus
+ m_fFocus = atof(szValue);
+ }
+ else if (!stricmp(szKey, "_fifty_percent_distance"))
+ {
+ // Focus
+ m_fFiftyPercentDistance = atof(szValue);
+ }
+ else if (!stricmp(szKey, "_zero_percent_distance"))
+ {
+ // Focus
+ m_fZeroPercentDistance = atof(szValue);
+ }
+ else if (!stricmp(szKey, m_szInnerConeKeyName) || !stricmp(szKey, m_szOuterConeKeyName))
+ {
+ // check both of these together since they might be the same key.
+ if( !stricmp(szKey, m_szInnerConeKeyName ))
+ {
+ // Inner Cone angle
+ m_fInnerConeAngle = atof(szValue);
+ }
+ if( !stricmp(szKey, m_szOuterConeKeyName ))
+ {
+ // Outer Cone angle
+ m_fOuterConeAngle = atof(szValue);
+ }
+ }
+ else
+ {
+ bRebuild = false;
+ }
+
+ if (bRebuild)
+ {
+ SignalUpdate( EVTYPE_LIGHTING_CHANGED );
+ BuildCone();
+ PostUpdate(Notify_Changed);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// 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 CMapLightCone::PostloadWorld(CMapWorld *pWorld)
+{
+ CMapClass::PostloadWorld(pWorld);
+
+ BuildCone();
+ SignalUpdate( EVTYPE_LIGHTING_CHANGED );
+ CalcBounds();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pRender -
+//-----------------------------------------------------------------------------
+void CMapLightCone::Render3D(CRender3D *pRender)
+{
+ if (m_pParent->IsSelected())
+ {
+ CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PushMatrix();
+
+ pRenderContext->Translate(m_Origin[0], m_Origin[1], m_Origin[2]);
+
+ QAngle Angles;
+ GetAngles(Angles);
+
+ pRenderContext->Rotate(Angles[YAW], 0, 0, 1);
+ pRenderContext->Rotate(m_flPitchScale * Angles[PITCH], 0, -1, 0);
+ pRenderContext->Rotate(Angles[ROLL], 1, 0, 0);
+
+ if (
+ (pRender->GetCurrentRenderMode() != RENDER_MODE_LIGHT_PREVIEW2) &&
+ (pRender->GetCurrentRenderMode() != RENDER_MODE_LIGHT_PREVIEW_RAYTRACED) &&
+ (GetSelectionState() != SELECT_MODIFY )
+ )
+ {
+ // Render the cone faces flatshaded.
+ pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT );
+
+ for (int i = 0; i < m_Faces.Count(); i++)
+ {
+ CMapFace *pFace = m_Faces.Element(i);
+ pFace->Render3D(pRender);
+ }
+
+ pRender->PopRenderMode();
+ }
+
+ //
+ // Render the cone faces in yellow wireframe (on top)
+ //
+ pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
+
+ for (int i = 0; i < m_Faces.Count(); i++)
+ {
+ CMapFace *pFace = m_Faces.Element(i);
+ pFace->Render3D(pRender);
+ }
+
+ //
+ // Restore the default rendering mode.
+ //
+ pRender->PopRenderMode();
+
+ pRenderContext->PopMatrix();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : File -
+// bRMF -
+// Output : int
+//-----------------------------------------------------------------------------
+int CMapLightCone::SerializeRMF(std::fstream &File, BOOL bRMF)
+{
+ return(0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : File -
+// bRMF -
+// Output : int
+//-----------------------------------------------------------------------------
+int CMapLightCone::SerializeMAP(std::fstream &File, BOOL bRMF)
+{
+ return(0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Solves a quadratic equation with the given coefficients.
+// Input : x - Receives solution.
+// y - Root to solve for.
+// A, B, C - Quadratic, linear, and constant coefficients.
+// Output : Returns true if a real solution was found, false if not.
+//-----------------------------------------------------------------------------
+bool CMapLightCone::SolveQuadratic(float &x, float y, float A, float B, float C)
+{
+ C -= y;
+
+ if (A == 0)
+ {
+ if (B != 0)
+ {
+ x = -C / B;
+ return(true);
+ }
+ }
+ else
+ {
+ float fDeterminant = B * B - 4 * A * C;
+ if (fDeterminant > 0)
+ {
+ x = (-B + sqrt(fDeterminant)) / (2 * A);
+ return(true);
+ }
+ }
+
+ return(false);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Never select anything because of this helper.
+//-----------------------------------------------------------------------------
+CMapClass *CMapLightCone::PrepareSelection(SelectMode_t eSelectMode)
+{
+ return NULL;
+}