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.
1379 lines
46 KiB
1379 lines
46 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "HeadSpin.h" |
|
|
|
#include "max.h" |
|
#include "meshdlib.h" |
|
#include "dummy.h" |
|
#include "resource.h" |
|
#include "plComponent.h" |
|
#include "plComponentReg.h" |
|
#include "MaxMain/plPlasmaRefMsgs.h" |
|
|
|
#include "MaxMain/plMaxNode.h" |
|
|
|
#include "plWaterComponent.h" |
|
#include "plSoftVolumeComponent.h" |
|
|
|
#include "hsTypes.h" |
|
#include "plTweak.h" |
|
|
|
#include "plDrawable/plWaveSetBase.h" |
|
#include "plDrawable/plWaveSet7.h" |
|
#include "plDrawable/plFixedWaterState7.h" |
|
|
|
#include "plPipeline/plDynamicEnvMap.h" |
|
|
|
#include "MaxMain/plPluginResManager.h" |
|
|
|
#include "pnSceneObject/plSceneObject.h" |
|
#include "pnMessage/plObjRefMsg.h" |
|
|
|
#include "plScene/plVisRegion.h" |
|
|
|
static const float kPercentToFrac(1.e-2f); |
|
static const float kDegreeToRad(hsScalarPI/180.f); |
|
|
|
|
|
// Preliminary setup bookkeeping |
|
void DummyCodeIncludeFuncWater() |
|
{ |
|
} |
|
|
|
CLASS_DESC(plWaterComponent, gWaterCompDesc, "Large Water", "Water", COMP_TYPE_WATER, WATER_COMP_CID) |
|
|
|
ParamBlockDesc2 gWaterBk |
|
( |
|
plComponent::kBlkComp, _T("Water"), 0, &gWaterCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, |
|
|
|
plWaterComponent::kNumRollups, |
|
plWaterComponent::kRef, IDD_COMP_W_REFOBJECT, IDS_COMP_W_REFOBJECT, 0, 0, 0, |
|
// plWaterComponent::kBasicWater, IDD_COMP_W_BASICWATER, IDS_COMP_W_BASICWATER, 0, 0, 0, |
|
plWaterComponent::kGeoWater, IDD_COMP_W_GEOWATER, IDS_COMP_W_GEOWATER, 0, 0, 0, |
|
plWaterComponent::kTexWater, IDD_COMP_W_TEXWATER, IDS_COMP_W_TEXWATER, 0, 0, 0, |
|
plWaterComponent::kAdvWater, IDD_COMP_W_ADVWATER, IDS_COMP_W_ADVWATER, 0, 0, 0, |
|
plWaterComponent::kEnvMap, IDD_COMP_W_ENVMAP, IDS_COMP_W_ENVMAP, 0, 0, 0, |
|
plWaterComponent::kVtxHelp, IDD_COMP_W_VTXHELP, IDS_COMP_W_VTXHELP, 0, 0, 0, |
|
plWaterComponent::kBasicShore, IDD_COMP_W_BASICSHORE, IDS_COMP_W_BASICSHORE, 0, 0, 0, |
|
plWaterComponent::kAdvShore, IDD_COMP_W_ADVSHORE, IDS_COMP_W_ADVSHORE, 0, 0, 0, |
|
|
|
plWaterComponent::kRefObject, _T("RefObject"), TYPE_INODE, 0, 0, |
|
p_ui, plWaterComponent::kRef, TYPE_PICKNODEBUTTON, IDC_COMP_W_REFOBJECT, |
|
p_prompt, IDS_COMP_CHOOSE_OBJECT, |
|
end, |
|
|
|
// WATER BASIC |
|
plWaterComponent::kWindSpeed, _T("WindSpeed"), TYPE_FLOAT, 0, 0, |
|
// p_default, 30.0, |
|
// p_range, -1.0, 50.0, |
|
// p_ui, plWaterComponent::kBasicWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
// IDC_COMP_W_WINDSPEED, IDC_COMP_W_WINDSPEED_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kWaterTint, _T("WaterTint"), TYPE_RGBA, 0, 0, |
|
// p_default, Color(0.1, 0.2, 0.2), |
|
// p_ui, plWaterComponent::kBasicWater, TYPE_COLORSWATCH, IDC_COMP_W_WATERTINT, |
|
end, |
|
|
|
plWaterComponent::kWaterOpac, _T("WaterOpac"), TYPE_FLOAT, 0, 0, |
|
// p_default, 100.0, |
|
// p_range, 0.0, 100.0, |
|
// p_ui, plWaterComponent::kBasicWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
// IDC_COMP_W_WATEROPAC, IDC_COMP_W_WATEROPAC_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kSpecularTint, _T("SpecularTint"), TYPE_RGBA, 0, 0, |
|
p_default, Color(1.0, 1.0, 1.0), |
|
p_ui, plWaterComponent::kGeoWater, TYPE_COLORSWATCH, IDC_COMP_W_SPECULARTINT, |
|
end, |
|
|
|
plWaterComponent::kRippleScale, _T("RippleScale"), TYPE_FLOAT, 0, 0, |
|
p_default, 25.0, |
|
p_range, 5.0, 1000.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_RIPPLESCALE, IDC_COMP_W_TEX_RIPPLESCALE_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kDispersion, _T("Dispersion"), TYPE_FLOAT, 0, 0, |
|
// p_default, 0.0, |
|
// p_range, 0.0, 100.0, |
|
// p_ui, plWaterComponent::kBasicWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
// IDC_COMP_W_DISPERSION, IDC_COMP_W_DISPERSION_SPIN, 1.0, |
|
end, |
|
|
|
// WATER ADVANCED |
|
plWaterComponent::kDepthOpac, _T("DepthOpac"), TYPE_FLOAT, 0, 0, |
|
p_default, 3.0, |
|
p_range, 0.5, 20.0, |
|
p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_DEPTHOPAC, IDC_COMP_W_DEPTHOPAC_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kDepthRefl, _T("DepthRefl"), TYPE_FLOAT, 0, 0, |
|
p_default, 3.0, |
|
p_range, 0.5, 20.0, |
|
p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_DEPTHREFL, IDC_COMP_W_DEPTHREFL_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kDepthWave, _T("DepthWave"), TYPE_FLOAT, 0, 0, |
|
p_default, 4.0, |
|
p_range, 0.5, 20.0, |
|
p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_DEPTHWAVE, IDC_COMP_W_DEPTHWAVE_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kZeroOpac, _T("ZeroOpac"), TYPE_FLOAT, 0, 0, |
|
p_default, -1.0, |
|
p_range, -10.0, 10.0, |
|
p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_FLOAT, |
|
IDC_COMP_W_ZEROOPAC, IDC_COMP_W_ZEROOPAC_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kZeroRefl, _T("ZeroRefl"), TYPE_FLOAT, 0, 0, |
|
p_default, 0.0, |
|
p_range, -10.0, 10.0, |
|
p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_FLOAT, |
|
IDC_COMP_W_ZEROREFL, IDC_COMP_W_ZEROREFL_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kZeroWave, _T("ZeroWave"), TYPE_FLOAT, 0, 0, |
|
p_default, 0.0, |
|
p_range, -10.0, 10.0, |
|
p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_FLOAT, |
|
IDC_COMP_W_ZEROWAVE, IDC_COMP_W_ZEROWAVE_SPIN, 1.0, |
|
end, |
|
|
|
// SHORE BASIC |
|
plWaterComponent::kShoreTint, _T("ShoreTint"), TYPE_RGBA, 0, 0, |
|
p_default, Color(0.2, 0.4, 0.4), |
|
p_ui, plWaterComponent::kBasicShore, TYPE_COLORSWATCH, IDC_COMP_W_SHORETINT, |
|
end, |
|
|
|
plWaterComponent::kShoreOpac, _T("ShoreOpac"), TYPE_FLOAT, 0, 0, |
|
p_default, 40.0, |
|
p_range, 0.0, 100.0, |
|
p_ui, plWaterComponent::kBasicShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_SHOREOPAC, IDC_COMP_W_SHOREOPAC_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kWispiness, _T("Wispiness"), TYPE_FLOAT, 0, 0, |
|
p_default, 50.0, |
|
p_range, 0.0, 200.0, |
|
p_ui, plWaterComponent::kBasicShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_WISPINESS, IDC_COMP_W_WISPINESS_SPIN, 1.0, |
|
end, |
|
|
|
// SHORE ADVANCED |
|
plWaterComponent::kPeriod, _T("Period"), TYPE_FLOAT, 0, 0, |
|
p_default, 100.0, |
|
p_range, 50.0, 200.0, |
|
p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_PERIOD, IDC_COMP_W_PERIOD_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kFinger, _T("Finger"), TYPE_FLOAT, 0, 0, |
|
p_default, 100.0, |
|
p_range, 50.0, 300.0, |
|
p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_FINGER, IDC_COMP_W_FINGER_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kEnvObject, _T("EnvObject"), TYPE_INODE, 0, 0, |
|
p_ui, plWaterComponent::kEnvMap, TYPE_PICKNODEBUTTON, IDC_COMP_W_ENVOBJECT, |
|
p_prompt, IDS_COMP_CHOOSE_OBJECT, |
|
end, |
|
|
|
plWaterComponent::kEnvSize, _T("EnvSize"), TYPE_INT, 0, 0, |
|
p_default, 256, |
|
p_range, 32, 512, |
|
p_ui, plWaterComponent::kEnvMap, TYPE_SPINNER, EDITTYPE_INT, |
|
IDC_COMP_W_ENVSIZE, IDC_COMP_W_ENVSIZE_SPIN, 1.f, |
|
end, |
|
|
|
plWaterComponent::kEnvRadius, _T("EnvRadius"), TYPE_FLOAT, 0, 0, |
|
p_default, 500.0, |
|
p_range, 5.0, 10000.0, |
|
p_ui, plWaterComponent::kEnvMap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_ENVRADIUS, IDC_COMP_W_ENVRADIUS_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kEdgeOpac, _T("EdgeOpac"), TYPE_FLOAT, 0, 0, |
|
p_default, 100.0, |
|
p_range, 0.0, 100.0, |
|
p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_EDGEOPAC, IDC_COMP_W_EDGEOPAC_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kEdgeRadius, _T("EdgeRadius"), TYPE_FLOAT, 0, 0, |
|
p_default, 100.0, |
|
p_range, 50.0, 300.0, |
|
p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_EDGERADIUS, IDC_COMP_W_EDGERADIUS_SPIN, 1.0, |
|
end, |
|
|
|
|
|
plWaterComponent::kEnvRefresh, _T("EnvRefresh"), TYPE_FLOAT, 0, 0, |
|
p_default, 0.0, |
|
p_range, 0.0, 3600.0, |
|
p_ui, plWaterComponent::kEnvMap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_ENVREFRESH, IDC_COMP_W_ENVREFRESH_SPIN, 10.0, |
|
end, |
|
|
|
plWaterComponent::kGeoMinLen, _T("GeoMinLen"), TYPE_FLOAT, 0, 0, |
|
p_default, 4.0, |
|
p_range, 0.1, 50.0, |
|
p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_GEO_MINLEN, IDC_COMP_W_GEO_MINLEN_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kGeoMaxLen, _T("GeoMaxLen"), TYPE_FLOAT, 0, 0, |
|
p_default, 8.0, |
|
p_range, 0.1, 50.0, |
|
p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_GEO_MAXLEN, IDC_COMP_W_GEO_MAXLEN_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kGeoAmpOverLen, _T("GeoAngleDev"), TYPE_FLOAT, 0, 0, |
|
p_default, 10.0, |
|
p_range, 0.0, 100.0, |
|
p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_GEO_AMPOVERLEN, IDC_COMP_W_GEO_AMPOVERLEN_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kGeoAngleDev, _T("GeoAngleDev"), TYPE_FLOAT, 0, 0, |
|
p_default, 20.0, |
|
p_range, 0.0, 180.0, |
|
p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_GEO_ANGLEDEV, IDC_COMP_W_GEO_ANGLEDEV_SPIN, 10.0, |
|
end, |
|
|
|
plWaterComponent::kGeoChop, _T("GeoChop"), TYPE_FLOAT, 0, 0, |
|
p_default, 50.0, |
|
p_range, 0.0, 500.0, |
|
p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_GEO_CHOP, IDC_COMP_W_GEO_CHOP_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kTexMinLen, _T("TexMinLen"), TYPE_FLOAT, 0, 0, |
|
p_default, 0.1, |
|
p_range, 0.01, 4.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_MINLEN, IDC_COMP_W_TEX_MINLEN_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kTexMaxLen, _T("TexMaxLen"), TYPE_FLOAT, 0, 0, |
|
p_default, 4.0, |
|
p_range, 0.1, 50.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_MAXLEN, IDC_COMP_W_TEX_MAXLEN_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kTexAmpOverLen, _T("TexAngleDev"), TYPE_FLOAT, 0, 0, |
|
p_default, 10.0, |
|
p_range, 0.0, 100.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_AMPOVERLEN, IDC_COMP_W_TEX_AMPOVERLEN_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kTexAngleDev, _T("TexAngleDev"), TYPE_FLOAT, 0, 0, |
|
p_default, 20.0, |
|
p_range, 0.0, 180.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_ANGLEDEV, IDC_COMP_W_TEX_ANGLEDEV_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kNoise, _T("Noise"), TYPE_FLOAT, 0, 0, |
|
p_default, 50.0, |
|
p_range, 0.0, 300.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_NOISE, IDC_COMP_W_TEX_NOISE_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kTexChop, _T("TexChop"), TYPE_FLOAT, 0, 0, |
|
p_default, 50.0, |
|
p_range, 0.0, 500.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_CHOP, IDC_COMP_W_TEX_CHOP_SPIN, 1.0, |
|
end, |
|
|
|
plWaterComponent::kSpecStart, _T("SpecStart"), TYPE_FLOAT, 0, 0, |
|
p_default, 50.0, |
|
p_range, 0.0, 1000.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_SPECSTART, IDC_COMP_W_TEX_SPECSTART_SPIN, 10.0, |
|
end, |
|
|
|
plWaterComponent::kSpecEnd, _T("SpecEnd"), TYPE_FLOAT, 0, 0, |
|
p_default, 1000.0, |
|
p_range, 0.0, 10000.0, |
|
p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_TEX_SPECEND, IDC_COMP_W_TEX_SPECEND_SPIN, 10.0, |
|
end, |
|
|
|
plWaterComponent::kSpecularMute, _T("SpecularMute"), TYPE_FLOAT, 0, 0, |
|
p_default, 30.0, |
|
p_range, 0.0, 100.0, |
|
p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_W_GEO_SPECMUTE, IDC_COMP_W_GEO_SPECMUTE_SPIN, 1.0, |
|
end, |
|
|
|
end |
|
); |
|
|
|
|
|
class plWaterCompPostLoadCallback : public PostLoadCallback |
|
{ |
|
public: |
|
plWaterComponent* fWaterComp; |
|
|
|
plWaterCompPostLoadCallback(plWaterComponent* wc) : fWaterComp(wc) {} |
|
|
|
void proc(ILoad *iload) |
|
{ |
|
fWaterComp->CheckForObsoleteParams(); |
|
|
|
delete this; |
|
} |
|
}; |
|
|
|
IOResult plWaterComponent::Load(ILoad* iLoad) |
|
{ |
|
iLoad->RegisterPostLoadCallback(new plWaterCompPostLoadCallback(this)); |
|
|
|
return plComponent::Load(iLoad); |
|
} |
|
|
|
void plWaterComponent::CheckForObsoleteParams() |
|
{ |
|
if( (fCompPB->GetFloat(kDispersion) >= 0) |
|
||(fCompPB->GetFloat(kWindSpeed) >= 0) ) |
|
{ |
|
// Okay, these are old. Need to set some default values based |
|
// on the old obsolete ones. Basically, we need to go from: |
|
// |
|
// kDispersion => |
|
// GeoAngleDev |
|
// TexAngleDev |
|
// |
|
// kWindSpeed |
|
// GeoMinLen |
|
// GeoMaxLen |
|
// GeoAmpOverLen = 0.1 |
|
// |
|
// TexMinLen |
|
// TexMaxLen |
|
// TexAmpOverLen |
|
// |
|
// Noise |
|
// |
|
// kEnvRadius |
|
// SpecStart = kEnvRadius / 2.f |
|
// SpecEnd = kEnvRadius * 2.f |
|
|
|
// Okay, here we go. |
|
hsScalar dispersion = fCompPB->GetFloat(kDispersion) / 100.f; |
|
plConst(hsScalar) kMinAng(5.f); |
|
plConst(hsScalar) kMaxAng(180.f); |
|
|
|
hsScalar angleDev = kMinAng + dispersion * (kMaxAng - kMinAng); |
|
fCompPB->SetValue(kGeoAngleDev, TimeValue(0), angleDev); |
|
fCompPB->SetValue(kTexAngleDev, TimeValue(0), angleDev); |
|
|
|
hsScalar windSpeed = fCompPB->GetFloat(kWindSpeed); |
|
const hsScalar kGravConst(32.f); // ft/s^2 |
|
hsScalar waveLen = windSpeed * windSpeed / kGravConst; |
|
waveLen /= 2.f; |
|
if( waveLen < 1.f ) |
|
waveLen = 1.f; |
|
fCompPB->SetValue(kGeoMinLen, TimeValue(0), waveLen/2.f); |
|
fCompPB->SetValue(kGeoMaxLen, TimeValue(0), waveLen*2.f); |
|
fCompPB->SetValue(kGeoAmpOverLen, TimeValue(0), 10.f); |
|
|
|
hsScalar rippleScale = fCompPB->GetFloat(kRippleScale); |
|
fCompPB->SetValue(kTexMinLen, TimeValue(0), 4.f / 256.f * rippleScale); |
|
fCompPB->SetValue(kTexMaxLen, TimeValue(0), 32.f / 256.f * rippleScale); |
|
hsScalar amp = 0.01f; |
|
hsScalar specMute = 0.5f; |
|
if( windSpeed < 15.f ) |
|
{ |
|
hsScalar p = windSpeed / 15.f; |
|
amp += p * (0.1f - 0.01f); |
|
|
|
specMute += (1-p)* 0.5f; |
|
} |
|
fCompPB->SetValue(kTexAmpOverLen, TimeValue(0), amp*100.f); |
|
fCompPB->SetValue(kSpecularMute, TimeValue(0), specMute*100.f); |
|
|
|
fCompPB->SetValue(kNoise, TimeValue(0), 50.f); |
|
|
|
hsScalar envRad = fCompPB->GetFloat(kEnvRadius); |
|
hsScalar specStart = envRad / 2.f; |
|
hsScalar specEnd = envRad * 2.f; |
|
fCompPB->SetValue(kSpecStart, TimeValue(0), specStart); |
|
fCompPB->SetValue(kSpecEnd, TimeValue(0), specEnd); |
|
|
|
// Set them negative so we don't keep doing this. |
|
fCompPB->SetValue(kDispersion, TimeValue(0), -1.f); |
|
fCompPB->SetValue(kWindSpeed, TimeValue(0), -1.f); |
|
} |
|
} |
|
|
|
|
|
// Component implementation |
|
plWaterComponent::plWaterComponent() |
|
: fWaveSet(nil) |
|
{ |
|
fClassDesc = &gWaterCompDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
hsBool plWaterComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
node->SetRunTimeLight(true); |
|
node->SetForceMaterialCopy(true); |
|
node->SetForceShadow(false); |
|
node->SetNoShadow(true); |
|
node->SetSmoothAll(true); |
|
node->SetForceSortable(true); |
|
node->SetNoSpanSort(true); |
|
node->SetNoPreShade(true); |
|
|
|
// Turn on calculation of edge lengths XXX |
|
node->SetCalcEdgeLens(true); |
|
|
|
// Make a note that we're vertex shaded (at least ripplecomponent needs to know).XXX |
|
node->SetVS(true); |
|
|
|
// Turn off the convexity test. We want to be sorted.XXX |
|
node->SetConcave(true); |
|
|
|
node->SetReverseSort(true); |
|
|
|
node->SetWaterHeight(IGetWaterHeight()); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plWaterComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
if( !fWaveSet ) |
|
IMakeWaveSet(node, pErrMsg); |
|
|
|
// Do it again in case some idiot is trying to override.XXX |
|
|
|
pErrMsg->Set(!node->HasLoadMask(), node->GetName(), "PS water has no representation component").CheckAndAsk(); |
|
pErrMsg->Set(false); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plWaterComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
if( !fWaveSet ) |
|
return true; |
|
|
|
plObjRefMsg* refMsg = TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnRequest, -1, plObjRefMsg::kModifier); |
|
hsgResMgr::ResMgr()->AddViaNotify(fWaveSet->GetKey(), refMsg, plRefFlags::kActiveRef); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plWaterComponent::DeInit(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
if( fWaveSet ) |
|
fWaveSet->GetKey()->UnRefObject(); |
|
fWaveSet = nil; |
|
|
|
return true; |
|
} |
|
|
|
hsBool plWaterComponent::IReadRefObject(plMaxNodeBase* node, plFixedWaterState7& ws) |
|
{ |
|
INode* ref = fCompPB->GetINode(kRefObject); |
|
if( !ref ) |
|
{ |
|
ref = node; |
|
} |
|
if( !ref ) |
|
return false; |
|
|
|
Matrix3 xfm = ref->GetNodeTM(TimeValue(0)); |
|
ws.fWaterHeight = xfm.GetTrans().z; |
|
|
|
Point3 y = xfm.GetRow(1); |
|
hsVector3 dir(-y.x, -y.y, 0); |
|
dir.Normalize(); |
|
ws.fWindDir = dir; |
|
|
|
return true; |
|
} |
|
|
|
hsBool plWaterComponent::IReadEnvObject(plMaxNode* node, plErrorMsg* pErrMsg, plFixedWaterState7& ws) |
|
{ |
|
INode* ref = fCompPB->GetINode(kEnvObject); |
|
if( !ref ) |
|
{ |
|
ref = node; |
|
} |
|
plDynamicEnvMap* env = plEnvMapComponent::GetEnvMap((plMaxNode*)ref); |
|
if( !env ) |
|
{ |
|
UInt32 size = fCompPB->GetInt(kEnvSize); |
|
UInt32 i; |
|
for( i = 9; i > 5; i-- ) |
|
{ |
|
if( (1UL << i) <= size ) |
|
break; |
|
} |
|
size = UInt32(1 << i); |
|
|
|
env = TRACKED_NEW plDynamicEnvMap(size, size, 32); |
|
hsgResMgr::ResMgr()->NewKey(ref->GetName(), env, node->GetLocation(), node->GetLoadMask()); |
|
|
|
Point3 pos = ref->GetNodeTM(TimeValue(0)).GetTrans(); |
|
env->SetPosition(hsPoint3(pos.x, pos.y, pos.z)); |
|
env->SetYon(10000.f); |
|
env->SetRefreshRate(fCompPB->GetFloat(kEnvRefresh)); |
|
} |
|
if( !env ) |
|
return false; |
|
|
|
ws.fEnvCenter = env->GetPosition(); |
|
ws.fEnvRefresh = env->GetRefreshRate(); |
|
|
|
fWaveSet->SetEnvSize(env->GetWidth()); |
|
|
|
|
|
plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fWaveSet->GetKey(), plRefMsg::kOnCreate, -1, plWaveSet7::kRefEnvMap); |
|
hsgResMgr::ResMgr()->SendRef(env->GetKey(), refMsg, plRefFlags::kActiveRef); |
|
|
|
ws.fEnvRadius = fCompPB->GetFloat(kEnvRadius); |
|
|
|
|
|
return true; |
|
} |
|
|
|
hsBool plWaterComponent::IGetRefObject(plMaxNode* node) |
|
{ |
|
plMaxNode* ref = (plMaxNode*)fCompPB->GetINode(kRefObject); |
|
if( (ref != node) // We have an exterior reference node |
|
&& ref->CanConvert() // it's being exported |
|
&& ref->IsTMAnimated() ) |
|
{ |
|
plSceneObject* refObj = ref->GetSceneObject(); |
|
|
|
fWaveSet->SetRefObject(refObj); |
|
|
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
hsBool plWaterComponent::IMakeWaveSet(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
// Go ahead and create the WaveSet modifier. There will be just |
|
// one created by this component, everyone has to share. |
|
fWaveSet = TRACKED_NEW plWaveSet7; |
|
hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), fWaveSet, node->GetLocation(), node->GetLoadMask()); |
|
|
|
// Set up the parameters |
|
plFixedWaterState7 ws; |
|
|
|
// Things we get off the reference (plane) object |
|
// First we look to see if it's an animated runtime object we need to keep track of. |
|
// Either way, we just grab the static info we need off of it, for init or forever. |
|
IGetRefObject(node); |
|
IReadRefObject(node, ws); |
|
|
|
// Things we get from our paramblock |
|
plFixedWaterState7::WaveState& geoState = ws.fGeoState; |
|
geoState.fMaxLength = fCompPB->GetFloat(kGeoMaxLen); |
|
geoState.fMinLength = fCompPB->GetFloat(kGeoMinLen); |
|
geoState.fAmpOverLen = fCompPB->GetFloat(kGeoAmpOverLen) * kPercentToFrac; |
|
geoState.fChop = fCompPB->GetFloat(kGeoChop) * kPercentToFrac; |
|
geoState.fAngleDev = fCompPB->GetFloat(kGeoAngleDev) * kDegreeToRad; |
|
|
|
plFixedWaterState7::WaveState& texState = ws.fTexState; |
|
texState.fMaxLength = fCompPB->GetFloat(kTexMaxLen); |
|
texState.fMinLength = fCompPB->GetFloat(kTexMinLen); |
|
texState.fAmpOverLen = fCompPB->GetFloat(kTexAmpOverLen) * kPercentToFrac; |
|
texState.fChop = fCompPB->GetFloat(kTexChop) * kPercentToFrac; |
|
texState.fAngleDev = fCompPB->GetFloat(kTexAngleDev) * kDegreeToRad; |
|
|
|
hsVector3 specVec; |
|
specVec[ws.kNoise] = fCompPB->GetFloat(kNoise) * kPercentToFrac; |
|
specVec[ws.kSpecStart] = fCompPB->GetFloat(kSpecStart); |
|
specVec[ws.kSpecEnd] = fCompPB->GetFloat(kSpecEnd); |
|
ws.fSpecVec = specVec; |
|
|
|
ws.fWispiness = fCompPB->GetFloat(kWispiness) * kPercentToFrac; |
|
ws.fPeriod = fCompPB->GetFloat(kPeriod) * kPercentToFrac; |
|
ws.fRippleScale = fCompPB->GetFloat(kRippleScale); |
|
ws.fFingerLength = fCompPB->GetFloat(kFinger) * kPercentToFrac; |
|
|
|
ws.fDepthFalloff = hsVector3(fCompPB->GetFloat(kDepthOpac), fCompPB->GetFloat(kDepthRefl), fCompPB->GetFloat(kDepthWave)); |
|
ws. fWaterOffset = hsVector3(-fCompPB->GetFloat(kZeroOpac), -fCompPB->GetFloat(kZeroRefl), -fCompPB->GetFloat(kZeroWave)); |
|
ws.fMaxAtten = hsVector3(1.f, 1.f, 1.f); |
|
ws.fMinAtten = hsVector3(0, 0, 0); |
|
|
|
IReadEnvObject(node, pErrMsg, ws); |
|
|
|
// Some colors |
|
ws.fWaterTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); |
|
|
|
Color specTint = fCompPB->GetColor(kSpecularTint); |
|
float specMute = fCompPB->GetFloat(kSpecularMute) * kPercentToFrac; |
|
ws.fSpecularTint = hsColorRGBA().Set(specTint.r, specTint.g, specTint.b, specMute); |
|
|
|
ws.fMaxColor = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); // Always white/opaque? |
|
Color shoreTint = fCompPB->GetColor(kShoreTint); |
|
float shoreOpac = fCompPB->GetFloat(kShoreOpac) * kPercentToFrac; |
|
ws.fMinColor = hsColorRGBA().Set(shoreTint.r, shoreTint.g, shoreTint.b, shoreOpac); |
|
|
|
// Not really used. |
|
ws.fShoreTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); |
|
|
|
ws.fEdgeOpac = fCompPB->GetFloat(kEdgeOpac) * kPercentToFrac; |
|
ws.fEdgeRadius = fCompPB->GetFloat(kEdgeRadius) * kPercentToFrac; |
|
|
|
fWaveSet->SetState(ws, 0.f); |
|
|
|
// Add a ref to the waveset. |
|
fWaveSet->GetKey()->RefObject(); |
|
|
|
return true; |
|
} |
|
|
|
hsScalar plWaterComponent::IGetWaterHeight() |
|
{ |
|
plMaxNodeBase* node = nil; |
|
|
|
int i; |
|
for( i = 0; i < NumTargets(); i++ ) |
|
{ |
|
node = GetTarget(i); |
|
if( node ) |
|
break; |
|
} |
|
|
|
plFixedWaterState7 ws; |
|
IReadRefObject(node, ws); |
|
|
|
return ws.fWaterHeight; |
|
} |
|
|
|
hsScalar plWaterComponent::GetWaterHeight(INode* node) |
|
{ |
|
if( !node ) |
|
return 0.f; |
|
|
|
plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); |
|
if( !comp ) |
|
return 0.f; |
|
|
|
if( comp->ClassID() != WATER_COMP_CID ) |
|
return 0.f; |
|
|
|
plWaterComponent* water = (plWaterComponent*)comp; |
|
return water->IGetWaterHeight(); |
|
} |
|
|
|
|
|
plWaveSetBase* plWaterComponent::GetWaveSet(INode* node) |
|
{ |
|
if( !node ) |
|
return nil; |
|
|
|
plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); |
|
if( !comp ) |
|
return nil; |
|
|
|
if( comp->ClassID() != WATER_COMP_CID ) |
|
return nil; |
|
|
|
plWaterComponent* water = (plWaterComponent*)comp; |
|
return water->IGetWaveSet(); |
|
} |
|
|
|
plWaveSetBase* plWaterComponent::GetWaveSetFromNode(plMaxNode* node) |
|
{ |
|
if( !node ) |
|
return nil; |
|
|
|
int n = node->NumAttachedComponents(); |
|
int i; |
|
for( i = 0; i < n; i++ ) |
|
{ |
|
plComponentBase* comp = node->GetAttachedComponent(i); |
|
if( comp && (comp->ClassID() == WATER_COMP_CID) ) |
|
{ |
|
plWaterComponent* water = (plWaterComponent*)comp; |
|
return water->IGetWaveSet(); |
|
} |
|
} |
|
return nil; |
|
} |
|
|
|
static void ISetWaterDependencies(plMaxNode* node, INode* waterNode) |
|
{ |
|
if( !waterNode ) |
|
return; |
|
|
|
plComponentBase *comp = ((plMaxNodeBase*)waterNode)->ConvertToComponent(); |
|
if( !comp ) |
|
return; |
|
|
|
if( comp->ClassID() != WATER_COMP_CID ) |
|
return; |
|
|
|
INodeTab nodeList; |
|
comp->AddTargetsToList(nodeList); |
|
int i; |
|
for( i = 0; i < nodeList.Count(); i++ ) |
|
{ |
|
if( nodeList[i] ) |
|
node->AddRenderDependency((plMaxNodeBase*)nodeList[i]); |
|
} |
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
class plShoreCompSelProc : public ParamMap2UserDlgProc |
|
{ |
|
public: |
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); |
|
void DeleteThis() { } |
|
}; |
|
|
|
#include "plPickNode.h" |
|
|
|
BOOL plShoreCompSelProc::DlgProc(TimeValue t, IParamMap2 *paramMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
IParamBlock2 *pb = paramMap->GetParamBlock(); |
|
INode* node = pb->GetINode(plShoreComponent::kWaveSet); |
|
TSTR newName(node ? node->GetName() : "Pick"); |
|
::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_SHORE_CHOSE), newName); |
|
} |
|
return true; |
|
|
|
case WM_COMMAND: |
|
if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_SHORE_CHOSE) ) |
|
{ |
|
IParamBlock2 *pb = paramMap->GetParamBlock(); |
|
std::vector<Class_ID> cids; |
|
cids.push_back(WATER_COMP_CID); |
|
if( plPick::Node(pb, plShoreComponent::kWaveSet, &cids, true, true) ) |
|
{ |
|
INode* node = pb->GetINode(plShoreComponent::kWaveSet); |
|
TSTR newName(node ? node->GetName() : "Pick"); |
|
::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_SHORE_CHOSE), newName); |
|
paramMap->Invalidate(plShoreComponent::kWaveSet); |
|
ShowWindow(hWnd, SW_HIDE); |
|
ShowWindow(hWnd, SW_SHOW); |
|
} |
|
|
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
plShoreCompSelProc gShoreCompSelProc; |
|
|
|
CLASS_DESC(plShoreComponent, gShoreCompDesc, "Shore Line", "Shore", COMP_TYPE_WATER, SHORE_COMP_CID) |
|
|
|
|
|
|
|
ParamBlockDesc2 gShoreCompBk |
|
( |
|
plComponent::kBlkComp, _T("Shore"), 0, &gShoreCompDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, |
|
|
|
IDD_COMP_SHORE, IDS_COMP_SHORE, 0, 0, &gShoreCompSelProc, |
|
|
|
plShoreComponent::kWaveSet, _T("WaveSet"), TYPE_INODE, 0, 0, |
|
end, |
|
|
|
end |
|
); |
|
|
|
plShoreComponent::plShoreComponent() |
|
{ |
|
fClassDesc = &gShoreCompDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading |
|
// of properties on the MaxNode, as it's still indeterminant. |
|
hsBool plShoreComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
node->SetRunTimeLight(true); |
|
node->SetForceMaterialCopy(true); |
|
node->SetForceShadow(false); |
|
node->SetNoShadow(true); |
|
node->SetSmoothAll(true); |
|
node->SetForceSortable(true); |
|
node->SetNoSpanSort(true); |
|
node->SetNoPreShade(true); |
|
|
|
ISetWaterDependencies(node, fCompPB->GetINode(kWaveSet, 0, 0)); |
|
|
|
// Turn on calculation of edge lengths XXX |
|
node->SetCalcEdgeLens(true); |
|
|
|
// Make a note that we're vertex shaded (at least ripplecomponent needs to know).XXX |
|
node->SetVS(true); |
|
|
|
// Turn off the convexity test. We want to be sorted.XXX |
|
node->SetConcave(true); |
|
|
|
node->SetReverseSort(true); |
|
|
|
node->SetWaterHeight(plWaterComponent::GetWaterHeight(fCompPB->GetINode(kWaveSet, 0, 0))); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plShoreComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
pErrMsg->Set(!node->HasLoadMask(), node->GetName(), "PS shore has no representation component").CheckAndAsk(); |
|
pErrMsg->Set(false); |
|
|
|
|
|
return true; |
|
} |
|
|
|
hsBool plShoreComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
plWaveSetBase* waveSet = plWaterComponent::GetWaveSet(fCompPB->GetINode(kWaveSet, 0, 0)); |
|
if( waveSet ) |
|
{ |
|
plSceneObject* obj = node->GetSceneObject(); |
|
if( obj ) |
|
{ |
|
waveSet->AddShore(obj->GetKey()); |
|
} |
|
else |
|
{ |
|
pErrMsg->Set(true, node->GetName(), "Invalid object selected for shore. Ignoring").CheckAndAsk(); |
|
pErrMsg->Set(false); |
|
} |
|
} |
|
else |
|
{ |
|
pErrMsg->Set(true, node->GetName(), "No Water Component selected for shore. Ignoring").CheckAndAsk(); |
|
pErrMsg->Set(false); |
|
} |
|
return true; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
class plWDecalCompSelProc : public ParamMap2UserDlgProc |
|
{ |
|
public: |
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); |
|
void DeleteThis() { } |
|
}; |
|
|
|
#include "plPickNode.h" |
|
|
|
BOOL plWDecalCompSelProc::DlgProc(TimeValue t, IParamMap2 *paramMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
IParamBlock2 *pb = paramMap->GetParamBlock(); |
|
INode* node = pb->GetINode(plWDecalComponent::kWaveSet); |
|
TSTR newName(node ? node->GetName() : "Pick"); |
|
::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_WDECAL_CHOSE), newName); |
|
} |
|
return true; |
|
|
|
case WM_COMMAND: |
|
if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_WDECAL_CHOSE) ) |
|
{ |
|
IParamBlock2 *pb = paramMap->GetParamBlock(); |
|
std::vector<Class_ID> cids; |
|
cids.push_back(WATER_COMP_CID); |
|
if( plPick::Node(pb, plWDecalComponent::kWaveSet, &cids, true, true) ) |
|
{ |
|
INode* node = pb->GetINode(plWDecalComponent::kWaveSet); |
|
TSTR newName(node ? node->GetName() : "Pick"); |
|
::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_WDECAL_CHOSE), newName); |
|
paramMap->Invalidate(plWDecalComponent::kWaveSet); |
|
ShowWindow(hWnd, SW_HIDE); |
|
ShowWindow(hWnd, SW_SHOW); |
|
} |
|
|
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
plWDecalCompSelProc gWDecalCompSelProc; |
|
|
|
CLASS_DESC(plWDecalComponent, gWDecalCompDesc, "Water Decal", "WDecal", COMP_TYPE_WATER, WDECAL_COMP_CID) |
|
|
|
|
|
|
|
ParamBlockDesc2 gWDecalCompBk |
|
( |
|
plComponent::kBlkComp, _T("WDecal"), 0, &gWDecalCompDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, |
|
|
|
IDD_COMP_WDECAL, IDS_COMP_WDECAL, 0, 0, &gWDecalCompSelProc, |
|
|
|
plWDecalComponent::kWaveSet, _T("WaveSet"), TYPE_INODE, 0, 0, |
|
end, |
|
|
|
plWDecalComponent::kEnv, _T("Env"), TYPE_BOOL, 0, 0, |
|
p_default, FALSE, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_WDECAL_ENV, |
|
end, |
|
|
|
end |
|
); |
|
|
|
plWDecalComponent::plWDecalComponent() |
|
{ |
|
fClassDesc = &gWDecalCompDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading |
|
// of properties on the MaxNode, as it's still indeterminant. |
|
hsBool plWDecalComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
node->SetRunTimeLight(true); |
|
node->SetForceMaterialCopy(true); |
|
node->SetForceShadow(false); |
|
node->SetNoShadow(true); |
|
node->SetSmoothAll(true); |
|
node->SetForceSortable(true); |
|
node->SetNoSpanSort(true); |
|
node->SetNoPreShade(true); |
|
|
|
// This should be optional. |
|
if( fCompPB->GetInt(kEnv) ) |
|
node->SetWaterDecEnv(true); |
|
|
|
ISetWaterDependencies(node, fCompPB->GetINode(kWaveSet, 0, 0)); |
|
|
|
// Turn on calculation of edge lengths XXX |
|
node->SetCalcEdgeLens(true); |
|
|
|
// Make a note that we're vertex shaded (at least ripplecomponent needs to know).XXX |
|
node->SetVS(true); |
|
|
|
// Turn off the convexity test. We want to be sorted.XXX |
|
node->SetConcave(true); |
|
|
|
node->SetReverseSort(true); |
|
|
|
node->SetWaterHeight(plWaterComponent::GetWaterHeight(fCompPB->GetINode(kWaveSet, 0, 0))); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plWDecalComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
pErrMsg->Set(!node->HasLoadMask(), node->GetName(), "PS water decal has no representation component").CheckAndAsk(); |
|
pErrMsg->Set(false); |
|
|
|
|
|
return true; |
|
} |
|
|
|
hsBool plWDecalComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
plWaveSetBase* waveSet = plWaterComponent::GetWaveSet(fCompPB->GetINode(kWaveSet, 0, 0)); |
|
if( waveSet ) |
|
{ |
|
plSceneObject* obj = node->GetSceneObject(); |
|
if( obj ) |
|
{ |
|
waveSet->AddDecal(obj->GetKey()); |
|
} |
|
else |
|
{ |
|
pErrMsg->Set(true, node->GetName(), "Invalid object selected for decal. Ignoring").CheckAndAsk(); |
|
pErrMsg->Set(false); |
|
} |
|
} |
|
else |
|
{ |
|
pErrMsg->Set(true, node->GetName(), "No Water Component selected for decal. Ignoring").CheckAndAsk(); |
|
pErrMsg->Set(false); |
|
} |
|
return true; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
class plEnvMapCompSelProc : public ParamMap2UserDlgProc |
|
{ |
|
public: |
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
for(int i = 0; i < map->GetParamBlock()->Count(plEnvMapComponent::kVisSetNames); i++ ) |
|
{ |
|
HWND hList = GetDlgItem(hWnd, IDC_COMP_ENVMAP_NAMES_LISTBOX); |
|
ListBox_AddString(hList, map->GetParamBlock()->GetStr(plEnvMapComponent::kVisSetNames, 0, i)); |
|
} |
|
} |
|
return true; |
|
|
|
case WM_COMMAND: |
|
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_TARGS) |
|
{ |
|
std::vector<Class_ID> cids; |
|
cids.push_back(EFFVISSET_CID); |
|
IParamBlock2 *pb = map->GetParamBlock(); |
|
plPick::Node(pb, plEnvMapComponent::kVisSets, &cids, false, false); |
|
|
|
map->Invalidate(plEnvMapComponent::kVisSets); |
|
return TRUE; |
|
} |
|
else if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ENVMAP_ADD_STRING) |
|
{ |
|
char str[256]; |
|
char *pStr = str; |
|
ICustEdit *custEdit = GetICustEdit(GetDlgItem(hWnd, IDC_COMP_ENVMAP_ADD_STRING_BOX)); |
|
custEdit->GetText(str, 256); |
|
custEdit->SetText(""); // clear text box |
|
|
|
if(!strcmp(str, "")) // don't allow empty strings |
|
return TRUE; |
|
|
|
HWND hList = GetDlgItem(hWnd, IDC_COMP_ENVMAP_NAMES_LISTBOX); |
|
ListBox_AddString(hList, pStr); |
|
map->GetParamBlock()->Append(plEnvMapComponent::kVisSetNames, 1, &pStr); |
|
return TRUE; |
|
} |
|
else if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ENVMAP_REMOVE_STRING) |
|
{ |
|
HWND hList = GetDlgItem(hWnd, IDC_COMP_ENVMAP_NAMES_LISTBOX); |
|
int curSel = ((int)(DWORD)SNDMSG((hList), LB_GETCURSEL, 0L, 0L)); |
|
ListBox_DeleteString(hList, curSel); |
|
if (curSel >= 0) |
|
map->GetParamBlock()->Delete(ParamID(plEnvMapComponent::kVisSetNames), curSel, 1); |
|
|
|
return TRUE; |
|
} |
|
break; |
|
} |
|
|
|
return false; |
|
} |
|
void DeleteThis() {} |
|
}; |
|
static plEnvMapCompSelProc gEnvMapCompSelProc; |
|
|
|
CLASS_DESC(plEnvMapComponent, gEnvMapCompDesc, "Environment Map", "EnvMap", COMP_TYPE_WATER, ENVMAP_COMP_CID) |
|
|
|
|
|
|
|
ParamBlockDesc2 gEnvMapCompBk |
|
( |
|
plComponent::kBlkComp, _T("EnvMap"), 0, &gEnvMapCompDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, |
|
|
|
IDD_COMP_ENVMAP, IDS_COMP_ENVMAP, 0, 0, &gEnvMapCompSelProc, |
|
|
|
plEnvMapComponent::kVisSets, _T("VisSets"), TYPE_INODE_TAB, 0, 0, 0, |
|
p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, |
|
end, |
|
|
|
plEnvMapComponent::kHither, _T("Hither"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.0, 500.0, |
|
// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
// IDC_COMP_ENVMAP_HITHER, IDC_COMP_ENVMAP_HITHER_SPIN, 1.0, |
|
end, |
|
|
|
plEnvMapComponent::kYon, _T("Yon"), TYPE_FLOAT, 0, 0, |
|
p_default, 1000.0, |
|
p_range, 10.0, 50000.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ENVMAP_YON, IDC_COMP_ENVMAP_YON_SPIN, 1.0, |
|
end, |
|
|
|
plEnvMapComponent::kFogEnable, _T("FogEnable"), TYPE_BOOL, 0, 0, |
|
p_default, FALSE, |
|
// p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ENVMAP_FOGENABLE, |
|
// p_enable_ctrls, 2, plEnvMapComponent::kFogStart, plEnvMapComponent::kFogColor, |
|
end, |
|
|
|
plEnvMapComponent::kFogStart, _T("FogStart"), TYPE_FLOAT, 0, 0, |
|
p_default, 0.0, |
|
p_range, 0.0, 200.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ENVMAP_FOGSTART, IDC_COMP_ENVMAP_FOGSTART_SPIN, 1.0, |
|
end, |
|
|
|
|
|
plEnvMapComponent::kFogColor, _T("FogColor"), TYPE_RGBA, 0, 0, |
|
p_ui, TYPE_COLORSWATCH, IDC_COMP_ENVMAP_FOGCOLOR, |
|
p_default, Color(0,0,0), |
|
end, |
|
|
|
plEnvMapComponent::kRefreshRate, _T("RefreshRate"), TYPE_FLOAT, 0, 0, |
|
p_default, 0.0, |
|
p_range, 0.0, 3600.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ENVMAP_REFRESHRATE, IDC_COMP_ENVMAP_REFRESHRATE_SPIN, 1.0, |
|
end, |
|
|
|
plEnvMapComponent::kEnvSize, _T("EnvSize"), TYPE_INT, 0, 0, |
|
p_default, 256, |
|
p_range, 32, 1024, |
|
p_ui, TYPE_SPINNER, EDITTYPE_INT, |
|
IDC_COMP_ENVMAP_ENVSIZE, IDC_COMP_ENVMAP_ENVSIZE_SPIN, 1.f, |
|
end, |
|
|
|
plEnvMapComponent::kIncChars, _T("IncChars"), TYPE_BOOL, 0, 0, |
|
p_default, FALSE, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ENVMAP_INCCHARS, |
|
end, |
|
|
|
plEnvMapComponent::kMapType, _T("mapType"), TYPE_INT, 0, 0, |
|
p_ui, TYPE_RADIO, 2, IDC_COMP_ENVMAP_CUBIC, IDC_COMP_ENVMAP_SINGLE_CAM, |
|
p_vals, plEnvMapComponent::kMapCubic, plEnvMapComponent::kMapSingle, |
|
p_default, plEnvMapComponent::kMapCubic, |
|
end, |
|
|
|
plEnvMapComponent::kVisSetNames, _T("VisSetNames"), TYPE_STRING_TAB, 0, 0, 0, |
|
end, |
|
|
|
end |
|
); |
|
|
|
plEnvMapComponent::plEnvMapComponent() |
|
{ |
|
fClassDesc = &gEnvMapCompDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading |
|
// of properties on the MaxNode, as it's still indeterminant. |
|
hsBool plEnvMapComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
fMap = nil; |
|
|
|
node->SetForceLocal(true); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plEnvMapComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
|
|
return true; |
|
} |
|
|
|
hsBool plEnvMapComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) |
|
{ |
|
// If we make a handler that will update the EnvMap's position when this object |
|
// moves, we can put it's creation here. Otherwise, there's nought to do, since |
|
// we generate the envmap on demand. |
|
return true; |
|
} |
|
|
|
plDynamicEnvMap* plEnvMapComponent::GetEnvMap() |
|
{ |
|
return plDynamicEnvMap::ConvertNoRef(IGetMap()); |
|
} |
|
|
|
plDynamicCamMap* plEnvMapComponent::GetCamMap() |
|
{ |
|
return plDynamicCamMap::ConvertNoRef(IGetMap()); |
|
} |
|
|
|
plRenderTarget* plEnvMapComponent::IGetMap() |
|
{ |
|
plMaxNode* firstTarg = nil; |
|
int numTarg = NumTargets(); |
|
int i; |
|
for( i = 0; i < numTarg; i++ ) |
|
{ |
|
if( GetTarget(i) ) |
|
{ |
|
firstTarg = (plMaxNode*)GetTarget(i); |
|
break; |
|
} |
|
} |
|
if( !firstTarg ) |
|
return nil; |
|
|
|
if( !fMap ) |
|
{ |
|
UInt32 size = fCompPB->GetInt(kEnvSize); |
|
for( i = 9; i > 5; i-- ) |
|
{ |
|
if( (1UL << UInt32(i)) <= size ) |
|
break; |
|
} |
|
size = 1 << UInt32(i); |
|
|
|
plDynamicEnvMap* env = nil; |
|
plDynamicCamMap* cam = nil; |
|
fMap = nil; |
|
if (fCompPB->GetInt((ParamID(kMapType))) == kMapCubic) |
|
fMap = env = TRACKED_NEW plDynamicEnvMap(size, size, 32); |
|
else if (fCompPB->GetInt((ParamID(kMapType))) == kMapSingle) |
|
fMap = cam = TRACKED_NEW plDynamicCamMap(size, size, 32); |
|
|
|
// Need to assign the key before we call all the setup functions. |
|
hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fMap, firstTarg->GetLocation(), firstTarg->GetLoadMask()); |
|
|
|
|
|
if (fCompPB->GetInt((ParamID(kMapType))) == kMapCubic) |
|
{ |
|
Point3 pos = firstTarg->GetNodeTM(TimeValue(0)).GetTrans(); |
|
env->SetPosition(hsPoint3(pos.x, pos.y, pos.z)); |
|
env->SetRefreshRate(fCompPB->GetFloat(kRefreshRate)); |
|
env->SetHither(fCompPB->GetFloat(kHither)); |
|
env->SetYon(fCompPB->GetFloat(kYon)); |
|
env->SetFogStart(fCompPB->GetFloat(kFogStart) * kPercentToFrac); |
|
Color fogColor = fCompPB->GetColor(kFogColor); |
|
env->SetColor(hsColorRGBA().Set(fogColor.r, fogColor.g, fogColor.b, 1.f)); |
|
} |
|
else if (fCompPB->GetInt((ParamID(kMapType))) == kMapSingle) |
|
{ |
|
cam->SetRefreshRate(fCompPB->GetFloat(ParamID(kRefreshRate))); |
|
cam->fHither = fCompPB->GetFloat(ParamID(kHither)); |
|
cam->fYon = fCompPB->GetFloat(ParamID(kYon)); |
|
cam->fFogStart = fCompPB->GetFloat(ParamID(kFogStart)) * kPercentToFrac; |
|
Color fogColor = fCompPB->GetColor(kFogColor); |
|
cam->fColor.Set(fogColor.r, fogColor.g, fogColor.b, 1.f); |
|
} |
|
if (!fMap) |
|
return nil; |
|
|
|
int visGot = 0; |
|
int numVis = fCompPB->Count(kVisSets); |
|
for( i = 0; i < numVis; i++ ) |
|
{ |
|
plEffVisSetComponent* effComp = plEffVisSetComponent::ConvertToEffVisSetComponent((plMaxNode*)fCompPB->GetINode(kVisSets, 0, i)); |
|
if( effComp ) |
|
{ |
|
plVisRegion* effReg = effComp->GetVisRegion(firstTarg); |
|
if( effReg ) |
|
{ |
|
plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fMap->GetKey(), plRefMsg::kOnCreate, -1, plDynamicEnvMap::kRefVisSet); |
|
hsgResMgr::ResMgr()->SendRef(effReg->GetKey(), refMsg, plRefFlags::kPassiveRef); |
|
|
|
visGot++; |
|
} |
|
} |
|
} |
|
|
|
// This allows you to enter the name of an effect vis set(key name), from another max file and use it |
|
// as if it we're in the same max file. |
|
int numVisNames = fCompPB->Count(kVisSetNames); |
|
for( i = 0; i < numVisNames; i++) |
|
{ |
|
fMap->SetVisRegionName((char*)fCompPB->GetStr(kVisSetNames, 0, i)); |
|
} |
|
|
|
if (visGot) |
|
{ |
|
if (env) |
|
env->SetIncludeCharacters(fCompPB->GetInt(ParamID(kIncChars)) != 0); |
|
if (cam) |
|
cam->SetIncludeCharacters(fCompPB->GetInt(ParamID(kIncChars)) != 0); |
|
} |
|
|
|
// Right now, the envMap doesn't use this, but I plan to make it do so, so I'm |
|
// going ahead and adding the ref regardless of which type of map we made. |
|
UInt8 refType = cam ? plDynamicCamMap::kRefRootNode : plDynamicEnvMap::kRefRootNode; |
|
hsgResMgr::ResMgr()->AddViaNotify(firstTarg->GetSceneObject()->GetKey(), TRACKED_NEW plGenRefMsg(fMap->GetKey(), plRefMsg::kOnCreate, -1, refType), plRefFlags::kPassiveRef); |
|
} |
|
return fMap; |
|
} |
|
|
|
plDynamicEnvMap* plEnvMapComponent::GetEnvMap(plMaxNode* node) |
|
{ |
|
plEnvMapComponent* envComp = GetEnvMapComponent(node); |
|
if (envComp) |
|
return envComp->GetEnvMap(); |
|
|
|
return nil; |
|
} |
|
|
|
plDynamicCamMap* plEnvMapComponent::GetCamMap(plMaxNode *node) |
|
{ |
|
plEnvMapComponent *envComp = GetEnvMapComponent(node); |
|
if (envComp) |
|
return envComp->GetCamMap(); |
|
|
|
return nil; |
|
} |
|
|
|
plEnvMapComponent *plEnvMapComponent::GetEnvMapComponent(plMaxNode *node) |
|
{ |
|
if (!node) |
|
return nil; |
|
|
|
int n = node->NumAttachedComponents(); |
|
int i; |
|
for (i = 0; i < n; i++) |
|
{ |
|
plComponentBase *comp = node->GetAttachedComponent(i); |
|
if (comp && (comp->ClassID() == ENVMAP_COMP_CID)) |
|
{ |
|
return (plEnvMapComponent*)comp; |
|
} |
|
} |
|
return nil; |
|
}
|
|
|