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.
1003 lines
24 KiB
1003 lines
24 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
|
|
#include "hsTypes.h" |
|
#include "plLightInfo.h" |
|
#include "plLightKonstants.h" |
|
#include "hsBounds.h" |
|
#include "hsStream.h" |
|
#include "hsResMgr.h" |
|
#include "pnMessage/plNodeRefMsg.h" |
|
#include "plgDispatch.h" |
|
#include "plIntersect/plVolumeIsect.h" |
|
#include "plDrawable/plSpaceTree.h" |
|
#include "plDrawable/plDrawableGenerator.h" |
|
#include "plDrawable/plDrawableSpans.h" |
|
#include "plPipeline/hsGDeviceRef.h" |
|
#include "plPipeline/plRenderTarget.h" |
|
#include "hsFastMath.h" |
|
#include "pnSceneObject/plDrawInterface.h" |
|
#include "plSurface/plLayerInterface.h" |
|
#include "plSurface/plLayer.h" |
|
#include "plSurface/hsGMaterial.h" |
|
#include "plGImage/plMipmap.h" |
|
#include "plMessage/plRenderMsg.h" |
|
#include "plMessage/plRenderRequestMsg.h" |
|
#include "plScene/plRenderRequest.h" |
|
#include "plPipeline.h" |
|
#include "plIntersect/plSoftVolume.h" |
|
#include "plPipeline/plPipeDebugFlags.h" |
|
#include "pnMessage/plPipeResMakeMsg.h" |
|
|
|
#include "plScene/plVisRegion.h" |
|
#include "plScene/plVisMgr.h" |
|
|
|
// heinous |
|
#include "plNetClient/plNetClientMgr.h" |
|
#include "pnMessage/plEnableMsg.h" |
|
static hsScalar kMaxYon = 1000.f; |
|
static hsScalar kMinHither = 1.f; |
|
|
|
#include "plLightProxy.h" |
|
|
|
#include "plDrawable/plDrawableGenerator.h" |
|
|
|
plLightInfo::plLightInfo() |
|
: fSceneNode(nil), |
|
fDeviceRef(nil), |
|
fVolFlags(0), |
|
fProjection(nil), |
|
fSoftVolume(nil) |
|
{ |
|
fLightToWorld.Reset(); |
|
fWorldToLight.Reset(); |
|
|
|
fLocalToWorld.Reset(); |
|
fWorldToLocal.Reset(); |
|
|
|
fLightToLocal.Reset(); |
|
fLocalToLight.Reset(); |
|
|
|
fWorldToProj.Reset(); |
|
|
|
fNextDevPtr = nil; |
|
fPrevDevPtr = nil; |
|
|
|
fProxyGen = TRACKED_NEW plLightProxy; |
|
fProxyGen->Init(this); |
|
|
|
fRegisteredForRenderMsg = false; |
|
|
|
fVisSet.SetBit(plVisMgr::kNormal); |
|
} |
|
|
|
plLightInfo::~plLightInfo() |
|
{ |
|
if( fNextDevPtr != nil || fPrevDevPtr != nil ) |
|
Unlink(); |
|
|
|
hsRefCnt_SafeUnRef( fDeviceRef ); |
|
|
|
if( fRegisteredForRenderMsg ) |
|
{ |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); |
|
fRegisteredForRenderMsg = false; |
|
} |
|
|
|
delete fProxyGen; |
|
} |
|
|
|
void plLightInfo::SetDeviceRef( hsGDeviceRef *ref ) |
|
{ |
|
hsRefCnt_SafeAssign( fDeviceRef, ref ); |
|
} |
|
|
|
void plLightInfo::IRefresh() |
|
{ |
|
ICheckMaxStrength(); |
|
if( fDeviceRef ) |
|
fDeviceRef->SetDirty( true ); |
|
} |
|
|
|
void plLightInfo::ICheckMaxStrength() |
|
{ |
|
hsScalar r = GetDiffuse().r >= 0 ? GetDiffuse().r : -GetDiffuse().r; |
|
hsScalar g = GetDiffuse().g >= 0 ? GetDiffuse().g : -GetDiffuse().g; |
|
hsScalar b = GetDiffuse().b >= 0 ? GetDiffuse().b : -GetDiffuse().b; |
|
fMaxStrength = |
|
r > g |
|
? ( |
|
r > b |
|
? r |
|
: b |
|
) |
|
: ( |
|
g > b |
|
? g |
|
: b |
|
); |
|
|
|
const hsScalar kMinMaxStrength = 1.e-2f; |
|
SetZero(fMaxStrength < kMinMaxStrength); |
|
} |
|
|
|
void plLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const |
|
{ |
|
if( IsIdle() ) |
|
{ |
|
strength = scale = 0.f; |
|
return; |
|
} |
|
|
|
strength = fMaxStrength; |
|
|
|
scale = 1.f; |
|
if( fSoftVolume ) |
|
{ |
|
scale = fSoftVolume->GetStrength(bnd.GetCenter()); |
|
strength *= scale; |
|
|
|
} |
|
return; |
|
} |
|
|
|
void plLightInfo::GetAffectedForced(const plSpaceTree* space, hsBitVector& list, hsBool charac) |
|
{ |
|
Refresh(); |
|
|
|
if( IGetIsect() ) |
|
{ |
|
space->HarvestLeaves(IGetIsect(), list); |
|
} |
|
else |
|
{ |
|
list.Set(space->GetNumLeaves()); |
|
} |
|
} |
|
|
|
void plLightInfo::GetAffected(const plSpaceTree* space, hsBitVector& list, hsBool charac) |
|
{ |
|
Refresh(); |
|
|
|
if( IsIdle() ) |
|
return; |
|
|
|
if( !GetProperty(kLPHasIncludes) || (GetProperty(kLPIncludesChars) && charac) ) |
|
{ |
|
if( IGetIsect() ) |
|
{ |
|
space->HarvestLeaves(IGetIsect(), list); |
|
} |
|
else |
|
{ |
|
list.Set(space->GetNumLeaves()); |
|
} |
|
} |
|
} |
|
|
|
const hsTArray<Int16>& plLightInfo::GetAffected(plSpaceTree* space, const hsTArray<Int16>& visList, hsTArray<Int16>& litList, hsBool charac) |
|
{ |
|
Refresh(); |
|
|
|
if( !IsIdle() ) |
|
{ |
|
if( !GetProperty(kLPHasIncludes) || (GetProperty(kLPIncludesChars) && charac) ) |
|
{ |
|
if( IGetIsect() ) |
|
{ |
|
static hsBitVector cache; |
|
cache.Clear(); |
|
space->EnableLeaves(visList, cache); |
|
|
|
space->HarvestEnabledLeaves(IGetIsect(), cache, litList); |
|
|
|
return litList; |
|
} |
|
else |
|
{ |
|
return visList; |
|
} |
|
} |
|
} |
|
|
|
litList.SetCount(0); |
|
return litList; |
|
} |
|
|
|
//// Set/GetProperty ///////////////////////////////////////////////////////// |
|
// Sets/gets a property just like the normal Set/GetNativeProperty, but the |
|
// flag taken in is from plDrawInterface, not our props flags. So we have to |
|
// translate... |
|
|
|
void plLightInfo::SetProperty( int prop, hsBool on ) |
|
{ |
|
plObjInterface::SetProperty(prop, on); |
|
if( kDisable == prop ) |
|
fProxyGen->SetDisable(on); |
|
} |
|
|
|
//// SetSpecular ///////////////////////////////////////////////////////////// |
|
// A bit more complicated here--make sure we set/clear the kLPHasSpecular |
|
// flag so we can test more easily for such a condition. |
|
|
|
void plLightInfo::SetSpecular( const hsColorRGBA& c ) |
|
{ |
|
fSpecular = c; |
|
|
|
if( fSpecular.r == 0.f && fSpecular.g == 0.f && fSpecular.b == 0.f ) |
|
SetProperty( kLPHasSpecular, false ); |
|
else |
|
SetProperty( kLPHasSpecular, true ); |
|
|
|
SetDirty(); |
|
} |
|
|
|
void plLightInfo::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) |
|
{ |
|
fLocalToWorld = l2w; |
|
fWorldToLocal = w2l; |
|
|
|
fLightToWorld = l2w * fLightToLocal; |
|
fWorldToLight = fLocalToLight * w2l; |
|
|
|
if( IGetIsect() ) |
|
IGetIsect()->SetTransform(fLightToWorld, fWorldToLight); |
|
|
|
if( fDeviceRef != nil ) |
|
fDeviceRef->SetDirty( true ); |
|
|
|
fProxyGen->SetTransform(fLightToWorld, fWorldToLight); |
|
|
|
SetDirty(true); |
|
|
|
if( GetProjection() ) |
|
{ |
|
Refresh(); |
|
hsMatrix44 w2proj = IGetWorldToProj(); |
|
plLayer* lay = plLayer::ConvertNoRef(GetProjection()->BottomOfStack()); |
|
if( lay ) |
|
{ |
|
lay->SetTransform(w2proj); |
|
} |
|
} |
|
} |
|
|
|
void plLightInfo::SetLocalToLight(const hsMatrix44& l2lt, const hsMatrix44& lt2l) |
|
{ |
|
fLocalToLight = l2lt; |
|
fLightToLocal = lt2l; |
|
} |
|
|
|
const hsMatrix44& plLightInfo::GetLocalToWorld() const |
|
{ |
|
return fLocalToWorld; |
|
} |
|
|
|
const hsMatrix44& plLightInfo::GetWorldToLocal() const |
|
{ |
|
return fWorldToLocal; |
|
} |
|
|
|
const hsMatrix44& plLightInfo::GetLightToWorld() const |
|
{ |
|
return fLightToWorld; |
|
} |
|
|
|
const hsMatrix44& plLightInfo::GetWorldToLight() const |
|
{ |
|
return fWorldToLight; |
|
} |
|
|
|
#include "plProfile.h" |
|
plProfile_CreateTimer("Light Info", "RenderSetup", LightInfo); |
|
|
|
void plLightInfo::IAddVisRegion(plVisRegion* reg) |
|
{ |
|
if( reg ) |
|
{ |
|
int idx = fVisRegions.Find(reg); |
|
if( fVisRegions.kMissingIndex == idx ) |
|
{ |
|
fVisRegions.Append(reg); |
|
if( reg->GetProperty(plVisRegion::kIsNot) ) |
|
fVisNot.SetBit(reg->GetIndex()); |
|
else |
|
{ |
|
fVisSet.SetBit(reg->GetIndex()); |
|
if( reg->ReplaceNormal() ) |
|
fVisSet.ClearBit(plVisMgr::kNormal); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void plLightInfo::IRemoveVisRegion(plVisRegion* reg) |
|
{ |
|
if( reg ) |
|
{ |
|
int idx = fVisRegions.Find(reg); |
|
if( fVisRegions.kMissingIndex != idx ) |
|
{ |
|
fVisRegions.Remove(idx); |
|
if( reg->GetProperty(plVisRegion::kIsNot) ) |
|
fVisNot.ClearBit(reg->GetIndex()); |
|
else |
|
fVisSet.ClearBit(reg->GetIndex()); |
|
} |
|
} |
|
} |
|
|
|
|
|
hsBool plLightInfo::MsgReceive(plMessage* msg) |
|
{ |
|
plRenderMsg* rendMsg = plRenderMsg::ConvertNoRef(msg); |
|
if( rendMsg ) |
|
{ |
|
plProfile_BeginLap(LightInfo, this->GetKey()->GetUoid().GetObjectName()); |
|
|
|
if( !fDeviceRef && !GetProperty(kLPShadowOnly) ) |
|
{ |
|
rendMsg->Pipeline()->RegisterLight( this ); |
|
} |
|
|
|
ICheckMaxStrength(); |
|
|
|
plProfile_EndLap(LightInfo, this->GetKey()->GetUoid().GetObjectName()); |
|
return true; |
|
} |
|
plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); |
|
if( refMsg ) |
|
{ |
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) |
|
{ |
|
switch( refMsg->fType ) |
|
{ |
|
case kProjection: |
|
fProjection = plLayerInterface::ConvertNoRef(refMsg->GetRef()); |
|
{ |
|
if( GetKey() && GetKey()->GetName() && !strncmp(GetKey()->GetName(), "RTPatternLight", strlen("RTPatternLight")) ) |
|
SetProperty(kLPForceProj, true); |
|
} |
|
break; |
|
case kSoftVolume: |
|
fSoftVolume = plSoftVolume::ConvertNoRef(refMsg->GetRef()); |
|
break; |
|
case kVisRegion: |
|
IAddVisRegion(plVisRegion::ConvertNoRef(refMsg->GetRef())); |
|
break; |
|
} |
|
} |
|
else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) ) |
|
{ |
|
switch( refMsg->fType ) |
|
{ |
|
case kProjection: |
|
fProjection = nil; |
|
break; |
|
case kSoftVolume: |
|
fSoftVolume = nil; |
|
break; |
|
case kVisRegion: |
|
IRemoveVisRegion(plVisRegion::ConvertNoRef(refMsg->GetRef())); |
|
break; |
|
} |
|
} |
|
return true; |
|
} |
|
plPipeRTMakeMsg* rtMake = plPipeRTMakeMsg::ConvertNoRef(msg); |
|
if( rtMake ) |
|
{ |
|
// Make sure we're registered with the pipeline |
|
// If we're only here to cast shadows, just don't tell anyone |
|
// about us. |
|
if( !fDeviceRef && !GetProperty(kLPShadowOnly) ) |
|
{ |
|
rtMake->Pipeline()->RegisterLight( this ); |
|
} |
|
return true; |
|
} |
|
plEnableMsg* enaMsg = plEnableMsg::ConvertNoRef(msg); |
|
if( enaMsg ) |
|
{ |
|
SetProperty(kDisable, enaMsg->Cmd(plEnableMsg::kDisable)); |
|
return true; |
|
} |
|
|
|
|
|
|
|
return plObjInterface::MsgReceive(msg); |
|
} |
|
|
|
|
|
void plLightInfo::Read(hsStream* s, hsResMgr* mgr) |
|
{ |
|
hsRefCnt_SafeUnRef( fDeviceRef ); |
|
fDeviceRef = nil; |
|
|
|
plObjInterface::Read(s, mgr); |
|
|
|
fAmbient.Read(s); |
|
fDiffuse.Read(s); |
|
fSpecular.Read(s); |
|
|
|
fLightToLocal.Read(s); |
|
fLocalToLight.Read(s); |
|
|
|
fLightToWorld.Read(s); |
|
fWorldToLight.Read(s); |
|
|
|
fLocalToWorld = fLightToWorld * fLocalToLight; |
|
fWorldToLocal = fLightToLocal * fWorldToLight; |
|
|
|
mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kProjection), plRefFlags::kActiveRef); |
|
|
|
mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kSoftVolume), plRefFlags::kActiveRef); |
|
|
|
// Let our sceneNode know we're here. |
|
plKey nodeKey = mgr->ReadKey(s); |
|
ISetSceneNode(nodeKey); |
|
|
|
int n = s->ReadSwap32(); |
|
fVisRegions.SetCountAndZero(n); |
|
int i; |
|
for( i = 0; i < n; i++ ) |
|
mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kVisRegion), plRefFlags::kActiveRef); |
|
|
|
SetDirty(true); |
|
} |
|
|
|
void plLightInfo::Write(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plObjInterface::Write(s, mgr); |
|
|
|
fAmbient.Write(s); |
|
fDiffuse.Write(s); |
|
fSpecular.Write(s); |
|
|
|
fLightToLocal.Write(s); |
|
fLocalToLight.Write(s); |
|
|
|
fLightToWorld.Write(s); |
|
fWorldToLight.Write(s); |
|
|
|
mgr->WriteKey(s, GetProjection()); |
|
|
|
mgr->WriteKey(s, fSoftVolume); |
|
|
|
mgr->WriteKey(s, fSceneNode); |
|
|
|
s->WriteSwap32(fVisRegions.GetCount()); |
|
int i; |
|
for( i = 0; i < fVisRegions.GetCount(); i++ ) |
|
mgr->WriteKey(s, fVisRegions[i]); |
|
} |
|
|
|
// These two should only be called by the SceneObject |
|
void plLightInfo::ISetSceneNode(plKey node) |
|
{ |
|
if( node != fSceneNode ) |
|
{ |
|
if( node ) |
|
{ |
|
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(node, plRefMsg::kOnCreate, -1, plNodeRefMsg::kLight); |
|
hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef); |
|
} |
|
if( fSceneNode ) |
|
{ |
|
fSceneNode->Release(GetKey()); |
|
} |
|
} |
|
fSceneNode = node; |
|
|
|
if( fSceneNode != nil ) |
|
{ |
|
if( !fRegisteredForRenderMsg ) |
|
{ |
|
plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); |
|
plgDispatch::Dispatch()->RegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); |
|
fRegisteredForRenderMsg = true; |
|
} |
|
} |
|
else if( fRegisteredForRenderMsg ) |
|
{ |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); |
|
fRegisteredForRenderMsg = false; |
|
} |
|
} |
|
|
|
plKey plLightInfo::GetSceneNode() const |
|
{ |
|
return fSceneNode; |
|
} |
|
|
|
//// Link & Unlink /////////////////////////////////////////////////////// |
|
|
|
void plLightInfo::Unlink( void ) |
|
{ |
|
hsAssert( fPrevDevPtr, "Light info not in list" ); |
|
if( fNextDevPtr ) |
|
fNextDevPtr->fPrevDevPtr = fPrevDevPtr; |
|
*fPrevDevPtr = fNextDevPtr; |
|
|
|
fNextDevPtr = nil; |
|
fPrevDevPtr = nil; |
|
} |
|
|
|
void plLightInfo::Link( plLightInfo **back ) |
|
{ |
|
hsAssert( fNextDevPtr == nil && fPrevDevPtr == nil, "Trying to link a lightInfo that's already linked" ); |
|
|
|
fNextDevPtr = *back; |
|
if( *back ) |
|
(*back)->fPrevDevPtr = &fNextDevPtr; |
|
fPrevDevPtr = back; |
|
*back = this; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////// |
|
///// Standard light types |
|
////////////////////////////////////////////////////////////////////////// |
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// Directional |
|
|
|
plDirectionalLightInfo::plDirectionalLightInfo() |
|
{ |
|
} |
|
|
|
plDirectionalLightInfo::~plDirectionalLightInfo() |
|
{ |
|
} |
|
|
|
void plDirectionalLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const |
|
{ |
|
plLightInfo::GetStrengthAndScale(bnd, strength, scale); |
|
} |
|
|
|
void plDirectionalLightInfo::Read(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plLightInfo::Read(s, mgr); |
|
} |
|
|
|
void plDirectionalLightInfo::Write(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plLightInfo::Write(s, mgr); |
|
} |
|
|
|
|
|
hsVector3 plDirectionalLightInfo::GetWorldDirection() const |
|
{ |
|
return -fLightToWorld.GetAxis( hsMatrix44::kUp ); |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// Limited Directional |
|
plLimitedDirLightInfo::plLimitedDirLightInfo() |
|
: fParPlanes(nil) |
|
{ |
|
} |
|
|
|
plLimitedDirLightInfo::~plLimitedDirLightInfo() |
|
{ |
|
delete fParPlanes; |
|
} |
|
|
|
void plLimitedDirLightInfo::IRefresh() |
|
{ |
|
plLightInfo::IRefresh(); |
|
|
|
if( !IGetIsect() ) |
|
IMakeIsect(); |
|
|
|
if( GetProjection() ) |
|
{ |
|
hsMatrix44 l2ndc; |
|
l2ndc.Reset(); |
|
|
|
hsScalar width = fWidth; |
|
hsScalar height = fHeight; |
|
|
|
l2ndc.fMap[0][0] = 1.f / width; |
|
l2ndc.fMap[0][3] = 0.5f; |
|
|
|
l2ndc.fMap[1][1] = -1.f / height; |
|
l2ndc.fMap[1][3] = 0.5f; |
|
|
|
// Map Screen Z to range 0 (at hither) to 1 (at yon) |
|
// No, map z dead on to 1.f |
|
l2ndc.fMap[2][2] = 1.f / fDepth; |
|
l2ndc.fMap[2][3] = 0; |
|
|
|
l2ndc.fMap[3][3] = 1.f; |
|
l2ndc.NotIdentity(); |
|
|
|
fWorldToProj = l2ndc * fWorldToLight; |
|
} |
|
} |
|
|
|
void plLimitedDirLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const |
|
{ |
|
// If we haven't culled the object, return that we're full strength. |
|
plLightInfo::GetStrengthAndScale(bnd, strength, scale); |
|
} |
|
|
|
void plLimitedDirLightInfo::Read(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plDirectionalLightInfo::Read(s, mgr); |
|
|
|
fWidth = s->ReadSwapScalar(); |
|
fHeight = s->ReadSwapScalar(); |
|
fDepth = s->ReadSwapScalar(); |
|
} |
|
|
|
void plLimitedDirLightInfo::Write(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plDirectionalLightInfo::Write(s, mgr); |
|
|
|
s->WriteSwapScalar(fWidth); |
|
s->WriteSwapScalar(fHeight); |
|
s->WriteSwapScalar(fDepth); |
|
} |
|
|
|
void plLimitedDirLightInfo::IMakeIsect() |
|
{ |
|
if( !fParPlanes ) |
|
fParPlanes = TRACKED_NEW plParallelIsect; |
|
|
|
fParPlanes->SetNumPlanes(3); |
|
|
|
hsPoint3 p0, p1; |
|
|
|
hsScalar width = fWidth; |
|
hsScalar height = fHeight; |
|
p0.Set(-width*0.5f, 0, 0); |
|
p1.Set(width*0.5f, 0, 0); |
|
fParPlanes->SetPlane(0, p0, p1); |
|
|
|
p0.Set(0, -height * 0.5f, 0); |
|
p1.Set(0, height * 0.5f, 0); |
|
fParPlanes->SetPlane(1, p0, p1); |
|
|
|
p0.Set(0, 0, 0); |
|
p1.Set(0, 0, -fDepth); |
|
fParPlanes->SetPlane(2, p0, p1); |
|
|
|
fParPlanes->SetTransform(fLightToWorld, fWorldToLight); |
|
} |
|
|
|
//// ICreateProxy ////////////////////////////////////////////////////// |
|
// Creates a new box drawable for showing the light's |
|
// influence. |
|
|
|
plDrawableSpans* plLimitedDirLightInfo::CreateProxy(hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo) |
|
{ |
|
hsPoint3 corner; |
|
corner.Set(-fWidth*0.5f, -fHeight*0.5f, -fDepth); |
|
hsVector3 vecs[3]; |
|
vecs[0].Set(fWidth, 0, 0); |
|
vecs[1].Set(0, fHeight, 0); |
|
vecs[2].Set(0, 0, fDepth); |
|
// Generate a rectangular drawable based on a corner and three vectors |
|
plDrawableSpans* draw = plDrawableGenerator::GenerateBoxDrawable( corner, vecs[0], vecs[1], vecs[2], |
|
mat, |
|
fLightToWorld, |
|
true, |
|
nil, |
|
&idx, |
|
addTo ); |
|
|
|
return draw; |
|
} |
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// Omni |
|
plOmniLightInfo::plOmniLightInfo() |
|
: fAttenConst(0), |
|
fAttenLinear(1.f), |
|
fAttenQuadratic(0), |
|
fAttenCutoff(0), |
|
fSphere(nil) |
|
{ |
|
} |
|
|
|
plOmniLightInfo::~plOmniLightInfo() |
|
{ |
|
delete fSphere; |
|
} |
|
|
|
void plOmniLightInfo::IMakeIsect() |
|
{ |
|
fSphere = TRACKED_NEW plSphereIsect; |
|
fSphere->SetTransform(fLightToWorld, fWorldToLight); |
|
} |
|
|
|
void plOmniLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const |
|
{ |
|
plLightInfo::GetStrengthAndScale(bnd, strength, scale); |
|
|
|
// Volume - Want to base this on the closest point on the bounds, instead of just the center. |
|
const hsPoint3& pos = bnd.GetCenter(); |
|
|
|
hsScalar dist = hsVector3(&pos, &GetWorldPosition()).MagnitudeSquared(); |
|
dist = 1.f / hsFastMath::InvSqrtAppr(dist); |
|
if( fAttenQuadratic > 0 ) |
|
{ |
|
strength /= (fAttenConst + fAttenLinear * dist + fAttenQuadratic * dist * dist); |
|
} |
|
else if( fAttenLinear > 0 ) |
|
{ |
|
strength /= (fAttenConst + fAttenLinear * dist); |
|
} |
|
else if( fAttenConst > 0 ) |
|
{ |
|
strength /= fAttenConst; |
|
} |
|
else if( fAttenCutoff > 0 ) |
|
{ |
|
if( dist > fAttenCutoff ) |
|
strength = 0; |
|
} |
|
} |
|
|
|
hsScalar plOmniLightInfo::GetRadius() const |
|
{ |
|
hsScalar radius = 0; |
|
if( fAttenQuadratic > 0 ) |
|
{ |
|
hsScalar mult = fDiffuse.a >= 0 ? fDiffuse.a : -fDiffuse.a; |
|
hsScalar det = fAttenLinear*fAttenLinear - 4.f * fAttenQuadratic * fAttenConst * (1.f - mult * plSillyLightKonstants::GetFarPowerKonst()); |
|
if( det > 0 ) |
|
{ |
|
det = hsSquareRoot(det); |
|
|
|
radius = -fAttenLinear + det; |
|
radius /= fAttenQuadratic * 2.f; |
|
if( radius < 0 ) |
|
radius = 0; |
|
} |
|
} |
|
else if( fAttenLinear > 0 ) |
|
{ |
|
hsScalar mult = fDiffuse.a >= 0 ? fDiffuse.a : -fDiffuse.a; |
|
radius = (mult * plSillyLightKonstants::GetFarPowerKonst() - 1.f ) * fAttenConst / fAttenLinear; |
|
} |
|
else if( fAttenCutoff > 0 ) |
|
{ |
|
radius = fAttenCutoff; |
|
} |
|
|
|
return radius; |
|
} |
|
|
|
void plOmniLightInfo::IRefresh() |
|
{ |
|
plLightInfo::IRefresh(); |
|
|
|
if( IsAttenuated() ) |
|
{ |
|
if( !fSphere ) |
|
IMakeIsect(); |
|
fSphere->SetRadius(GetRadius()); |
|
} |
|
else |
|
{ |
|
delete fSphere; |
|
fSphere = nil; |
|
} |
|
} |
|
|
|
hsVector3 plOmniLightInfo::GetNegativeWorldDirection(const hsPoint3& pos) const |
|
{ |
|
return hsFastMath::NormalizeAppr(hsVector3(&GetWorldPosition(), &pos)); |
|
} |
|
|
|
void plOmniLightInfo::Read(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plLightInfo::Read(s, mgr); |
|
|
|
fAttenConst = s->ReadSwapScalar(); |
|
fAttenLinear = s->ReadSwapScalar(); |
|
fAttenQuadratic = s->ReadSwapScalar(); |
|
fAttenCutoff = s->ReadSwapScalar(); |
|
} |
|
|
|
void plOmniLightInfo::Write(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plLightInfo::Write(s, mgr); |
|
|
|
s->WriteSwapScalar(fAttenConst); |
|
s->WriteSwapScalar(fAttenLinear); |
|
s->WriteSwapScalar(fAttenQuadratic); |
|
s->WriteSwapScalar( fAttenCutoff ); |
|
} |
|
|
|
|
|
//// ICreateProxy ////////////////////////////////////////////////////// |
|
// Creates a new sphere drawable for showing the omnilight's |
|
// sphere (haha) of influence. |
|
|
|
plDrawableSpans* plOmniLightInfo::CreateProxy(hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo) |
|
{ |
|
float rad = GetRadius(); |
|
if( rad == 0 ) |
|
rad = 50; |
|
|
|
plDrawableSpans* draw = plDrawableGenerator::GenerateSphericalDrawable(hsPoint3(0,0,0), |
|
rad, |
|
mat, |
|
fLightToWorld, |
|
true, |
|
nil, |
|
&idx, |
|
addTo); |
|
|
|
return draw; |
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// Spot |
|
plSpotLightInfo::plSpotLightInfo() |
|
: fFalloff(1.f), |
|
fSpotInner(hsScalarPI * 0.125f), |
|
fSpotOuter(hsScalarPI * 0.25f), |
|
fCone(nil) |
|
{ |
|
} |
|
|
|
plSpotLightInfo::~plSpotLightInfo() |
|
{ |
|
delete fCone; |
|
} |
|
|
|
void plSpotLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const |
|
{ |
|
plOmniLightInfo::GetStrengthAndScale(bnd, strength, scale); |
|
|
|
// Volume - Want to base this on the closest point on the bounds, instead of just the center. |
|
const hsPoint3& pos = bnd.GetCenter(); |
|
|
|
hsVector3 del; |
|
del.Set(&pos, &GetWorldPosition()); |
|
hsScalar invDist = del.MagnitudeSquared(); |
|
invDist = hsFastMath::InvSqrtAppr(invDist); |
|
|
|
hsScalar dot = del.InnerProduct(GetWorldDirection()); |
|
dot *= invDist; |
|
|
|
hsScalar cosInner, cosOuter, t; |
|
hsFastMath::SinCosInRangeAppr(fSpotInner, t, cosInner); |
|
hsFastMath::SinCosInRangeAppr(fSpotOuter, t, cosOuter); |
|
if( dot < cosOuter ) |
|
strength = 0; |
|
else if( dot < cosInner ) |
|
strength *= (dot - cosOuter) / (cosInner - cosOuter); |
|
} |
|
|
|
void plSpotLightInfo::IMakeIsect() |
|
{ |
|
fCone = TRACKED_NEW plConeIsect; |
|
fCone->SetTransform(fLightToWorld, fWorldToLight); |
|
} |
|
|
|
void plSpotLightInfo::IRefresh() |
|
{ |
|
plLightInfo::IRefresh(); |
|
|
|
if( !fCone ) |
|
IMakeIsect(); |
|
|
|
hsScalar effFOV = fSpotOuter; |
|
fCone->SetAngle(effFOV); |
|
|
|
|
|
if( IsAttenuated() ) |
|
{ |
|
fCone->SetLength(GetRadius()); |
|
fCone->SetTransform(fLightToWorld, fWorldToLight); |
|
} |
|
|
|
if( GetProjection() ) |
|
{ |
|
hsScalar yon = GetRadius(); |
|
if( yon < kMinHither ) |
|
yon = kMaxYon; |
|
hsScalar hither = hsMinimum(kMinHither, yon * 0.5f); |
|
|
|
hsScalar sinFOV, cosFOV; |
|
hsFastMath::SinCos(effFOV, sinFOV, cosFOV); |
|
|
|
hsMatrix44 l2ndc; |
|
l2ndc.Reset(); |
|
l2ndc.fMap[0][0] = cosFOV / sinFOV * 0.5f; |
|
l2ndc.fMap[0][2] = -0.5f; |
|
l2ndc.fMap[1][1] = -cosFOV / sinFOV * 0.5f; |
|
l2ndc.fMap[1][2] = -0.5f; |
|
l2ndc.fMap[2][2] = -yon / (yon - hither); |
|
l2ndc.fMap[3][3] = 0; |
|
l2ndc.fMap[3][2] = -1.f; |
|
|
|
l2ndc.NotIdentity(); |
|
|
|
fWorldToProj = l2ndc * fWorldToLight; |
|
} |
|
} |
|
|
|
void plSpotLightInfo::Read(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plOmniLightInfo::Read(s, mgr); |
|
|
|
fFalloff = s->ReadSwapScalar(); |
|
fSpotInner = s->ReadSwapScalar(); |
|
fSpotOuter = s->ReadSwapScalar(); |
|
} |
|
|
|
void plSpotLightInfo::Write(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plOmniLightInfo::Write(s, mgr); |
|
|
|
s->WriteSwapScalar(fFalloff); |
|
s->WriteSwapScalar(fSpotInner); |
|
s->WriteSwapScalar(fSpotOuter); |
|
} |
|
|
|
|
|
hsVector3 plSpotLightInfo::GetWorldDirection() const |
|
{ |
|
return -fLightToWorld.GetAxis( hsMatrix44::kUp ); |
|
} |
|
|
|
//// ICreateProxy ////////////////////////////////////////////////////// |
|
// Generates a new drawable for showing the spotlight's |
|
// sphere of influence. |
|
|
|
plDrawableSpans* plSpotLightInfo::CreateProxy(hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo) |
|
{ |
|
float rad = GetRadius(); |
|
float x, y; |
|
|
|
|
|
if( rad == 0 ) |
|
rad = 80; |
|
|
|
hsFastMath::SinCosAppr( GetSpotOuter(), x, y ); |
|
|
|
plDrawableSpans* draw = plDrawableGenerator::GenerateConicalDrawable(rad * x / y, -rad, |
|
mat, |
|
fLightToWorld, |
|
true, |
|
nil, |
|
&idx, |
|
addTo); |
|
return draw; |
|
} |
|
|
|
|