|
|
//========= Copyright � 1996-2005, 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"
// 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], 10 * 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; }
|