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.

1788 lines
55 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 "plDynaDecalMgr.h"
#include "plDynaDecal.h"
#include "plCutter.h"
#include "plAccessGeometry.h"
#include "plAccessSpan.h"
#include "plDrawableSpans.h"
#include "plAuxSpan.h"
#include "plSpaceTree.h"
#include "plPrintShape.h"
#include "plAvatar/plArmatureMod.h"
#include "plParticleSystem/plParticleSystem.h"
#include "plParticleSystem/plParticleEmitter.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plDrawInterface.h"
#include "plSurface/hsGMaterial.h"
#include "plSurface/plLayerInterface.h"
#include "plScene/plPageTreeMgr.h"
#include "plPipeline/plGBufferGroup.h"
#include "plPipeline/hsGDeviceRef.h"
#include "plMessage/plAgeLoadedMsg.h"
#include "plMessage/plDynaDecalEnableMsg.h"
#include "pnMessage/plRefMsg.h"
#include "pnMessage/plTimeMsg.h"
#include "plgDispatch.h"
#include "plMath/plRandom.h"
#include "hsFastMath.h"
#include "hsStream.h"
#include "hsResMgr.h"
#include "hsTimer.h"
#include "pnMessage/plPipeResMakeMsg.h"
// Stuff for creating a bumpenv decal on demand.
#include "plGImage/plMipmap.h"
#include "plSurface/plLayer.h"
#include "plMessage/plLayRefMsg.h"
//### Hackage
#include "plMessage/plRenderMsg.h"
#include "plMessage/plListenerMsg.h"
#include "plPipeline.h"
#include "plTweak.h"
#include "plProfile.h"
plProfile_CreateTimerNoReset("Total", "DynaDecal", Total);
plProfile_CreateTimerNoReset("Cutter", "DynaDecal", Cutter);
plProfile_CreateTimerNoReset("Process", "DynaDecal", Process);
plProfile_CreateTimerNoReset("Callback", "DynaDecal", Callback);
static plRandom sRand;
static const int kBinBlockSize = 20;
static const UInt16 kDefMaxNumVerts = 1000;
static const UInt16 kDefMaxNumIdx = kDefMaxNumVerts;
static const hsScalar kDefLifeSpan = 30.f;
static const hsScalar kDefDecayStart = kDefLifeSpan * 0.5f;
static const hsScalar kDefRampEnd = kDefLifeSpan * 0.1f;
static const hsScalar kInitAuxSpans = 5;
#define MF_NO_INIT_ALLOC
#define MF_NEVER_RUN_OUT
// If we aren't doing an initial alloc, we MUST have NEVER_RUN_OUT
// It's also useful to not have initial alloc but do have NEVER_RUN_OUT
// So:
// MF_NO_INIT_ALLOC && MF_NEVER_RUN_OUT - Okay
// !MF_NO_INIT_ALLOC && !MF_NEVER_RUN_OUT - Okay
// !MF_NO_INIT_ALLOC && MF_NEVER_RUN_OUT - Okay
// MF_NO_INIT_ALLOC && !MF_NEVER_RUN_OUT - Bad (you'll never get any decals)
#if defined(MF_NO_INIT_ALLOC) && !defined(MF_NEVER_RUN_OUT)
#define MF_NEVER_RUN_OUT
#endif // defined(MF_NO_INIT_ALLOC) && !defined(MF_NEVER_RUN_OUT)
using namespace std;
hsBool plDynaDecalMgr::fDisableAccumulate = false;
hsBool plDynaDecalMgr::fDisableUpdate = false;
plDynaDecalMgr::plDynaDecalMgr()
:
fMatPreShade(nil),
fMatRTShade(nil),
fMaxNumVerts(kDefMaxNumVerts),
fMaxNumIdx(kDefMaxNumIdx),
fWetLength(0),
fRampEnd(kDefRampEnd),
fDecayStart(kDefDecayStart),
fLifeSpan(kDefLifeSpan),
fInitAtten(1.f),
fIntensity(1.f),
fWaitOnEnable(false),
fGridSizeU(2.5f),
fGridSizeV(2.5f),
fScale(1.f, 1.f, 1.f),
fPartyTime(1.f)
{
fCutter = TRACKED_NEW plCutter;
}
plDynaDecalMgr::~plDynaDecalMgr()
{
int i;
for( i = 0; i < fDecals.GetCount(); i++ )
delete fDecals[i];
for( i = 0; i < fAuxSpans.GetCount(); i++ )
{
if( fAuxSpans[i]->fDrawable )
{
plSpan* span = const_cast<plSpan*>(fAuxSpans[i]->fDrawable->GetSpan(fAuxSpans[i]->fBaseSpanIdx));
span->RemoveAuxSpan(fAuxSpans[i]);
}
delete fAuxSpans[i]->fGroup;
delete fAuxSpans[i];
}
delete fCutter;
}
void plDynaDecalMgr::SetKey(plKey k)
{
hsKeyedObject::SetKey(k);
if( k )
{
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plPipeGeoMakeMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
}
}
void plDynaDecalMgr::Read(hsStream* stream, hsResMgr* mgr)
{
plSynchedObject::Read(stream, mgr);
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatPreShade), plRefFlags::kActiveRef);
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatRTShade), plRefFlags::kActiveRef);
int n = stream->ReadSwap32();
int i;
for( i = 0; i < n; i++ )
{
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefTarget), plRefFlags::kPassiveRef);
}
// Associated slave particle systems. We read in the scene objects now, and find the associated systems on loaded message.
n = stream->ReadSwap32();
for( i = 0; i < n; i++ )
{
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefPartyObject), plRefFlags::kPassiveRef);
}
fMaxNumVerts = (UInt16)(stream->ReadSwap32());
fMaxNumIdx = (UInt16)(stream->ReadSwap32());
fWaitOnEnable = stream->ReadSwap32();
fIntensity = stream->ReadSwapScalar();
fInitAtten = fIntensity;
fWetLength = stream->ReadSwapScalar();
fRampEnd = stream->ReadSwapScalar();
fDecayStart = stream->ReadSwapScalar();
fLifeSpan = stream->ReadSwapScalar();
fGridSizeU = stream->ReadSwapScalar();
fGridSizeV = stream->ReadSwapScalar();
fScale.Read(stream);
fPartyTime = stream->ReadSwapScalar();
n = stream->ReadSwap32();
fNotifies.SetCount(n);
for( i = 0; i < n; i++ )
fNotifies[i] = mgr->ReadKey(stream);
// If we need to be creating DynaDecalMgrs on the fly, this should go in the
// constructor, or we should call it explicitly on the DynaDecalMgr we create.
// But putting it here makes it automatic for normal scene loading, without
// popping up during the export conversion process.
#ifndef MF_NO_INIT_ALLOC
InitAuxSpans();
#endif // MF_NO_INIT_ALLOC
/////////////////////////////////////////////////////
// ###Things that should be in derived classes follow.
}
void plDynaDecalMgr::Write(hsStream* stream, hsResMgr* mgr)
{
plSynchedObject::Write(stream, mgr);
mgr->WriteKey(stream, fMatPreShade);
mgr->WriteKey(stream, fMatRTShade);
stream->WriteSwap32(fTargets.GetCount());
int i;
for( i = 0; i < fTargets.GetCount(); i++ )
{
mgr->WriteKey(stream, fTargets[i]);
}
// Particle systems (really their associated sceneobjects).
stream->WriteSwap32(fPartyObjects.GetCount());
for( i = 0; i < fPartyObjects.GetCount(); i++ )
{
mgr->WriteKey(stream, fPartyObjects[i]);
}
stream->WriteSwap32(fMaxNumVerts);
stream->WriteSwap32(fMaxNumIdx);
stream->WriteSwap32(fWaitOnEnable);
stream->WriteSwapScalar(fIntensity);
stream->WriteSwapScalar(fWetLength);
stream->WriteSwapScalar(fRampEnd);
stream->WriteSwapScalar(fDecayStart);
stream->WriteSwapScalar(fLifeSpan);
stream->WriteSwapScalar(fGridSizeU);
stream->WriteSwapScalar(fGridSizeV);
fScale.Write(stream);
stream->WriteSwapScalar(fPartyTime);
stream->WriteSwap32(fNotifies.GetCount());
for( i = 0; i < fNotifies.GetCount(); i++ )
mgr->WriteKey(stream, fNotifies[i]);
/////////////////////////////////////////////////////
// ###Things that should be in derived classes follow.
}
hsBool plDynaDecalMgr::IMakeAuxRefs(plPipeline* pipe)
{
int i;
for( i = 0; i < fGroups.GetCount(); i++ )
fGroups[i]->PrepForRendering(pipe, false);
return true;
}
const plPrintShape* plDynaDecalMgr::IGetPrintShape(const plKey& objKey) const
{
const plPrintShape* shape = nil;
plSceneObject* part = plSceneObject::ConvertNoRef(objKey->ObjectIsLoaded());
if( part )
{
// This is a safe cast, because GetGenericInterface(type) will only return
// either a valid object of that type, or nil.
shape = static_cast<plPrintShape*>(part->GetGenericInterface(plPrintShape::Index()));
}
return shape;
}
const plPrintShape* plDynaDecalMgr::IGetPrintShape(plArmatureMod* avMod, UInt32 id) const
{
const plPrintShape* shape = nil;
const plSceneObject* part = avMod->FindBone(id);
if( part )
{
// This is a safe cast, because GetGenericInterface(type) will only return
// either a valid object of that type, or nil.
shape = static_cast<plPrintShape*>(part->GetGenericInterface(plPrintShape::Index()));
}
return shape;
}
hsBool plDynaDecalMgr::IHandleEnableMsg(const plDynaDecalEnableMsg* enaMsg)
{
IWetParts(enaMsg);
return true;
}
hsBool plDynaDecalMgr::IWetParts(const plDynaDecalEnableMsg* enaMsg)
{
if( !enaMsg->IsArmature() )
{
const plPrintShape* shape = IGetPrintShape(enaMsg->GetShapeKey());
if( shape )
{
plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey());
IWetInfo(info, enaMsg);
}
}
else
if( enaMsg->GetID() == UInt32(-1) )
{
plArmatureMod* avMod = plArmatureMod::ConvertNoRef(enaMsg->GetArmKey()->ObjectIsLoaded());
int i;
for( i = 0; i < fPartIDs.GetCount(); i++ )
{
const plPrintShape* shape = IGetPrintShape(avMod, fPartIDs[i]);
if( shape )
{
plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey());
IWetInfo(info, enaMsg);
}
}
}
else
{
IWetPart(enaMsg->GetID(), enaMsg);
}
return true;
}
hsBool plDynaDecalMgr::IWetPart(UInt32 id, const plDynaDecalEnableMsg* enaMsg)
{
plArmatureMod* avMod = plArmatureMod::ConvertNoRef(enaMsg->GetArmKey()->ObjectIsLoaded());
const plPrintShape* shape = IGetPrintShape(avMod, id);
if( shape )
{
plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey());
IWetInfo(info, enaMsg);
}
return true;
}
void plDynaDecalMgr::IWetInfo(plDynaDecalInfo& info, const plDynaDecalEnableMsg* enaMsg) const
{
info.fWetTime = enaMsg->GetContactTime();
info.fWetLength = enaMsg->GetWetLength();
if( !enaMsg->AtEnd() )
info.fFlags |= plDynaDecalInfo::kImmersed;
else
info.fFlags &= ~plDynaDecalInfo::kImmersed;
}
hsBool plDynaDecalMgr::MsgReceive(plMessage* msg)
{
// On eval pulse, update all our active decals, letting old ones die off.
plEvalMsg* eval = plEvalMsg::ConvertNoRef(msg);
if( eval )
{
IUpdateDecals(hsTimer::GetSysSeconds());
return true;
}
plDynaDecalEnableMsg* enaMsg = plDynaDecalEnableMsg::ConvertNoRef(msg);
if( enaMsg )
{
IHandleEnableMsg(enaMsg);
return true;
}
plPipeGeoMakeMsg* make = plPipeGeoMakeMsg::ConvertNoRef(msg);
if( make )
{
return IMakeAuxRefs(make->Pipeline());
}
plAgeLoadedMsg* ageLoadMsg = plAgeLoadedMsg::ConvertNoRef(msg);
if( ageLoadMsg && ageLoadMsg->fLoaded )
{
IGetParticles();
return true;
}
plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg);
if( refMsg )
{
switch( refMsg->fType )
{
case kRefMatPreShade:
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
fMatPreShade = hsGMaterial::ConvertNoRef(refMsg->GetRef());
else
fMatPreShade = nil;
return true;
case kRefMatRTShade:
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
fMatRTShade = hsGMaterial::ConvertNoRef(refMsg->GetRef());
else
fMatRTShade = nil;
return true;
case kRefTarget:
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
{
fTargets.Append(plSceneObject::ConvertNoRef(refMsg->GetRef()));
}
else
{
int idx = fTargets.Find((plSceneObject*)refMsg->GetRef());
if( idx != fTargets.kMissingIndex )
fTargets.Remove(idx);
}
return true;
case kRefPartyObject:
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
{
fPartyObjects.Append(plSceneObject::ConvertNoRef(refMsg->GetRef()));
}
else
{
int idx = fPartyObjects.Find((plSceneObject*)refMsg->GetRef());
if( idx != fPartyObjects.kMissingIndex )
fPartyObjects.Remove(idx);
}
return true;
case kRefParticles:
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
{
fParticles.Append(plParticleSystem::ConvertNoRef(refMsg->GetRef()));
fParticles[fParticles.GetCount()-1]->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate;
}
else
{
int idx = fParticles.Find((plParticleSystem*)refMsg->GetRef());
if( idx != fParticles.kMissingIndex )
fParticles.Remove(idx);
}
return true;
case kRefAvatar:
if( refMsg->GetContext() & (plRefMsg::kOnRemove|plRefMsg::kOnDestroy) )
IRemoveDecalInfo(UInt32(refMsg->GetRef()));
return true;
}
}
return plSynchedObject::MsgReceive(msg);
}
//////////////////////////////////////////////////////////////////////////////////
//
void plDynaDecalMgr::INotifyActive(plDynaDecalInfo& info, const plKey& armKey, UInt32 id) const
{
if( !(info.fFlags & plDynaDecalInfo::kActive) )
{
double secs = hsTimer::GetSysSeconds();
int i;
for( i = 0; i < fNotifies.GetCount(); i++ )
{
plDynaDecalEnableMsg* enaMsg = TRACKED_NEW plDynaDecalEnableMsg(fNotifies[i], armKey, secs, fWetLength, false, id);
enaMsg->Send();
}
info.fFlags |= plDynaDecalInfo::kActive;
}
}
void plDynaDecalMgr::INotifyInactive(plDynaDecalInfo& info, const plKey& armKey, UInt32 id) const
{
if( info.fFlags & plDynaDecalInfo::kActive )
{
double secs = hsTimer::GetSysSeconds();
int i;
for( i = 0; i < fNotifies.GetCount(); i++ )
{
plDynaDecalEnableMsg* enaMsg = TRACKED_NEW plDynaDecalEnableMsg(fNotifies[i], armKey, secs, fWetLength, true, id);
enaMsg->Send();
}
info.fFlags &= ~plDynaDecalInfo::kActive;
}
}
plDynaDecalInfo& plDynaDecalInfo::Init(const plKey& key)
{
fKey = key;
fLastTime = -1.e33f;
fLastPos.Set(-1.e33f, -1.e33f, -1.e33f);
fWetTime = -1.e33f;
fWetLength = 0;
fFlags = kNone;
return *this;
}
plDynaDecalInfo& plDynaDecalMgr::IGetDecalInfo(UInt32 id, const plKey& key)
{
plDynaDecalMap::iterator iter = fDecalMap.find(id);
if( iter == fDecalMap.end() )
{
plDynaDecalInfo decalInfo;
decalInfo.Init(key);
plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefAvatar);
hsgResMgr::ResMgr()->AddViaNotify(plKey(key), refMsg, plRefFlags::kPassiveRef);
pair<plDynaDecalMap::iterator, bool> iterPair;
iterPair = fDecalMap.insert(plDynaDecalMap::value_type(id, decalInfo));
iter = iterPair.first;
}
return iter->second;
}
void plDynaDecalMgr::IRemoveDecalInfo(UInt32 id)
{
plDynaDecalMap::iterator iter = fDecalMap.find(id);
if( iter != fDecalMap.end() )
fDecalMap.erase(iter, iter);
}
void plDynaDecalMgr::IRemoveDecalInfos(const plKey& key)
{
plDynaDecalMap::iterator iter = fDecalMap.begin();
while( iter != fDecalMap.end() )
{
if( iter->second.fKey == key )
{
plDynaDecalMap::iterator nuke0 = iter;
plDynaDecalMap::iterator nuke1 = iter;
iter++;
while( (iter != fDecalMap.end()) && (iter->second.fKey == key) )
{
nuke1 = iter;
iter++;
}
fDecalMap.erase(nuke0, nuke1);
}
}
}
hsScalar plDynaDecalMgr::IHowWet(plDynaDecalInfo& info, double t) const
{
// We aren't playing this wet/dry/enable/disable thing.
if( !fWaitOnEnable )
return fIntensity;
// We've been notified we've entered the pool,
// and haven't been notified we've left it.
if( info.fFlags & plDynaDecalInfo::kImmersed )
{
info.fWetTime = t;
return fIntensity;
}
// We've never been enabled.
if( info.fWetLength <= 0 )
return 0;
// We're wet, let's see how wet.
hsScalar wet = (hsScalar)(1.f - (t - info.fWetTime) / info.fWetLength);
if( wet > 1.f ) // This should never happen. It means t < info.fWetTime (we get wet in the future).
return fIntensity;
if( wet < 0 )
return 0;
return wet * fIntensity;
}
//
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
plAuxSpan* plDynaDecalMgr::IGetAuxSpan(plDrawableSpans* targ, int iSpan, hsGMaterial* mat, UInt16 numVerts, UInt16 numIdx)
{
// Some of this code just assumes you get the number of verts you ask for.
// Which was causing errors when you asked for more than the max and didn't
// get it. So now if you ask for too many verts, you just lose.
if (numVerts > fMaxNumVerts || numIdx > fMaxNumIdx)
return nil;
plSpan* span = const_cast<plSpan*>(targ->GetSpan(iSpan));
int i;
// First, see if we've got an aux span already sitting on this span.
// We can use an existing aux span iff
// a) we own it
// b) there's enough room to append our stuff.
//
// This is kind of overkill in both respects, because:
// a) we don't care if we own it, it just really needs the same material
// b) there might be room at the beginning or middle of the span
//
// Relaxing those brings on the additional bookkeeping with having a span
// that requires multiple drawprimitive calls.
// Case a) can cause it if we share with a DynaDecalMgr with a different
// lifespan than ours. That would allow decals in the middle of the span
// to die out.
// Case b) won't have random bits dropping out of the middle, but will
// create a gap in the middle (and probably end) when we wrap around.
//
// The simplicity in bookkeeping should make up for any space/speed advantages
// in packing more into a single AuxSpan.
for( i = 0; i < span->GetNumAuxSpans(); i++ )
{
plAuxSpan* aux = span->GetAuxSpan(i);
if( (aux->fOwner == (void*)this)
&&(aux->fVStartIdx + aux->fVLength + numVerts < aux->fVBufferLimit)
&&(aux->fIStartIdx + aux->fILength + numIdx < aux->fIBufferLimit) )
return aux;
}
hsBool rtLit = span->fProps & plSpan::kLiteVtxNonPreshaded;
// Now look to see if we've got one sitting around unused that's suitable.
// Here the suitable criteria is a little different. We know we are the owner,
// and we know there's enough room (because it's sitting idle).
for( i = 0; i < fAuxSpans.GetCount(); i++ )
{
plAuxSpan* aux = fAuxSpans[i];
if( !aux->fDrawable
&&(aux->fVStartIdx + aux->fVLength + numVerts < aux->fVBufferLimit)
&&(aux->fIStartIdx + aux->fILength + numIdx < aux->fIBufferLimit) )
{
aux->fDrawable = targ;
aux->fBaseSpanIdx = iSpan;
ISetAuxMaterial(aux, mat, rtLit);
span->AddAuxSpan(aux);
return aux;
}
}
// Ain't got one. We could allocate another one, or we can just say too bad doo-dad.
// If we allocate a new one:
// A) we'll need to flush managed memory to load it in.
// B) we'll be stuck with that memory (video and system) used up until this age is paged out and reloaded
// C) we've got a whole bunch of extra faces to draw.
// If we just return nil:
// A) opposite of above
// B) we stop leaving footprints/ripples for a while.
// I'm going to try the latter for a bit.
#ifdef MF_NEVER_RUN_OUT
// Okay, nothing there. Let's get a new one.
plAuxSpan* aux = TRACKED_NEW plAuxSpan;
fAuxSpans.Append(aux);
IAllocAuxSpan(aux, numVerts, numIdx);
aux->fOwner = (void*)this;
aux->fDrawable = targ;
aux->fBaseSpanIdx = iSpan;
ISetAuxMaterial(aux, mat, rtLit);
span->AddAuxSpan(aux);
return aux;
#else // MF_NEVER_RUN_OUT
return nil;
#endif // MF_NEVER_RUN_OUT
}
void plDynaDecalMgr::InitAuxSpans()
{
int i;
for( i = 0; i < kInitAuxSpans; i++ )
{
plAuxSpan* aux = TRACKED_NEW plAuxSpan;
fAuxSpans.Append(aux);
IAllocAuxSpan(aux, fMaxNumVerts, fMaxNumIdx);
}
}
void plDynaDecalMgr::IAllocAuxSpan(plAuxSpan* aux, UInt32 maxNumVerts, UInt32 maxNumIdx)
{
int iGrp = fGroups.GetCount();
plGBufferGroup* grp = TRACKED_NEW plGBufferGroup(kDecalVtxFormat, true, false);
fGroups.Append(grp);
grp->ReserveVertStorage(maxNumVerts,
&aux->fVBufferIdx,
&aux->fCellIdx,
&aux->fCellOffset,
plGBufferGroup::kReserveInterleaved);
aux->fFlags = 0;
aux->fVStartIdx = grp->GetVertStartFromCell(aux->fVBufferIdx, aux->fCellIdx, aux->fCellOffset);
aux->fVLength = 0;
UInt16* dataPtr = nil;
grp->ReserveIndexStorage(maxNumIdx, &aux->fIBufferIdx, &aux->fIStartIdx, &dataPtr);
aux->fIStartIdx;
aux->fILength = 0;
aux->fGroup = grp;
aux->fVBufferInit = aux->fVStartIdx;
aux->fVBufferLimit = aux->fVBufferInit + maxNumVerts;
aux->fIBufferInit = aux->fIStartIdx;
aux->fIBufferLimit = aux->fIBufferInit + maxNumIdx;
aux->fOrigPos.SetCount(maxNumVerts);
aux->fOrigUVW.SetCount(maxNumVerts);
aux->fOwner = (void*)this;
aux->fDrawable = nil;
aux->fBaseSpanIdx = 0;
grp->SetVertBufferStart(aux->fVBufferIdx, aux->fVStartIdx);
grp->SetIndexBufferStart(aux->fIBufferIdx, aux->fIStartIdx);
grp->SetVertBufferEnd(aux->fVBufferIdx, aux->fVStartIdx);
grp->SetIndexBufferEnd(aux->fIBufferIdx, aux->fIStartIdx);
}
hsGMaterial* plDynaDecalMgr::ISetAuxMaterial(plAuxSpan* aux, hsGMaterial* mat, hsBool rtLit)
{
if( !mat )
mat = fMatRTShade;
hsBool attenColor = 0 != (mat->GetLayer(0)->GetBlendFlags()
& (hsGMatState::kBlendAdd
| hsGMatState::kBlendMult
| hsGMatState::kBlendMADD));
hsBool bump = 0 != (mat->GetLayer(0)->GetMiscFlags() & hsGMatState::kMiscBumpChans);
hsBool hasVS = nil != mat->GetLayer(0)->GetVertexShader();
if( hasVS )
{
aux->fFlags |= plAuxSpan::kVertexShader;
aux->fFlags &= ~plAuxSpan::kAttenColor;
aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kRTLit);
aux->fMaterial = mat;
}
else
if( bump )
{
aux->fFlags &= ~plAuxSpan::kVertexShader;
aux->fFlags |= plAuxSpan::kAttenColor;
aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kRTLit);
aux->fMaterial = mat;
}
else
if( attenColor )
{
aux->fFlags &= ~plAuxSpan::kVertexShader;
aux->fFlags |= plAuxSpan::kOverrideLiteModel | plAuxSpan::kAttenColor;
aux->fFlags &= ~plAuxSpan::kRTLit;
aux->fMaterial = mat;
}
else
if( rtLit )
{
aux->fFlags &= ~plAuxSpan::kVertexShader;
aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kAttenColor);
aux->fFlags |= plAuxSpan::kRTLit;
aux->fMaterial = mat;
}
else
{
aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kAttenColor);
aux->fFlags &= ~plAuxSpan::kRTLit;
aux->fMaterial = fMatPreShade;
}
return aux->fMaterial;
}
//////////////////////////////////////////////////////////////////////////////////
plDynaDecal* plDynaDecalMgr::IInitDecal(plAuxSpan* aux, double t, UInt16 numVerts, UInt16 numIdx)
{
int idx = INewDecal();
fDecals[idx]->fStartVtx = (UInt16)(aux->fVStartIdx + aux->fVLength);
fDecals[idx]->fNumVerts = numVerts;
fDecals[idx]->fStartIdx = (UInt16)(aux->fIStartIdx + aux->fILength);
fDecals[idx]->fNumIdx = numIdx;
fDecals[idx]->fBirth = t;
fDecals[idx]->fFlags = plDynaDecal::kFresh;
fDecals[idx]->fInitAtten = fInitAtten;
fDecals[idx]->fAuxSpan = aux;
aux->fVLength += numVerts;
aux->fGroup->SetVertBufferEnd(aux->fVBufferIdx, aux->fVStartIdx + aux->fVLength);
aux->fGroup->DirtyVertexBuffer(aux->fVBufferIdx);
aux->fILength += numIdx;
aux->fGroup->SetIndexBufferEnd(aux->fIBufferIdx, aux->fIStartIdx + aux->fILength);
aux->fGroup->DirtyIndexBuffer(aux->fIBufferIdx);
if( aux->fFlags & plAuxSpan::kVertexShader )
fDecals[idx]->fFlags |= plDynaDecal::kVertexShader;
else
if( aux->fFlags & plAuxSpan::kAttenColor )
fDecals[idx]->fFlags |= plDynaDecal::kAttenColor;
// We should probably assert here that our span hasn't just overrun buffergroup storage.
hsAssert(aux->fVStartIdx + aux->fVLength <= aux->fVBufferLimit, "Overrunning allocated storage");
hsAssert(aux->fIStartIdx + aux->fILength <= aux->fIBufferLimit, "Overrunning allocated storage");
hsAssert(aux->fGroup->GetVertBufferEnd(aux->fVBufferIdx) >= aux->fGroup->GetVertBufferStart(aux->fVBufferIdx), "Going out of range on verts");
hsAssert(aux->fGroup->GetIndexBufferEnd(aux->fIBufferIdx) >= aux->fGroup->GetIndexBufferStart(aux->fIBufferIdx), "Going out of range on verts");
return fDecals[idx];
}
void plDynaDecalMgr::IKillDecal(int i)
{
// Update this decal's span.
// Since decals die off in the same order they are created, and we always
// append a decal to a span, we only need to advance the span's start indices,
// and decrement the lengths.
plAuxSpan* aux = fDecals[i]->fAuxSpan;
aux->fVStartIdx += fDecals[i]->fNumVerts;
aux->fGroup->SetVertBufferStart(aux->fVBufferIdx, aux->fVStartIdx);
aux->fVLength -= fDecals[i]->fNumVerts;
aux->fIStartIdx += fDecals[i]->fNumIdx;
aux->fGroup->SetIndexBufferStart(aux->fIBufferIdx, aux->fIStartIdx);
aux->fILength -= fDecals[i]->fNumIdx;
hsAssert(aux->fGroup->GetVertBufferEnd(aux->fVBufferIdx) >= aux->fGroup->GetVertBufferStart(aux->fVBufferIdx), "Going out of range on verts");
hsAssert(aux->fGroup->GetIndexBufferEnd(aux->fIBufferIdx) >= aux->fGroup->GetIndexBufferStart(aux->fIBufferIdx), "Going out of range on verts");
if( !aux->fVLength )
{
hsAssert(!aux->fILength, "Ran out of verts before indices");
aux->fVStartIdx = aux->fVBufferInit;
aux->fIStartIdx = aux->fIBufferInit;
aux->fGroup->SetVertBufferStart(aux->fVBufferIdx, aux->fVStartIdx);
aux->fGroup->SetIndexBufferStart(aux->fIBufferIdx, aux->fIStartIdx);
aux->fGroup->SetVertBufferEnd(aux->fVBufferIdx, aux->fVStartIdx);
aux->fGroup->SetIndexBufferEnd(aux->fIBufferIdx, aux->fIStartIdx);
hsAssert(aux->fGroup->GetVertBufferEnd(aux->fVBufferIdx) >= aux->fGroup->GetVertBufferStart(aux->fVBufferIdx), "Going out of range on verts");
hsAssert(aux->fGroup->GetIndexBufferEnd(aux->fIBufferIdx) >= aux->fGroup->GetIndexBufferStart(aux->fIBufferIdx), "Going out of range on verts");
if( aux->fDrawable )
const_cast<plSpan*>(aux->fDrawable->GetSpan(aux->fBaseSpanIdx))->RemoveAuxSpan(aux);
aux->fDrawable = nil;
aux->fBaseSpanIdx = 0;
}
delete fDecals[i];
int newCount = fDecals.GetCount()-1;
if( i < newCount )
{
memmove(&fDecals[i], &fDecals[i+1], (newCount-i) * sizeof(fDecals[i]));
}
fDecals.SetCount(newCount);
}
void plDynaDecalMgr::IUpdateDecals(double t)
{
if( fDisableUpdate )
return;
int i;
for( i = 0; i < fDecals.GetCount(); i++ )
{
if( fDecals[i]->Age(t, fRampEnd, fDecayStart, fLifeSpan) )
{
IKillDecal(i);
i--;
}
}
for( i = 0; i < fAuxSpans.GetCount(); i++ )
{
if( fAuxSpans[i]->fVLength )
{
plAuxSpan* aux = fAuxSpans[i];
aux->fGroup->DirtyVertexBuffer(aux->fVBufferIdx);
}
}
}
//////////////////////////////////////////////////////////////////////////////////
void plDynaDecalMgr::ICountIncoming(hsTArray<plCutoutPoly>& src, UInt16& numVerts, UInt16& numIdx) const
{
numVerts = 0;
numIdx = 0;
int j;
for( j = 0; j < src.GetCount(); j++ )
{
if( src[j].fVerts.GetCount() )
{
numVerts += src[j].fVerts.GetCount();
numIdx += src[j].fVerts.GetCount()-2;
}
}
numIdx *= 3;
}
plDecalVtxFormat* plDynaDecalMgr::IGetBaseVtxPtr(const plAuxSpan* auxSpan) const
{
plGBufferGroup* grp = auxSpan->fGroup;
plGBufferCell* cell = grp->GetCell(auxSpan->fVBufferIdx, auxSpan->fCellIdx);
UInt8* ptr = grp->GetVertBufferData(auxSpan->fVBufferIdx);
ptr += cell->fVtxStart + auxSpan->fCellOffset;
return (plDecalVtxFormat*)ptr;
}
UInt16* plDynaDecalMgr::IGetBaseIdxPtr(const plAuxSpan* auxSpan) const
{
plGBufferGroup* grp = auxSpan->fGroup;
return grp->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferInit;
}
hsBool plDynaDecalMgr::IConvertFlatGrid(plAuxSpan* auxSpan,
plDynaDecal* decal,
const plFlatGridMesh& grid) const
{
plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan);
vtx += decal->fStartVtx;
decal->fVtxBase = vtx;
hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx];
hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx];
UInt32 initColor = decal->fFlags & plDynaDecal::kAttenColor
? 0xff000000
: 0x00ffffff;
int iv;
for( iv = 0; iv < decal->fNumVerts; iv++ )
{
*origPos = vtx->fPos = grid.fVerts[iv].fPos;
vtx->fNorm.Set(0.f, 0.f, 1.f);
vtx->fDiffuse = initColor;
vtx->fSpecular = 0;
vtx->fUVW[0] = grid.fVerts[iv].fUVW;
vtx->fUVW[1].Set(0.f, 0.f, 0.f);
*origUVW = grid.fVerts[iv].fUVW;
origUVW->fZ = 1.f;
vtx++;
origPos++;
origUVW++;
}
UInt16* idx = IGetBaseIdxPtr(auxSpan);
idx += decal->fStartIdx;
hsAssert(grid.fIdx.GetCount() == decal->fNumIdx, "Mismatch on dynamic indices");
UInt16 base = decal->fStartVtx;
int ii;
for( ii = 0; ii < grid.fIdx.GetCount(); ii++ )
{
hsAssert(grid.fIdx[ii] + base - decal->fStartVtx < decal->fNumVerts, "Index going out of range");
hsAssert(grid.fIdx[ii] + base < auxSpan->fIStartIdx + auxSpan->fILength, "Index going out of range.");
*idx++ = grid.fIdx[ii] + base;
}
auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx);
auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx);
return true;
}
void plDynaDecalMgr::ISetDepthFalloff()
{
const hsScalar totalDepth = fCutter->GetLengthW();
// Currently all constants, but these could be set per DecalMgr.
plConst(hsScalar) kMinFeet(3.f);
plConst(hsScalar) kMaxFeet(10.f);
plConst(hsScalar) kMinDepth(0.25f);
plConst(hsScalar) kMaxDepth(0.75f);
fMinDepth = kMinFeet / totalDepth;
if( fMinDepth > kMinDepth )
fMinDepth = kMinDepth;
fMinDepthRange = 1.f / fMinDepth;
fMaxDepth = 1.f - (kMaxFeet / totalDepth);
if( fMaxDepth < kMaxDepth )
fMaxDepth = kMaxDepth;
fMaxDepthRange = 1.f / (1.f - fMaxDepth);
}
hsBool plDynaDecalMgr::IConvertPolys(plAuxSpan* auxSpan,
plDynaDecal* decal,
hsTArray<plCutoutPoly>& src)
{
ISetDepthFalloff();
if( decal->fFlags & plDynaDecal::kVertexShader )
return IConvertPolysVS(auxSpan, decal, src);
if( decal->fFlags & plDynaDecal::kAttenColor )
return IConvertPolysColor(auxSpan, decal, src);
return IConvertPolysAlpha(auxSpan, decal, src);
}
hsBool plDynaDecalMgr::IConvertPolysAlpha(plAuxSpan* auxSpan,
plDynaDecal* decal,
hsTArray<plCutoutPoly>& src)
{
hsBool loU = false;
hsBool hiU = false;
hsBool loV = false;
hsBool hiV = false;
plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan);
vtx += decal->fStartVtx;
decal->fVtxBase = vtx;
hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx];
hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx];
const hsVector3 backDir = fCutter->GetBackDir();
int iPoly = 0;
int iVert = 0;
int iv;
for( iv = 0; iv < decal->fNumVerts; iv++ )
{
*origPos = vtx->fPos = src[iPoly].fVerts[iVert].fPos;
vtx->fNorm = src[iPoly].fVerts[iVert].fNorm;
vtx->fUVW[0] = src[iPoly].fVerts[iVert].fUVW;
if( vtx->fUVW[0].fX < 0.5f )
loU = true;
else
hiU = true;
if( vtx->fUVW[0].fY < 0.5f )
loV = true;
else
hiV = true;
hsColorRGBA col = src[iPoly].fVerts[iVert].fColor;
hsScalar depth = vtx->fUVW[0].fZ;
hsScalar opac = depth < fMinDepth
? depth * fMinDepthRange
: depth > fMaxDepth
? (1.f - depth) * fMaxDepthRange
: 1.f;
hsScalar normOpac = 1.f - vtx->fNorm.InnerProduct(backDir);
opac *= 1.f - normOpac * normOpac;
if( opac < 0 )
opac = 0;
if( src[iPoly].fBaseHasAlpha )
opac *= col.a;
col.a = 0;
origUVW->fX = vtx->fUVW[0].fX;
origUVW->fY = vtx->fUVW[0].fY;
origUVW->fZ = opac;
vtx->fUVW[1].Set(0, 0, 0);
vtx->fDiffuse = col.ToARGB32();
vtx->fSpecular = 0;
if( ++iVert >= src[iPoly].fVerts.GetCount() )
{
iVert = 0;
iPoly++;
}
vtx++;
origPos++;
origUVW++;
}
hsAssert(vtx <= IGetBaseVtxPtr(auxSpan) + auxSpan->fVBufferLimit, "Vtx pointer gone wild");
UInt16* idx = IGetBaseIdxPtr(auxSpan);
idx += decal->fStartIdx;
UInt16 base = decal->fStartVtx;
int j;
for( j = 0; j < src.GetCount(); j++ )
{
UInt16 next = base+1;
int k;
for( k = 2; k < src[j].fVerts.GetCount(); k++ )
{
*idx++ = base;
*idx++ = next++;
*idx++ = next;
}
base = ++next;
}
hsAssert(idx <= auxSpan->fGroup->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferLimit, "Index ptr gone wild");
auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx);
auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx);
return loU & hiU & loV & hiV;
}
hsBool plDynaDecalMgr::IConvertPolysColor(plAuxSpan* auxSpan,
plDynaDecal* decal,
hsTArray<plCutoutPoly>& src)
{
hsBool loU = false;
hsBool hiU = false;
hsBool loV = false;
hsBool hiV = false;
plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan);
vtx += decal->fStartVtx;
decal->fVtxBase = vtx;
hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx];
hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx];
const hsVector3 backDir = fCutter->GetBackDir();
int iPoly = 0;
int iVert = 0;
int iv;
for( iv = 0; iv < decal->fNumVerts; iv++ )
{
*origPos = vtx->fPos = src[iPoly].fVerts[iVert].fPos;
vtx->fNorm = src[iPoly].fVerts[iVert].fNorm;
vtx->fUVW[0] = src[iPoly].fVerts[iVert].fUVW;
if( vtx->fUVW[0].fX < 0.5f )
loU = true;
else
hiU = true;
if( vtx->fUVW[0].fY < 0.5f )
loV = true;
else
hiV = true;
hsScalar depth = vtx->fUVW[0].fZ;
hsScalar opac = depth < fMinDepth
? depth * fMinDepthRange
: depth > fMaxDepth
? (1.f - depth) * fMaxDepthRange
: 1.f;
hsScalar normOpac = 1.f - vtx->fNorm.InnerProduct(backDir);
opac *= 1.f - normOpac * normOpac;
if( opac < 0 )
opac = 0;
origUVW->fX = vtx->fUVW[0].fX;
origUVW->fY = vtx->fUVW[0].fY;
origUVW->fZ = opac;
vtx->fUVW[1].Set(0, 0, 0);
vtx->fDiffuse = 0xff000000;
vtx->fSpecular = 0;
if( ++iVert >= src[iPoly].fVerts.GetCount() )
{
iVert = 0;
iPoly++;
}
vtx++;
origPos++;
origUVW++;
}
hsAssert(vtx <= IGetBaseVtxPtr(auxSpan) + auxSpan->fVBufferLimit, "Vtx pointer gone wild");
UInt16* idx = IGetBaseIdxPtr(auxSpan);
idx += decal->fStartIdx;
UInt16 base = decal->fStartVtx;
int j;
for( j = 0; j < src.GetCount(); j++ )
{
UInt16 next = base+1;
int k;
for( k = 2; k < src[j].fVerts.GetCount(); k++ )
{
*idx++ = base;
*idx++ = next++;
*idx++ = next;
}
base = ++next;
}
hsAssert(idx <= auxSpan->fGroup->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferLimit, "Index ptr gone wild");
auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx);
auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx);
return loU & hiU & loV & hiV;
}
hsBool plDynaDecalMgr::IConvertPolysVS(plAuxSpan* auxSpan,
plDynaDecal* decal,
hsTArray<plCutoutPoly>& src)
{
hsBool loU = false;
hsBool hiU = false;
hsBool loV = false;
hsBool hiV = false;
plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan);
vtx += decal->fStartVtx;
decal->fVtxBase = vtx;
hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx];
hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx];
int iPoly = 0;
int iVert = 0;
int iv;
for( iv = 0; iv < decal->fNumVerts; iv++ )
{
*origPos = vtx->fPos = src[iPoly].fVerts[iVert].fPos;
vtx->fNorm = src[iPoly].fVerts[iVert].fNorm;
vtx->fUVW[0] = src[iPoly].fVerts[iVert].fUVW;
if( vtx->fUVW[0].fX < 0.5f )
loU = true;
else
hiU = true;
if( vtx->fUVW[0].fY < 0.5f )
loV = true;
else
hiV = true;
origUVW->fX = vtx->fUVW[0].fX;
origUVW->fY = vtx->fUVW[0].fY;
origUVW->fZ = vtx->fUVW[0].fZ = (hsScalar)decal->fBirth;
vtx->fUVW[1].Set(0, 0, 0);
const hsColorRGBA& col = src[iPoly].fVerts[iVert].fColor;
vtx->fDiffuse = col.ToARGB32();
vtx->fSpecular = 0;
if( ++iVert >= src[iPoly].fVerts.GetCount() )
{
iVert = 0;
iPoly++;
}
vtx++;
origPos++;
origUVW++;
}
hsAssert(vtx <= IGetBaseVtxPtr(auxSpan) + auxSpan->fVBufferLimit, "Vtx pointer gone wild");
UInt16* idx = IGetBaseIdxPtr(auxSpan);
idx += decal->fStartIdx;
UInt16 base = decal->fStartVtx;
int j;
for( j = 0; j < src.GetCount(); j++ )
{
UInt16 next = base+1;
int k;
for( k = 2; k < src[j].fVerts.GetCount(); k++ )
{
*idx++ = base;
*idx++ = next++;
*idx++ = next;
}
base = ++next;
}
hsAssert(idx <= auxSpan->fGroup->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferLimit, "Index ptr gone wild");
auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx);
auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx);
return loU & hiU & loV & hiV;
}
hsBool plDynaDecalMgr::IHitTestPolys(hsTArray<plCutoutPoly>& src) const
{
hsBool loU = false;
hsBool hiU = false;
hsBool loV = false;
hsBool hiV = false;
int iPoly = 0;
int iVert = 0;
while( iPoly < src.GetCount() )
{
const hsPoint3& uvw = src[iPoly].fVerts[iVert].fUVW;
if( uvw.fX < 0.5f )
loU = true;
else
hiU = true;
if( uvw.fY < 0.5f )
loV = true;
else
hiV = true;
if( ++iVert >= src[iPoly].fVerts.GetCount() )
{
iVert = 0;
iPoly++;
}
}
return loU & hiU & loV & hiV;
}
hsBool plDynaDecalMgr::IProcessPolys(plDrawableSpans* targ, int iSpan, double t, hsTArray<plCutoutPoly>& src)
{
// Figure out how many verts and idxs are coming in.
UInt16 numVerts, numIdx;
ICountIncoming(src, numVerts, numIdx);
if( !numVerts )
return false;
// Find a span to put them in. Either the current span, or a new
// one if it's full up.
plAuxSpan* auxSpan = IGetAuxSpan(targ, iSpan, nil, numVerts, numIdx);
// If we're full up, just see if we hit anything, but don't
// make any more decals. Might be nice to accelerate decay
// here, since we definitely aren't keeping up.
if( !auxSpan )
return IHitTestPolys(src);
// Get a decal to manage this group's aging.
// Update the span to point to enough room.
plDynaDecal* decal = IInitDecal(auxSpan, t, numVerts, numIdx);
// Convert the polys from src into the accessor tris
return IConvertPolys(auxSpan, decal, src);
}
hsBool plDynaDecalMgr::IProcessGrid(plDrawableSpans* targ, int iSpan, hsGMaterial* mat, double t, const plFlatGridMesh& grid)
{
// Find a span to put them in. Either the current span, or a new
// one if it's full up.
plAuxSpan* auxSpan = IGetAuxSpan(targ, iSpan, mat, grid.fVerts.GetCount(), grid.fIdx.GetCount());
// If we're full up, just see if we hit anything, but don't
// make any more decals.
if( !auxSpan )
return IHitTestFlatGrid(grid);
auxSpan->fFlags |= plAuxSpan::kWorldSpace;
// Get a decal to manage this group's aging.
// Update the span to point to enough room.
plDynaDecal* decal = IInitDecal(auxSpan, t, grid.fVerts.GetCount(), grid.fIdx.GetCount());
// Convert the grid from src into the accessor tris
return IConvertFlatGrid(auxSpan, decal, grid);
}
hsBool plDynaDecalMgr::IHitTestFlatGrid(const plFlatGridMesh& grid) const
{
return true;
}
//////////////////////////////////////////////////////////////////////////////////
hsBool plDynaDecalMgr::ICutoutGrid(plDrawableSpans* drawable, int iSpan, hsGMaterial* mat, double secs)
{
static plFlatGridMesh grid;
grid.Reset();
int nWid = int(fCutter->GetLengthU() / fGridSizeU);
int nLen = int(fCutter->GetLengthV() / fGridSizeV);
fCutter->CutoutGrid(nWid, nLen, grid);
return IProcessGrid(drawable, iSpan, mat, secs, grid);
}
hsBool plDynaDecalMgr::ICutoutObject(plSceneObject* so, double secs)
{
if( fDisableAccumulate )
return false;
hsBool retVal = false;
if( !so )
return retVal;
const plDrawInterface* di = so->GetDrawInterface();
if( !di )
return retVal;
plProfile_BeginTiming(Total);
int numGot = 0;
int j;
for( j = 0; j < di->GetNumDrawables(); j++ )
{
plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j));
// Nil dr - it hasn't loaded yet or something.
if( dr )
{
plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j));
if( !diIndex.IsMatrixOnly() )
{
int k;
for( k = 0; k < diIndex.GetCount(); k++ )
{
const plSpan* span = dr->GetSpan(diIndex[k]);
if( kVolumeCulled != fCutter->GetIsect().Test(span->fWorldBounds) )
{
plAccessSpan src;
plAccessGeometry::Instance()->OpenRO(dr, diIndex[k], src);
static hsTArray<plCutoutPoly> dst;
dst.SetCount(0);
plProfile_BeginTiming(Cutter);
fCutter->Cutout(src, dst);
plProfile_EndTiming(Cutter);
plProfile_BeginTiming(Process);
if( IProcessPolys(dr, diIndex[k], secs, dst) )
{
plProfile_BeginTiming(Callback);
if( src.HasWaterHeight() )
ICutoutCallback(dst, true, src.GetWaterHeight());
else
ICutoutCallback(dst);
plProfile_EndTiming(Callback);
retVal = true;
}
plProfile_EndTiming(Process);
plAccessGeometry::Instance()->Close(src);
}
}
}
}
}
plProfile_EndTiming(Total);
return retVal;
}
hsBool plDynaDecalMgr::ICutoutList(hsTArray<plDrawVisList>& drawVis, double secs)
{
if( fDisableAccumulate )
return false;
hsBool retVal = false;
if( !drawVis.GetCount() )
return retVal;
hsTArray<plAccessSpan> src;
int numSpan = 0;
int iDraw;
for( iDraw = 0; iDraw < drawVis.GetCount(); iDraw++ )
numSpan += drawVis[iDraw].fVisList.GetCount();
src.SetCount(numSpan);
int i;
iDraw = 0;
int iSpan = 0;
for( i = 0; i < numSpan; i++ )
{
static hsTArray<plCutoutPoly> dst;
dst.SetCount(0);
plAccessGeometry::Instance()->OpenRO(drawVis[iDraw].fDrawable, drawVis[iDraw].fVisList[iSpan], src[i]);
fCutter->Cutout(src[i], dst);
if( IProcessPolys((plDrawableSpans*)drawVis[iDraw].fDrawable, drawVis[iDraw].fVisList[iSpan], secs, dst) )
retVal = true;
plAccessGeometry::Instance()->Close(src[i]);
if( ++iSpan >= drawVis[iDraw].fVisList.GetCount() )
{
iDraw++;
iSpan = 0;
}
}
return retVal;
}
hsBool plDynaDecalMgr::ICutoutTargets(double secs)
{
if( fDisableAccumulate )
return false;
hsBool retVal = false;
int i;
for( i = 0; i < fTargets.GetCount(); i++ )
{
if( fTargets[i] )
retVal |= ICutoutObject(fTargets[i], secs);
}
return retVal;
}
//////////////////////////////////////////////////////////////////////////////////
#include "../plGImage/plBumpMapGen.h"
hsGMaterial* plDynaDecalMgr::IConvertToEnvMap(hsGMaterial* mat, plBitmap* envMap)
{
if( !mat || !envMap )
return nil;
plLayerInterface* oldLay = mat->GetLayer(0);
plMipmap* oldMip = plMipmap::ConvertNoRef(oldLay->GetTexture());
if( !oldMip )
return mat;
oldMip->SetCurrLevel(0);
hsGMaterial* newMat = TRACKED_NEW hsGMaterial;
char buff[256];
sprintf(buff, "%s_%s", GetKey()->GetName(), "EnvMat");
hsgResMgr::ResMgr()->NewKey(buff, newMat, GetKey()->GetUoid().GetLocation());
static plTweak<hsScalar> kSmooth(1.f);
plMipmap* bumpMap = plBumpMapGen::QikNormalMap(nil, oldMip, 0xffffffff, plBumpMapGen::kBubbleTest, kSmooth);
// plMipmap* bumpMap = plBumpMapGen::QikNormalMap(nil, oldMip, 0xffffffff, plBumpMapGen::kNormalize, kSmooth);
// plMipmap* bumpMap = plBumpMapGen::QikNormalMap(nil, oldMip, 0xffffffff, 0, 0);
sprintf(buff, "%s_%s", GetKey()->GetName(), "BumpMap");
hsgResMgr::ResMgr()->NewKey(buff, bumpMap, GetKey()->GetUoid().GetLocation());
bumpMap->SetFlags(bumpMap->GetFlags() | plMipmap::kBumpEnvMap | plMipmap::kForceNonCompressed);
plLayer* bumpLay = TRACKED_NEW plLayer;
sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "BumpMap", 0);
hsgResMgr::ResMgr()->NewKey(buff, bumpLay, GetKey()->GetUoid().GetLocation());
bumpLay->SetState(oldLay->GetState());
bumpLay->SetBlendFlags(hsGMatState::kBlendAdd | hsGMatState::kBlendEnvBumpNext);
bumpLay->SetTransform(hsMatrix44::IdentityMatrix());
bumpLay->SetUVWSrc(0);
hsMatrix44 bumpEnvXfm;
bumpEnvXfm.Reset();
bumpLay->SetBumpEnvMatrix(bumpEnvXfm);
bumpLay->SetAmbientColor(oldLay->GetAmbientColor());
bumpLay->SetRuntimeColor(oldLay->GetRuntimeColor());
bumpLay->SetOpacity(1.f);
plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(bumpLay->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture);
hsgResMgr::ResMgr()->SendRef(bumpMap->GetKey(), refMsg, plRefFlags::kActiveRef);
newMat->AddLayerViaNotify(bumpLay);
plLayer* envLay = TRACKED_NEW plLayer;
sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "EnvMap", 0);
hsgResMgr::ResMgr()->NewKey(buff, envLay, GetKey()->GetUoid().GetLocation());
envLay->SetBlendFlags(hsGMatState::kBlendMult);
envLay->SetClampFlags(0);
envLay->SetShadeFlags(bumpLay->GetShadeFlags());
envLay->SetZFlags(hsGMatState::kZNoZWrite);
envLay->SetMiscFlags(hsGMatState::kMiscUseReflectionXform);
envLay->SetUVWSrc(plLayer::kUVWReflect);
envLay->SetAmbientColor(oldLay->GetAmbientColor());
envLay->SetRuntimeColor(oldLay->GetRuntimeColor());
envLay->SetOpacity(1.f);
refMsg = TRACKED_NEW plLayRefMsg(envLay->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture);
hsgResMgr::ResMgr()->SendRef(envMap->GetKey(), refMsg, plRefFlags::kActiveRef);
newMat->AddLayerViaNotify(envLay);
return newMat;
}
void plDynaDecalMgr::ConvertToEnvMap(plBitmap* envMap)
{
hsGMaterial* newPreShade = IConvertToEnvMap(fMatPreShade, envMap);
if( newPreShade && (newPreShade != fMatPreShade) )
hsgResMgr::ResMgr()->SendRef(newPreShade->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatPreShade), plRefFlags::kActiveRef);
hsGMaterial* newRTShade = IConvertToEnvMap(fMatRTShade, envMap);
if( newRTShade && (newRTShade != fMatRTShade) )
hsgResMgr::ResMgr()->SendRef(newRTShade->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatRTShade), plRefFlags::kActiveRef);
}
const plMipmap* plDynaDecalMgr::GetMipmap() const
{
plMipmap* mip = nil;
if( fMatRTShade )
mip = plMipmap::ConvertNoRef(fMatRTShade->GetLayer(0)->GetTexture());
if( !mip && fMatPreShade )
mip = plMipmap::ConvertNoRef(fMatPreShade->GetLayer(0)->GetTexture());
if( mip )
mip->SetCurrLevel(0);
return mip;
}
hsVector3 plDynaDecalMgr::IRandomUp(hsVector3 dir) const
{
hsVector3 retVal;
// Okay, we want a pretty random vector perpindicular to the
// direction of fire. So we take that direction and cross product
// it with the 3 world axes. Now we have 3 random vectors perpindicular
// to the dir (but not necessarily each other). Note that some (but not all)
// of these vectors may be zero length, it the direction happens to line up
// with an axis. We scale each by a random amount, sum them up, and since
// they are all perpindicular to dir, the weighted sum is too.
// Only problem here is that our random scalings might wind us up with
// a zero vector. Unlikely, which means almost certain to happen. So
// we keep trying till we get a non-zero vector.
hsScalar lenSq(-1.f);
do {
hsScalar ranXx = sRand.RandMinusOneToOne();
hsScalar ranXy = sRand.RandMinusOneToOne();
hsScalar ranXz = sRand.RandMinusOneToOne();
retVal.fX = -dir.fZ * ranXy + dir.fY * ranXz;
retVal.fY = dir.fZ * ranXx + -dir.fX * ranXz;
retVal.fZ = -dir.fY * ranXx + dir.fX * ranXy;
lenSq = retVal.MagnitudeSquared();
} while( lenSq <= 0 );
retVal *= hsFastMath::InvSqrtAppr(lenSq);
return retVal;
}
hsVector3 plDynaDecalMgr::IReflectDir(hsVector3 dir) const
{
hsFastMath::NormalizeAppr(dir); // it's been interpolated.
// parm of zero returns unaffected dir, parm of one returns cutter direction reflected about dir.
// Here's the original math.
// Here N is dir, B is -cutter back direction (incoming), k is parm.
// Reflection R of B is 2*(N dot B)*N + B
// Interpolating gives K*R + (1-K)*N
// Simplifying gives (2*K*(N dot B) + (1-K)) * N + K*B
// Or something.
plConst(hsScalar) parm(0.5f);
hsVector3 b = -fCutter->GetBackDir();
hsScalar t = dir.InnerProduct(b);
t *= -2.f * parm;
t += (1.f - parm);
hsVector3 ret = dir * t;
ret += b * parm;
return ret;
}
hsMatrix44 plDynaDecalMgr::IL2WFromHit(hsPoint3 pos, hsVector3 dir) const
{
dir = IReflectDir(dir);
// Negate the firing direction before constructing our psys l2w, because
// particles fire in the negative Z direction.
dir = -dir;
hsVector3 up = IRandomUp(dir);
hsVector3 acc = up % dir;
hsMatrix44 l2w;
l2w.Reset();
l2w.fMap[0][0] = acc[0];
l2w.fMap[1][0] = acc[1];
l2w.fMap[2][0] = acc[2];
l2w.fMap[0][1] = up[0];
l2w.fMap[1][1] = up[1];
l2w.fMap[2][1] = up[2];
l2w.fMap[0][2] = dir[0];
l2w.fMap[1][2] = dir[1];
l2w.fMap[2][2] = dir[2];
l2w.fMap[0][3] = pos[0];
l2w.fMap[1][3] = pos[1];
l2w.fMap[2][3] = pos[2];
l2w.NotIdentity();
return l2w;
}
void plDynaDecalMgr::ICutoutCallback(const hsTArray<plCutoutPoly>& cutouts, hsBool hasWaterHeight, hsScalar waterHeight)
{
hsTArray<plCutoutHit> hits;
if( (fPartyTime > 0) && fParticles.GetCount() )
{
if( hasWaterHeight )
fCutter->FindHitPointsConstHeight(cutouts, hits, waterHeight);
else
fCutter->FindHitPoints(cutouts, hits);
int i;
for( i = 0; i < hits.GetCount(); i++ )
{
int j;
for( j = 0; j < fParticles.GetCount(); j++ )
{
plParticleEmitter* emit = fParticles[j]->GetAvailEmitter();
if( emit )
{
hsMatrix44 l2w = IL2WFromHit(hits[i].fPos, hits[i].fNorm);
emit->OverrideLocalToWorld(l2w);
emit->SetTimeToLive(fPartyTime);
}
}
}
}
}
void plDynaDecalMgr::IGetParticles()
{
if( fParticles.GetCount() != fPartyObjects.GetCount() )
{
int i;
for( i = 0; i < fPartyObjects.GetCount(); i++ )
{
const plParticleSystem *sys = plParticleSystem::ConvertNoRef(fPartyObjects[i]->GetModifierByType(plParticleSystem::Index()));
// const_cast here is just to see if it's in our list, make Find happy.
if( sys && (fParticles.kMissingIndex == fParticles.Find(const_cast<plParticleSystem*>(sys))) )
{
hsgResMgr::ResMgr()->AddViaNotify(sys->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefParticles), plRefFlags::kPassiveRef);
}
}
}
}