diff options
Diffstat (limited to 'hammer/maplightcone.cpp')
| -rw-r--r-- | hammer/maplightcone.cpp | 685 |
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; +} |