You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4469 lines
138 KiB
4469 lines
138 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
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); |
|
} |
|
} |
|
} |
|
|
|
|
|
|