|
|
|
/*==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/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
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 "HeadSpin.h"
|
|
|
|
#include "hsFastMath.h"
|
|
|
|
#include "hsTimer.h"
|
|
|
|
|
|
|
|
#include "plWaveSet7.h"
|
|
|
|
#include "plWaveSetShaderConsts.h"
|
|
|
|
#include "plRipVSConsts.h"
|
|
|
|
|
|
|
|
#include "plAccessGeometry.h"
|
|
|
|
#include "plAccessSpan.h"
|
|
|
|
#include "plAccessVtxSpan.h"
|
|
|
|
|
|
|
|
#include "plAuxSpan.h"
|
|
|
|
#include "plDynaDecal.h"
|
|
|
|
#include "plDynaRippleVSMgr.h"
|
|
|
|
|
|
|
|
#include "plMessage/plRenderMsg.h"
|
|
|
|
#include "pnMessage/plTimeMsg.h"
|
|
|
|
|
|
|
|
#include "pnMessage/plObjRefMsg.h"
|
|
|
|
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "plPipeline.h"
|
|
|
|
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
|
|
|
|
#include "pnSceneObject/plDrawInterface.h"
|
|
|
|
|
|
|
|
#include "plPhysical.h"
|
|
|
|
#include "plMessage/plSimInfluenceMsg.h"
|
|
|
|
|
|
|
|
#include "plSurface/hsGMaterial.h"
|
|
|
|
#include "plDrawable/plDrawableSpans.h"
|
|
|
|
#include "plDrawable/plDrawableGenerator.h"
|
|
|
|
|
|
|
|
#include "plMessage/plAvatarMsg.h"
|
|
|
|
#include "plAvatar/plArmatureMod.h"
|
|
|
|
|
|
|
|
#include "plGImage/plMipmap.h"
|
|
|
|
#include "plGImage/plCubicEnvironmap.h"
|
|
|
|
#include "plSurface/plLayer.h"
|
|
|
|
#include "plMessage/plLayRefMsg.h"
|
|
|
|
|
|
|
|
#include "plSurface/plShader.h"
|
|
|
|
|
|
|
|
#include "plPipeline/plRenderTarget.h"
|
|
|
|
#include "plScene/plRenderRequest.h"
|
|
|
|
#include "plMessage/plRenderRequestMsg.h"
|
|
|
|
#include "plScene/plPageTreeMgr.h"
|
|
|
|
|
|
|
|
#include "plPipeline/plDynamicEnvMap.h"
|
|
|
|
|
|
|
|
#include "plGImage/plBumpMapGen.h"
|
|
|
|
|
|
|
|
#include "plMessage/plMatRefMsg.h"
|
|
|
|
#include "plMessage/plAgeLoadedMsg.h"
|
|
|
|
|
|
|
|
#include "plTweak.h"
|
|
|
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
#include "plStatusLog/plStatusLog.h"
|
|
|
|
#include "plPipeline/plPlates.h"
|
|
|
|
#endif // PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
|
|
|
using namespace plShaderID;
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
#include "plProfile.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#define TEST_ENVSPH
|
|
|
|
|
|
|
|
// #define TEST_UVWS
|
|
|
|
|
|
|
|
static const float kPiOverTwo = M_PI * 0.5f;
|
|
|
|
|
|
|
|
static const float kGravConst = 30.f;
|
|
|
|
|
|
|
|
static const float kOOEightNsqPI = 1.f / (8.f * M_PI * 4.f * 4.f);
|
|
|
|
static float currOOEightNsqPI = kOOEightNsqPI;
|
|
|
|
|
|
|
|
static inline float FreqToLen(float f) { return 2.f * M_PI / f; }
|
|
|
|
static inline float LenToFreq(float l) { return 2.f * M_PI / l; }
|
|
|
|
|
|
|
|
static inline float MPH2FPS(float f) { return f * 5280.f / 3600.f; }
|
|
|
|
static inline float FPS2MPH(float f) { return f / 5280.f * 3600.f; }
|
|
|
|
|
|
|
|
plCONST(float) kTimeClamp(0.3f);
|
|
|
|
|
|
|
|
inline void plWorldWave7::Accumulate(hsPoint3& accumPos, hsVector3& accumNorm) const
|
|
|
|
{
|
|
|
|
float dist = accumPos.fX * fDir.fX + accumPos.fY * fDir.fY;
|
|
|
|
|
|
|
|
dist *= fFreq;
|
|
|
|
dist += fPhase;
|
|
|
|
|
|
|
|
float s, c;
|
|
|
|
hsFastMath::SinCosAppr(dist, s, c);
|
|
|
|
// s += 1.f;
|
|
|
|
|
|
|
|
// Add scaled local Z instead? So decals can move up and down non-vertically?
|
|
|
|
// Thing is, for the height is still based on the unperterbed position, so when
|
|
|
|
// we move it laterally, the height is wrong. Maybe not too wrong, but still wrong.
|
|
|
|
// Same for normal.
|
|
|
|
accumPos.fZ += s * fAmplitude;
|
|
|
|
|
|
|
|
c *= -fFreq * fAmplitude;
|
|
|
|
accumNorm.fX += fDir.fX * c;
|
|
|
|
accumNorm.fY += fDir.fY * c;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
inline void plWaveSet7::GraphLen(float len) const
|
|
|
|
{
|
|
|
|
if( fStatusGraph )
|
|
|
|
{
|
|
|
|
float maxLen = TexState().fMaxLength * kCompositeSize / State().fRippleScale;
|
|
|
|
int32_t val = int32_t(len / maxLen * 100.f);
|
|
|
|
fStatusGraph->AddData(val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::IRestartGraph() const
|
|
|
|
{
|
|
|
|
if( fStatusGraph )
|
|
|
|
fStatusGraph->ClearData();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StartGraph()
|
|
|
|
{
|
|
|
|
delete fStatusGraph;
|
|
|
|
plPlateManager::Instance().CreateGraphPlate(&fStatusGraph);
|
|
|
|
fStatusGraph->SetSize(0.25f, 0.25f);
|
|
|
|
fStatusGraph->SetDataRange(0, 100, kNumTexWaves);
|
|
|
|
fStatusGraph->SetVisible(true);
|
|
|
|
fStatusGraph->SetPosition(0.75f, -0.75f);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StopGraph()
|
|
|
|
{
|
|
|
|
delete fStatusGraph;
|
|
|
|
fStatusGraph = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::LogF(const char *format, ...) const
|
|
|
|
{
|
|
|
|
if( fStatusLog )
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args,format);
|
|
|
|
fStatusLog->AddLineV(format, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::LogF(uint32_t color, const char *format, ...) const
|
|
|
|
{
|
|
|
|
if( fStatusLog )
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args,format);
|
|
|
|
fStatusLog->AddLineV(color, format, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::IRestartLog() const
|
|
|
|
{
|
|
|
|
if( fStatusLog )
|
|
|
|
fStatusLog->Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StartLog()
|
|
|
|
{
|
|
|
|
delete fStatusLog;
|
|
|
|
fStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(kNumTexWaves, "TexWaves",
|
|
|
|
plStatusLog::kDontWriteFile | plStatusLog::kDeleteForMe | plStatusLog::kFilledBackground);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StopLog()
|
|
|
|
{
|
|
|
|
delete fStatusLog;
|
|
|
|
fStatusLog = nil;
|
|
|
|
}
|
|
|
|
#else // PLASMA_EXTERNAL_RELEASE
|
|
|
|
inline void plWaveSet7::GraphLen(float len) const
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::IRestartGraph() const
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StartGraph()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StopGraph()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::LogF(const char *format, ...) const
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::LogF(uint32_t color, const char *format, ...) const
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::IRestartLog() const
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StartLog()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::StopLog()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif // PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Enough of that, WaveSet (system manager) follows
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
plWaveSet7::plWaveSet7()
|
|
|
|
: fLastTime(0),
|
|
|
|
fCurrTime(0),
|
|
|
|
fScrunchLen(8.f),
|
|
|
|
fScrunchScale(2.f),
|
|
|
|
fFreqScale(1.f),
|
|
|
|
fRipVShader(nil),
|
|
|
|
fRipPShader(nil),
|
|
|
|
fShoreVShader(nil),
|
|
|
|
fShorePShader(nil),
|
|
|
|
fFixedVShader(nil),
|
|
|
|
fFixedPShader(nil),
|
|
|
|
fBiasVShader(nil),
|
|
|
|
fBiasPShader(nil),
|
|
|
|
fBumpMat(nil),
|
|
|
|
fBumpDraw(nil),
|
|
|
|
fBumpReq(nil),
|
|
|
|
fBumpReqMsg(nil),
|
|
|
|
fEnvMap(nil),
|
|
|
|
fRefObj(nil),
|
|
|
|
fCosineLUT(nil),
|
|
|
|
fGraphShoreTex(nil),
|
|
|
|
fBubbleShoreTex(nil),
|
|
|
|
fEdgeShoreTex(nil),
|
|
|
|
fMaxLen(0),
|
|
|
|
|
|
|
|
fTrialUpdate(0),
|
|
|
|
fTransistor(-1),
|
|
|
|
fTransCountDown(30.f),
|
|
|
|
fTransDel(0),
|
|
|
|
|
|
|
|
fTexTrans(0),
|
|
|
|
fTexTransCountDown(0),
|
|
|
|
fTexTransDel(0),
|
|
|
|
|
|
|
|
fStatusLog(nil),
|
|
|
|
fStatusGraph(nil)
|
|
|
|
{
|
|
|
|
IInitState();
|
|
|
|
IInitWaveConsts();
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
fFreqMod[i] = 1.f;
|
|
|
|
IInitWave(i);
|
|
|
|
}
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
|
|
{
|
|
|
|
fFixedLayers[i] = nil;
|
|
|
|
}
|
|
|
|
for( i = 0; i < kNumBumpShaders; i++ )
|
|
|
|
{
|
|
|
|
fBumpVShader[i] = nil;
|
|
|
|
fBumpPShader[i] = nil;
|
|
|
|
}
|
|
|
|
fBiasLayer[0] = nil;
|
|
|
|
fBiasLayer[1] = nil;
|
|
|
|
for( i = 0; i < kNumTexWaves; i++ )
|
|
|
|
{
|
|
|
|
fBumpLayers[i] = nil;
|
|
|
|
fTexWaveFade[i] = 1.f;
|
|
|
|
}
|
|
|
|
for( i = 0; i < kGraphShorePasses; i++ )
|
|
|
|
{
|
|
|
|
fGraphVShader[i] = nil;
|
|
|
|
fGraphPShader[i] = nil;
|
|
|
|
|
|
|
|
fGraphShoreMat[i] = nil;
|
|
|
|
fGraphShoreRT[i] = nil;
|
|
|
|
fGraphShoreDraw[i] = nil;
|
|
|
|
fGraphReq[i] = nil;
|
|
|
|
fGraphReqMsg[i] = nil;
|
|
|
|
}
|
|
|
|
for( i = 0; i < kNumDecalVShaders; i++ )
|
|
|
|
fDecalVShaders[i] = nil;
|
|
|
|
for( i = 0; i < kNumDecalPShaders; i++ )
|
|
|
|
fDecalPShaders[i] = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plWaveSet7::~plWaveSet7()
|
|
|
|
{
|
|
|
|
delete fStatusLog;
|
|
|
|
|
|
|
|
delete fBumpReqMsg;
|
|
|
|
delete fBumpReq;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kGraphShorePasses; i++ )
|
|
|
|
{
|
|
|
|
delete fGraphReqMsg[i];
|
|
|
|
delete fGraphReq[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::Read(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plMultiModifier::Read(stream, mgr);
|
|
|
|
|
|
|
|
fMaxLen = stream->ReadLEScalar();
|
|
|
|
|
|
|
|
fState.Read(stream);
|
|
|
|
IUpdateWindDir(0);
|
|
|
|
|
|
|
|
int n = stream->ReadLE32();
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefShore), plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
n = stream->ReadLE32();
|
|
|
|
for( i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefDecal), plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefEnvMap), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
if( HasFlag(kHasRefObject) )
|
|
|
|
{
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefRefObj), plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
ISetupTextureWaves();
|
|
|
|
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
fFreqMod[i] = 1.f;
|
|
|
|
IInitWave(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::Write(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plMultiModifier::Write(stream, mgr);
|
|
|
|
|
|
|
|
stream->WriteLEScalar(fMaxLen);
|
|
|
|
|
|
|
|
fState.Write(stream);
|
|
|
|
|
|
|
|
stream->WriteLE32(fShores.GetCount());
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fShores.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
mgr->WriteKey(stream, fShores[i]);
|
|
|
|
}
|
|
|
|
stream->WriteLE32(fDecals.GetCount());
|
|
|
|
for( i = 0; i < fDecals.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
mgr->WriteKey(stream, fDecals[i]);
|
|
|
|
}
|
|
|
|
mgr->WriteKey(stream, fEnvMap);
|
|
|
|
|
|
|
|
if( HasFlag(kHasRefObject) )
|
|
|
|
{
|
|
|
|
mgr->WriteKey(stream, fRefObj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plWaveSet7::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
plEvalMsg* update = plEvalMsg::ConvertNoRef(msg);
|
|
|
|
if( update )
|
|
|
|
{
|
|
|
|
if (fFixedVShader == nil)
|
|
|
|
{
|
|
|
|
ICheckTargetMaterials();
|
|
|
|
ICheckShoreMaterials();
|
|
|
|
ICheckDecalMaterials();
|
|
|
|
}
|
|
|
|
IRestartLog();
|
|
|
|
IRestartGraph();
|
|
|
|
|
|
|
|
plCONST(hsBool) reRender(false);
|
|
|
|
if( reRender || (fTrialUpdate & kReRenderEnvMap) )
|
|
|
|
{
|
|
|
|
plDynamicEnvMap* envMap = plDynamicEnvMap::ConvertNoRef(fEnvMap);
|
|
|
|
if( envMap )
|
|
|
|
{
|
|
|
|
envMap->ReRender();
|
|
|
|
|
|
|
|
fTrialUpdate &= ~kReRenderEnvMap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float dt = update->DelSeconds();
|
|
|
|
if( dt > kTimeClamp )
|
|
|
|
dt = kTimeClamp;
|
|
|
|
|
|
|
|
IUpdateWindDir(dt);
|
|
|
|
|
|
|
|
IFloatBuoys(dt);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg);
|
|
|
|
if( rend )
|
|
|
|
{
|
|
|
|
if( !IAnyBoundsVisible(rend->Pipeline()) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fCurrTime = hsTimer::GetSysSeconds();
|
|
|
|
// Can't just use GetDelSysSeconds() or else we lose time if we skip a frame render because of high FPS.
|
|
|
|
float dt = fLastTime > 0 ? float(fCurrTime - fLastTime) : hsTimer::GetDelSysSeconds();
|
|
|
|
if( dt > kTimeClamp )
|
|
|
|
dt = kTimeClamp;
|
|
|
|
|
|
|
|
IUpdateRefObject();
|
|
|
|
|
|
|
|
IUpdateLayers(dt);
|
|
|
|
|
|
|
|
IUpdateWaves(dt);
|
|
|
|
|
|
|
|
hsMatrix44 l2w;
|
|
|
|
hsMatrix44 w2l;
|
|
|
|
IUpdateShaders(rend->Pipeline(), l2w, w2l);
|
|
|
|
IUpdateGraphShaders(rend->Pipeline(), dt);
|
|
|
|
|
|
|
|
fLastTime = fCurrTime;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAgeLoadedMsg* ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg);
|
|
|
|
if( (ageLoaded && ageLoaded->fLoaded) || plInitialAgeStateLoadedMsg::ConvertNoRef(msg) )
|
|
|
|
{
|
|
|
|
ICheckTargetMaterials();
|
|
|
|
ICheckShoreMaterials();
|
|
|
|
ICheckDecalMaterials();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg);
|
|
|
|
if( refMsg )
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace) )
|
|
|
|
{
|
|
|
|
IOnReceive(refMsg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IOnRemove(refMsg);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return plMultiModifier::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plWaveSet7::IAnyBoundsVisible(plPipeline* pipe) const
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fTargBnds.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( pipe->TestVisibleWorld(fTargBnds[i]) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plWaveSet7::IOnReceive(plGenRefMsg* refMsg)
|
|
|
|
{
|
|
|
|
switch( refMsg->fType )
|
|
|
|
{
|
|
|
|
case kRefRefObj:
|
|
|
|
fRefObj = plSceneObject::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefCosineLUT:
|
|
|
|
fCosineLUT = plMipmap::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefEnvMap:
|
|
|
|
fEnvMap = plBitmap::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefShore:
|
|
|
|
fShores.Append(plSceneObject::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case kRefDecal:
|
|
|
|
fDecals.Append(plSceneObject::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case kRefDecVShader:
|
|
|
|
fDecalVShaders[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefDecPShader:
|
|
|
|
fDecalPShaders[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreRT:
|
|
|
|
fGraphShoreRT[refMsg->fWhich] = plRenderTarget::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefGraphVShader:
|
|
|
|
fGraphVShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefGraphPShader:
|
|
|
|
fGraphPShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreTex:
|
|
|
|
fGraphShoreTex = plMipmap::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefBubbleShoreTex:
|
|
|
|
fBubbleShoreTex = plMipmap::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefEdgeShoreTex:
|
|
|
|
fEdgeShoreTex = plMipmap::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreMat:
|
|
|
|
fGraphShoreMat[refMsg->fWhich] = hsGMaterial::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreDraw:
|
|
|
|
fGraphShoreDraw[refMsg->fWhich] = plDrawableSpans::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefBiasVShader:
|
|
|
|
fBiasVShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefBiasPShader:
|
|
|
|
fBiasPShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefBumpVShader:
|
|
|
|
fBumpVShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefBumpPShader:
|
|
|
|
fBumpPShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefRipVShader:
|
|
|
|
fRipVShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefRipPShader:
|
|
|
|
fRipPShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefShoreVShader:
|
|
|
|
fShoreVShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefShorePShader:
|
|
|
|
fShorePShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefFixedVShader:
|
|
|
|
fFixedVShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefFixedPShader:
|
|
|
|
fFixedPShader = plShader::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefBumpDraw:
|
|
|
|
fBumpDraw = plDrawableSpans::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefBumpMat:
|
|
|
|
fBumpMat = hsGMaterial::ConvertNoRef(refMsg->GetRef());
|
|
|
|
return true;
|
|
|
|
case kRefDynaDecalMgr:
|
|
|
|
{
|
|
|
|
plDynaDecalMgr* dyna = plDynaDecalMgr::ConvertNoRef(refMsg->GetRef());
|
|
|
|
if( fDecalMgrs.Find(dyna) == fDecalMgrs.kMissingIndex )
|
|
|
|
{
|
|
|
|
fDecalMgrs.Append(dyna);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case kRefBuoy:
|
|
|
|
{
|
|
|
|
plSceneObject* so = plSceneObject::ConvertNoRef(refMsg->GetRef());
|
|
|
|
if( fBuoys.Find(so) == fBuoys.kMissingIndex )
|
|
|
|
{
|
|
|
|
IShiftCenter(so);
|
|
|
|
fBuoys.Append(so);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plWaveSet7::IOnRemove(plGenRefMsg* refMsg)
|
|
|
|
{
|
|
|
|
switch( refMsg->fType )
|
|
|
|
{
|
|
|
|
case kRefRefObj:
|
|
|
|
fRefObj = nil;
|
|
|
|
return true;
|
|
|
|
case kRefCosineLUT:
|
|
|
|
fCosineLUT = nil;
|
|
|
|
return true;
|
|
|
|
case kRefEnvMap:
|
|
|
|
fEnvMap = nil;
|
|
|
|
return true;
|
|
|
|
case kRefShore:
|
|
|
|
{
|
|
|
|
plSceneObject* shore = (plSceneObject*)refMsg->GetRef();
|
|
|
|
int idx = fShores.Find(shore);
|
|
|
|
if( idx != fShores.kMissingIndex )
|
|
|
|
fShores.Remove(idx);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case kRefDecal:
|
|
|
|
{
|
|
|
|
plSceneObject* decal = (plSceneObject*)refMsg->GetRef();
|
|
|
|
int idx = fDecals.Find(decal);
|
|
|
|
if( idx != fDecals.kMissingIndex )
|
|
|
|
fDecals.Remove(idx);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case kRefDecVShader:
|
|
|
|
fDecalVShaders[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefDecPShader:
|
|
|
|
fDecalPShaders[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreRT:
|
|
|
|
fGraphShoreRT[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefGraphVShader:
|
|
|
|
fGraphVShader[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefGraphPShader:
|
|
|
|
fGraphPShader[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreTex:
|
|
|
|
fGraphShoreTex = nil;
|
|
|
|
return true;
|
|
|
|
case kRefBubbleShoreTex:
|
|
|
|
fBubbleShoreTex = nil;
|
|
|
|
return true;
|
|
|
|
case kRefEdgeShoreTex:
|
|
|
|
fEdgeShoreTex = nil;
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreMat:
|
|
|
|
fGraphShoreMat[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefGraphShoreDraw:
|
|
|
|
fGraphShoreDraw[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefBiasVShader:
|
|
|
|
fBiasVShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefBiasPShader:
|
|
|
|
fBiasPShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefBumpVShader:
|
|
|
|
fBumpVShader[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefBumpPShader:
|
|
|
|
fBumpPShader[refMsg->fWhich] = nil;
|
|
|
|
return true;
|
|
|
|
case kRefRipVShader:
|
|
|
|
fRipVShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefRipPShader:
|
|
|
|
fRipPShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefShoreVShader:
|
|
|
|
fShoreVShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefShorePShader:
|
|
|
|
fShorePShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefFixedVShader:
|
|
|
|
fFixedVShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefFixedPShader:
|
|
|
|
fFixedPShader = nil;
|
|
|
|
return true;
|
|
|
|
case kRefBumpDraw:
|
|
|
|
fBumpDraw = nil;
|
|
|
|
return true;
|
|
|
|
case kRefBumpMat:
|
|
|
|
fBumpMat = nil;
|
|
|
|
return true;
|
|
|
|
case kRefDynaDecalMgr:
|
|
|
|
{
|
|
|
|
plDynaDecalMgr* dyna = (plDynaDecalMgr*)refMsg->GetRef();
|
|
|
|
int idx = fDecalMgrs.Find(dyna);
|
|
|
|
if( fDecalMgrs.kMissingIndex != idx )
|
|
|
|
fDecalMgrs.Remove(idx);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case kRefBuoy:
|
|
|
|
{
|
|
|
|
plSceneObject* so = (plSceneObject*)refMsg->GetRef();
|
|
|
|
int idx = fBuoys.Find(so);
|
|
|
|
if( fBuoys.kMissingIndex != idx )
|
|
|
|
fBuoys.Remove(idx);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::SetState(const plFixedWaterState7& state, float dur)
|
|
|
|
{
|
|
|
|
fState.Set(state, dur);
|
|
|
|
|
|
|
|
if( fFixedLayers[0] )
|
|
|
|
{
|
|
|
|
plLayer* lay = plLayer::ConvertNoRef(fFixedLayers[0]->BottomOfStack());
|
|
|
|
lay->SetRuntimeColor(state.fWaterTint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateWaves(float dt)
|
|
|
|
{
|
|
|
|
ITransition(dt);
|
|
|
|
ITransTex(dt);
|
|
|
|
ICalcScale();
|
|
|
|
fScrunchLen = 1.e33f;
|
|
|
|
|
|
|
|
if( fTrialUpdate & kReInitWaves )
|
|
|
|
{
|
|
|
|
IReInitWaves();
|
|
|
|
ISetupTextureWaves();
|
|
|
|
ICreateBumpMipmapPS();
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
IUpdateWave(dt, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return true if we've finished this transition.
|
|
|
|
hsBool plWaveSet7::ITransContinue(float dt)
|
|
|
|
{
|
|
|
|
float currFade = (fFreqMod[fTransistor] += fTransDel * dt);
|
|
|
|
|
|
|
|
if( currFade <= 0 )
|
|
|
|
{
|
|
|
|
// Done fading down, time to fade back up.
|
|
|
|
fTransDel = -fTransDel;
|
|
|
|
fFreqMod[fTransistor] = fTransDel * dt;
|
|
|
|
|
|
|
|
// Reinit the wave. This gives a chance to introduce more randomization,
|
|
|
|
// as well as react to changing weather conditions.
|
|
|
|
IInitWave(fTransistor);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( currFade >= 1.f )
|
|
|
|
{
|
|
|
|
fFreqMod[fTransistor] = 1.f;
|
|
|
|
fTransDel = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IStartTransition(float dt)
|
|
|
|
{
|
|
|
|
// select the next wave for transitioning
|
|
|
|
if( ++fTransistor >= kNumWaves )
|
|
|
|
fTransistor = 0;
|
|
|
|
|
|
|
|
// set the transFade to be fading down.
|
|
|
|
plCONST(float) kTransDel(0.5f);
|
|
|
|
fTransDel = -kTransDel;
|
|
|
|
}
|
|
|
|
|
|
|
|
float plWaveSet7::ITransitionDelay() const
|
|
|
|
{
|
|
|
|
plCONST(float) kTransDelay(2.f);
|
|
|
|
return kTransDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ITransition(float dt)
|
|
|
|
{
|
|
|
|
// If we're in a transition, keep transitioning till it's done.
|
|
|
|
if( fTransDel != 0 )
|
|
|
|
{
|
|
|
|
if( ITransContinue(dt) )
|
|
|
|
fTransCountDown = ITransitionDelay();
|
|
|
|
}
|
|
|
|
// else if our transition countdown has gone to zero, start a new one.
|
|
|
|
else if( (fTransCountDown -= dt) <= 0 )
|
|
|
|
{
|
|
|
|
IStartTransition(dt);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plWaveSet7::ITransTexContinue(float dt)
|
|
|
|
{
|
|
|
|
float currFade = (fTexWaveFade[fTexTrans] += fTexTransDel * dt);
|
|
|
|
|
|
|
|
if( currFade <= 0 )
|
|
|
|
{
|
|
|
|
// Done fading down, time to fade back up.
|
|
|
|
fTexTransDel = -fTexTransDel;
|
|
|
|
fTexWaveFade[fTexTrans] = fTexTransDel * dt;
|
|
|
|
|
|
|
|
// ReInit the wave
|
|
|
|
IInitTexWave(fTexTrans);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( currFade >= 1.f )
|
|
|
|
{
|
|
|
|
// This one is back to full on, time to pick on another one.
|
|
|
|
fTexWaveFade[fTexTrans] = 1.f;
|
|
|
|
fTexTransDel = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IStartTexTransition(float dt)
|
|
|
|
{
|
|
|
|
if( ++fTexTrans >= kNumTexWaves )
|
|
|
|
fTexTrans = 0;
|
|
|
|
|
|
|
|
plConst(float) kTexTransDel(4.f);
|
|
|
|
fTexTransDel = -kTexTransDel;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ITransTex(float dt)
|
|
|
|
{
|
|
|
|
|
|
|
|
// If we're in a transition, keep with it.
|
|
|
|
if( fTexTransDel != 0 )
|
|
|
|
{
|
|
|
|
plConst(float) kTexTransDelay(0);
|
|
|
|
if( ITransTexContinue(dt) )
|
|
|
|
fTexTransCountDown = kTexTransDelay;
|
|
|
|
}
|
|
|
|
// else if it's time to start another...
|
|
|
|
else if( (fTexTransCountDown -= dt) <= 0 )
|
|
|
|
{
|
|
|
|
IStartTexTransition(dt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICalcScale()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateWave(float dt, int i)
|
|
|
|
{
|
|
|
|
plWorldWave7& wave = fWorldWaves[i];
|
|
|
|
|
|
|
|
float len = FreqToLen(wave.fFreq);
|
|
|
|
|
|
|
|
float speed = hsFastMath::InvSqrtAppr(len / (2.f * M_PI * kGravConst));
|
|
|
|
|
|
|
|
static float speedHack = 1.f;
|
|
|
|
speed *= speedHack;
|
|
|
|
wave.fPhase += speed * dt;
|
|
|
|
// wave.fPhase = fmod( speed * t, 2.f * M_PI);
|
|
|
|
|
|
|
|
float amp = GeoState().fAmpOverLen * len / float(kNumWaves);
|
|
|
|
|
|
|
|
amp *= fFreqMod[i] * fFreqScale;
|
|
|
|
|
|
|
|
wave.fAmplitude = amp;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IReInitWaves()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
IInitWave(i);
|
|
|
|
}
|
|
|
|
fTransDel = 0;
|
|
|
|
fTransCountDown = ITransitionDelay();
|
|
|
|
fTrialUpdate &= ~kReInitWaves;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plWaveSet7::IInitWave(int i)
|
|
|
|
{
|
|
|
|
plWorldWave7& wave = fWorldWaves[i];
|
|
|
|
|
|
|
|
wave.fLength = GeoState().fMinLength + fRand.RandZeroToOne() * (GeoState().fMaxLength - GeoState().fMinLength);
|
|
|
|
|
|
|
|
float len = wave.fLength;
|
|
|
|
|
|
|
|
wave.fFreq = LenToFreq(len);
|
|
|
|
|
|
|
|
wave.fPhase = 0;
|
|
|
|
|
|
|
|
wave.fAmplitude = 0;
|
|
|
|
|
|
|
|
// Figure out the direction based on wind direction.
|
|
|
|
// Even waves go in the wind direction,
|
|
|
|
// odd waves go opposite direction
|
|
|
|
plConst(float) kMinRotDeg(15.f);
|
|
|
|
plConst(float) kMaxRotDeg(180.f);
|
|
|
|
hsVector3 dir = fWindDir;
|
|
|
|
|
|
|
|
float rotBase = GeoState().fAngleDev;
|
|
|
|
|
|
|
|
float rads = rotBase * fRand.RandMinusOneToOne();
|
|
|
|
float rx = float(cosf(rads));
|
|
|
|
float ry = float(sinf(rads));
|
|
|
|
|
|
|
|
float x = dir.fX;
|
|
|
|
float y = dir.fY;
|
|
|
|
dir.fX = x * rx + y * ry;
|
|
|
|
dir.fY = x * -ry + y * rx;
|
|
|
|
|
|
|
|
wave.fDir.Set(dir.fX, dir.fY, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void plWaveSet7::IScrunch(hsPoint3& pos, hsVector3& norm) const
|
|
|
|
{
|
|
|
|
pos.fX += -norm.fX * fScrunchLen;
|
|
|
|
pos.fY += -norm.fY * fScrunchLen;
|
|
|
|
|
|
|
|
norm.fX *= fScrunchScale;
|
|
|
|
norm.fY *= fScrunchScale;
|
|
|
|
|
|
|
|
hsFastMath::NormalizeAppr(norm);
|
|
|
|
}
|
|
|
|
|
|
|
|
float plWaveSet7::EvalPoint(hsPoint3& pos, hsVector3& norm)
|
|
|
|
{
|
|
|
|
hsPoint3 accumPos;
|
|
|
|
hsVector3 accumNorm;
|
|
|
|
accumPos.Set(pos.fX, pos.fY, State().fWaterHeight);
|
|
|
|
accumNorm.Set(0,0,0);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
fWorldWaves[i].Accumulate(accumPos, accumNorm);
|
|
|
|
|
|
|
|
accumNorm.fZ = 1.f;
|
|
|
|
|
|
|
|
hsFastMath::NormalizeAppr(accumNorm);
|
|
|
|
|
|
|
|
IScrunch(accumPos, accumNorm);
|
|
|
|
|
|
|
|
// Project original pos along Z onto the plane tangent at accumPos with norm accumNorm
|
|
|
|
float t = hsVector3(&accumPos, &pos).InnerProduct(accumNorm);
|
|
|
|
t /= accumNorm.fZ;
|
|
|
|
|
|
|
|
pos.fZ += t;
|
|
|
|
|
|
|
|
norm = accumNorm;
|
|
|
|
|
|
|
|
return pos.fZ;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateWindDir(float dt)
|
|
|
|
{
|
|
|
|
fWindDir = -State().fWindDir;
|
|
|
|
hsFastMath::NormalizeAppr(fWindDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateRefObject()
|
|
|
|
{
|
|
|
|
if( fRefObj )
|
|
|
|
{
|
|
|
|
hsMatrix44 l2w = fRefObj->GetLocalToWorld();
|
|
|
|
|
|
|
|
float h = l2w.fMap[2][3];
|
|
|
|
|
|
|
|
float x = -l2w.fMap[0][1];
|
|
|
|
float y = -l2w.fMap[1][1];
|
|
|
|
|
|
|
|
fState.fWaterHeight = h;
|
|
|
|
|
|
|
|
fState.fWindDir = hsVector3(x, y, 0.f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IFloatBuoy(float dt, plSceneObject* so)
|
|
|
|
{
|
|
|
|
// Compute force based on world bounds
|
|
|
|
hsBounds3Ext wBnd = so->GetDrawInterface()->GetWorldBounds();
|
|
|
|
hsBounds3Ext lBnd = so->GetDrawInterface()->GetLocalBounds();
|
|
|
|
|
|
|
|
hsPoint3 pos(wBnd.GetCenter());
|
|
|
|
hsPoint3 surfPos(pos);
|
|
|
|
hsVector3 surfNorm;
|
|
|
|
|
|
|
|
EvalPoint(surfPos, surfNorm);
|
|
|
|
|
|
|
|
// Direction of impulse is surfNorm. Magnitude is proportional to depth
|
|
|
|
// (in an approximation lazy hackish way).
|
|
|
|
hsPoint2 boxDepth;
|
|
|
|
wBnd.TestPlane(surfNorm, boxDepth);
|
|
|
|
float surfDepth = surfNorm.InnerProduct(surfPos);
|
|
|
|
float depth = surfDepth - boxDepth.fX;
|
|
|
|
|
|
|
|
if( depth < 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( depth > wBnd.GetMaxs().fZ - wBnd.GetMins().fZ )
|
|
|
|
depth = wBnd.GetMaxs().fZ - wBnd.GetMins().fZ;
|
|
|
|
|
|
|
|
// We really want the cross section area as facing into the water,
|
|
|
|
// but life is full of little disappointments.
|
|
|
|
float area = (wBnd.GetMaxs().fX - wBnd.GetMins().fX) * (wBnd.GetMaxs().fY - wBnd.GetMins().fY);
|
|
|
|
|
|
|
|
float volume = area * depth;
|
|
|
|
|
|
|
|
plCONST(float) kWaterDensity(1.0f);
|
|
|
|
float forceMag = volume * kWaterDensity;
|
|
|
|
|
|
|
|
// surfNorm is now the impulse vector. But where to apply it.
|
|
|
|
// Don't currently have anything informative from the physical to use.
|
|
|
|
// So, let's fake something for the moment.
|
|
|
|
|
|
|
|
plKey physKey = so->GetSimulationInterface()->GetPhysical()->GetKey();
|
|
|
|
|
|
|
|
// plImpulseMsg* iMsg = new plImpulseMsg(GetKey(), physKey, hsVector3(0, 0, 1.f) * forceMag * dt);
|
|
|
|
// iMsg->Send();
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
plCONST(float) kRotScale(1.f);
|
|
|
|
hsVector3 rotAx = hsVector3(0, 0, 1.f) % surfNorm;
|
|
|
|
rotAx *= kRotScale * dt * volume;
|
|
|
|
|
|
|
|
plAngularImpulseMsg* aMsg = new plAngularImpulseMsg(GetKey(), physKey, rotAx);
|
|
|
|
aMsg->Send();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
plCONST(float) kDampener(0.1f);
|
|
|
|
plCONST(float) kBaseDamp(0.1f);
|
|
|
|
if( wBnd.GetMaxs().fZ > wBnd.GetMins().fZ )
|
|
|
|
{
|
|
|
|
// Remember, we've already limited depth to be <= Max.fZ - Min.fZ;
|
|
|
|
float damp = depth / (wBnd.GetMaxs().fZ - wBnd.GetMins().fZ);
|
|
|
|
damp *= kDampener;
|
|
|
|
damp += kBaseDamp;
|
|
|
|
|
|
|
|
// plDampMsg* dMsg = new plDampMsg(GetKey(), physKey, damp);
|
|
|
|
// dMsg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IFloatBuoys(float dt)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fBuoys.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fBuoys[i] && fBuoys[i]->GetSimulationInterface() && fBuoys[i]->GetSimulationInterface()->GetPhysical() && fBuoys[i]->GetDrawInterface() )
|
|
|
|
{
|
|
|
|
IFloatBuoy(dt, fBuoys[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IShiftCenter(plSceneObject* so) const
|
|
|
|
{
|
|
|
|
// HACKAGE
|
|
|
|
if( 0 && so->GetSimulationInterface() && so->GetSimulationInterface()->GetPhysical() && so->GetDrawInterface() )
|
|
|
|
{
|
|
|
|
hsPoint3 center = so->GetDrawInterface()->GetWorldBounds().GetCenter();
|
|
|
|
hsPoint3 pos = so->GetLocalToWorld().GetTranslate();
|
|
|
|
hsVector3 offset(&pos, ¢er);
|
|
|
|
// plShiftMassMsg* msg = new plShiftMassMsg(GetKey(), so->GetSimulationInterface()->GetPhysical()->GetKey(), offset);
|
|
|
|
// msg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICheckTargetMaterials()
|
|
|
|
{
|
|
|
|
hsBounds3Ext targBnd;
|
|
|
|
targBnd.MakeEmpty();
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < GetNumTargets(); i++ )
|
|
|
|
{
|
|
|
|
plSceneObject* so = GetTarget(i);
|
|
|
|
if( !so )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const plDrawInterface* di = so->GetDrawInterface();
|
|
|
|
if( !di )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
hsTArray<plAccessSpan> src;
|
|
|
|
plAccessGeometry::Instance()->OpenRO(di, src, false);
|
|
|
|
|
|
|
|
const int numUVWs = src.GetCount() && src[0].AccessVtx().HasUVWs() ? src[0].AccessVtx().NumUVWs() : 0;
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < src.GetCount(); j++ )
|
|
|
|
{
|
|
|
|
hsAssert(src[j].AccessVtx().NumUVWs() == numUVWs, "Must have same number uvws on each water mesh");
|
|
|
|
ICreateFixedMat(src[j].GetMaterial(), numUVWs); // no-op if it's already setup.
|
|
|
|
|
|
|
|
targBnd.Union(&src[j].GetWorldBounds());
|
|
|
|
}
|
|
|
|
|
|
|
|
plAccessGeometry::Instance()->Close(src);
|
|
|
|
|
|
|
|
for( j = 0; j < di->GetNumDrawables(); j++ )
|
|
|
|
{
|
|
|
|
plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j));
|
|
|
|
if( dr )
|
|
|
|
{
|
|
|
|
dr->SetNativeProperty(di->GetDrawableMeshIndex(j), plSpan::kPropRunTimeLight | plSpan::kPropSkipProjection | plSpan::kPropNoShadow | plSpan::kLiteVtxNonPreshaded | plSpan::kPropReverseSort, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( targBnd.GetType() == kBoundsNormal )
|
|
|
|
{
|
|
|
|
if( !fTargBnds.GetCount() )
|
|
|
|
fTargBnds.SetCount(1);
|
|
|
|
|
|
|
|
plConst(float) kMaxWaveHeight(5.f);
|
|
|
|
|
|
|
|
hsPoint3 p;
|
|
|
|
p = targBnd.GetMins();
|
|
|
|
p.fZ = GetHeight() - kMaxWaveHeight;
|
|
|
|
fTargBnds[0].Reset(&p);
|
|
|
|
p = targBnd.GetMaxs();
|
|
|
|
p.fZ = GetHeight() + kMaxWaveHeight;
|
|
|
|
fTargBnds[0].Union(&p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plWaveSet7::IAddTarget(const plKey& key)
|
|
|
|
{
|
|
|
|
plObjRefMsg* refMsg = new plObjRefMsg(key, plRefMsg::kOnRequest, -1, plObjRefMsg::kModifier);
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IRemoveTarget(const plKey& key)
|
|
|
|
{
|
|
|
|
plObjRefMsg* refMsg = new plObjRefMsg(key, plRefMsg::kOnRemove, -1, plObjRefMsg::kModifier);
|
|
|
|
refMsg->SetRef(this);
|
|
|
|
refMsg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::AddTarget(const plKey& key)
|
|
|
|
{
|
|
|
|
IAddTarget(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::RemoveTarget(const plKey& key)
|
|
|
|
{
|
|
|
|
IRemoveTarget(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::SetRefObject(plSceneObject* refObj)
|
|
|
|
{
|
|
|
|
fFlags.SetBit(kHasRefObject, refObj != nil);
|
|
|
|
|
|
|
|
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefRefObj);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(refObj, msg, plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::AddBuoy(plKey soKey)
|
|
|
|
{
|
|
|
|
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBuoy);
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(soKey, msg, plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::RemoveBuoy(plKey soKey)
|
|
|
|
{
|
|
|
|
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnRemove, 0, kRefBuoy);
|
|
|
|
msg->SetRef(soKey->ObjectIsLoaded());
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// This is very evil. I can't believe I'm doing this. Stop me, please.
|
|
|
|
class plFilterMask
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
int fExt;
|
|
|
|
float **fMask;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
plFilterMask( float sig );
|
|
|
|
virtual ~plFilterMask();
|
|
|
|
|
|
|
|
int Begin() const { return -fExt; }
|
|
|
|
int End() const { return fExt; }
|
|
|
|
|
|
|
|
float Mask( int i, int j ) const { return fMask[ i ][ j ]; }
|
|
|
|
};
|
|
|
|
// End evil.
|
|
|
|
|
|
|
|
void plWaveSet7::IInitState()
|
|
|
|
{
|
|
|
|
plConst(float) kWaterTable(-10.f);
|
|
|
|
plConst(float) kWaterOffset[3] = { 3.f, 3.f, 0.f };
|
|
|
|
plConst(float) kMaxAtten[3] = { 1.f, 1.f, 1.f };
|
|
|
|
plConst(float) kMinAtten[3] = { 0.f, 0.f, 0.f };
|
|
|
|
plConst(float) kDepthFalloff[3] = { 4.f, 4.f, 6.f };
|
|
|
|
|
|
|
|
plConst(float) kGeoMinLen(3.f);
|
|
|
|
plConst(float) kGeoMaxLen(8.f);
|
|
|
|
plConst(float) kGeoAmpOverLen(0.1f);
|
|
|
|
plConst(float) kGeoAngleDev(30.f * M_PI / 180.f);
|
|
|
|
plConst(float) kGeoChop(1.f);
|
|
|
|
|
|
|
|
plConst(float) kTexMinLen(4.f);
|
|
|
|
plConst(float) kTexMaxLen(30.f);
|
|
|
|
plConst(float) kTexAmpOverLen(0.1f);
|
|
|
|
plConst(float) kTexAngleDev(30.f * M_PI / 180.f);
|
|
|
|
plConst(float) kTexChop(1.f);
|
|
|
|
|
|
|
|
plFixedWaterState7 state;
|
|
|
|
|
|
|
|
state.fWindDir = hsVector3(0, 1.f, 0);
|
|
|
|
|
|
|
|
state.fGeoState.fMaxLength = kGeoMaxLen;
|
|
|
|
state.fGeoState.fMinLength = kGeoMinLen;
|
|
|
|
state.fGeoState.fAmpOverLen = kGeoAmpOverLen;
|
|
|
|
state.fGeoState.fChop = kGeoChop;
|
|
|
|
state.fGeoState.fAngleDev = kGeoAngleDev;
|
|
|
|
|
|
|
|
state.fTexState.fMaxLength = kTexMaxLen;
|
|
|
|
state.fTexState.fMinLength = kTexMinLen;
|
|
|
|
state.fTexState.fAmpOverLen = kTexAmpOverLen;
|
|
|
|
state.fTexState.fChop = kTexChop;
|
|
|
|
state.fTexState.fAngleDev = kTexAngleDev;
|
|
|
|
|
|
|
|
state.fRippleScale = 25.f;
|
|
|
|
|
|
|
|
plConst(float) kNoise(1.f);
|
|
|
|
plConst(float) kSpecStart(50.f);
|
|
|
|
plConst(float) kSpecEnd(10000.f);
|
|
|
|
|
|
|
|
hsVector3 spec;
|
|
|
|
spec[state.kNoise] = kNoise;
|
|
|
|
spec[state.kSpecStart] = kSpecStart;
|
|
|
|
spec[state.kSpecEnd] = kSpecEnd;
|
|
|
|
state.fSpecVec = spec;
|
|
|
|
|
|
|
|
state.fWaterHeight = kWaterTable;
|
|
|
|
state.fWaterOffset = hsVector3(kWaterOffset[0], kWaterOffset[1], kWaterOffset[2]);
|
|
|
|
state.fMaxAtten = hsVector3(kMaxAtten[0], kMaxAtten[1], kMaxAtten[2]);
|
|
|
|
state.fMinAtten = hsVector3(kMinAtten[0], kMinAtten[1], kMinAtten[2]);
|
|
|
|
state.fDepthFalloff = hsVector3(kDepthFalloff[0], kDepthFalloff[1], kDepthFalloff[2]);
|
|
|
|
|
|
|
|
state.fWispiness = 0;
|
|
|
|
state.fShoreTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f);
|
|
|
|
state.fMaxColor = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f);
|
|
|
|
state.fMinColor = hsColorRGBA().Set(0.2f, 0.4f, 0.4f, 0.6f);
|
|
|
|
state.fEdgeOpac = 1.f;
|
|
|
|
state.fEdgeRadius = 1.f;
|
|
|
|
|
|
|
|
state.fPeriod = 3.f;
|
|
|
|
state.fFingerLength = 1.f;
|
|
|
|
|
|
|
|
state.fWaterTint = hsColorRGBA().Set(0.1f, 0.2f, 0.2f, 1.f);
|
|
|
|
state.fSpecularTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f);
|
|
|
|
|
|
|
|
state.fEnvCenter = hsPoint3(0,0,0);
|
|
|
|
state.fEnvRadius = 500.f;
|
|
|
|
state.fEnvRefresh = 0.f;
|
|
|
|
|
|
|
|
fState = state;
|
|
|
|
|
|
|
|
IUpdateWindDir(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plWaveSet7::IInitWaveConsts()
|
|
|
|
{
|
|
|
|
// See header for what fS, fK and fD actually mean.
|
|
|
|
WaveK waveKs[] = {
|
|
|
|
// fS fK fD
|
|
|
|
{ 1.f, 5.f, 0.f },
|
|
|
|
{ 1.f, 10.f, 2.f },
|
|
|
|
{ -1.f, 20.f, -9.f },
|
|
|
|
{ 1.f, 30.f, 16.f }
|
|
|
|
};
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumTexWaves; i++ )
|
|
|
|
fWaveKs[i] = waveKs[i];
|
|
|
|
|
|
|
|
TexWaveWindDep windDeps[] = {
|
|
|
|
// WindSpeed, Height, Specular
|
|
|
|
{ 0.f, 0.01f, 1.f },
|
|
|
|
{ 5.f, 0.05f, 0.8f },
|
|
|
|
{ 10.f, 0.05f, 0.7f },
|
|
|
|
{ 15.f, 0.1f, 0.6f },
|
|
|
|
{ 20.f, 0.1f, 0.5f },
|
|
|
|
{ 25.f, 0.1f, 0.3f }
|
|
|
|
};
|
|
|
|
|
|
|
|
for( i = 0; i < kNumWindDep; i++ )
|
|
|
|
fWindDeps[i] = windDeps[i];
|
|
|
|
|
|
|
|
ISetupTextureWaves();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ISetupTextureWaves()
|
|
|
|
{
|
|
|
|
// Fill in with sine waves.
|
|
|
|
// Start with 1/4 length of map = size / 4
|
|
|
|
// Go to 4 times length of pixel = 4
|
|
|
|
// Dir = U +- iLen/(numLen/2) * V
|
|
|
|
//
|
|
|
|
// For each mip level
|
|
|
|
// For each pixel
|
|
|
|
// For each wavelen
|
|
|
|
// Add height (sine) to alpha, normal (cosine) to color
|
|
|
|
// Scrunch
|
|
|
|
// For each wavelen
|
|
|
|
// // Half the wavelen to match higher mip level
|
|
|
|
// // If a wavelen falls below min (4), discard it and smaller lens
|
|
|
|
// len /= 2
|
|
|
|
// if( len < 4 )
|
|
|
|
// {
|
|
|
|
// numLen = iLen
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
|
|
|
// Set up our table of amplitudes, wavelengths and wave directions
|
|
|
|
|
|
|
|
// our rotation/scale in our texture transform will be
|
|
|
|
// scale = waveKs.fK
|
|
|
|
// dir.x * scale, dir.y * scale
|
|
|
|
// -dir.y * scale, dir.x * scale
|
|
|
|
// So to tile, dir.x * scale and dir.y * scale must be integers.
|
|
|
|
// We select a randomized direction and a wavelength, then
|
|
|
|
// round the scaled direction components to be integral, then
|
|
|
|
// figure out what wavelength we actually came up with.
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumTexWaves; i++ )
|
|
|
|
{
|
|
|
|
IInitTexWave(i);
|
|
|
|
}
|
|
|
|
fTrialUpdate &= ~kReInitWaves;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IInitTexWave(int i)
|
|
|
|
{
|
|
|
|
float rads = fRand.RandMinusOneToOne() * TexState().fAngleDev;
|
|
|
|
float dx = sin(rads);
|
|
|
|
float dy = cos(rads);
|
|
|
|
|
|
|
|
|
|
|
|
float tx = dx;
|
|
|
|
dx = fWindDir.fY * dx - fWindDir.fX * dy;
|
|
|
|
dy = fWindDir.fX * tx + fWindDir.fY * dy;
|
|
|
|
|
|
|
|
float maxLen = TexState().fMaxLength * kCompositeSize / State().fRippleScale;
|
|
|
|
float minLen = TexState().fMinLength * kCompositeSize / State().fRippleScale;
|
|
|
|
float len = float(i) / float(kNumTexWaves-1) * (maxLen - minLen) + minLen;
|
|
|
|
|
|
|
|
float reps = float(kCompositeSize) / len;
|
|
|
|
|
|
|
|
dx *= reps;
|
|
|
|
dy *= reps;
|
|
|
|
dx = float(int(dx >= 0 ? dx + 0.5f : dx - 0.5f));
|
|
|
|
dy = float(int(dy >= 0 ? dy + 0.5f : dy - 0.5f));
|
|
|
|
|
|
|
|
fTexWaves[i].fRotScale00 = dx;
|
|
|
|
fTexWaves[i].fRotScale01 = dy;
|
|
|
|
|
|
|
|
float effK = hsFastMath::InvSqrt(dx*dx + dy*dy);
|
|
|
|
fTexWaves[i].fLen = float(kCompositeSize) * effK;
|
|
|
|
fTexWaves[i].fFreq = M_PI * 2.f / fTexWaves[i].fLen;
|
|
|
|
fTexWaves[i].fAmp = fTexWaves[i].fLen * TexState().fAmpOverLen;
|
|
|
|
fTexWaves[i].fPhase = fRand.RandZeroToOne();
|
|
|
|
|
|
|
|
fTexWaves[i].fDirX = dx * effK;
|
|
|
|
fTexWaves[i].fDirY = dy * effK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void plWaveSet7::SetSceneNode(const plKey& key)
|
|
|
|
{
|
|
|
|
fSceneNode = key;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::AddDynaDecalMgr(plKey& key)
|
|
|
|
{
|
|
|
|
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefDynaDecalMgr);
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(key, msg, plRefFlags::kPassiveRef);
|
|
|
|
|
|
|
|
msg = new plGenRefMsg(key, plRefMsg::kOnRequest, 0, plDynaRippleVSMgr::kRefWaveSetBase);
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(GetKey(), msg, plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::RemoveDynaDecalMgr(plKey& key)
|
|
|
|
{
|
|
|
|
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnRemove, 0, kRefDynaDecalMgr);
|
|
|
|
msg->SetRef(key->ObjectIsLoaded());
|
|
|
|
msg->Send();
|
|
|
|
|
|
|
|
msg = new plGenRefMsg(key, plRefMsg::kOnRemove, 0, plDynaRippleVSMgr::kRefWaveSetBase);
|
|
|
|
msg->SetRef(this);
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateLayers(float dt)
|
|
|
|
{
|
|
|
|
IUpdateBumpLayers(dt);
|
|
|
|
|
|
|
|
ISubmitRenderRequests();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateBumpLayers(float dt)
|
|
|
|
{
|
|
|
|
plCONST(float) speedHack(1.f / 3.f);
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumTexWaves; i++ )
|
|
|
|
{
|
|
|
|
float speed = hsFastMath::InvSqrtAppr(fTexWaves[i].fLen / (2.f * M_PI * kGravConst)) * speedHack;
|
|
|
|
fTexWaves[i].fPhase -= dt * speed;
|
|
|
|
fTexWaves[i].fPhase -= int(fTexWaves[i].fPhase);
|
|
|
|
|
|
|
|
if( fBumpLayers[i] )
|
|
|
|
{
|
|
|
|
hsMatrix44 xfm = fBumpLayers[i]->GetTransform();
|
|
|
|
|
|
|
|
xfm.fMap[0][0] = fTexWaves[i].fRotScale00;
|
|
|
|
xfm.fMap[0][1] = fTexWaves[i].fRotScale01;
|
|
|
|
xfm.fMap[1][0] = -fTexWaves[i].fRotScale01;
|
|
|
|
xfm.fMap[1][1] = fTexWaves[i].fRotScale00;
|
|
|
|
|
|
|
|
xfm.fMap[0][3] = fTexWaves[i].fPhase;
|
|
|
|
fBumpLayers[i]->SetTransform(xfm);
|
|
|
|
}
|
|
|
|
|
|
|
|
LogF("%.4d - L%8.4f R(%5.3f, %5.3f)", i, fTexWaves[i].fLen, fTexWaves[i].fRotScale00, fTexWaves[i].fRotScale01);
|
|
|
|
GraphLen(fTexWaves[i].fLen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ISubmitRenderRequests()
|
|
|
|
{
|
|
|
|
if( fBumpReqMsg )
|
|
|
|
fBumpReqMsg->SendAndKeep();
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kGraphShorePasses; i++ )
|
|
|
|
{
|
|
|
|
if( fGraphReqMsg[i] )
|
|
|
|
fGraphReqMsg[i]->SendAndKeep();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plMipmap* plWaveSet7::ICreateBumpBitmapFFP(float amp, float dx, float dy) const
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsGMaterial* plWaveSet7::ICreateBumpLayersFFP()
|
|
|
|
{
|
|
|
|
// Create kNumTexWaves bitmaps, each containing a single cycle of values:
|
|
|
|
// mag = 0.25 * -Amplitude * cos(i/sz*2*Pi)
|
|
|
|
// r = mag * dx * 0.5 + 0.5
|
|
|
|
// g = mag * dy * 0.5 + 0.5
|
|
|
|
// b = 0.7 * 0.25 * 0.5 + 0.5
|
|
|
|
// a = 1.f
|
|
|
|
//
|
|
|
|
// Where:
|
|
|
|
// Amplitude == wave.amp * wave.freq * kNormHack(3.0)
|
|
|
|
// dx, dy == wave.fDir
|
|
|
|
//
|
|
|
|
|
|
|
|
// Create a blank material
|
|
|
|
|
|
|
|
// Create base layer
|
|
|
|
// Blend mode is none
|
|
|
|
// Transform scales to wave[0].freq
|
|
|
|
// No rotation, translation
|
|
|
|
// Add bitmap 0.
|
|
|
|
|
|
|
|
// For layers 1..kNumTexWaves
|
|
|
|
// Create blank layer
|
|
|
|
// Blend mode is ADDSIGNED
|
|
|
|
// Transform scales to wave[i].freq
|
|
|
|
// No rotation or translation.
|
|
|
|
// Add bitmap i;
|
|
|
|
|
|
|
|
// NOTE: if we use the fixed function pipeline to composite
|
|
|
|
// these into the CompositeMaterial, then they all need to
|
|
|
|
// be scaled down to avoid saturation on summation.
|
|
|
|
// Doing the composite with a pixel shader would allow the
|
|
|
|
// textures to be stored in full 256 range, and then scaled
|
|
|
|
// down during the summation, after interpolation. Would
|
|
|
|
// that be better? Not sure. Pixel shader registers only have
|
|
|
|
// a range of [-1..1], and it's hinted to be 8-bit fixed point.
|
|
|
|
// So we'd still have to scale down texture value (losing precision),
|
|
|
|
// then add it in.
|
|
|
|
//
|
|
|
|
// Also, for pixel shaded compositing, we can just store the cosine
|
|
|
|
// values in each layer, with no rotation in the bitmap values, just
|
|
|
|
// in the layer transform. No rotation would make for a cleaner
|
|
|
|
// interpolation (not interpolating diagonally across the waves).
|
|
|
|
// How much difference is that? Dunno. But it would also allow the
|
|
|
|
// bitmap to hold the full range [-1..1]=>[0..255] of cosine values,
|
|
|
|
// scaling them down with constants. The pixel shader would look like:
|
|
|
|
//
|
|
|
|
// tex t0;
|
|
|
|
// tex t1;
|
|
|
|
// tex t2;
|
|
|
|
// tex t3;
|
|
|
|
//
|
|
|
|
// mul r0, t0_bx2, c0;
|
|
|
|
// mad r0, t1_bx2, c1, r0;
|
|
|
|
// mad r0, t2_bx2, c2, r0;
|
|
|
|
// mad r0, t3_bx2, c3, r0;
|
|
|
|
// // Now bias it back into range [0..1] for output.
|
|
|
|
// mad r0, r0, c4, c5; // c4 = (0.5, 0.5, 0.5, 1), c5 = (0.5, 0.5, 0.5, 0)
|
|
|
|
// // or add_d2 r0, r0, c0.b; // except this is likely to saturate before the divide. Dunno.
|
|
|
|
//
|
|
|
|
// where the c[i] constants would be:
|
|
|
|
// c[i] = -wave[i].amp * wave[i].freq * kSomeNormHack * (wave[i].fDir.x, wave[i].fDir.y, 0, 1)
|
|
|
|
// except c[0].b = 0.7;
|
|
|
|
// In fact, we could cut it down to 2 textures using alpha replicate, except we need different
|
|
|
|
// transforms for each (for animation). So screw that.
|
|
|
|
//
|
|
|
|
// Note that the above won't run on a ps.1.0 pixel shader, because the combined number of tex and
|
|
|
|
// arith instructions is 9. Could make a version that was okay for ps.1.0 by doing:
|
|
|
|
//
|
|
|
|
// mad r0, t0_bias, c0, c4; // t0[-0.5..0.5]*c0 + 0.5, which is [0..1]
|
|
|
|
// mad r0, t1_bias, c1, r0; // + t1[-0.5..0.5]*c1
|
|
|
|
// mad r0, t2_bias, c2, r0; // + t2[-0.5..0.5]*c2
|
|
|
|
// mad r0, t3_bias, c3, r0; // + t3[-0.5..0.5]*c3
|
|
|
|
//
|
|
|
|
// which doesn't need a final bias instruction because it remains biased all the way through
|
|
|
|
// at the cost of 1 bit of precision.
|
|
|
|
// Whatever.
|
|
|
|
//
|
|
|
|
|
|
|
|
// return material;
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMipmap* plWaveSet7::ICreateBiasNoiseMap()
|
|
|
|
{
|
|
|
|
const int size = kCompositeSize >> 2;
|
|
|
|
plMipmap* mipMap = new plMipmap(
|
|
|
|
size, size,
|
|
|
|
plMipmap::kARGB32Config,
|
|
|
|
1,
|
|
|
|
plMipmap::kUncompressed,
|
|
|
|
plMipmap::UncompressedInfo::kRGB8888);
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s", GetKey()->GetName(), "BiasBitPS");
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, mipMap, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < size; i++ )
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < size; j++ )
|
|
|
|
{
|
|
|
|
float x = fRand.RandMinusOneToOne();
|
|
|
|
float y = fRand.RandMinusOneToOne();
|
|
|
|
|
|
|
|
uint8_t r = uint8_t((x * 0.5f + 0.5f) * 255.999f);
|
|
|
|
uint8_t g = uint8_t((y * 0.5f + 0.5f) * 255.999f);
|
|
|
|
|
|
|
|
// r = g = 0xff; // SATURATE
|
|
|
|
|
|
|
|
uint32_t* val = mipMap->GetAddr32(i, j);
|
|
|
|
*val = (0xff << 24)
|
|
|
|
| (r << 16)
|
|
|
|
| (g << 8)
|
|
|
|
| 0xff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// For bonus points, modulate by a VERY coherent noise function.
|
|
|
|
return mipMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMipmap* plWaveSet7::ICreateBumpMipmapPS()
|
|
|
|
{
|
|
|
|
// const int sizeV = kCompositeSize;
|
|
|
|
const int sizeV = 1;
|
|
|
|
|
|
|
|
const int kNumLevels = 8; // must be log2(kCompositeSize)
|
|
|
|
hsAssert(kCompositeSize == (1 << kNumLevels), "Mismatch on size and num mip levels");
|
|
|
|
|
|
|
|
if( !fCosineLUT )
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = new plMipmap(
|
|
|
|
kCompositeSize, sizeV,
|
|
|
|
plMipmap::kARGB32Config,
|
|
|
|
kNumLevels,
|
|
|
|
plMipmap::kUncompressed,
|
|
|
|
plMipmap::UncompressedInfo::kRGB8888);
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s", GetKey()->GetName(), "BumpBitPS");
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, mipMap, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefCosineLUT), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
hsAssert(fCosineLUT, "Failed to make cosine lookup table");
|
|
|
|
|
|
|
|
int k;
|
|
|
|
for( k = 0; k < kNumLevels; k++ )
|
|
|
|
{
|
|
|
|
fCosineLUT->SetCurrLevel(k);
|
|
|
|
|
|
|
|
const int sizeU = kCompositeSize >> k;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < sizeU; i++ )
|
|
|
|
{
|
|
|
|
float y = float(i);
|
|
|
|
float dist = float(i) / float(sizeU-1) * 2.f * M_PI;
|
|
|
|
float c = cos(dist);
|
|
|
|
float s = sin(dist);
|
|
|
|
s *= 0.5f;
|
|
|
|
s += 0.5f;
|
|
|
|
s = float(pow(s, TexState().fChop));
|
|
|
|
c *= s;
|
|
|
|
uint8_t cosDist = uint8_t((c * 0.5 + 0.5) * 255.999f);
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < sizeV; j++ )
|
|
|
|
{
|
|
|
|
uint32_t* val = fCosineLUT->GetAddr32(i, j);
|
|
|
|
*val = (0xff << 24)
|
|
|
|
| (cosDist << 16)
|
|
|
|
| (cosDist << 8)
|
|
|
|
| 0xff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fCosineLUT->MakeDirty();
|
|
|
|
|
|
|
|
return fCosineLUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddBumpBiasLayer(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( !fBiasLayer[0] )
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = ICreateBiasNoiseMap();
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < 2; i++ )
|
|
|
|
{
|
|
|
|
plLayer* layer = new plLayer;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "Bias", i);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
layer->SetBlendFlags(hsGMatState::kBlendAdd);
|
|
|
|
layer->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite);
|
|
|
|
layer->SetShadeFlags(hsGMatState::kShadeReallyNoFog
|
|
|
|
| hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade);
|
|
|
|
layer->SetClampFlags(0);
|
|
|
|
layer->SetMiscFlags(0);
|
|
|
|
layer->SetMiscFlags(i ? 0 : hsGMatState::kMiscRestartPassHere);
|
|
|
|
|
|
|
|
layer->SetAmbientColor(hsColorRGBA().Set(0.25f, 0.25f, 0.f, 0.f));
|
|
|
|
layer->SetRuntimeColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 0.f));
|
|
|
|
layer->SetOpacity(0.f);
|
|
|
|
|
|
|
|
layer->SetUVWSrc(0);
|
|
|
|
|
|
|
|
plLayRefMsg* refMsg = new plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
IAddBumpBiasShaders(layer);
|
|
|
|
|
|
|
|
fBiasLayer[i] = layer; // SHOULD BE REFFED (SO SHOULD fBumpLayers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mat->AddLayerViaNotify(fBiasLayer[0]);
|
|
|
|
mat->AddLayerViaNotify(fBiasLayer[1]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
plLayer* plWaveSet7::ICreateBumpLayerPS(plMipmap* mipMap, hsGMaterial* bumpMat, int which)
|
|
|
|
{
|
|
|
|
plLayer* layer = new plLayer;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "BumpLayerPS", which);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
layer->SetBlendFlags(which ? hsGMatState::kBlendAdd : 0);
|
|
|
|
layer->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite);
|
|
|
|
layer->SetShadeFlags(hsGMatState::kShadeReallyNoFog
|
|
|
|
| hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade
|
|
|
|
| hsGMatState::kShadeWhite);
|
|
|
|
layer->SetClampFlags(0);
|
|
|
|
layer->SetMiscFlags(0);
|
|
|
|
if( (which / kBumpPerPass) * kBumpPerPass == which )
|
|
|
|
layer->SetMiscFlags(hsGMatState::kMiscRestartPassHere);
|
|
|
|
|
|
|
|
layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
|
|
|
|
layer->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
|
|
|
|
layer->SetOpacity(1.f);
|
|
|
|
|
|
|
|
layer->SetUVWSrc(0);
|
|
|
|
|
|
|
|
// Set up the transform.
|
|
|
|
hsMatrix44 xfm;
|
|
|
|
xfm.Reset();
|
|
|
|
xfm.NotIdentity();
|
|
|
|
|
|
|
|
xfm.fMap[0][0] = fTexWaves[which].fRotScale00;
|
|
|
|
xfm.fMap[0][1] = fTexWaves[which].fRotScale01;
|
|
|
|
xfm.fMap[1][0] = -fTexWaves[which].fRotScale01;
|
|
|
|
xfm.fMap[1][1] = fTexWaves[which].fRotScale00;
|
|
|
|
layer->SetTransform(xfm);
|
|
|
|
|
|
|
|
bumpMat->AddLayerViaNotify(layer);
|
|
|
|
|
|
|
|
plLayRefMsg* refMsg = new plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsGMaterial* plWaveSet7::ICreateBumpLayersPS()
|
|
|
|
{
|
|
|
|
if( fBumpMat )
|
|
|
|
return fBumpMat;
|
|
|
|
|
|
|
|
// Create four bitmaps, each containing a single cycle of values:
|
|
|
|
// mag = cos(i/sz*2*Pi)
|
|
|
|
// r = 0
|
|
|
|
// g = mag* 0.5 + 0.5
|
|
|
|
// b = 1.f
|
|
|
|
// a = 1.f
|
|
|
|
//
|
|
|
|
// Where:
|
|
|
|
// Amplitude == wave.amp * wave.freq * kNormHack(3.0)
|
|
|
|
//
|
|
|
|
|
|
|
|
// Create a blank material
|
|
|
|
hsGMaterial* bumpMat = new hsGMaterial;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s", GetKey()->GetName(), "BumpMatPS");
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, bumpMat, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
plMipmap* mipMap = ICreateBumpMipmapPS();
|
|
|
|
|
|
|
|
// Create base layer
|
|
|
|
// Blend mode is none
|
|
|
|
// Transform scales to wave[0].freq
|
|
|
|
// No translation
|
|
|
|
// Rotation is by wave[0].fDir
|
|
|
|
// Add bitmap 0.
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumBumpShaders; i++ )
|
|
|
|
{
|
|
|
|
int nBegin = bumpMat->GetNumLayers();
|
|
|
|
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < kBumpPerPass; j++ )
|
|
|
|
fBumpLayers[i*kBumpPerPass + j] = ICreateBumpLayerPS(mipMap, bumpMat, i*kBumpPerPass + j);
|
|
|
|
|
|
|
|
IAddBumpVertexShader(bumpMat, i, nBegin, nBegin + kBumpPerPass-1);
|
|
|
|
IAddBumpPixelShader(bumpMat, i, nBegin, nBegin + kBumpPerPass-1);
|
|
|
|
|
|
|
|
}
|
|
|
|
IAddBumpBiasLayer(bumpMat);
|
|
|
|
|
|
|
|
|
|
|
|
// Need to add this via notify to ourselves.
|
|
|
|
hsgResMgr::ResMgr()->SendRef(bumpMat->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBumpMat), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
ICreateBumpDrawable();
|
|
|
|
|
|
|
|
return fBumpMat = bumpMat;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddBumpBiasShaders(plLayer* layer)
|
|
|
|
{
|
|
|
|
if( !fBiasVShader )
|
|
|
|
{
|
|
|
|
plShader* vShader = new plShader;
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_BiasVS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
vShader->SetIsPixelShader(false);
|
|
|
|
|
|
|
|
vShader->SetNumConsts(plBiasVS::kNumConsts);
|
|
|
|
|
|
|
|
vShader->SetVector(plBiasVS::kTexU0,
|
|
|
|
1.f,
|
|
|
|
0.f,
|
|
|
|
0.f,
|
|
|
|
0.f);
|
|
|
|
vShader->SetVector(plBiasVS::kTexV0,
|
|
|
|
0.f,
|
|
|
|
1.f,
|
|
|
|
0.f,
|
|
|
|
0.f);
|
|
|
|
|
|
|
|
vShader->SetVector(plBiasVS::kTexU1,
|
|
|
|
1.f,
|
|
|
|
0.f,
|
|
|
|
0.f,
|
|
|
|
0.f);
|
|
|
|
vShader->SetVector(plBiasVS::kTexV1,
|
|
|
|
0.f,
|
|
|
|
1.f,
|
|
|
|
0.f,
|
|
|
|
0.f);
|
|
|
|
|
|
|
|
vShader->SetVector(plBiasVS::kNumbers,
|
|
|
|
0.f,
|
|
|
|
0.5f,
|
|
|
|
1.f,
|
|
|
|
2.f);
|
|
|
|
|
|
|
|
hsVector3 specVec = State().fSpecVec;
|
|
|
|
float biasScale = 0.5f * specVec[State().kNoise] / (float(kNumBumpShaders) + specVec[State().kNoise]);
|
|
|
|
vShader->SetVector(plBiasVS::kScaleBias,
|
|
|
|
biasScale,
|
|
|
|
biasScale,
|
|
|
|
0.f,
|
|
|
|
1.f);
|
|
|
|
|
|
|
|
vShader->SetInputFormat(1);
|
|
|
|
vShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
// static const plShaderDecl vDecl("sha/vs_BiasNormals.inl");
|
|
|
|
// vShader->SetDecl(&vDecl);
|
|
|
|
vShader->SetDecl(plShaderTable::Decl(vs_BiasNormals));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBiasVShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
|
|
|
|
fBiasVShader = vShader;
|
|
|
|
}
|
|
|
|
|
|
|
|
plLayRefMsg* refMsg = new plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kVertexShader);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(fBiasVShader->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
|
|
|
|
if( !fBiasPShader )
|
|
|
|
{
|
|
|
|
plShader* pShader = new plShader;
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_BiasPS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
pShader->SetIsPixelShader(true);
|
|
|
|
|
|
|
|
pShader->SetNumConsts(plBiasPS::kNumConsts);
|
|
|
|
|
|
|
|
pShader->SetInputFormat(0);
|
|
|
|
pShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
// static const plShaderDecl pDecl("sha/ps_BiasNormals.inl");
|
|
|
|
// pShader->SetDecl(&pDecl);
|
|
|
|
pShader->SetDecl(plShaderTable::Decl(ps_BiasNormals));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBiasPShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fBiasPShader = pShader;
|
|
|
|
}
|
|
|
|
|
|
|
|
refMsg = new plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kPixelShader);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(fBiasPShader->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddBumpVertexShader(hsGMaterial* mat, int iShader, int iFirst, int iLast)
|
|
|
|
{
|
|
|
|
if( !fBumpVShader[0] )
|
|
|
|
{
|
|
|
|
int iBase = 0;
|
|
|
|
while( iBase < kNumTexWaves )
|
|
|
|
{
|
|
|
|
int iShader = iBase / kBumpPerPass;
|
|
|
|
|
|
|
|
plShader* vShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_BumpVS_%d", GetKey()->GetName(), iShader);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
vShader->SetIsPixelShader(false);
|
|
|
|
|
|
|
|
vShader->SetNumConsts(plBumpVS::kNumConsts);
|
|
|
|
|
|
|
|
vShader->SetVector(plBumpVS::kNumbers,
|
|
|
|
0.f,
|
|
|
|
0.5f,
|
|
|
|
1.f,
|
|
|
|
2.f);
|
|
|
|
|
|
|
|
vShader->SetInputFormat(1);
|
|
|
|
vShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
vShader->SetNumPipeConsts(kBumpPerPass);
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kBumpPerPass; i++ )
|
|
|
|
{
|
|
|
|
vShader->SetPipeConst(i, static_cast<plPipeConst::Type>(plPipeConst::kTex1x4_0 + i), plBumpVS::kUXform0 + i);
|
|
|
|
}
|
|
|
|
|
|
|
|
vShader->SetDecl(plShaderTable::Decl(vs_CompCosines));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iShader, kRefBumpVShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fBumpVShader[iShader] = vShader;
|
|
|
|
|
|
|
|
iBase += kBumpPerPass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, iFirst, iLast, plLayRefMsg::kVertexShader, fBumpVShader[iShader]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddBumpPixelShader(hsGMaterial* mat, int iShader, int iFirst, int iLast)
|
|
|
|
{
|
|
|
|
if( !fBumpPShader[0] )
|
|
|
|
{
|
|
|
|
int iBase = 0;
|
|
|
|
while( iBase < kNumTexWaves )
|
|
|
|
{
|
|
|
|
int iShader = iBase / kBumpPerPass;
|
|
|
|
|
|
|
|
plShader* pShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_BumpPS_%d", GetKey()->GetName(), iShader);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
pShader->SetIsPixelShader(true);
|
|
|
|
|
|
|
|
pShader->SetNumConsts(plBumpPS::kNumConsts);
|
|
|
|
|
|
|
|
int iLay;
|
|
|
|
for( iLay = 0; iLay < kBumpPerPass; iLay++ )
|
|
|
|
{
|
|
|
|
pShader->SetVector(plBumpPS::kWave0,
|
|
|
|
-fTexWaves[iBase + iLay].fDirX * (1.f / float(kBumpPerPass)),
|
|
|
|
-fTexWaves[iBase + iLay].fDirY * (1.f / float(kBumpPerPass)),
|
|
|
|
1.f,
|
|
|
|
1.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
pShader->SetInputFormat(0);
|
|
|
|
pShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
// pShader->SetShaderFileName("sha/ps_CompCosines.inl");
|
|
|
|
// pShader->SetShaderFileName("sha/ps_TestPos.inl");
|
|
|
|
// static const plShaderDecl moreDecl("sha/ps_MoreCosines.inl");
|
|
|
|
// pShader->SetDecl(&moreDecl);
|
|
|
|
|
|
|
|
pShader->SetDecl(plShaderTable::Decl(ps_MoreCosines));
|
|
|
|
|
|
|
|
pShader->SetVector(plBumpPS::kHalfOne, 0.25f, 0.25f, 0.25f, 1.f);
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iShader, kRefBumpPShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fBumpPShader[iShader] = pShader;
|
|
|
|
|
|
|
|
iBase += kBumpPerPass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, iFirst, iLast, plLayRefMsg::kPixelShader, fBumpPShader[iShader]);
|
|
|
|
}
|
|
|
|
|
|
|
|
plDrawableSpans* plWaveSet7::ICreateBumpDrawable()
|
|
|
|
{
|
|
|
|
fBumpDraw = new plDrawableSpans;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_BumpDraw", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, fBumpDraw, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
ICreateClearDrawable(fBumpDraw, fBumpMat);
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(fBumpDraw->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBumpDraw), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return fBumpDraw;
|
|
|
|
}
|
|
|
|
|
|
|
|
plDrawableSpans* plWaveSet7::ICreateClearDrawable(plDrawableSpans* drawable, hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
// drawable->SetNativeProperty(plDrawable::kPropVolatile, true);
|
|
|
|
|
|
|
|
hsPoint3 pos[4];
|
|
|
|
hsVector3 norm[4];
|
|
|
|
hsPoint3 uvw[4];
|
|
|
|
|
|
|
|
pos[0].fX = -1.f;
|
|
|
|
pos[0].fY = -1.f;
|
|
|
|
pos[0].fZ = 0.5f;
|
|
|
|
|
|
|
|
norm[0].Set(0.f, 0.f, -1.f);
|
|
|
|
|
|
|
|
uvw[0].fX = 0.5f / kCompositeSize;
|
|
|
|
uvw[0].fY = 0.5f / kCompositeSize;
|
|
|
|
uvw[0].fZ = 0;
|
|
|
|
|
|
|
|
// P1
|
|
|
|
pos[1] = pos[0];
|
|
|
|
pos[1].fY += 2.f;
|
|
|
|
|
|
|
|
norm[1] = norm[0];
|
|
|
|
|
|
|
|
uvw[1] = uvw[0];
|
|
|
|
uvw[1].fY += 1.f;
|
|
|
|
|
|
|
|
// P2
|
|
|
|
pos[2] = pos[0];
|
|
|
|
pos[2].fX += 2.f;
|
|
|
|
pos[2].fY += 2.f;
|
|
|
|
|
|
|
|
norm[2] = norm[0];
|
|
|
|
|
|
|
|
uvw[2] = uvw[0];
|
|
|
|
uvw[2].fX += 1.f;
|
|
|
|
uvw[2].fY += 1.f;
|
|
|
|
|
|
|
|
// P3
|
|
|
|
pos[3] = pos[0];
|
|
|
|
pos[3].fX += 2.f;
|
|
|
|
|
|
|
|
norm[3] = norm[0];
|
|
|
|
|
|
|
|
uvw[3] = uvw[0];
|
|
|
|
uvw[3].fX += 1.f;
|
|
|
|
|
|
|
|
uint16_t idx[6];
|
|
|
|
idx[0] = 1;
|
|
|
|
idx[1] = 0;
|
|
|
|
idx[2] = 2;
|
|
|
|
|
|
|
|
idx[3] = 2;
|
|
|
|
idx[4] = 0;
|
|
|
|
idx[5] = 3;
|
|
|
|
|
|
|
|
|
|
|
|
plDrawableGenerator::GenerateDrawable( 4, pos, norm,
|
|
|
|
uvw, 1,
|
|
|
|
nil, false, nil,
|
|
|
|
6, idx,
|
|
|
|
mat,
|
|
|
|
hsMatrix44::IdentityMatrix(),
|
|
|
|
false,
|
|
|
|
nil,
|
|
|
|
drawable);
|
|
|
|
|
|
|
|
return drawable;
|
|
|
|
}
|
|
|
|
|
|
|
|
plRenderRequest* plWaveSet7::ICreateRenderRequest(plRenderTarget* rt, plDrawableSpans* draw, float pri)
|
|
|
|
{
|
|
|
|
plRenderRequest* req = new plRenderRequest;
|
|
|
|
|
|
|
|
static plPageTreeMgr emptyMgr;
|
|
|
|
req->SetPageTreeMgr(&emptyMgr);
|
|
|
|
|
|
|
|
req->SetClearDrawable(draw);
|
|
|
|
|
|
|
|
req->SetOrthogonal(true);
|
|
|
|
|
|
|
|
req->SetLocalTransform(hsMatrix44::IdentityMatrix(), hsMatrix44::IdentityMatrix());
|
|
|
|
req->SetCameraTransform(hsMatrix44::IdentityMatrix(), hsMatrix44::IdentityMatrix());
|
|
|
|
|
|
|
|
req->SetHither(0);
|
|
|
|
req->SetYon(2.f);
|
|
|
|
req->SetSizeX(2.f);
|
|
|
|
req->SetSizeY(2.f);
|
|
|
|
|
|
|
|
req->SetPriority(pri);
|
|
|
|
|
|
|
|
req->SetRenderTarget(rt);
|
|
|
|
|
|
|
|
return req;
|
|
|
|
}
|
|
|
|
|
|
|
|
plRenderTarget* plWaveSet7::ICreateTransferRenderTarget(const char* name, int size)
|
|
|
|
{
|
|
|
|
uint16_t flags = plRenderTarget::kIsTexture | plRenderTarget::kIsOrtho;
|
|
|
|
uint8_t bitDepth = 32;
|
|
|
|
uint8_t zDepth = 0;
|
|
|
|
uint8_t stencilDepth = 0;
|
|
|
|
|
|
|
|
plRenderTarget* rt = new plRenderTarget(flags, size, size, bitDepth, zDepth, stencilDepth);
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s", GetKey()->GetName(), name);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, rt, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
return rt;
|
|
|
|
}
|
|
|
|
|
|
|
|
plLayer* plWaveSet7::ICreateTotalLayer(plBitmap* bm, hsGMaterial* mat, int which, const char* suff)
|
|
|
|
{
|
|
|
|
plLayer* layer = mat->GetNumLayers() > which ? plLayer::ConvertNoRef(mat->GetLayer(which)->BottomOfStack()) : nil;
|
|
|
|
if( !layer )
|
|
|
|
{
|
|
|
|
layer = new plLayer;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%sLayerPS_%d", GetKey()->GetName(), suff, which);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
|
|
|
|
layer->SetRuntimeColor(State().fWaterTint);
|
|
|
|
layer->SetOpacity(1.f);
|
|
|
|
|
|
|
|
mat->AddLayerViaNotify(layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
layer->SetBlendFlags(which ? hsGMatState::kBlendAddSigned : hsGMatState::kBlendAlpha);
|
|
|
|
layer->SetZFlags(which ? hsGMatState::kZNoZWrite : 0);
|
|
|
|
layer->SetShadeFlags(hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade);
|
|
|
|
layer->SetClampFlags(0);
|
|
|
|
layer->SetMiscFlags(hsGMatState::kMiscTwoSided);
|
|
|
|
|
|
|
|
layer->SetUVWSrc(which);
|
|
|
|
|
|
|
|
// Set up the transform.
|
|
|
|
hsMatrix44 xfm;
|
|
|
|
xfm.Reset();
|
|
|
|
|
|
|
|
layer->SetTransform(xfm);
|
|
|
|
|
|
|
|
plLayRefMsg* refMsg = new plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(bm->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
plLayer* plWaveSet7::ICreateTotalEnvLayer(plBitmap* envMap, hsGMaterial* mat, int which, const char* pref)
|
|
|
|
{
|
|
|
|
plLayer* layer = new plLayer;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s_%s_%d", GetKey()->GetName(), pref, "EnvLayerPS", which);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
layer->SetBlendFlags(which ? hsGMatState::kBlendAddSigned : 0);
|
|
|
|
layer->SetZFlags(which ? hsGMatState::kZNoZWrite : 0);
|
|
|
|
layer->SetShadeFlags(hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade);
|
|
|
|
layer->SetClampFlags(0);
|
|
|
|
layer->SetMiscFlags(0);
|
|
|
|
|
|
|
|
layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
|
|
|
|
layer->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
|
|
|
|
layer->SetOpacity(1.f);
|
|
|
|
|
|
|
|
layer->SetUVWSrc(which);
|
|
|
|
|
|
|
|
// Set up the transform.
|
|
|
|
hsMatrix44 xfm;
|
|
|
|
xfm.Reset();
|
|
|
|
|
|
|
|
layer->SetTransform(xfm);
|
|
|
|
|
|
|
|
plLayRefMsg* refMsg = new plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(envMap->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
mat->AddLayerViaNotify(layer);
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsGMaterial* plWaveSet7::ICreateFixedMatPS(hsGMaterial* mat, const int numUVWs)
|
|
|
|
{
|
|
|
|
hsAssert(mat, "Nil mat should have been filtered out before calling this");
|
|
|
|
|
|
|
|
// Check if we've already done this one.
|
|
|
|
if( (mat->GetNumLayers() == 4)
|
|
|
|
&&(mat->GetLayer(3) == fFixedLayers[3]) )
|
|
|
|
return mat;
|
|
|
|
|
|
|
|
// First, strip off whatever's on there now.
|
|
|
|
// If this is the
|
|
|
|
int i;
|
|
|
|
for( i = mat->GetNumLayers()-1; i > 0; i-- )
|
|
|
|
{
|
|
|
|
plMatRefMsg* refMsg = new plMatRefMsg(mat->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(mat->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
plRenderTarget* rt = ICreateTransferRenderTarget("CompRT", kCompositeSize);
|
|
|
|
|
|
|
|
if( mat->GetNumLayers() && mat->GetLayer(0) )
|
|
|
|
{
|
|
|
|
fState.fWaterTint = mat->GetLayer(0)->GetRuntimeColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
fFixedLayers[0] = ICreateTotalLayer(rt, mat, 0, "Fix");
|
|
|
|
fFixedLayers[1] = ICreateTotalLayer(rt, mat, 1, "Fix");
|
|
|
|
fFixedLayers[2] = ICreateTotalLayer(rt, mat, 2, "Fix");
|
|
|
|
fFixedLayers[3] = ICreateTotalEnvLayer(fEnvMap, mat, 3, "Fix");
|
|
|
|
|
|
|
|
|
|
|
|
fBumpReq = ICreateRenderRequest(rt, fBumpDraw, -100.f);
|
|
|
|
fBumpReqMsg = new plRenderRequestMsg(GetKey(), fBumpReq);
|
|
|
|
|
|
|
|
IAddFixedVertexShader(mat, numUVWs);
|
|
|
|
IAddFixedPixelShader(mat);
|
|
|
|
|
|
|
|
return mat;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICreateFixedMat(hsGMaterial* mat, const int numUVWs)
|
|
|
|
{
|
|
|
|
if( !mat )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( !fEnvMap )
|
|
|
|
{
|
|
|
|
plDynamicEnvMap* env = new plDynamicEnvMap((uint16_t)fEnvSize, (uint16_t)fEnvSize, 32);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(GetKey()->GetName(), env, GetKey()->GetUoid().GetLocation());
|
|
|
|
fEnvMap = env;
|
|
|
|
env->SetPosition(hsPoint3(0, 0, 50.f));
|
|
|
|
env->SetPosition(State().fEnvCenter);
|
|
|
|
env->SetYon(10000.f);
|
|
|
|
env->SetRefreshRate(State().fEnvRefresh);
|
|
|
|
env->Init();
|
|
|
|
env->ReRender();
|
|
|
|
}
|
|
|
|
|
|
|
|
ICreateBumpLayersPS();
|
|
|
|
ICreateFixedMatPS(mat, numUVWs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plWaveSet7::IAddShoreVertexShader(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( !fShoreVShader )
|
|
|
|
{
|
|
|
|
|
|
|
|
plShader* vShader = new plShader;
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_ShoreVS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
vShader->SetIsPixelShader(false);
|
|
|
|
|
|
|
|
vShader->SetNumConsts(plShoreVS::kNumConsts);
|
|
|
|
|
|
|
|
vShader->SetInputFormat(1); // This should really be one!!!
|
|
|
|
vShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
vShader->SetVector(plShoreVS::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f);
|
|
|
|
vShader->SetVector(plShoreVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f);
|
|
|
|
vShader->SetVector(plShoreVS::kPiConsts, 1.f / (8.f*M_PI*4.f*4.f), M_PI/2.f, M_PI, M_PI*2.f);
|
|
|
|
vShader->SetVector(plShoreVS::kNumericConsts, 0, 0.5f, 1.f, 2.f);
|
|
|
|
|
|
|
|
plConst(float) kK1(0.5f);
|
|
|
|
plConst(float) kK2(1.5f);
|
|
|
|
float negK1OverK2Sq = -kK1 / (kK2 * kK2);
|
|
|
|
vShader->SetVector(plShoreVS::kIncline, negK1OverK2Sq, kK1, 0.f, 0.f);
|
|
|
|
|
|
|
|
vShader->SetNumPipeConsts(5);
|
|
|
|
vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plShoreVS::kWorldToNDC);
|
|
|
|
vShader->SetPipeConst(1, plPipeConst::kTex3x4_0, plShoreVS::kTex0Transform);
|
|
|
|
vShader->SetPipeConst(2, plPipeConst::kLocalToWorld, plShoreVS::kLocalToWorld);
|
|
|
|
vShader->SetPipeConst(3, plPipeConst::kLayRuntime, plShoreVS::kShoreTint);
|
|
|
|
vShader->SetPipeConst(4, plPipeConst::kFogSet, plShoreVS::kFogSet);
|
|
|
|
|
|
|
|
// vShader->SetShaderFileName("sha/vs_Shore.inl");
|
|
|
|
// vShader->SetShaderFileName("sha/vs_ShoreSteep.inl");
|
|
|
|
// vShader->SetShaderFileName("sha/vs_ShoreSucks.inl");
|
|
|
|
// vShader->SetShaderFileName("sha/vs_ShoreLeave.inl");
|
|
|
|
// vShader->SetShaderFileName("sha/vs_ShoreLeave6.inl");
|
|
|
|
// vShader->SetDecl(plShaderTable::Decl(vs_ShoreLeave6));
|
|
|
|
|
|
|
|
// static const plShaderDecl decl("sha/vs_ShoreLeave7.inl");
|
|
|
|
// vShader->SetDecl(&decl);
|
|
|
|
vShader->SetDecl(plShaderTable::Decl(vs_ShoreLeave7));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefShoreVShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fShoreVShader = vShader;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kVertexShader, fShoreVShader);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddShorePixelShader(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( !fShorePShader )
|
|
|
|
{
|
|
|
|
plShader* pShader = new plShader;
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_ShorePS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
pShader->SetIsPixelShader(true);
|
|
|
|
|
|
|
|
// pShader->SetShaderFileName("sha/ps_ShoreSucks.inl");
|
|
|
|
// pShader->SetShaderFileName("sha/ps_ShoreLeave6.inl");
|
|
|
|
pShader->SetDecl(plShaderTable::Decl(ps_ShoreLeave6));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefShorePShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fShorePShader = pShader;
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kPixelShader, fShorePShader);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddFixedVertexShader(hsGMaterial* mat, const int numUVWs)
|
|
|
|
{
|
|
|
|
if( !fFixedVShader )
|
|
|
|
{
|
|
|
|
|
|
|
|
plShader* vShader = new plShader;
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_FixedVS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
vShader->SetIsPixelShader(false);
|
|
|
|
|
|
|
|
vShader->SetNumConsts(plFixedVS7::kNumConsts);
|
|
|
|
|
|
|
|
vShader->SetInputFormat(numUVWs);
|
|
|
|
vShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
vShader->SetVector(plFixedVS7::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f);
|
|
|
|
vShader->SetVector(plFixedVS7::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f);
|
|
|
|
vShader->SetVector(plFixedVS7::kPiConsts, 1.f / (8.f*M_PI*4.f*4.f), M_PI/2.f, M_PI, M_PI*2.f);
|
|
|
|
vShader->SetVector(plFixedVS7::kNumericConsts, 0, 0.5f, 1.f, 2.f);
|
|
|
|
|
|
|
|
vShader->SetNumPipeConsts(5);
|
|
|
|
vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plFixedVS7::kWorldToNDC);
|
|
|
|
vShader->SetPipeConst(1, plPipeConst::kCamPosWorld, plFixedVS7::kCameraPos);
|
|
|
|
vShader->SetPipeConst(2, plPipeConst::kLocalToWorld, plFixedVS7::kLocalToWorld);
|
|
|
|
vShader->SetPipeConst(3, plPipeConst::kLayRuntime, plFixedVS7::kWaterTint);
|
|
|
|
vShader->SetPipeConst(4, plPipeConst::kFogSet, plFixedVS7::kFogSet);
|
|
|
|
|
|
|
|
|
|
|
|
// static const plShaderDecl decl("sha/vs_WaveFixedFin7.inl");
|
|
|
|
// vShader->SetDecl(&decl);
|
|
|
|
vShader->SetDecl(plShaderTable::Decl(vs_WaveFixedFin7));
|
|
|
|
|
|
|
|
|
|
|
|
// vShader->SetShaderFileName("sha/vs_WaveFixedFin6.inl");
|
|
|
|
// vShader->SetShaderFileName("sha/vs_WaveFixedFin.inl");
|
|
|
|
// vShader->SetShaderFileName("sha/vs_TestPos.inl");
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefFixedVShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fFixedVShader = vShader;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kVertexShader, fFixedVShader);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// type is either plLayRefMsg::kVertexShader or plLayRefMsg::kPixelShader.
|
|
|
|
void plWaveSet7::IAddShaderToLayers(hsGMaterial* mat, int iFirst, int iLast, uint8_t type, plShader* shader)
|
|
|
|
{
|
|
|
|
if( iFirst < 0 )
|
|
|
|
iFirst = 0;
|
|
|
|
if( uint32_t(iLast) >= mat->GetNumLayers() )
|
|
|
|
iLast = mat->GetNumLayers()-1;
|
|
|
|
int i;
|
|
|
|
for( i = iFirst; i <= iLast; i++ )
|
|
|
|
{
|
|
|
|
plLayer* layer = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack());
|
|
|
|
if( layer
|
|
|
|
&& (layer->GetVertexShader() != shader)
|
|
|
|
&& (layer->GetPixelShader() != shader) )
|
|
|
|
{
|
|
|
|
plLayRefMsg* refMsg = new plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, type);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(shader->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
// layer->SetShadeFlags(layer->GetShadeFlags() | hsGMatState::kShadeReallyNoFog);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddFixedPixelShader(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( !fFixedPShader )
|
|
|
|
{
|
|
|
|
plShader* pShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_FixedPS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
pShader->SetIsPixelShader(true);
|
|
|
|
|
|
|
|
// pShader->SetNumConsts(plFixedPS::kNumConsts);
|
|
|
|
pShader->SetNumConsts(0);
|
|
|
|
|
|
|
|
pShader->SetInputFormat(0);
|
|
|
|
pShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
// pShader->SetShaderFileName("sha/ps_WaveFixed.inl");
|
|
|
|
// pShader->SetShaderFileName("sha/ps_TestPos.inl");
|
|
|
|
pShader->SetDecl(plShaderTable::Decl(ps_WaveFixed));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefFixedPShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fFixedPShader = pShader;
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kPixelShader, fFixedPShader);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddRipVertexShader(hsGMaterial* mat, const plRipVSConsts& ripConsts)
|
|
|
|
{
|
|
|
|
if( !fRipVShader )
|
|
|
|
{
|
|
|
|
plShader* vShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_RipVS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
vShader->SetIsPixelShader(false);
|
|
|
|
|
|
|
|
vShader->SetInputFormat(1); // This should really be one!!!
|
|
|
|
vShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
vShader->SetNumConsts(plRipVS::kNumConsts);
|
|
|
|
|
|
|
|
vShader->SetVector(plRipVS::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f);
|
|
|
|
vShader->SetVector(plRipVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f);
|
|
|
|
vShader->SetVector(plRipVS::kPiConsts, 1.f / (8.f*M_PI*4.f*4.f), M_PI/2.f, M_PI, M_PI*2.f);
|
|
|
|
vShader->SetVector(plRipVS::kNumericConsts, 0, 0.5f, 1.f, 2.f);
|
|
|
|
|
|
|
|
hsVector3 waterOffset = State().fWaterOffset;
|
|
|
|
vShader->SetVector(plRipVS::kWaterLevel,
|
|
|
|
State().fWaterHeight + waterOffset.fX,
|
|
|
|
State().fWaterHeight + waterOffset.fY,
|
|
|
|
State().fWaterHeight + waterOffset.fZ,
|
|
|
|
State().fWaterHeight);
|
|
|
|
|
|
|
|
hsVector3 maxAtten = State().fMaxAtten;
|
|
|
|
hsVector3 minAtten = State().fMinAtten;
|
|
|
|
hsVector3 depthFalloff = State().fDepthFalloff;
|
|
|
|
vShader->SetVector(plRipVS::kDepthFalloff,
|
|
|
|
(maxAtten.fX - minAtten.fX) / depthFalloff.fX,
|
|
|
|
(maxAtten.fY - minAtten.fY) / depthFalloff.fY,
|
|
|
|
(maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ,
|
|
|
|
1.f
|
|
|
|
);
|
|
|
|
vShader->SetVector(plRipVS::kMinAtten,
|
|
|
|
minAtten.fX,
|
|
|
|
minAtten.fY,
|
|
|
|
minAtten.fZ,
|
|
|
|
0.f
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Set up the ones passed in from the dynarippleVSmgr.
|
|
|
|
vShader->SetVector(plRipVS::kTexConsts,
|
|
|
|
ripConsts.fC1U,
|
|
|
|
ripConsts.fC2U,
|
|
|
|
ripConsts.fC1V,
|
|
|
|
ripConsts.fC2V);
|
|
|
|
|
|
|
|
vShader->SetVector(plRipVS::kLifeConsts,
|
|
|
|
ripConsts.fInitAtten,
|
|
|
|
0, // current time, we'll fill that in later
|
|
|
|
ripConsts.fLife,
|
|
|
|
1.f / (ripConsts.fLife - ripConsts.fDecay));
|
|
|
|
|
|
|
|
plConst(float) kRipBias(0.1);
|
|
|
|
vShader->SetVector(plRipVS::kRampBias,
|
|
|
|
ripConsts.fRamp,
|
|
|
|
1.f / ripConsts.fRamp,
|
|
|
|
kRipBias,
|
|
|
|
0); // Last one still unused.
|
|
|
|
|
|
|
|
vShader->SetNumPipeConsts(4);
|
|
|
|
vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plRipVS::kWorldToNDC);
|
|
|
|
vShader->SetPipeConst(1, plPipeConst::kCamPosWorld, plRipVS::kCameraPos);
|
|
|
|
vShader->SetPipeConst(2, plPipeConst::kLocalToWorld, plRipVS::kLocalToWorld);
|
|
|
|
vShader->SetPipeConst(3, plPipeConst::kFogSet, plRipVS::kFogSet);
|
|
|
|
|
|
|
|
// vShader->SetShaderFileName("sha/vs_WaveRip.inl");
|
|
|
|
// vShader->SetDecl(plShaderTable::Decl(vs_WaveRip));
|
|
|
|
|
|
|
|
// static const plShaderDecl decl("sha/vs_WaveRip7.inl");
|
|
|
|
// vShader->SetDecl(&decl);
|
|
|
|
vShader->SetDecl(plShaderTable::Decl(vs_WaveRip7));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefRipVShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
hsAssert(vShader == fRipVShader, "Should have been set by SendRef");
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kVertexShader, fRipVShader);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddRipPixelShader(hsGMaterial* mat, const plRipVSConsts& ripConsts)
|
|
|
|
{
|
|
|
|
if( !fRipPShader )
|
|
|
|
{
|
|
|
|
plShader* pShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_RipPS", GetKey()->GetName());
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
pShader->SetIsPixelShader(true);
|
|
|
|
|
|
|
|
pShader->SetNumConsts(0);
|
|
|
|
|
|
|
|
pShader->SetInputFormat(0);
|
|
|
|
pShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
// pShader->SetShaderFileName("sha/ps_WaveRip.inl");
|
|
|
|
pShader->SetDecl(plShaderTable::Decl(ps_WaveRip));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefRipPShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
hsAssert(pShader == fRipPShader, "Should have been set by SendRef");
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kPixelShader, fRipPShader);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
plShader* plWaveSet7::ICreateDecalVShader(DecalVType t)
|
|
|
|
{
|
|
|
|
if( !fDecalVShaders[t] )
|
|
|
|
{
|
|
|
|
static const char* fname[kNumDecalVShaders] = {
|
|
|
|
"vs_WaveDec1Lay_7",
|
|
|
|
"vs_WaveDec2Lay11_7",
|
|
|
|
"vs_WaveDec2Lay12_7",
|
|
|
|
"vs_WaveDecEnv_7"
|
|
|
|
};
|
|
|
|
static const plShaderDecl shaderDecls[kNumDecalVShaders] = {
|
|
|
|
plShaderDecl("sha/vs_WaveDec1Lay_7.inl"),
|
|
|
|
plShaderDecl("sha/vs_WaveDec2Lay11_7.inl"),
|
|
|
|
plShaderDecl("sha/vs_WaveDec2Lay12_7.inl"),
|
|
|
|
plShaderDecl("sha/vs_WaveDecEnv_7.inl")
|
|
|
|
};
|
|
|
|
|
|
|
|
static const plShaderID::ID shaderIDs[kNumDecalVShaders] = {
|
|
|
|
vs_WaveDec1Lay_7,
|
|
|
|
vs_WaveDec2Lay11_7,
|
|
|
|
vs_WaveDec2Lay12_7,
|
|
|
|
vs_WaveDecEnv_7
|
|
|
|
};
|
|
|
|
static const int numUVWs[kNumDecalVShaders] = {
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
3
|
|
|
|
};
|
|
|
|
static const int numLayXfms[kNumDecalVShaders] = {
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
2,
|
|
|
|
1
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
plShader* vShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s", GetKey()->GetName(), fname[t]);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
vShader->SetIsPixelShader(false);
|
|
|
|
|
|
|
|
vShader->SetInputFormat(numUVWs[t]); // This should really be one!!!
|
|
|
|
vShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
vShader->SetNumConsts(plWaveDecVS::kNumConsts);
|
|
|
|
|
|
|
|
vShader->SetVector(plWaveDecVS::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f);
|
|
|
|
vShader->SetVector(plWaveDecVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f);
|
|
|
|
vShader->SetVector(plWaveDecVS::kPiConsts, 1.f / (8.f*M_PI*4.f*4.f), M_PI/2.f, M_PI, M_PI*2.f);
|
|
|
|
vShader->SetVector(plWaveDecVS::kNumericConsts, 0, 0.5f, 1.f, 2.f);
|
|
|
|
|
|
|
|
hsVector3 waterOffset = State().fWaterOffset;
|
|
|
|
vShader->SetVector(plWaveDecVS::kWaterLevel,
|
|
|
|
State().fWaterHeight + waterOffset.fX,
|
|
|
|
State().fWaterHeight + waterOffset.fY,
|
|
|
|
State().fWaterHeight + waterOffset.fZ,
|
|
|
|
State().fWaterHeight);
|
|
|
|
|
|
|
|
hsVector3 maxAtten = State().fMaxAtten;
|
|
|
|
hsVector3 minAtten = State().fMinAtten;
|
|
|
|
hsVector3 depthFalloff = State().fDepthFalloff;
|
|
|
|
vShader->SetVector(plWaveDecVS::kDepthFalloff,
|
|
|
|
(maxAtten.fX - minAtten.fX) / depthFalloff.fX,
|
|
|
|
(maxAtten.fY - minAtten.fY) / depthFalloff.fY,
|
|
|
|
(maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ,
|
|
|
|
1.f
|
|
|
|
);
|
|
|
|
vShader->SetVector(plWaveDecVS::kMinAtten,
|
|
|
|
minAtten.fX,
|
|
|
|
minAtten.fY,
|
|
|
|
minAtten.fZ,
|
|
|
|
0.f
|
|
|
|
);
|
|
|
|
|
|
|
|
plConst(float) kBias(0.1);
|
|
|
|
vShader->SetVector(plWaveDecVS::kBias,
|
|
|
|
kBias,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0); // Last one still unused.
|
|
|
|
|
|
|
|
const int kNumPipe = 5;
|
|
|
|
vShader->SetNumPipeConsts(kNumPipe + numLayXfms[t]);
|
|
|
|
vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plWaveDecVS::kWorldToNDC);
|
|
|
|
vShader->SetPipeConst(1, plPipeConst::kLocalToWorld, plWaveDecVS::kLocalToWorld);
|
|
|
|
vShader->SetPipeConst(2, plPipeConst::kLayRuntime, plWaveDecVS::kMatColor);
|
|
|
|
vShader->SetPipeConst(3, plPipeConst::kCamPosWorld, plWaveDecVS::kCameraPos);
|
|
|
|
vShader->SetPipeConst(4, plPipeConst::kFogSet, plWaveDecVS::kFogSet);
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < numLayXfms[t]; i++ )
|
|
|
|
{
|
|
|
|
vShader->SetPipeConst(kNumPipe + i, plPipeConst::Type(plPipeConst::kTex2x4_0+i), plWaveDecVS::kTex0Transform + i);
|
|
|
|
}
|
|
|
|
|
|
|
|
// vShader->SetDecl(&shaderDecls[t]);
|
|
|
|
vShader->SetDecl(plShaderTable::Decl(shaderIDs[t]));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, t, kRefDecVShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
hsAssert(vShader == fDecalVShaders[t], "Should have been set by SendRef");
|
|
|
|
}
|
|
|
|
return fDecalVShaders[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
plShader* plWaveSet7::IGetDecalVShader(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( mat->GetLayer(0)->GetShadeFlags() & hsGMatState::kShadeEnvironMap )
|
|
|
|
return ICreateDecalVShader(kDecalVEnv);
|
|
|
|
|
|
|
|
switch( mat->GetNumLayers() )
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
return ICreateDecalVShader(kDecalV1Lay);
|
|
|
|
case 2:
|
|
|
|
hsAssert(mat->GetLayer(0)->GetUVWSrc() == 0, "First layer must use uvw channel 1");
|
|
|
|
hsAssert(mat->GetLayer(1)->GetUVWSrc() < 2, "Second layer must use uvw channel 1 or 2");
|
|
|
|
switch( mat->GetLayer(1)->GetUVWSrc() )
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return ICreateDecalVShader(kDecalV2Lay11);
|
|
|
|
case 1:
|
|
|
|
return ICreateDecalVShader(kDecalV2Lay12);
|
|
|
|
default:
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
hsAssert(false, "Only 1 or 2 layers currently supported");
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plShader* plWaveSet7::ICreateDecalPShader(DecalPType t)
|
|
|
|
{
|
|
|
|
if( !fDecalPShaders[t] )
|
|
|
|
{
|
|
|
|
static const char* fname[kNumDecalPShaders] = {
|
|
|
|
"ps_CbaseAbase",
|
|
|
|
"ps_CalphaAbase",
|
|
|
|
"ps_CalphaAMult",
|
|
|
|
"ps_CalphaAadd",
|
|
|
|
"ps_CaddAbase",
|
|
|
|
"ps_CaddAMult",
|
|
|
|
"ps_CaddAAdd",
|
|
|
|
"ps_CmultAbase",
|
|
|
|
"ps_CmultAMult",
|
|
|
|
"ps_CmultAAdd",
|
|
|
|
"ps_WaveDecEnv"
|
|
|
|
};
|
|
|
|
static const plShaderID::ID shaderIDs[kNumDecalPShaders] = {
|
|
|
|
ps_CbaseAbase,
|
|
|
|
ps_CalphaAbase,
|
|
|
|
ps_CalphaAMult,
|
|
|
|
ps_CalphaAadd,
|
|
|
|
ps_CaddAbase,
|
|
|
|
ps_CaddAMult,
|
|
|
|
ps_CaddAAdd,
|
|
|
|
ps_CmultAbase,
|
|
|
|
ps_CmultAMult,
|
|
|
|
ps_CmultAAdd,
|
|
|
|
ps_WaveDecEnv
|
|
|
|
};
|
|
|
|
|
|
|
|
plShader* pShader = new plShader;
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s", GetKey()->GetName(), fname[t]);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
pShader->SetIsPixelShader(true);
|
|
|
|
|
|
|
|
// sprintf(buff, "sha/%s.inl", fname[t]);
|
|
|
|
// pShader->SetShaderFileName(buff);
|
|
|
|
pShader->SetDecl(plShaderTable::Decl(shaderIDs[t]));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, t, kRefDecPShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
hsAssert(fDecalPShaders[t] == pShader, "Should have been set by SendRef");
|
|
|
|
}
|
|
|
|
return fDecalPShaders[t];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
plShader* plWaveSet7::IGetDecalPShader(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( mat->GetLayer(0)->GetShadeFlags() & hsGMatState::kShadeEnvironMap )
|
|
|
|
return ICreateDecalPShader(kDecalPEnv);
|
|
|
|
|
|
|
|
hsAssert(mat->GetNumLayers() < 3, "Only 2 layers supported on water decal");
|
|
|
|
if( mat->GetNumLayers() >= 3 )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if( mat->GetNumLayers() == 1 )
|
|
|
|
return ICreateDecalPShader(kDecalPBB);
|
|
|
|
|
|
|
|
DecalPType t = kNumDecalPShaders; // Initialize to illegal value.
|
|
|
|
|
|
|
|
switch( mat->GetLayer(1)->GetBlendFlags() & (hsGMatState::kBlendMask | hsGMatState::kBlendAlphaMult | hsGMatState::kBlendAlphaAdd) )
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
case hsGMatState::kBlendTest:
|
|
|
|
case hsGMatState::kBlendAlpha:
|
|
|
|
t = kDecalPaB;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendAdd:
|
|
|
|
t = kDecalPAB;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendMult:
|
|
|
|
t = kDecalPMB;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendAlpha | hsGMatState::kBlendAlphaAdd:
|
|
|
|
t = kDecalPaA;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendAdd | hsGMatState::kBlendAlphaAdd:
|
|
|
|
t = kDecalPAA;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendMult | hsGMatState::kBlendAlphaAdd:
|
|
|
|
t = kDecalPMA;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendAlpha | hsGMatState::kBlendAlphaMult:
|
|
|
|
t = kDecalPaM;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendAdd | hsGMatState::kBlendAlphaMult:
|
|
|
|
t = kDecalPAM;
|
|
|
|
break;
|
|
|
|
case hsGMatState::kBlendMult | hsGMatState::kBlendAlphaMult:
|
|
|
|
t = kDecalPMM;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
hsAssert(false, "Unsupported layer blend mode");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ICreateDecalPShader(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateShaders(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
IUpdateBiasVShader();
|
|
|
|
|
|
|
|
IUpdateBumpVShader(pipe, l2w, w2l);
|
|
|
|
|
|
|
|
IUpdateBumpPShader(pipe, l2w, w2l);
|
|
|
|
|
|
|
|
IUpdateRipPShader(pipe, l2w, w2l);
|
|
|
|
|
|
|
|
IUpdateRipVShader(pipe, l2w, w2l);
|
|
|
|
|
|
|
|
IUpdateShoreVShader(pipe, l2w, w2l);
|
|
|
|
|
|
|
|
IUpdateFixedVShader(pipe, l2w, w2l);
|
|
|
|
|
|
|
|
IUpdateFixedPShader(pipe, l2w, w2l);
|
|
|
|
|
|
|
|
IUpdateDecVShaders(pipe, l2w, w2l);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateBumpPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
plCONST(int32_t) skip(0);
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumBumpShaders; i++ )
|
|
|
|
{
|
|
|
|
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < kBumpPerPass; j++ )
|
|
|
|
{
|
|
|
|
int iTex = i*kBumpPerPass + j;
|
|
|
|
|
|
|
|
hsVector3 specVec = State().fSpecVec;
|
|
|
|
float scale = 1.f / (float(kNumBumpShaders) + specVec[State().kNoise]);
|
|
|
|
|
|
|
|
float maxLen = TexState().fMaxLength * kCompositeSize / State().fRippleScale;
|
|
|
|
float rescale = fTexWaves[iTex].fLen / maxLen;
|
|
|
|
|
|
|
|
float bias = 0.5f * scale;
|
|
|
|
fBumpPShader[i]->SetVector(plBumpPS::kHalfOne, scale, scale, 1.f, 1.f);
|
|
|
|
fBumpPShader[i]->SetVector(plBumpPS::kBias, bias, bias, 1.f, 1.f);
|
|
|
|
|
|
|
|
float layScale = skip & (1 << iTex) ? 0.f : (1.f / float(kBumpPerPass));
|
|
|
|
layScale *= fTexWaveFade[iTex];
|
|
|
|
|
|
|
|
fBumpPShader[i]->SetVector(plBumpPS::kWave0 + j,
|
|
|
|
-fTexWaves[iTex].fDirX * layScale,
|
|
|
|
-fTexWaves[iTex].fDirY * layScale,
|
|
|
|
1.f,
|
|
|
|
1.f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateBumpVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline float IRound(float f)
|
|
|
|
{
|
|
|
|
return float(int(f + (f > 0 ? 0.5f : -0.5f)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateBiasVShader()
|
|
|
|
{
|
|
|
|
|
|
|
|
if( fBiasVShader )
|
|
|
|
{
|
|
|
|
// Can't just use GetDelSysSeconds() or else we lose time if we skip a frame render because of high FPS.
|
|
|
|
float dt = fLastTime > 0 ? float(fCurrTime - fLastTime) : hsTimer::GetDelSysSeconds();
|
|
|
|
plConst(float) kRate(-0.1f);
|
|
|
|
float tx = kRate * dt;
|
|
|
|
float ty = kRate * dt;
|
|
|
|
plConst(float) kScaleU(4.f);
|
|
|
|
plConst(float) kScaleV(1.f);
|
|
|
|
tx += fBiasVShader->GetFloat(plBiasVS::kTexU0, 3);
|
|
|
|
tx -= float(int(tx));
|
|
|
|
|
|
|
|
ty += fBiasVShader->GetFloat(plBiasVS::kTexV0, 3);
|
|
|
|
ty -= float(int(ty));
|
|
|
|
|
|
|
|
float scale = 1.f + (4.f - 1.f) * TexState().fAngleDev/M_PI;
|
|
|
|
|
|
|
|
float m00 = IRound(fWindDir.fY * scale);
|
|
|
|
float m01 = IRound(fWindDir.fX * scale);
|
|
|
|
float m10 = IRound(-fWindDir.fX * 4.f);
|
|
|
|
float m11 = IRound(fWindDir.fY * 4.f);
|
|
|
|
|
|
|
|
fBiasVShader->SetVector(plBiasVS::kTexU0,
|
|
|
|
m00,
|
|
|
|
m01,
|
|
|
|
0,
|
|
|
|
tx);
|
|
|
|
fBiasVShader->SetVector(plBiasVS::kTexV0,
|
|
|
|
m10,
|
|
|
|
m11,
|
|
|
|
0,
|
|
|
|
ty);
|
|
|
|
|
|
|
|
plConst(float) kUpperNoiseOffU(0.f);
|
|
|
|
plConst(float) kUpperNoiseOffV(0.3f);
|
|
|
|
fBiasVShader->SetVector(plBiasVS::kTexU1,
|
|
|
|
m00,
|
|
|
|
m01,
|
|
|
|
0,
|
|
|
|
-tx + kUpperNoiseOffU);
|
|
|
|
fBiasVShader->SetVector(plBiasVS::kTexV1,
|
|
|
|
m10,
|
|
|
|
m11,
|
|
|
|
0,
|
|
|
|
ty + kUpperNoiseOffV);
|
|
|
|
|
|
|
|
hsVector3 specVec = State().fSpecVec;
|
|
|
|
float biasScale = 0.5f * specVec[State().kNoise] / (float(kNumBumpShaders) + specVec[State().kNoise]);
|
|
|
|
fBiasVShader->SetVector(plBiasVS::kScaleBias,
|
|
|
|
biasScale,
|
|
|
|
biasScale,
|
|
|
|
0.f,
|
|
|
|
1.f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateFixedPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateRipPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
// Nothing to do
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateRipVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
if( fRipVShader )
|
|
|
|
{
|
|
|
|
fRipVShader->SetVector(plRipVS::kFrequency,
|
|
|
|
fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kPhase,
|
|
|
|
fWorldWaves[0].fPhase,
|
|
|
|
fWorldWaves[1].fPhase,
|
|
|
|
fWorldWaves[2].fPhase,
|
|
|
|
fWorldWaves[3].fPhase);
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kAmplitude,
|
|
|
|
fWorldWaves[0].fAmplitude,
|
|
|
|
fWorldWaves[1].fAmplitude,
|
|
|
|
fWorldWaves[2].fAmplitude,
|
|
|
|
fWorldWaves[3].fAmplitude);
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kDirectionX,
|
|
|
|
fWorldWaves[0].fDir.fX,
|
|
|
|
fWorldWaves[1].fDir.fX,
|
|
|
|
fWorldWaves[2].fDir.fX,
|
|
|
|
fWorldWaves[3].fDir.fX);
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kDirectionY,
|
|
|
|
fWorldWaves[0].fDir.fY,
|
|
|
|
fWorldWaves[1].fDir.fY,
|
|
|
|
fWorldWaves[2].fDir.fY,
|
|
|
|
fWorldWaves[3].fDir.fY);
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kWindRot,
|
|
|
|
fWindDir.fY,
|
|
|
|
-fWindDir.fX,
|
|
|
|
fWindDir.fX,
|
|
|
|
0);
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kLengths,
|
|
|
|
fWorldWaves[0].fLength,
|
|
|
|
fWorldWaves[1].fLength,
|
|
|
|
fWorldWaves[2].fLength,
|
|
|
|
fWorldWaves[3].fLength);
|
|
|
|
|
|
|
|
fRipVShader->SetFloat(plRipVS::kLifeConsts, 1, float(hsTimer::GetSysSeconds()));
|
|
|
|
float normQ[kNumWaves];
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
normQ[i] = GeoState().fChop / (2.f*M_PI * GeoState().fAmpOverLen * kNumWaves);
|
|
|
|
}
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kQADirX,
|
|
|
|
fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fX * normQ[0],
|
|
|
|
fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fX * normQ[1],
|
|
|
|
fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fX * normQ[2],
|
|
|
|
fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fX * normQ[3]);
|
|
|
|
|
|
|
|
fRipVShader->SetVector(plRipVS::kQADirY,
|
|
|
|
fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fY * normQ[0],
|
|
|
|
fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fY * normQ[1],
|
|
|
|
fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fY * normQ[2],
|
|
|
|
fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fY * normQ[3]);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateDecVShaders(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumDecalVShaders; i++ )
|
|
|
|
IUpdateDecVShader(i, pipe);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateDecVShader(int t, plPipeline* pipe)
|
|
|
|
{
|
|
|
|
plShader* shader = fDecalVShaders[t];
|
|
|
|
if( shader )
|
|
|
|
{
|
|
|
|
shader->SetVector(plWaveDecVS::kFrequency,
|
|
|
|
fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kPhase,
|
|
|
|
fWorldWaves[0].fPhase,
|
|
|
|
fWorldWaves[1].fPhase,
|
|
|
|
fWorldWaves[2].fPhase,
|
|
|
|
fWorldWaves[3].fPhase);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kAmplitude,
|
|
|
|
fWorldWaves[0].fAmplitude,
|
|
|
|
fWorldWaves[1].fAmplitude,
|
|
|
|
fWorldWaves[2].fAmplitude,
|
|
|
|
fWorldWaves[3].fAmplitude);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kDirectionX,
|
|
|
|
fWorldWaves[0].fDir.fX,
|
|
|
|
fWorldWaves[1].fDir.fX,
|
|
|
|
fWorldWaves[2].fDir.fX,
|
|
|
|
fWorldWaves[3].fDir.fX);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kDirectionY,
|
|
|
|
fWorldWaves[0].fDir.fY,
|
|
|
|
fWorldWaves[1].fDir.fY,
|
|
|
|
fWorldWaves[2].fDir.fY,
|
|
|
|
fWorldWaves[3].fDir.fY);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kLengths,
|
|
|
|
fWorldWaves[0].fLength,
|
|
|
|
fWorldWaves[1].fLength,
|
|
|
|
fWorldWaves[2].fLength,
|
|
|
|
fWorldWaves[3].fLength);
|
|
|
|
|
|
|
|
if( t == kDecalVEnv )
|
|
|
|
{
|
|
|
|
hsPoint3 worldCam = pipe->GetViewTransform().GetCameraToWorld().GetTranslate();
|
|
|
|
|
|
|
|
hsPoint3 envCenter(State().fEnvCenter);
|
|
|
|
|
|
|
|
float envRadius = State().fEnvRadius;
|
|
|
|
|
|
|
|
hsVector3 camToCen(&envCenter, &worldCam);
|
|
|
|
float G = camToCen.MagnitudeSquared() - envRadius * envRadius;
|
|
|
|
shader->SetVectorW(plWaveDecVS::kEnvAdjust, camToCen, G);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
float normQ[kNumWaves];
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
normQ[i] = GeoState().fChop / (2.f*M_PI * GeoState().fAmpOverLen * kNumWaves);
|
|
|
|
}
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kQADirX,
|
|
|
|
fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fX * normQ[0],
|
|
|
|
fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fX * normQ[1],
|
|
|
|
fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fX * normQ[2],
|
|
|
|
fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fX * normQ[3]);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kQADirY,
|
|
|
|
fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fY * normQ[0],
|
|
|
|
fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fY * normQ[1],
|
|
|
|
fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fY * normQ[2],
|
|
|
|
fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fY * normQ[3]);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kDirXW,
|
|
|
|
fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kDirYW,
|
|
|
|
fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kWK,
|
|
|
|
normQ[0] * fWorldWaves[0].fFreq,
|
|
|
|
normQ[1] * fWorldWaves[1].fFreq,
|
|
|
|
normQ[2] * fWorldWaves[2].fFreq,
|
|
|
|
normQ[3] * fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kDirXSqKW,
|
|
|
|
fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq * normQ[3]);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kDirXDirYKW,
|
|
|
|
fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]);
|
|
|
|
|
|
|
|
shader->SetVector(plWaveDecVS::kDirYSqKW,
|
|
|
|
fWorldWaves[0].fDir.fY * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fY * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fY * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fY * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateShoreVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
if( fShoreVShader )
|
|
|
|
{
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kFrequency,
|
|
|
|
fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kPhase,
|
|
|
|
fWorldWaves[0].fPhase,
|
|
|
|
fWorldWaves[1].fPhase,
|
|
|
|
fWorldWaves[2].fPhase,
|
|
|
|
fWorldWaves[3].fPhase);
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kAmplitude,
|
|
|
|
fWorldWaves[0].fAmplitude,
|
|
|
|
fWorldWaves[1].fAmplitude,
|
|
|
|
fWorldWaves[2].fAmplitude,
|
|
|
|
fWorldWaves[3].fAmplitude);
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kDirectionX,
|
|
|
|
fWorldWaves[0].fDir.fX,
|
|
|
|
fWorldWaves[1].fDir.fX,
|
|
|
|
fWorldWaves[2].fDir.fX,
|
|
|
|
fWorldWaves[3].fDir.fX);
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kDirectionY,
|
|
|
|
fWorldWaves[0].fDir.fY,
|
|
|
|
fWorldWaves[1].fDir.fY,
|
|
|
|
fWorldWaves[2].fDir.fY,
|
|
|
|
fWorldWaves[3].fDir.fY);
|
|
|
|
|
|
|
|
hsVector3 waterOffset = State().fWaterOffset;
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kWaterLevel,
|
|
|
|
State().fWaterHeight + waterOffset.fX,
|
|
|
|
State().fWaterHeight + waterOffset.fY,
|
|
|
|
State().fWaterHeight + waterOffset.fZ,
|
|
|
|
State().fWaterHeight);
|
|
|
|
|
|
|
|
hsVector3 maxAtten = State().fMaxAtten;
|
|
|
|
hsVector3 minAtten = State().fMinAtten;
|
|
|
|
hsVector3 depthFalloff = State().fDepthFalloff;
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kDepthFalloff,
|
|
|
|
(maxAtten.fX - minAtten.fX) / depthFalloff.fX,
|
|
|
|
(maxAtten.fY - minAtten.fY) / depthFalloff.fY,
|
|
|
|
(maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ,
|
|
|
|
1.f
|
|
|
|
);
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kMinAtten,
|
|
|
|
minAtten.fX,
|
|
|
|
minAtten.fY,
|
|
|
|
minAtten.fZ,
|
|
|
|
0.f
|
|
|
|
);
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kLengths,
|
|
|
|
fWorldWaves[0].fLength,
|
|
|
|
fWorldWaves[1].fLength,
|
|
|
|
fWorldWaves[2].fLength,
|
|
|
|
fWorldWaves[3].fLength);
|
|
|
|
|
|
|
|
plConst(float) kK1(2.f);
|
|
|
|
plConst(float) kK2(5.f);
|
|
|
|
float negK1OverK2Sq = -kK1 / (kK2 * kK2);
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kIncline, negK1OverK2Sq, kK1, 0.f, 0.f);
|
|
|
|
|
|
|
|
float normQ[kNumWaves];
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
normQ[i] = GeoState().fChop / (2.f*M_PI * GeoState().fAmpOverLen * kNumWaves);
|
|
|
|
}
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kQADirX,
|
|
|
|
fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fX * normQ[0],
|
|
|
|
fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fX * normQ[1],
|
|
|
|
fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fX * normQ[2],
|
|
|
|
fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fX * normQ[3]);
|
|
|
|
|
|
|
|
fShoreVShader->SetVector(plShoreVS::kQADirY,
|
|
|
|
fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fY * normQ[0],
|
|
|
|
fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fY * normQ[1],
|
|
|
|
fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fY * normQ[2],
|
|
|
|
fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fY * normQ[3]);
|
|
|
|
|
|
|
|
|
|
|
|
if( fTrialUpdate & kRemakeBubble )
|
|
|
|
IRefillBubbleShoreTex();
|
|
|
|
if( fTrialUpdate & kRemakeEdge )
|
|
|
|
IRefillEdgeShoreTex();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateFixedVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
if( fFixedVShader )
|
|
|
|
{
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kFrequency,
|
|
|
|
fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kPhase,
|
|
|
|
fWorldWaves[0].fPhase,
|
|
|
|
fWorldWaves[1].fPhase,
|
|
|
|
fWorldWaves[2].fPhase,
|
|
|
|
fWorldWaves[3].fPhase);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kAmplitude,
|
|
|
|
fWorldWaves[0].fAmplitude,
|
|
|
|
fWorldWaves[1].fAmplitude,
|
|
|
|
fWorldWaves[2].fAmplitude,
|
|
|
|
fWorldWaves[3].fAmplitude);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirectionX,
|
|
|
|
fWorldWaves[0].fDir.fX,
|
|
|
|
fWorldWaves[1].fDir.fX,
|
|
|
|
fWorldWaves[2].fDir.fX,
|
|
|
|
fWorldWaves[3].fDir.fX);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirectionY,
|
|
|
|
fWorldWaves[0].fDir.fY,
|
|
|
|
fWorldWaves[1].fDir.fY,
|
|
|
|
fWorldWaves[2].fDir.fY,
|
|
|
|
fWorldWaves[3].fDir.fY);
|
|
|
|
|
|
|
|
plCONST(float) kEnvRadius(500.f);
|
|
|
|
float envRadius = State().fEnvRadius;
|
|
|
|
|
|
|
|
hsPoint3 worldCam = pipe->GetViewTransform().GetCameraToWorld().GetTranslate();
|
|
|
|
|
|
|
|
hsPoint3 envCenter(State().fEnvCenter);
|
|
|
|
|
|
|
|
hsVector3 camToCen(&envCenter, &worldCam);
|
|
|
|
float G = camToCen.MagnitudeSquared() - envRadius * envRadius;
|
|
|
|
fFixedVShader->SetVectorW(plFixedVS7::kEnvAdjust, camToCen, G);
|
|
|
|
|
|
|
|
float texScale = 1.f / State().fRippleScale;
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kUVScale,
|
|
|
|
texScale,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0);
|
|
|
|
|
|
|
|
float specAtten = State().fTexState.fAmpOverLen * M_PI * 2.f;
|
|
|
|
|
|
|
|
plCONST(float) kScaleHack(0.1f);
|
|
|
|
float baseScale = kScaleHack;
|
|
|
|
// baseScale *= float(kBumpPerPass) * (float(kNumBumpShaders) + State().fSpecVec[State().kNoise]);
|
|
|
|
// Not sure what's right here. but we are currently scaling down by 1/(numBumpShaders + noise),
|
|
|
|
// so I guess we want to scale up by that amount here. Not sure we shouldn't figuring in bumpperpass
|
|
|
|
// on both, but at least now we're consistent.
|
|
|
|
hsVector3 specVec = State().fSpecVec;
|
|
|
|
baseScale *= (float(kNumBumpShaders) + specVec[State().kNoise]);
|
|
|
|
baseScale *= (TexState().fChop + 1.f);
|
|
|
|
|
|
|
|
|
|
|
|
float specStart = specVec[State().kSpecStart];
|
|
|
|
float specEnd = specVec[State().kSpecEnd];
|
|
|
|
if( specStart > specEnd )
|
|
|
|
specEnd = specStart + 1.f;
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kSpecAtten,
|
|
|
|
-specEnd,
|
|
|
|
1.f / (specStart - specEnd),
|
|
|
|
baseScale * specAtten,
|
|
|
|
0.f);
|
|
|
|
|
|
|
|
hsColorRGBA envTint = State().fSpecularTint;
|
|
|
|
if( fFixedLayers[0] && (fFixedLayers[0]->GetShadeFlags() & hsGMatState::kShadeSpecular) )
|
|
|
|
{
|
|
|
|
envTint *= fFixedLayers[0]->GetSpecularColor();
|
|
|
|
}
|
|
|
|
fFixedVShader->SetColor(plFixedVS::kEnvTint, envTint);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kWindRot,
|
|
|
|
fWindDir.fY,
|
|
|
|
-fWindDir.fX,
|
|
|
|
fWindDir.fX,
|
|
|
|
0);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kLengths,
|
|
|
|
fWorldWaves[0].fLength,
|
|
|
|
fWorldWaves[1].fLength,
|
|
|
|
fWorldWaves[2].fLength,
|
|
|
|
fWorldWaves[3].fLength);
|
|
|
|
|
|
|
|
// These don't change often, but they can change. We're going
|
|
|
|
// to be uploading them to the card anyway, might as well refresh
|
|
|
|
// the sysmem shader to be safe.
|
|
|
|
hsVector3 waterOffset = State().fWaterOffset;
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kWaterLevel,
|
|
|
|
State().fWaterHeight + waterOffset.fX,
|
|
|
|
State().fWaterHeight + waterOffset.fY,
|
|
|
|
State().fWaterHeight + waterOffset.fZ,
|
|
|
|
State().fWaterHeight);
|
|
|
|
|
|
|
|
hsVector3 maxAtten = State().fMaxAtten;
|
|
|
|
hsVector3 minAtten = State().fMinAtten;
|
|
|
|
hsVector3 depthFalloff = State().fDepthFalloff;
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDepthFalloff,
|
|
|
|
(maxAtten.fX - minAtten.fX) / depthFalloff.fX,
|
|
|
|
(maxAtten.fY - minAtten.fY) / depthFalloff.fY,
|
|
|
|
(maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ,
|
|
|
|
1.f
|
|
|
|
);
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kMinAtten,
|
|
|
|
minAtten.fX,
|
|
|
|
minAtten.fY,
|
|
|
|
minAtten.fZ,
|
|
|
|
0.f
|
|
|
|
);
|
|
|
|
|
|
|
|
float normQ[kNumWaves];
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kNumWaves; i++ )
|
|
|
|
{
|
|
|
|
normQ[i] = GeoState().fChop / (2.f*M_PI * GeoState().fAmpOverLen * kNumWaves);
|
|
|
|
}
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirXK,
|
|
|
|
fWorldWaves[0].fDir.fX * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fX * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fX * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fX * normQ[3]);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirYK,
|
|
|
|
fWorldWaves[0].fDir.fY * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fY * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fY * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fY * normQ[3]);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirXW,
|
|
|
|
fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirYW,
|
|
|
|
fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq,
|
|
|
|
fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq,
|
|
|
|
fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq,
|
|
|
|
fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kWK,
|
|
|
|
normQ[0] * fWorldWaves[0].fFreq,
|
|
|
|
normQ[1] * fWorldWaves[1].fFreq,
|
|
|
|
normQ[2] * fWorldWaves[2].fFreq,
|
|
|
|
normQ[3] * fWorldWaves[3].fFreq);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirXSqKW,
|
|
|
|
fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq * normQ[3]);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirXDirYKW,
|
|
|
|
fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]);
|
|
|
|
|
|
|
|
fFixedVShader->SetVector(plFixedVS7::kDirYSqKW,
|
|
|
|
fWorldWaves[0].fDir.fY * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0],
|
|
|
|
fWorldWaves[1].fDir.fY * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1],
|
|
|
|
fWorldWaves[2].fDir.fY * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2],
|
|
|
|
fWorldWaves[3].fDir.fY * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plWaveSet7::ICheckShoreMaterials()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fShores.GetCount(); i++ )
|
|
|
|
ICheckShoreMaterial(fShores[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICheckShoreMaterial(plSceneObject* so)
|
|
|
|
{
|
|
|
|
if( !so )
|
|
|
|
return;
|
|
|
|
|
|
|
|
const plDrawInterface* di = so->GetDrawInterface();
|
|
|
|
if( !di )
|
|
|
|
return;
|
|
|
|
|
|
|
|
hsTArray<plAccessSpan> src;
|
|
|
|
plAccessGeometry::Instance()->OpenRO(di, src);
|
|
|
|
|
|
|
|
if( !src.GetCount() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < src.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
ISetupGraphShore(src[i].GetMaterial());
|
|
|
|
}
|
|
|
|
|
|
|
|
plAccessGeometry::Instance()->Close(src);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICheckDecalMaterials()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fDecals.GetCount(); i++ )
|
|
|
|
ICheckDecalMaterial(fDecals[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICheckDecalMaterial(plSceneObject* so)
|
|
|
|
{
|
|
|
|
if( !so )
|
|
|
|
return;
|
|
|
|
|
|
|
|
const plDrawInterface* di = so->GetDrawInterface();
|
|
|
|
if( !di )
|
|
|
|
return;
|
|
|
|
|
|
|
|
hsTArray<plAccessSpan> src;
|
|
|
|
plAccessGeometry::Instance()->OpenRO(di, src);
|
|
|
|
|
|
|
|
if( !src.GetCount() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < src.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
ISetupDecal(src[i].GetMaterial());
|
|
|
|
}
|
|
|
|
|
|
|
|
plAccessGeometry::Instance()->Close(src);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICheckDecalEnvLayers(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
// Mat needs to be set up as:
|
|
|
|
// Lay0 = bumpmap - uv is lookup into bumpmap
|
|
|
|
// Lay1 = bumpmap - uv is col0 of tangent2local
|
|
|
|
// Lay2 = bumpmap - uv is col1 of tangent2local
|
|
|
|
// Lay3 = fEnvMap - uv is col2 of tangent2local, except we'll ignore it and use the normal instead
|
|
|
|
// Note we'll have to transpose that as part of creating tangent2world
|
|
|
|
// Easiest way to check whether we've done this one already is checking whether
|
|
|
|
// Lay3 == fEnvMap.
|
|
|
|
|
|
|
|
// If we haven't done this already
|
|
|
|
if( !fDecalVShaders[kDecalVEnv] || (mat->GetLayer(0)->GetVertexShader() != fDecalVShaders[kDecalVEnv]) )
|
|
|
|
{
|
|
|
|
plLayer* lay3 = nil;
|
|
|
|
|
|
|
|
plMatRefMsg* refMsg;
|
|
|
|
|
|
|
|
const int numLayers = mat->GetNumLayers();
|
|
|
|
int i;
|
|
|
|
for( i = numLayers-1; i >= 0; i-- )
|
|
|
|
{
|
|
|
|
plLayer* lay0 = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack());
|
|
|
|
lay0->SetBlendFlags(hsGMatState::kBlendAddColorTimesAlpha);
|
|
|
|
// lay0->SetBlendFlags(hsGMatState::kBlendAlpha);
|
|
|
|
lay0->SetMiscFlags(lay0->GetMiscFlags() | hsGMatState::kMiscRestartPassHere);
|
|
|
|
|
|
|
|
// Repeat lay3 as i+1, i+2 and i+3
|
|
|
|
// set base blend to timealphaadd
|
|
|
|
|
|
|
|
// If we are just creating lay3, then by creating it we've added (appended) it to mat.
|
|
|
|
// Otherwise, we need to add it as i+1.
|
|
|
|
if( !lay3 )
|
|
|
|
{
|
|
|
|
lay3 = ICreateTotalEnvLayer(fEnvMap, mat, 3, "Dec");
|
|
|
|
lay3->SetBlendFlags(hsGMatState::kBlendAlpha);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
refMsg = new plMatRefMsg(mat->GetKey(), plRefMsg::kOnRequest, i+1, plMatRefMsg::kLayer | plMatRefMsg::kInsert);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(lay3->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
refMsg = new plMatRefMsg(mat->GetKey(), plRefMsg::kOnRequest, i+2, plMatRefMsg::kLayer | plMatRefMsg::kInsert);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(lay3->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
refMsg = new plMatRefMsg(mat->GetKey(), plRefMsg::kOnRequest, i+3, plMatRefMsg::kLayer | plMatRefMsg::kInsert);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(lay3->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ISetupDecal(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( mat->GetLayer(0)->GetShadeFlags() & hsGMatState::kShadeEnvironMap )
|
|
|
|
{
|
|
|
|
ICheckDecalEnvLayers(mat);
|
|
|
|
}
|
|
|
|
|
|
|
|
plShader* vShader = IGetDecalVShader(mat);
|
|
|
|
if( mat->GetLayer(0)->GetVertexShader() != vShader )
|
|
|
|
IAddShaderToLayers(mat, 0, -1, plLayRefMsg::kVertexShader, vShader);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < mat->GetNumLayers(); i++ )
|
|
|
|
{
|
|
|
|
plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack());
|
|
|
|
if( lay )
|
|
|
|
lay->SetZFlags(lay->GetZFlags() | hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The FFP is actually quite adequate for the required blends,
|
|
|
|
// but may not stay that way (if we allow fancier blends and what not).
|
|
|
|
// So we can either use the Pixel Shaders or FFP, depending on this define.
|
|
|
|
// #define MF_USE_FFP
|
|
|
|
#ifndef MF_USE_FFP
|
|
|
|
plShader* pShader = IGetDecalPShader(mat);
|
|
|
|
if( mat->GetLayer(0)->GetPixelShader() != pShader )
|
|
|
|
IAddShaderToLayers(mat, 0, -1, plLayRefMsg::kPixelShader, pShader);
|
|
|
|
#endif // MF_USE_FFP
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::AddShoreTest(plKey& key)
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->SendRef(key, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefShore), plRefFlags::kPassiveRef);
|
|
|
|
|
|
|
|
plSceneObject* so = plSceneObject::ConvertNoRef(key->ObjectIsLoaded());
|
|
|
|
ICheckShoreMaterial(so);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
hsBool plWaveSet7::SetupRippleMat(hsGMaterial* mat, const plRipVSConsts& ripConsts)
|
|
|
|
{
|
|
|
|
// We'll assume that if we set the vertexshader, we set the pixelshader too.
|
|
|
|
if( fRipVShader && (mat->GetLayer(0)->GetVertexShader() == fRipVShader) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < mat->GetNumLayers(); i++ )
|
|
|
|
{
|
|
|
|
plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack());
|
|
|
|
if( lay )
|
|
|
|
lay->SetZFlags(lay->GetZFlags() | hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer);
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddRipVertexShader(mat, ripConsts);
|
|
|
|
IAddRipPixelShader(mat, ripConsts);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
plDrawableSpans* plWaveSet7::ICreateGraphDrawable(plDrawableSpans* drawable, hsGMaterial* mat, int nWid)
|
|
|
|
{
|
|
|
|
// drawable->SetNativeProperty(plDrawable::kPropVolatile, true);
|
|
|
|
|
|
|
|
const int nVerts = nWid * 2;
|
|
|
|
|
|
|
|
hsTArray<hsPoint3> pos;
|
|
|
|
hsTArray<hsVector3> norm;
|
|
|
|
|
|
|
|
pos.SetCount(nVerts);
|
|
|
|
norm.SetCount(nVerts);
|
|
|
|
|
|
|
|
#ifdef TEST_UVWS
|
|
|
|
hsTArray<hsPoint3> uvw; // Are we actually ever going to use these uvws?
|
|
|
|
uvw.SetCount(nVerts);
|
|
|
|
#endif // TEST_UVWS
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < nWid; i++ )
|
|
|
|
{
|
|
|
|
int iDn = i << 1;
|
|
|
|
int iUp = iDn + 1;
|
|
|
|
|
|
|
|
float delX = float(i) / float(nWid-1);
|
|
|
|
|
|
|
|
pos[iDn].fX = delX * 2.f - 1.f;
|
|
|
|
pos[iDn].fY = -1.f;
|
|
|
|
pos[iDn].fZ = 0.5f;
|
|
|
|
|
|
|
|
norm[iDn].Set(0.f, 0.f, 1.f);
|
|
|
|
|
|
|
|
#ifdef TEST_UVWS
|
|
|
|
uvw[iDn].fX = delX;
|
|
|
|
uvw[iDn].fY = 0.f;
|
|
|
|
uvw[iDn].fZ = 0.f;
|
|
|
|
#endif // TEST_UVWS
|
|
|
|
|
|
|
|
pos[iUp].fX = delX * 2.f - 1.f;
|
|
|
|
pos[iUp].fY = 1.f;
|
|
|
|
pos[iUp].fZ = 0.5f;
|
|
|
|
|
|
|
|
norm[iUp].Set(1.f, 0.f, 1.f);
|
|
|
|
|
|
|
|
#ifdef TEST_UVWS
|
|
|
|
uvw[iUp].fX = delX;
|
|
|
|
uvw[iUp].fY = 1.f;
|
|
|
|
uvw[iUp].fZ = 0.f;
|
|
|
|
#endif // TEST_UVWS
|
|
|
|
}
|
|
|
|
|
|
|
|
const int nTris = (nWid-1) * 2;
|
|
|
|
|
|
|
|
hsTArray<uint16_t> idxArr;
|
|
|
|
idxArr.SetCount(nTris * 3);
|
|
|
|
|
|
|
|
uint16_t* idx = idxArr.AcquireArray();
|
|
|
|
|
|
|
|
int iBase = 0;
|
|
|
|
for( i = 0; i < nTris; i += 2 )
|
|
|
|
{
|
|
|
|
*idx++ = i;
|
|
|
|
*idx++ = i + 2;
|
|
|
|
*idx++ = i + 1;
|
|
|
|
|
|
|
|
*idx++ = i + 1;
|
|
|
|
*idx++ = i + 2;
|
|
|
|
*idx++ = i + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
plDrawableGenerator::GenerateDrawable( nVerts, pos.AcquireArray(), norm.AcquireArray(),
|
|
|
|
#ifndef TEST_UVWS
|
|
|
|
nil, 0,
|
|
|
|
#else // TEST_UVWS
|
|
|
|
uvw.AcquireArray(), 1,
|
|
|
|
#endif // TEST_UVWS
|
|
|
|
nil, false, nil,
|
|
|
|
nTris * 3, idxArr.AcquireArray(),
|
|
|
|
mat,
|
|
|
|
hsMatrix44::IdentityMatrix(),
|
|
|
|
false,
|
|
|
|
nil,
|
|
|
|
drawable);
|
|
|
|
|
|
|
|
return drawable;
|
|
|
|
}
|
|
|
|
|
|
|
|
plDrawableSpans* plWaveSet7::ICreateEmptyGraphDrawable(const char* name, uint32_t ref, int which)
|
|
|
|
{
|
|
|
|
plDrawableSpans* drawable = new plDrawableSpans;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s_%d", GetKey()->GetName(), name, which);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, drawable, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(drawable->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, which, (int8_t)ref), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return drawable;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsGMaterial* plWaveSet7::ICreateEmptyMaterial(const char* name, uint32_t ref, int which)
|
|
|
|
{
|
|
|
|
hsGMaterial* mat = new hsGMaterial;
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s_%d", GetKey()->GetName(), name, which);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, mat, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(mat->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, which, (int8_t)ref), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return mat;
|
|
|
|
}
|
|
|
|
|
|
|
|
plLayer* plWaveSet7::ICreateBlankLayer(const char* name, int suff)
|
|
|
|
{
|
|
|
|
plLayer* lay = new plLayer;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s_%d", GetKey()->GetName(), name, suff);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, lay, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
return lay;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMipmap* plWaveSet7::ICreateBlankTex(const char* name, int width, int height, uint32_t ref)
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = new plMipmap(
|
|
|
|
width, height,
|
|
|
|
plMipmap::kARGB32Config,
|
|
|
|
1,
|
|
|
|
plMipmap::kUncompressed,
|
|
|
|
plMipmap::UncompressedInfo::kRGB8888);
|
|
|
|
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_%s", GetKey()->GetName(), name);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, mipMap, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, (int8_t)ref), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return mipMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMipmap* plWaveSet7::ICreateGraphShoreTex(int width, int height)
|
|
|
|
{
|
|
|
|
// GraphShoreLayer has a texture with white color (possibly noised),
|
|
|
|
// and a solid alpha.
|
|
|
|
// Alpha is constant along U (width).
|
|
|
|
// Alpha is 0 at base,
|
|
|
|
// ramps up to opaque
|
|
|
|
// then drops off sharply to transparent just before the top.
|
|
|
|
|
|
|
|
// If we haven't already made one...
|
|
|
|
if( !fGraphShoreTex )
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = ICreateBlankTex("Graph", width, height, kRefGraphShoreTex);
|
|
|
|
|
|
|
|
plConst(float) kRampFrac(0.4f);
|
|
|
|
plConst(float) kTruncFrac(0.8f);
|
|
|
|
const int rampEnd = int(kRampFrac * height + 0.5f);
|
|
|
|
int truncEnd = int(kTruncFrac * height);
|
|
|
|
if( truncEnd >= (height-1) )
|
|
|
|
truncEnd = height-2;
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < height; j++ )
|
|
|
|
{
|
|
|
|
uint32_t alpha = 255;
|
|
|
|
plConst(int) kRampStart(4);
|
|
|
|
if( j <= kRampStart )
|
|
|
|
{
|
|
|
|
alpha = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if( j - kRampStart < rampEnd )
|
|
|
|
{
|
|
|
|
alpha = ((j-kRampStart) * 255) / rampEnd;
|
|
|
|
}
|
|
|
|
else if( j > truncEnd )
|
|
|
|
{
|
|
|
|
alpha = 0;
|
|
|
|
}
|
|
|
|
uint32_t color = (alpha << 24)
|
|
|
|
| (0xff << 16)
|
|
|
|
| (0xff << 8)
|
|
|
|
| 0xff;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < width; i++ )
|
|
|
|
{
|
|
|
|
uint32_t* val = mipMap->GetAddr32(i, j);
|
|
|
|
*val = color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fGraphShoreTex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IRefillBubbleShoreTex()
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = fBubbleShoreTex;
|
|
|
|
hsAssert(mipMap, "Refilling a non-existent bubble texture");
|
|
|
|
|
|
|
|
const int width = mipMap->GetWidth();
|
|
|
|
const int height = mipMap->GetHeight();
|
|
|
|
|
|
|
|
// Initialize to white opaque.
|
|
|
|
memset(mipMap->GetAddr32(0,0), 0xff, width*height*sizeof(uint32_t));
|
|
|
|
|
|
|
|
plConst(int) kMinNumBub(1024);
|
|
|
|
plConst(int) kMaxNumBub(6000);
|
|
|
|
const int kNumBub = (int)(kMinNumBub + State().fWispiness * (kMaxNumBub - kMinNumBub));
|
|
|
|
int k;
|
|
|
|
for( k = 0; k < kNumBub; k++ )
|
|
|
|
{
|
|
|
|
// Select a random location.
|
|
|
|
int iLoc = (int)(fRand.RandZeroToOne() * width);
|
|
|
|
int jLoc = (int)(fRand.RandZeroToOne() * height);
|
|
|
|
|
|
|
|
// Select a random radius
|
|
|
|
plConst(float) kMinRad(2.f);
|
|
|
|
plConst(float) kMaxRad(5.0f);
|
|
|
|
int radius = int(kMinRad + fRand.RandZeroToOne() * (kMaxRad - kMinRad));
|
|
|
|
float invRadiusSq = 1.f / float(radius*radius);
|
|
|
|
|
|
|
|
// Carve out a hole.
|
|
|
|
int j;
|
|
|
|
for( j = -radius; j < radius; j++ )
|
|
|
|
{
|
|
|
|
int jj = jLoc + j;
|
|
|
|
if( jj < 0 )
|
|
|
|
jj += height;
|
|
|
|
else if( jj >= height )
|
|
|
|
jj -= height;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = -radius; i < radius; i++ )
|
|
|
|
{
|
|
|
|
int ii = iLoc + i;
|
|
|
|
if( ii < 0 )
|
|
|
|
ii += width;
|
|
|
|
else if( ii >= width )
|
|
|
|
ii -= width;
|
|
|
|
|
|
|
|
float f = float(i*i + j*j) * invRadiusSq;
|
|
|
|
if( f > 1.f )
|
|
|
|
f = 1.f;
|
|
|
|
plConst(float) kMinAlpha(0.8f);
|
|
|
|
plConst(float) kMaxAlpha(1.f);
|
|
|
|
f *= (kMaxAlpha - kMinAlpha);
|
|
|
|
f += kMinAlpha;
|
|
|
|
|
|
|
|
uint32_t* val = mipMap->GetAddr32(ii, jj);
|
|
|
|
uint32_t alpha = (*val) >> 24;
|
|
|
|
alpha = uint32_t(float(alpha) * f);
|
|
|
|
*val &= 0x00ffffff;
|
|
|
|
*val |= (alpha << 24);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const hsColorRGBA maxColor = State().fMaxColor;
|
|
|
|
const hsColorRGBA minColor = State().fMinColor;
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < height; j++ )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < width; i++ )
|
|
|
|
{
|
|
|
|
uint32_t* val = mipMap->GetAddr32(i, j);
|
|
|
|
hsColorRGBA col;
|
|
|
|
col.FromARGB32(*val);
|
|
|
|
float alpha = col.a;
|
|
|
|
col = maxColor - minColor;
|
|
|
|
col *= alpha;
|
|
|
|
col += minColor;
|
|
|
|
|
|
|
|
*val = col.ToARGB32();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mipMap->MakeDirty();
|
|
|
|
|
|
|
|
fTrialUpdate &= ~kRemakeBubble;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMipmap* plWaveSet7::ICreateBubbleShoreTex(int width, int height)
|
|
|
|
{
|
|
|
|
// Bubble layer is white in color (or noised).
|
|
|
|
// Alpha is just, well, random bubbles.
|
|
|
|
// Tile in U and V
|
|
|
|
|
|
|
|
// If we haven't already made one...
|
|
|
|
if( !fBubbleShoreTex )
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = ICreateBlankTex("Bubble", width, height, kRefBubbleShoreTex);
|
|
|
|
|
|
|
|
IRefillBubbleShoreTex();
|
|
|
|
}
|
|
|
|
|
|
|
|
return fBubbleShoreTex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IRefillEdgeShoreTex()
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = fEdgeShoreTex;
|
|
|
|
|
|
|
|
const int width = mipMap->GetWidth();
|
|
|
|
const int height = mipMap->GetHeight();
|
|
|
|
|
|
|
|
plConst(float) kCenter(0.8f);
|
|
|
|
plConst(float) kRadius(0.025f);
|
|
|
|
|
|
|
|
const int center = int(kCenter * height);
|
|
|
|
|
|
|
|
const int radius = int(kRadius * height * State().fEdgeRadius);
|
|
|
|
|
|
|
|
const int top = center + radius;
|
|
|
|
const int bot = center - radius;
|
|
|
|
const float invRadiusSq = 1.f / float(radius*radius);
|
|
|
|
|
|
|
|
hsAssert(top < height-1, "Center too high or radius too big");
|
|
|
|
|
|
|
|
const float maxAlpha = State().fEdgeOpac * 255.9f;
|
|
|
|
int j;
|
|
|
|
for( j = 0; j < height; j++ )
|
|
|
|
{
|
|
|
|
uint32_t alpha = 0;
|
|
|
|
if( (j > bot)&&(j < top) )
|
|
|
|
{
|
|
|
|
#if 0 // like x^2
|
|
|
|
float a = float(j-center);
|
|
|
|
a *= a;
|
|
|
|
a *= invRadiusSq;
|
|
|
|
a = 1.f - a;
|
|
|
|
#elif 1 // like 1/x^2
|
|
|
|
float a = float(j-center);
|
|
|
|
if( a < 0 )
|
|
|
|
a = -a;
|
|
|
|
a /= float(radius);
|
|
|
|
a = 1.f - a;
|
|
|
|
a *= a;
|
|
|
|
#else // like cos
|
|
|
|
float a = float(j - center);
|
|
|
|
a /= float(radius);
|
|
|
|
a *= M_PI;
|
|
|
|
a = hsFastMath::CosInRange(a);
|
|
|
|
a += 1.f;
|
|
|
|
a *= 0.5f;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
alpha = uint32_t(a * maxAlpha);
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < width; i++ )
|
|
|
|
{
|
|
|
|
uint32_t* val = mipMap->GetAddr32(i, j);
|
|
|
|
*val = (alpha << 24)
|
|
|
|
| (alpha << 16)
|
|
|
|
| (alpha << 8)
|
|
|
|
| (alpha << 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mipMap->MakeDirty();
|
|
|
|
|
|
|
|
fTrialUpdate &= ~kRemakeEdge;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMipmap* plWaveSet7::ICreateEdgeShoreTex(int width, int height)
|
|
|
|
{
|
|
|
|
// Edge layer is solid white color.
|
|
|
|
// Alpha is 0 from base almost up to where graph shore texture drops off
|
|
|
|
// sharply. There, alpha ramps up and back down. Probably get a better look
|
|
|
|
// from sqrt(k - d^2) (elliptical) than a linear ramp up and down.
|
|
|
|
// Tile in U, clamp in V.
|
|
|
|
|
|
|
|
// If we haven't already made one...
|
|
|
|
if( !fEdgeShoreTex )
|
|
|
|
{
|
|
|
|
plMipmap* mipMap = ICreateBlankTex("Edge", width, height, kRefEdgeShoreTex);
|
|
|
|
|
|
|
|
IRefillEdgeShoreTex();
|
|
|
|
}
|
|
|
|
|
|
|
|
return fEdgeShoreTex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ISetAsTexture(plLayer* lay, plBitmap* tex)
|
|
|
|
{
|
|
|
|
hsAssert(lay && tex, "Trying to set nil texture or nil layer");
|
|
|
|
plLayRefMsg* refMsg = new plLayRefMsg(lay->GetKey(), plRefMsg::kOnRequest, 0, plLayRefMsg::kTexture);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(tex->GetKey(), refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICreateGraphShoreLayer(hsGMaterial* mat, int iPass)
|
|
|
|
{
|
|
|
|
// GraphShoreLayer has a texture with white color (possibly noised),
|
|
|
|
// and a solid alpha.
|
|
|
|
// Alpha is constant along U (width).
|
|
|
|
// Alpha is 0 at base,
|
|
|
|
// ramps up to opaque
|
|
|
|
// then drops off sharply to transparent just before the top.
|
|
|
|
// Might as well make it pretty much 1 dimensional.
|
|
|
|
//
|
|
|
|
// Tile in U, clamp in V
|
|
|
|
|
|
|
|
plLayer* lay = ICreateBlankLayer("Graph", iPass);
|
|
|
|
|
|
|
|
// Set up it's state.
|
|
|
|
// First pass just overwrites, from then on alpha blend onto.
|
|
|
|
lay->SetBlendFlags(0);
|
|
|
|
lay->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite);
|
|
|
|
lay->SetShadeFlags(hsGMatState::kShadeReallyNoFog
|
|
|
|
| hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade
|
|
|
|
| hsGMatState::kShadeWhite);
|
|
|
|
lay->SetClampFlags(hsGMatState::kClampTextureV);
|
|
|
|
lay->SetMiscFlags(hsGMatState::kMiscRestartPassHere);
|
|
|
|
|
|
|
|
lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
|
|
|
|
lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
|
|
|
|
lay->SetOpacity(1.f);
|
|
|
|
|
|
|
|
lay->SetUVWSrc(0);
|
|
|
|
|
|
|
|
// Now set the texture.
|
|
|
|
plConst(int) kGraphWidth(1);
|
|
|
|
plConst(int) kGraphHeight(512);
|
|
|
|
plMipmap* tex = ICreateGraphShoreTex(kGraphWidth, kGraphHeight);
|
|
|
|
ISetAsTexture(lay, tex);
|
|
|
|
|
|
|
|
mat->AddLayerViaNotify(lay);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Second layer is the bubbles
|
|
|
|
void plWaveSet7::ICreateGraphBubbleLayer(hsGMaterial* mat, int iPass)
|
|
|
|
{
|
|
|
|
// Bubble layer is white in color (or noised).
|
|
|
|
// Alpha is just, well, random bubbles.
|
|
|
|
// Tile in U and V
|
|
|
|
plLayer* lay = ICreateBlankLayer("Bubble", iPass);
|
|
|
|
|
|
|
|
// Set up it's state.
|
|
|
|
lay->SetBlendFlags(hsGMatState::kBlendAlpha);
|
|
|
|
lay->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite);
|
|
|
|
lay->SetShadeFlags(hsGMatState::kShadeReallyNoFog
|
|
|
|
| hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade
|
|
|
|
| hsGMatState::kShadeWhite);
|
|
|
|
lay->SetClampFlags(0);
|
|
|
|
lay->SetMiscFlags(0);
|
|
|
|
|
|
|
|
lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
|
|
|
|
lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
|
|
|
|
lay->SetOpacity(1.f);
|
|
|
|
|
|
|
|
lay->SetUVWSrc(0);
|
|
|
|
|
|
|
|
// Now set the texture
|
|
|
|
plConst(int) kBubWidth(128);
|
|
|
|
plConst(int) kBubHeight(128);
|
|
|
|
plMipmap* tex = ICreateBubbleShoreTex(kBubWidth, kBubHeight);
|
|
|
|
ISetAsTexture(lay, tex);
|
|
|
|
|
|
|
|
mat->AddLayerViaNotify(lay);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Third layer is the alpha leading edge
|
|
|
|
void plWaveSet7::ICreateGraphEdgeLayer(hsGMaterial* mat, int iPass)
|
|
|
|
{
|
|
|
|
// Edge layer is solid white color.
|
|
|
|
// Alpha is 0 from base almost up to where graph shore texture drops off
|
|
|
|
// sharply. There, alpha ramps up and back down. Probably get a better look
|
|
|
|
// from sqrt(k - d^2) (elliptical) than a linear ramp up and down.
|
|
|
|
// Tile in U, clamp in V.
|
|
|
|
plLayer* lay = ICreateBlankLayer("Edge", iPass);
|
|
|
|
|
|
|
|
// Set up it's state.
|
|
|
|
lay->SetBlendFlags(hsGMatState::kBlendAlpha);
|
|
|
|
lay->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite);
|
|
|
|
lay->SetShadeFlags(hsGMatState::kShadeReallyNoFog
|
|
|
|
| hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade
|
|
|
|
| hsGMatState::kShadeWhite);
|
|
|
|
lay->SetClampFlags(hsGMatState::kClampTextureV);
|
|
|
|
lay->SetMiscFlags(0);
|
|
|
|
|
|
|
|
lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
|
|
|
|
lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
|
|
|
|
lay->SetOpacity(1.f);
|
|
|
|
|
|
|
|
lay->SetUVWSrc(0);
|
|
|
|
|
|
|
|
// Now set the texture.
|
|
|
|
plConst(int) kEdgeWidth(1);
|
|
|
|
plConst(int) kEdgeHeight(512);
|
|
|
|
plMipmap* tex = ICreateEdgeShoreTex(kEdgeWidth, kEdgeHeight);
|
|
|
|
ISetAsTexture(lay, tex);
|
|
|
|
|
|
|
|
mat->AddLayerViaNotify(lay);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ICreateGraphShoreMaterials()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kGraphShorePasses; i++ )
|
|
|
|
{
|
|
|
|
// Create our material
|
|
|
|
// and send ourselves a ref.
|
|
|
|
hsGMaterial* mat = ICreateEmptyMaterial("GraphShoreMat", kRefGraphShoreMat, i);
|
|
|
|
|
|
|
|
// GraphShoreMat's are the materials used to generate the shore texture layers
|
|
|
|
// which are then used on rendering the shore to the screen.
|
|
|
|
|
|
|
|
// Create 3 layers
|
|
|
|
// First layer is the graph shore
|
|
|
|
ICreateGraphShoreLayer(mat, i);
|
|
|
|
|
|
|
|
// Second layer is the bubbles
|
|
|
|
ICreateGraphBubbleLayer(mat, i);
|
|
|
|
|
|
|
|
// Third layer is the alpha leading edge
|
|
|
|
ICreateGraphEdgeLayer(mat, i);
|
|
|
|
|
|
|
|
IAddGraphVShader(mat, i);
|
|
|
|
IAddGraphPShader(mat, i);
|
|
|
|
|
|
|
|
IInitGraph(i);
|
|
|
|
|
|
|
|
hsAssert(fGraphShoreMat[i] == mat, "Should have been processed in ICreateEmptyMaterial()");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddGraphVShader(hsGMaterial* mat, int iPass)
|
|
|
|
{
|
|
|
|
if( !fGraphVShader[iPass] )
|
|
|
|
{
|
|
|
|
plShader* vShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_GraphVS_%d", GetKey()->GetName(), iPass);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
vShader->SetIsPixelShader(false);
|
|
|
|
|
|
|
|
vShader->SetNumConsts(plGraphVS::kNumConsts);
|
|
|
|
|
|
|
|
vShader->SetVector(plGraphVS::kNumericConsts, 0, 0.5f, 1.f, 2.f);
|
|
|
|
vShader->SetVector(plGraphVS::kPiConsts, 1.f / (2.f*M_PI), M_PI/2.f, M_PI, M_PI*2.f);
|
|
|
|
vShader->SetVector(plGraphVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f);
|
|
|
|
|
|
|
|
#ifndef TEST_UVWS
|
|
|
|
vShader->SetInputFormat(0);
|
|
|
|
#else // TEST_UVWS
|
|
|
|
vShader->SetInputFormat(1);
|
|
|
|
#endif // TEST_UVWS
|
|
|
|
vShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
// vShader->SetShaderFileName("sha/vs_WaveGraph.inl");
|
|
|
|
// vShader->SetShaderFileName("sha/vs_WaveGraph2.inl");
|
|
|
|
vShader->SetDecl(plShaderTable::Decl(vs_WaveGraph2));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iPass, kRefGraphVShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
hsAssert(fGraphVShader[iPass] == vShader, "SendRef should have set shader");
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, 2, plLayRefMsg::kVertexShader, fGraphVShader[iPass]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IAddGraphPShader(hsGMaterial* mat, int iPass)
|
|
|
|
{
|
|
|
|
if( !fGraphPShader[iPass] )
|
|
|
|
{
|
|
|
|
plShader* pShader = new plShader;
|
|
|
|
char buff[256];
|
|
|
|
sprintf(buff, "%s_GraphPS_%d", GetKey()->GetName(), iPass);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation());
|
|
|
|
pShader->SetIsPixelShader(true);
|
|
|
|
|
|
|
|
pShader->SetNumConsts(plGraphPS::kNumConsts);
|
|
|
|
|
|
|
|
pShader->SetInputFormat(0);
|
|
|
|
pShader->SetOutputFormat(0);
|
|
|
|
|
|
|
|
// pShader->SetShaderFileName("sha/ps_WaveGraph.inl");
|
|
|
|
pShader->SetDecl(plShaderTable::Decl(ps_WaveGraph));
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iPass, kRefGraphPShader), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
hsAssert(fGraphPShader[iPass] == pShader, "SendRef should have set shader");
|
|
|
|
}
|
|
|
|
|
|
|
|
IAddShaderToLayers(mat, 0, 2, plLayRefMsg::kPixelShader, fGraphPShader[iPass]);
|
|
|
|
}
|
|
|
|
|
|
|
|
plRenderTarget* plWaveSet7::ISetupGraphShoreRenderReq(int which)
|
|
|
|
{
|
|
|
|
plConst(int) kGraphSize(256);
|
|
|
|
char name[256];
|
|
|
|
sprintf(name, "Graph_%d", which);
|
|
|
|
plRenderTarget* rt = ICreateTransferRenderTarget(name, kGraphSize);
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->SendRef(rt->GetKey(), new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, which, kRefGraphShoreRT), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
fGraphReq[which] = ICreateRenderRequest(rt, fGraphShoreDraw[which], -100.f);
|
|
|
|
fGraphReqMsg[which] = new plRenderRequestMsg(GetKey(), fGraphReq[which]);
|
|
|
|
|
|
|
|
return rt;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ISetupGraphShore(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
if( !fGraphShoreRT[0] )
|
|
|
|
{
|
|
|
|
// Create the material
|
|
|
|
ICreateGraphShoreMaterials();
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < kGraphShorePasses; i++ )
|
|
|
|
{
|
|
|
|
// Create the mesh
|
|
|
|
plDrawableSpans* drawable = ICreateEmptyGraphDrawable("GraphShore", kRefGraphShoreDraw, i);
|
|
|
|
plConst(int) kGraphWidth(256);
|
|
|
|
fGraphShoreDraw[i] = ICreateGraphDrawable(drawable, fGraphShoreMat[i], kGraphWidth);
|
|
|
|
|
|
|
|
// Setup render requests
|
|
|
|
// Return value is the texture we want to use for the shore line.
|
|
|
|
fGraphShoreRT[i] = ISetupGraphShoreRenderReq(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we've already done this material, we're done (it may be shared
|
|
|
|
// with another shore mesh).
|
|
|
|
if( fShoreVShader && mat->GetLayer(0) && (fShoreVShader == mat->GetLayer(0)->GetVertexShader()) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We now have all our render target textures. Set them up as
|
|
|
|
// our kGraphShorePasses layers on this material.
|
|
|
|
ISetupShoreLayers(mat);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IMakeShoreLayer(hsGMaterial* mat, int which)
|
|
|
|
{
|
|
|
|
char name[512];
|
|
|
|
if( which >= mat->GetNumLayers() )
|
|
|
|
{
|
|
|
|
plLayer* lay = new plLayer;
|
|
|
|
sprintf(name, "%s_lay_%d", mat->GetKey()->GetName(), which);
|
|
|
|
hsgResMgr::ResMgr()->NewKey(name, lay, GetKey()->GetUoid().GetLocation());
|
|
|
|
|
|
|
|
lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
|
|
|
|
lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
|
|
|
|
lay->SetOpacity(1.f);
|
|
|
|
|
|
|
|
mat->AddLayerViaNotify(lay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::ISetupShoreLayers(hsGMaterial* mat)
|
|
|
|
{
|
|
|
|
// Make sure we have exactly kGraphShorePasses layers on this mat.
|
|
|
|
IMakeShoreLayer(mat, 0);
|
|
|
|
IMakeShoreLayer(mat, 1);
|
|
|
|
IMakeShoreLayer(mat, 2);
|
|
|
|
|
|
|
|
// Make sure the state is correct for each layer.
|
|
|
|
// And set the textures up to point at the render targets
|
|
|
|
plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(0)->BottomOfStack());
|
|
|
|
hsAssert(lay, "Bad first layer on material %s. Animated?");
|
|
|
|
// lay->SetBlendFlags(hsGMatState::kBlendAlpha | hsGMatState::kBlendInvertFinalAlpha | hsGMatState::kBlendAlphaAlways);
|
|
|
|
lay->SetBlendFlags(hsGMatState::kBlendAlpha);
|
|
|
|
lay->SetClampFlags(hsGMatState::kClampTextureV);
|
|
|
|
lay->SetShadeFlags(hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade
|
|
|
|
| hsGMatState::kShadeWhite);
|
|
|
|
lay->SetZFlags(hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer);
|
|
|
|
lay->SetMiscFlags(hsGMatState::kMiscTwoSided);
|
|
|
|
ISetAsTexture(lay, fGraphShoreRT[0]);
|
|
|
|
|
|
|
|
lay->SetUVWSrc(0);
|
|
|
|
|
|
|
|
lay = plLayer::ConvertNoRef(mat->GetLayer(1));
|
|
|
|
hsAssert(lay, "Bad second layer on material %s. Animated?");
|
|
|
|
lay->SetBlendFlags(hsGMatState::kBlendAlpha);
|
|
|
|
lay->SetClampFlags(hsGMatState::kClampTextureV);
|
|
|
|
lay->SetShadeFlags(hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade
|
|
|
|
| hsGMatState::kShadeWhite);
|
|
|
|
lay->SetZFlags(hsGMatState::kZNoZWrite);
|
|
|
|
lay->SetMiscFlags(hsGMatState::kMiscTwoSided);
|
|
|
|
ISetAsTexture(lay, fGraphShoreRT[1]);
|
|
|
|
|
|
|
|
lay->SetUVWSrc(1);
|
|
|
|
|
|
|
|
lay = plLayer::ConvertNoRef(mat->GetLayer(2));
|
|
|
|
hsAssert(lay, "Bad third layer on material %s. Animated?");
|
|
|
|
lay->SetBlendFlags(hsGMatState::kBlendAlpha);
|
|
|
|
lay->SetClampFlags(hsGMatState::kClampTextureV);
|
|
|
|
lay->SetShadeFlags(hsGMatState::kShadeNoProjectors
|
|
|
|
| hsGMatState::kShadeNoShade
|
|
|
|
| hsGMatState::kShadeWhite);
|
|
|
|
lay->SetZFlags(hsGMatState::kZNoZWrite);
|
|
|
|
lay->SetMiscFlags(hsGMatState::kMiscTwoSided);
|
|
|
|
ISetAsTexture(lay, fGraphShoreRT[2]);
|
|
|
|
|
|
|
|
lay->SetUVWSrc(2);
|
|
|
|
|
|
|
|
// Add the vertex and pixel shaders.
|
|
|
|
IAddShoreVertexShader(mat);
|
|
|
|
IAddShorePixelShader(mat);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IInitGraph(int iPass)
|
|
|
|
{
|
|
|
|
GraphState& gs = fGraphState[iPass];
|
|
|
|
plShader* shader = fGraphVShader[iPass];
|
|
|
|
|
|
|
|
// First the easy stuff.
|
|
|
|
|
|
|
|
// Age starts off 0.
|
|
|
|
gs.fAge = 0;
|
|
|
|
|
|
|
|
static int lastOne = 0;
|
|
|
|
|
|
|
|
plConst(float) kBasePeriod(3.f);
|
|
|
|
float life = State().fPeriod * kBasePeriod * (1.f + fRand.RandZeroToOne());
|
|
|
|
gs.fInvLife = (1.f + float(lastOne)/float(kGraphShorePasses-1)) / life;
|
|
|
|
|
|
|
|
lastOne = !lastOne;
|
|
|
|
|
|
|
|
gs.fUOff = fRand.RandZeroToOne();
|
|
|
|
|
|
|
|
// Now the rest we have to think about a little, and
|
|
|
|
// think about four times.
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
|
|
{
|
|
|
|
// Okay, phase we don't have to think too hard about,
|
|
|
|
// it doesn't matter as long as it's random.
|
|
|
|
gs.fPhase[i] = fRand.RandZeroToOne() * 2.f * M_PI;
|
|
|
|
|
|
|
|
// Next up is frequency, but frequency is the hard one.
|
|
|
|
// Remember frequency has to preserve tiling, so freq = k * 2 * PI.
|
|
|
|
// We'd like to keep at least one big one around all the time,
|
|
|
|
// so we'll always put a big one in first, and then a bunch of
|
|
|
|
// smaller ones to noise it up nice.
|
|
|
|
int k;
|
|
|
|
if( !i )
|
|
|
|
{
|
|
|
|
k = fRand.RandZeroToOne() > 0.5 ? 1 : 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plConst(int) kMinFreq(3);
|
|
|
|
plConst(int) kMaxFreq(10);
|
|
|
|
|
|
|
|
k = kMinFreq + int((kMaxFreq-kMinFreq) * fRand.RandZeroToOne());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Input will be in range [0..2], so we'll omit the customary 2*PI here.
|
|
|
|
gs.fFreq[i] = k * M_PI;
|
|
|
|
|
|
|
|
// Amplitude depends on freqency, or roughly inversely proportional
|
|
|
|
// to frequency (randomized about linear on period).
|
|
|
|
// Divide by 4 because that's how many oscillators we have, and they
|
|
|
|
// are summed.
|
|
|
|
float period = 1.f / float(k);
|
|
|
|
plConst(float) kAmpScale(1.f / 4.f / 2.f);
|
|
|
|
plConst(float) kMinPeriodFrac(1.f);
|
|
|
|
plConst(float) kMaxPeriodFrac(2.f);
|
|
|
|
period *= kMinPeriodFrac + fRand.RandZeroToOne() * (kMaxPeriodFrac - kMinPeriodFrac);
|
|
|
|
period *= kAmpScale;
|
|
|
|
gs.fAmp[i] = period;
|
|
|
|
}
|
|
|
|
// Go ahead and set the ones on the shader that won't be updated.
|
|
|
|
shader->SetVector(plGraphVS::kPhase,
|
|
|
|
gs.fPhase[0],
|
|
|
|
gs.fPhase[1],
|
|
|
|
gs.fPhase[2],
|
|
|
|
gs.fPhase[3]);
|
|
|
|
shader->SetVector(plGraphVS::kFrequency,
|
|
|
|
gs.fFreq[0],
|
|
|
|
gs.fFreq[1],
|
|
|
|
gs.fFreq[2],
|
|
|
|
gs.fFreq[3]);
|
|
|
|
|
|
|
|
// Propagate all this to the shader.
|
|
|
|
IUpdateGraphShader(0, iPass);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IShuffleDownGraphs(int iPass)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = iPass+1; i < kGraphShorePasses; i++ )
|
|
|
|
{
|
|
|
|
fGraphState[i-1] = fGraphState[i];
|
|
|
|
fGraphVShader[i-1]->CopyConsts(fGraphVShader[i]);
|
|
|
|
}
|
|
|
|
IInitGraph(kGraphShorePasses-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateGraphShader(float dt, int iPass)
|
|
|
|
{
|
|
|
|
if( fGraphShoreDraw[iPass] )
|
|
|
|
{
|
|
|
|
GraphState& gs = fGraphState[iPass];
|
|
|
|
plShader* shader = fGraphVShader[iPass];
|
|
|
|
|
|
|
|
gs.fAge += dt;
|
|
|
|
float rads = gs.fAge * gs.fInvLife;
|
|
|
|
if( rads >= M_PI )
|
|
|
|
{
|
|
|
|
// Recycle this one and restart the upper.
|
|
|
|
IShuffleDownGraphs(iPass);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float sinAge = hsFastMath::SinInRange(rads);
|
|
|
|
|
|
|
|
shader->SetVector(plGraphVS::kAmplitude,
|
|
|
|
gs.fAmp[0] * sinAge,
|
|
|
|
gs.fAmp[1] * sinAge,
|
|
|
|
gs.fAmp[2] * sinAge,
|
|
|
|
gs.fAmp[3] * sinAge);
|
|
|
|
|
|
|
|
// Might want to tint this sometime.
|
|
|
|
plConst(hsColorRGBA) kTint(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f));
|
|
|
|
hsColorRGBA tint = kTint;
|
|
|
|
tint.a *= sinAge;
|
|
|
|
shader->SetColor(plGraphVS::kColor, tint);
|
|
|
|
|
|
|
|
plConst(float) kCMax(1.f);
|
|
|
|
plConst(float) kCMin(3.f);
|
|
|
|
float cMin = kCMax + (kCMin - kCMax) * State().fFingerLength;
|
|
|
|
plConst(float) k2ndLayerScale(2.f);
|
|
|
|
plConst(float) k2ndLayerVoff(1.5f);
|
|
|
|
shader->SetVector(plGraphVS::kUVWConsts,
|
|
|
|
(kCMax - cMin) * sinAge + cMin,
|
|
|
|
gs.fUOff,
|
|
|
|
k2ndLayerVoff,
|
|
|
|
k2ndLayerScale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plWaveSet7::IUpdateGraphShaders(plPipeline* pipe, float dt)
|
|
|
|
{
|
|
|
|
if( fGraphShoreDraw[0] )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = kGraphShorePasses-1; i >= 0; i-- )
|
|
|
|
{
|
|
|
|
IUpdateGraphShader(dt, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|