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.
355 lines
9.3 KiB
355 lines
9.3 KiB
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include <ssemath.h>
|
|
#include <lightdesc.h>
|
|
#include "mathlib.h"
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
void LightDesc_t::RecalculateOneOverThetaDotMinusPhiDot()
|
|
{
|
|
float flSpread = m_ThetaDot - m_PhiDot;
|
|
if ( flSpread > 1.0e-10f )
|
|
{
|
|
// note - this quantity is very sensitive to round off error. the sse
|
|
// reciprocal approximation won't cut it here.
|
|
m_OneOverThetaDotMinusPhiDot = 1.0f / flSpread;
|
|
}
|
|
else
|
|
{
|
|
// hard falloff instead of divide by zero
|
|
m_OneOverThetaDotMinusPhiDot = 1.0f;
|
|
}
|
|
}
|
|
|
|
void LightDesc_t::RecalculateDerivedValues(void)
|
|
{
|
|
m_Flags = LIGHTTYPE_OPTIMIZATIONFLAGS_DERIVED_VALUES_CALCED;
|
|
if ( m_Attenuation0 )
|
|
{
|
|
m_Flags|=LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0;
|
|
}
|
|
if ( m_Attenuation1 )
|
|
{
|
|
m_Flags|=LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1;
|
|
}
|
|
if ( m_Attenuation2 )
|
|
{
|
|
m_Flags|=LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2;
|
|
}
|
|
|
|
if ( m_Type == MATERIAL_LIGHT_SPOT )
|
|
{
|
|
m_ThetaDot = cos( m_Theta );
|
|
m_PhiDot = cos( m_Phi );
|
|
RecalculateOneOverThetaDotMinusPhiDot();
|
|
}
|
|
if ( m_Type == MATERIAL_LIGHT_DIRECTIONAL )
|
|
{
|
|
// set position to be real far away in the right direction
|
|
m_Position = m_Direction;
|
|
m_Position *= 2.0e6;
|
|
}
|
|
|
|
m_RangeSquared = m_Range*m_Range;
|
|
}
|
|
|
|
void LightDesc_t::ComputeLightAtPointsForDirectional(
|
|
const FourVectors &pos, const FourVectors &normal,
|
|
FourVectors &color, bool DoHalfLambert ) const
|
|
{
|
|
FourVectors delta;
|
|
delta.DuplicateVector(m_Direction);
|
|
// delta.VectorNormalizeFast();
|
|
fltx4 strength=delta*normal;
|
|
if (DoHalfLambert)
|
|
{
|
|
strength=AddSIMD(MulSIMD(strength,Four_PointFives),Four_PointFives);
|
|
}
|
|
else
|
|
strength=MaxSIMD(Four_Zeros,delta*normal);
|
|
|
|
color.x=AddSIMD(color.x,MulSIMD(strength,ReplicateX4(m_Color.x)));
|
|
color.y=AddSIMD(color.y,MulSIMD(strength,ReplicateX4(m_Color.y)));
|
|
color.z=AddSIMD(color.z,MulSIMD(strength,ReplicateX4(m_Color.z)));
|
|
}
|
|
|
|
|
|
float LightDesc_t::DistanceAtWhichBrightnessIsLessThan( float flAmount ) const
|
|
{
|
|
float bright = m_Color.Length();
|
|
if ( bright > 0.0 )
|
|
{
|
|
flAmount /= m_Color.Length();
|
|
|
|
// calculate terms for quadratic equation
|
|
float a = flAmount * m_Attenuation2;
|
|
float b = flAmount * m_Attenuation1;
|
|
float c = flAmount * m_Attenuation0 - 1;
|
|
|
|
float r0, r1;
|
|
if ( SolveQuadratic( a, b, c, r0, r1 ) )
|
|
{
|
|
float rslt = MAX( 0, MAX( r0, r1 ) );
|
|
#ifdef _DEBUG
|
|
if ( rslt > 0.0 )
|
|
{
|
|
float fltest = 1.0 / ( m_Attenuation0 + rslt * m_Attenuation1 + rslt * rslt * m_Attenuation2 );
|
|
Assert( fabs( fltest - flAmount ) < 0.1 );
|
|
}
|
|
#endif
|
|
return rslt;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void LightDesc_t::ComputeLightAtPoints( const FourVectors &pos, const FourVectors &normal,
|
|
FourVectors &color, bool DoHalfLambert ) const
|
|
{
|
|
FourVectors delta;
|
|
Assert((m_Type==MATERIAL_LIGHT_POINT) || (m_Type==MATERIAL_LIGHT_SPOT) || (m_Type==MATERIAL_LIGHT_DIRECTIONAL));
|
|
switch (m_Type)
|
|
{
|
|
case MATERIAL_LIGHT_POINT:
|
|
case MATERIAL_LIGHT_SPOT:
|
|
delta.DuplicateVector(m_Position);
|
|
delta-=pos;
|
|
break;
|
|
|
|
case MATERIAL_LIGHT_DIRECTIONAL:
|
|
ComputeLightAtPointsForDirectional( pos, normal, color, DoHalfLambert );
|
|
return;
|
|
}
|
|
|
|
fltx4 dist2 = delta*delta;
|
|
|
|
dist2=MaxSIMD( Four_Ones, dist2 );
|
|
|
|
fltx4 falloff;
|
|
|
|
if( m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0 )
|
|
{
|
|
falloff = ReplicateX4(m_Attenuation0);
|
|
}
|
|
else
|
|
falloff= Four_Epsilons;
|
|
|
|
if( m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1 )
|
|
{
|
|
falloff=AddSIMD(falloff,MulSIMD(ReplicateX4(m_Attenuation1),SqrtEstSIMD(dist2)));
|
|
}
|
|
|
|
if( m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2 )
|
|
{
|
|
falloff=AddSIMD(falloff,MulSIMD(ReplicateX4(m_Attenuation2),dist2));
|
|
}
|
|
|
|
falloff=ReciprocalEstSIMD(falloff);
|
|
// Cull out light beyond this radius
|
|
// now, zero out elements for which dist2 was > range^2. !!speed!! lights should store dist^2 in sse format
|
|
if (m_Range != 0.f)
|
|
{
|
|
fltx4 RangeSquared=ReplicateX4(m_RangeSquared); // !!speed!!
|
|
falloff=AndSIMD(falloff,CmpLtSIMD(dist2,RangeSquared));
|
|
}
|
|
|
|
delta.VectorNormalizeFast();
|
|
fltx4 strength=delta*normal;
|
|
if (DoHalfLambert)
|
|
{
|
|
strength=AddSIMD(MulSIMD(strength,Four_PointFives),Four_PointFives);
|
|
}
|
|
else
|
|
strength=MaxSIMD(Four_Zeros,delta*normal);
|
|
|
|
switch(m_Type)
|
|
{
|
|
case MATERIAL_LIGHT_POINT:
|
|
// half-lambert
|
|
break;
|
|
|
|
case MATERIAL_LIGHT_SPOT:
|
|
{
|
|
fltx4 dot2=SubSIMD(Four_Zeros,delta*m_Direction); // dot position with spot light dir for cone falloff
|
|
|
|
|
|
fltx4 cone_falloff_scale=MulSIMD(ReplicateX4(m_OneOverThetaDotMinusPhiDot),
|
|
SubSIMD(dot2,ReplicateX4(m_PhiDot)));
|
|
cone_falloff_scale=MinSIMD(cone_falloff_scale,Four_Ones);
|
|
|
|
if ((m_Falloff!=0.0) && (m_Falloff!=1.0))
|
|
{
|
|
// !!speed!! could compute integer exponent needed by powsimd and store in light
|
|
cone_falloff_scale=PowSIMD(cone_falloff_scale,m_Falloff);
|
|
}
|
|
strength=MulSIMD(cone_falloff_scale,strength);
|
|
|
|
// now, zero out lighting where dot2<phidot. This will mask out any invalid results
|
|
// from pow function, etc
|
|
bi32x4 OutsideMask=CmpGtSIMD(dot2,ReplicateX4(m_PhiDot)); // outside light cone?
|
|
strength=AndSIMD(OutsideMask,strength);
|
|
}
|
|
break;
|
|
|
|
|
|
}
|
|
strength=MulSIMD(strength,falloff);
|
|
color.x=AddSIMD(color.x,MulSIMD(strength,ReplicateX4(m_Color.x)));
|
|
color.y=AddSIMD(color.y,MulSIMD(strength,ReplicateX4(m_Color.y)));
|
|
color.z=AddSIMD(color.z,MulSIMD(strength,ReplicateX4(m_Color.z)));
|
|
}
|
|
|
|
|
|
|
|
void LightDesc_t::ComputeNonincidenceLightAtPoints( const FourVectors &pos, FourVectors &color ) const
|
|
{
|
|
FourVectors delta;
|
|
Assert((m_Type==MATERIAL_LIGHT_POINT) || (m_Type==MATERIAL_LIGHT_SPOT) || (m_Type==MATERIAL_LIGHT_DIRECTIONAL));
|
|
switch (m_Type)
|
|
{
|
|
case MATERIAL_LIGHT_POINT:
|
|
case MATERIAL_LIGHT_SPOT:
|
|
delta.DuplicateVector(m_Position);
|
|
delta-=pos;
|
|
break;
|
|
|
|
case MATERIAL_LIGHT_DIRECTIONAL:
|
|
return;
|
|
}
|
|
|
|
fltx4 dist2 = delta*delta;
|
|
|
|
dist2=MaxSIMD( Four_Ones, dist2 );
|
|
|
|
fltx4 falloff;
|
|
|
|
if( m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0 )
|
|
{
|
|
falloff = ReplicateX4(m_Attenuation0);
|
|
}
|
|
else
|
|
falloff= Four_Epsilons;
|
|
|
|
if( m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1 )
|
|
{
|
|
falloff=AddSIMD(falloff,MulSIMD(ReplicateX4(m_Attenuation1),SqrtEstSIMD(dist2)));
|
|
}
|
|
|
|
if( m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2 )
|
|
{
|
|
falloff=AddSIMD(falloff,MulSIMD(ReplicateX4(m_Attenuation2),dist2));
|
|
}
|
|
|
|
falloff=ReciprocalEstSIMD(falloff);
|
|
// Cull out light beyond this radius
|
|
// now, zero out elements for which dist2 was > range^2. !!speed!! lights should store dist^2 in sse format
|
|
if (m_Range != 0.f)
|
|
{
|
|
fltx4 RangeSquared=ReplicateX4(m_RangeSquared); // !!speed!!
|
|
falloff=AndSIMD(falloff,CmpLtSIMD(dist2,RangeSquared));
|
|
}
|
|
|
|
delta.VectorNormalizeFast();
|
|
fltx4 strength = Four_Ones;
|
|
//fltx4 strength=delta;
|
|
//fltx4 strength = MaxSIMD(Four_Zeros,delta);
|
|
|
|
switch(m_Type)
|
|
{
|
|
case MATERIAL_LIGHT_POINT:
|
|
// half-lambert
|
|
break;
|
|
|
|
case MATERIAL_LIGHT_SPOT:
|
|
{
|
|
fltx4 dot2=SubSIMD(Four_Zeros,delta*m_Direction); // dot position with spot light dir for cone falloff
|
|
|
|
|
|
fltx4 cone_falloff_scale=MulSIMD(ReplicateX4(m_OneOverThetaDotMinusPhiDot),
|
|
SubSIMD(dot2,ReplicateX4(m_PhiDot)));
|
|
cone_falloff_scale=MinSIMD(cone_falloff_scale,Four_Ones);
|
|
|
|
if ((m_Falloff!=0.0) && (m_Falloff!=1.0))
|
|
{
|
|
// !!speed!! could compute integer exponent needed by powsimd and store in light
|
|
cone_falloff_scale=PowSIMD(cone_falloff_scale,m_Falloff);
|
|
}
|
|
strength=MulSIMD(cone_falloff_scale,strength);
|
|
|
|
// now, zero out lighting where dot2<phidot. This will mask out any invalid results
|
|
// from pow function, etc
|
|
bi32x4 OutsideMask=CmpGtSIMD(dot2,ReplicateX4(m_PhiDot)); // outside light cone?
|
|
strength=AndSIMD(OutsideMask,strength);
|
|
}
|
|
break;
|
|
|
|
|
|
}
|
|
strength=MulSIMD(strength,falloff);
|
|
color.x=AddSIMD(color.x,MulSIMD(strength,ReplicateX4(m_Color.x)));
|
|
color.y=AddSIMD(color.y,MulSIMD(strength,ReplicateX4(m_Color.y)));
|
|
color.z=AddSIMD(color.z,MulSIMD(strength,ReplicateX4(m_Color.z)));
|
|
}
|
|
|
|
|
|
|
|
void LightDesc_t::SetupOldStyleAttenuation( float fQuadraticAttn, float fLinearAttn, float fConstantAttn )
|
|
{
|
|
// old-style manually typed quadrtiac coefficients
|
|
if ( fQuadraticAttn < EQUAL_EPSILON )
|
|
fQuadraticAttn = 0;
|
|
|
|
if ( fLinearAttn < EQUAL_EPSILON)
|
|
fLinearAttn = 0;
|
|
|
|
if ( fConstantAttn < EQUAL_EPSILON)
|
|
fConstantAttn = 0;
|
|
|
|
if ( ( fConstantAttn < EQUAL_EPSILON ) &&
|
|
( fLinearAttn < EQUAL_EPSILON ) &&
|
|
( fQuadraticAttn < EQUAL_EPSILON ) )
|
|
fConstantAttn = 1;
|
|
|
|
m_Attenuation2=fQuadraticAttn;
|
|
m_Attenuation1=fLinearAttn;
|
|
m_Attenuation0=fConstantAttn;
|
|
float fScaleFactor = fQuadraticAttn * 10000 + fLinearAttn * 100 + fConstantAttn;
|
|
|
|
if ( fScaleFactor > 0 )
|
|
m_Color *= fScaleFactor;
|
|
}
|
|
|
|
void LightDesc_t::SetupNewStyleAttenuation( float fFiftyPercentDistance,
|
|
float fZeroPercentDistance )
|
|
{
|
|
// new style storing 50% and 0% distances
|
|
float d50=fFiftyPercentDistance;
|
|
float d0=fZeroPercentDistance;
|
|
if (d0<d50)
|
|
{
|
|
// !!warning in lib code???!!!
|
|
Warning("light has _fifty_percent_distance of %f but no zero_percent_distance\n",d50);
|
|
d0=2.0*d50;
|
|
}
|
|
float a=0,b=1,c=0;
|
|
if (! SolveInverseQuadraticMonotonic(0,1.0,d50,2.0,d0,256.0,a,b,c))
|
|
{
|
|
Warning("can't solve quadratic for light %f %f\n",d50,d0);
|
|
}
|
|
float v50=c+d50*(b+d50*a);
|
|
float scale=2.0/v50;
|
|
a*=scale;
|
|
b*=scale;
|
|
c*=scale;
|
|
m_Attenuation2=a;
|
|
m_Attenuation1=b;
|
|
m_Attenuation0=c;
|
|
}
|
|
|