/*==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 .
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 "plDistOpacityMod.h"
#include "plFadeOpacityLay.h"
#include "plSurface/hsGMaterial.h"
#include "plDrawable/plAccessGeometry.h"
#include "plDrawable/plAccessSpan.h"
#include "plMessage/plMatRefMsg.h"
// If we're tracking the camera
#include "plMessage/plRenderMsg.h"
#include "plPipeline.h"
// If we're tracking the avater
#include "plMessage/plAvatarMsg.h"
#include "plAvatar/plArmatureMod.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "hsQuat.h"
plDistOpacityMod::plDistOpacityMod()
: fSetup(false)
{
fDists[kNearTrans] = 0;
fDists[kNearOpaq] = 0;
fDists[kFarOpaq] = 0;
fDists[kFarTrans] = 0;
fRefPos.Set(0, 0, 0);
}
plDistOpacityMod::~plDistOpacityMod()
{
}
void plDistOpacityMod::SetKey(plKey k)
{
plSingleModifier::SetKey(k);
plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plArmatureUpdateMsg::Index(), GetKey());
}
hsScalar plDistOpacityMod::ICalcOpacity(const hsPoint3& targPos, const hsPoint3& refPos) const
{
hsScalar dist = hsVector3(&targPos, &refPos).Magnitude();
if( dist > fDists[kFarTrans] )
return 0;
if( dist < fDists[kNearTrans] )
return 0;
if( dist > fDists[kFarOpaq] )
{
dist -= fDists[kFarOpaq];
dist /= (fDists[kFarTrans] - fDists[kFarOpaq]);
hsAssert(dist >= 0, "unexpected interpolation param - neg");
hsAssert(dist <= 1.f, "unexpected interpolation param - > one");
return 1.f - dist;
}
if( dist < fDists[kNearOpaq] )
{
dist -= fDists[kNearTrans];
dist /= (fDists[kNearOpaq] - fDists[kNearTrans]);
hsAssert(dist >= 0, "unexpected interpolation param - neg");
hsAssert(dist <= 1.f, "unexpected interpolation param - > one");
return dist;
}
return 1.f;
}
void plDistOpacityMod::ISetOpacity()
{
if( !GetTarget() )
return;
if( !fSetup )
ISetup();
hsScalar opacity = ICalcOpacity(GetTarget()->GetLocalToWorld().GetTranslate(), fRefPos);
const int num = fFadeLays.GetCount();
int i;
for( i = 0; i < num; i++ )
fFadeLays[i]->SetOpacity(opacity);
}
hsBool plDistOpacityMod::MsgReceive(plMessage* msg)
{
plArmatureUpdateMsg* arm = plArmatureUpdateMsg::ConvertNoRef(msg);
if( arm && arm->IsLocal() )
{
arm->fArmature->GetPositionAndRotationSim(&fRefPos, nil);
return true;
}
plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg);
if( rend )
{
if( HasFlag(kTrackCamera) )
fRefPos = rend->Pipeline()->GetViewPositionWorld();
ISetOpacity();
return true;
}
plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg);
if( ref )
{
switch(ref->fType)
{
case kRefFadeLay:
if( ref->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
{
plFadeOpacityLay* lay = plFadeOpacityLay::ConvertNoRef(ref->GetRef());
int idx = fFadeLays.Find(lay);
if( idx != fFadeLays.kMissingIndex )
fFadeLays.Remove(idx);
}
break;
};
return true;
}
return plSingleModifier::MsgReceive(msg);
}
void plDistOpacityMod::Read(hsStream* s, hsResMgr* mgr)
{
plSingleModifier::Read(s, mgr);
int i;
for( i = 0; i < kNumDists; i++ )
fDists[i] = s->ReadSwapScalar();
ICheckDists();
fSetup = false;
}
void plDistOpacityMod::Write(hsStream* s, hsResMgr* mgr)
{
plSingleModifier::Write(s, mgr);
int i;
for( i = 0; i < kNumDists; i++ )
s->WriteSwapScalar(fDists[i]);
}
void plDistOpacityMod::SetTarget(plSceneObject* so)
{
plSingleModifier::SetTarget(so);
fSetup = false;
}
void plDistOpacityMod::SetFarDist(hsScalar opaque, hsScalar transparent)
{
fDists[kFarOpaq] = opaque;
fDists[kFarTrans] = transparent;
ICheckDists();
}
void plDistOpacityMod::SetNearDist(hsScalar transparent, hsScalar opaque)
{
fDists[kNearOpaq] = opaque;
fDists[kNearTrans] = transparent;
ICheckDists();
}
class MatLayer
{
public:
hsGMaterial* fMat;
plLayerInterface* fLay;
};
void plDistOpacityMod::ISetup()
{
fFadeLays.Reset();
plSceneObject* so = GetTarget();
if( !so )
return;
const plDrawInterface* di = so->GetDrawInterface();
if( !di )
return;
hsTArray todo;
hsTArray src;
plAccessGeometry::Instance()->OpenRO(di, src, false);
// We are guaranteed that each Max object will be given a unique
// copy of materials and associated layers. But within an object,
// a given layer may be shared across materials.
// So we'll build a list of the layers that need a FadeOpacityLay applied,
// making sure we don't include any layer more than once (strip repeats).
// This would be grossly inefficient if the numbers involved weren't all
// very small. So an n^2 search isn't bad if n <= 2.
int i;
for( i = 0; i < src.GetCount(); i++ )
{
hsGMaterial* mat = src[i].GetMaterial();
int j;
for( j = 0; j < mat->GetNumLayers(); j++ )
{
plLayerInterface* lay = mat->GetLayer(j);
if( !j || !(lay->GetZFlags() & hsGMatState::kZNoZWrite) || (lay->GetMiscFlags() & hsGMatState::kMiscRestartPassHere) )
{
int k;
for( k = 0; k < todo.GetCount(); k++ )
{
if( lay == todo[k].fLay )
break;
}
if( k == todo.GetCount() )
{
MatLayer* push = todo.Push();
push->fMat = mat;
push->fLay = lay;
}
}
}
}
plAccessGeometry::Instance()->Close(src);
for( i = 0; i < todo.GetCount(); i++ )
{
hsGMaterial* mat = todo[i].fMat;
plLayerInterface* lay = todo[i].fLay;
plFadeOpacityLay* fade = TRACKED_NEW plFadeOpacityLay;
hsgResMgr::ResMgr()->NewKey(lay->GetKey()->GetName(), fade, lay->GetKey()->GetUoid().GetLocation());
fade->AttachViaNotify(lay);
// We should add a ref or something here if we're going to hold on to this (even though we created and "own" it).
fFadeLays.Append(fade);
plMatRefMsg* msg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnReplace, i, plMatRefMsg::kLayer);
msg->SetOldRef(lay);
hsgResMgr::ResMgr()->SendRef(fade, msg, plRefFlags::kActiveRef);
plGenRefMsg* toMe = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefFadeLay);
hsgResMgr::ResMgr()->SendRef(fade, toMe, plRefFlags::kPassiveRef);
}
fSetup = true;
}