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.
4002 lines
125 KiB
4002 lines
125 KiB
4 years ago
|
/*==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 "plComponentProcBase.h"
|
||
|
|
||
|
#include "resource.h"
|
||
|
#include "plComponent.h"
|
||
|
#include "plComponentReg.h"
|
||
|
#include <map>
|
||
|
#include "plAudioComponents.h"
|
||
|
#include "plMiscComponents.h"
|
||
|
#include "plAnimComponent.h"
|
||
|
#include "../plInterp/plAnimEaseTypes.h"
|
||
|
#include "../plAvatar/plAGAnim.h"
|
||
|
|
||
|
#include "../pnSceneObject/plSceneObject.h"
|
||
|
#include "../pnSceneObject/plCoordinateInterface.h"
|
||
|
|
||
|
#include "../MaxConvert/plConvert.h"
|
||
|
#include "../MaxMain/plPluginResManager.h"
|
||
|
#include "../MaxMain/plPlasmaRefMsgs.h"
|
||
|
|
||
|
|
||
|
#include "plgDispatch.h"
|
||
|
#include "../pnMessage/plObjRefMsg.h"
|
||
|
#include "../pnMessage/plIntRefMsg.h"
|
||
|
#include "../pnMessage/plNodeRefMsg.h"
|
||
|
|
||
|
|
||
|
#include "../plScene/plSceneNode.h"
|
||
|
#include "../MaxConvert/hsConverterUtils.h"
|
||
|
#include "../MaxConvert/hsControlConverter.h"
|
||
|
#include "../plInterp/plController.h"
|
||
|
#include "../MaxMain/plMaxNode.h"
|
||
|
#include "../pnKeyedObject/plKey.h"
|
||
|
|
||
|
//Physics Related
|
||
|
//#include "../plHavok1/plHKPhysical.h" //Physics Comp
|
||
|
#include "../pnSceneObject/plSimulationInterface.h"
|
||
|
#include "../MaxMain/plPhysicalProps.h"
|
||
|
#include "../plPhysX/plPXPhysical.h"
|
||
|
|
||
|
// Sound Related
|
||
|
#include "../plPhysical/plEnvEffectDetector.h"
|
||
|
#include "../pnMessage/plEnvEffectMsg.h"
|
||
|
#include "../PubUtilLib/plAudible/plWinAudible.h"
|
||
|
#include "../pnSceneObject/plAudioInterface.h"
|
||
|
|
||
|
// Anim Related
|
||
|
#include "plMaxAnimUtils.h"
|
||
|
#include "plMaxWaveUtils.h"
|
||
|
#include "../pfAudio/plRandomSoundMod.h"
|
||
|
#include "../plAudio/plWin32StaticSound.h"
|
||
|
#include "../plAudio/plWin32StreamingSound.h"
|
||
|
#include "../plAudio/plWin32GroupedSound.h"
|
||
|
#include "../plAudioCore/plSoundBuffer.h"
|
||
|
#include "../plFile/plFileUtils.h"
|
||
|
|
||
|
// Valdez Asset Manager Related
|
||
|
#include "../../AssetMan/PublicInterface/MaxAssInterface.h"
|
||
|
#include <shlwapi.h>
|
||
|
|
||
|
// Fun soft volume stuff
|
||
|
#include "plSoftVolumeComponent.h"
|
||
|
#include "../plIntersect/plSoftVolume.h"
|
||
|
|
||
|
// Misc
|
||
|
#include "../MaxMain/plMaxCFGFile.h"
|
||
|
#include "plPickNode.h"
|
||
|
|
||
|
// EAX stuff
|
||
|
#include "../plAudio/plEAXListenerMod.h"
|
||
|
#include <eax-util.h>
|
||
|
#include <eaxlegacy.h>
|
||
|
|
||
|
#include "../plResMgr/plLocalization.h"
|
||
|
#include "../plPhysical/plPhysicalSndGroup.h"
|
||
|
|
||
|
// EAX3 values which eax4 no longer defines, but we still need.
|
||
|
// Single window material preset
|
||
|
#define EAX_MATERIAL_SINGLEWINDOW (-2800)
|
||
|
#define EAX_MATERIAL_SINGLEWINDOWLF 0.71f
|
||
|
#define EAX_MATERIAL_SINGLEWINDOWROOMRATIO 0.43f
|
||
|
|
||
|
// Double window material preset
|
||
|
#define EAX_MATERIAL_DOUBLEWINDOW (-5000)
|
||
|
#define EAX_MATERIAL_DOUBLEWINDOWLF 0.40f
|
||
|
#define EAX_MATERIAL_DOUBLEWINDOWROOMRATIO 0.24f
|
||
|
|
||
|
// Thin door material preset
|
||
|
#define EAX_MATERIAL_THINDOOR (-1800)
|
||
|
#define EAX_MATERIAL_THINDOORLF 0.66f
|
||
|
#define EAX_MATERIAL_THINDOORROOMRATIO 0.66f
|
||
|
|
||
|
// Thick door material preset
|
||
|
#define EAX_MATERIAL_THICKDOOR (-4400)
|
||
|
#define EAX_MATERIAL_THICKDOORLF 0.64f
|
||
|
#define EAX_MATERIAL_THICKDOORROOMRATIO 0.27f
|
||
|
|
||
|
// Wood wall material preset
|
||
|
#define EAX_MATERIAL_WOODWALL (-4000)
|
||
|
#define EAX_MATERIAL_WOODWALLLF 0.50f
|
||
|
#define EAX_MATERIAL_WOODWALLROOMRATIO 0.30f
|
||
|
|
||
|
// Brick wall material preset
|
||
|
#define EAX_MATERIAL_BRICKWALL (-5000)
|
||
|
#define EAX_MATERIAL_BRICKWALLLF 0.60f
|
||
|
#define EAX_MATERIAL_BRICKWALLROOMRATIO 0.24f
|
||
|
|
||
|
// Stone wall material preset
|
||
|
#define EAX_MATERIAL_STONEWALL (-6000)
|
||
|
#define EAX_MATERIAL_STONEWALLLF 0.68f
|
||
|
#define EAX_MATERIAL_STONEWALLROOMRATIO 0.20f
|
||
|
|
||
|
// Curtain material preset
|
||
|
#define EAX_MATERIAL_CURTAIN (-1200)
|
||
|
#define EAX_MATERIAL_CURTAINLF 0.15f
|
||
|
#define EAX_MATERIAL_CURTAINROOMRATIO 1.00f
|
||
|
|
||
|
void DummyCodeIncludeFuncAudio() {}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
/// Base Sound Emitter Component ////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kSoundFileName,
|
||
|
kLoopingChekBx_DEAD, //Removed in v3
|
||
|
kLoopBegin_DEAD, //Removed in v2
|
||
|
kLoopEnd_DEAD, //Removed in v2
|
||
|
kMinFallOffRad_DEAD, // removed in v6
|
||
|
kMaxFallOffRad_DEAD, // removed in v6
|
||
|
kSoundAutoStartCkBx,
|
||
|
kSoundLoopCkBx,
|
||
|
kSoundLoopSegCkBx_DEAD, //Removed in v3
|
||
|
kSoundLoopSegBeg_DEAD,
|
||
|
kSoundLoopSegEnd_DEAD,
|
||
|
kSoundLoopSegBegDDList_DEAD, //Inserted in v3
|
||
|
kSoundLoopSegEndDDList_DEAD, //Inserted in v3
|
||
|
kSoundLoopSegBeg2_DEAD, //Inserted in v3
|
||
|
kSoundLoopSegEnd2_DEAD, //Inserted in v3
|
||
|
kSFileNameTextField,
|
||
|
kOldSoundVolumeSlider, //Inserted in v4 OBLITERATE
|
||
|
kSoundIConeAngle, //Inserted in v5
|
||
|
kSoundOConeAngle, //Inserted in v5
|
||
|
kSoundOConeVolumeSlider, //Inserted in v5
|
||
|
kMinFallOffRad,
|
||
|
kMaxFallOffRad,
|
||
|
kSoundLoopName,
|
||
|
kSoundConeBool, //Inserted in v6,
|
||
|
kNotSoOldSoundVolumeSlider,
|
||
|
kSndFadeInEnable,
|
||
|
kSndFadeInType,
|
||
|
kSndFadeInLength,
|
||
|
kSndFadeOutEnable,
|
||
|
kSndFadeOutType,
|
||
|
kSndFadeOutLength,
|
||
|
kSndFadedVolume, // Currently unsupported
|
||
|
kSndSoftRegion,
|
||
|
kSndSoftRegionEnable,
|
||
|
kSndVersionCount, // So we can do version upgrading (DAMN YOU MAX!!!)
|
||
|
kSoundVolumeSlider,
|
||
|
kSndDisableLOD,
|
||
|
kSndChannelSelect,
|
||
|
kSndAllowChannelSelect,
|
||
|
kSndIsWMAFile_DEAD,
|
||
|
kSndWMAStartClip_DEAD,
|
||
|
kSndWMAEndClip_DEAD,
|
||
|
kSndEnableCrossfadeCover_DEAD,
|
||
|
kSndCrossfadeCoverFilename_DEAD,
|
||
|
kSndCoverIsWMAFile_DEAD,
|
||
|
kSndCoverWMAStartClip_DEAD,
|
||
|
kSndCoverWMAEndClip_DEAD,
|
||
|
kSndIsLocalOnly,
|
||
|
kSndCategory,
|
||
|
kSndPriority,
|
||
|
kSndIncidental,
|
||
|
kSndStreamCompressed,
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kSndFadeTypeLinear,
|
||
|
kSndFadeTypeLogarithmic,
|
||
|
kSndFadeTypeExponential
|
||
|
};
|
||
|
|
||
|
UInt32 plBaseSoundEmitterComponent::fWarningFlags = 0;
|
||
|
//bool plBaseSoundEmitterComponent::fAllowUnhide = false;
|
||
|
|
||
|
void plBaseSoundEmitterComponent::IShowError( UInt32 type, const char *errMsg, const char *nodeName, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
if( !( fWarningFlags & (1 << type) ) )
|
||
|
{
|
||
|
if( pErrMsg->Set( true, "Sound Component Error", errMsg, nodeName ).CheckAskOrCancel() )
|
||
|
fWarningFlags |= (1 << type);
|
||
|
pErrMsg->Set( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plBaseSoundEmitterComponent::plBaseSoundEmitterComponent()
|
||
|
{
|
||
|
fAllowUnhide = false;
|
||
|
fAssetsUpdated = false;
|
||
|
fCreateGrouped = false;
|
||
|
fIndices.clear();
|
||
|
fValidNodes.clear();
|
||
|
}
|
||
|
|
||
|
plBaseSoundEmitterComponent::~plBaseSoundEmitterComponent()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
RefTargetHandle plBaseSoundEmitterComponent::Clone( RemapDir &remap )
|
||
|
{
|
||
|
// Do the base clone
|
||
|
plBaseSoundEmitterComponent *obj = (plBaseSoundEmitterComponent *)plComponentBase::Clone( remap );
|
||
|
|
||
|
obj->fSoundAssetId = fSoundAssetId;
|
||
|
obj->fCoverSoundAssetID = fCoverSoundAssetID;
|
||
|
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
void plBaseSoundEmitterComponent::IConvertOldVolume( void )
|
||
|
{
|
||
|
int oldVol = fCompPB->GetInt( (ParamID)kOldSoundVolumeSlider, 0 );
|
||
|
if( oldVol != 4999 )
|
||
|
{
|
||
|
float v = (float)( oldVol - 5000 ) / 5000.f;
|
||
|
fCompPB->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, v );
|
||
|
fCompPB->SetValue( (ParamID)kOldSoundVolumeSlider, 0, 4999 );
|
||
|
}
|
||
|
|
||
|
// Shut up.
|
||
|
float notSoOldV = fCompPB->GetFloat( (ParamID)kNotSoOldSoundVolumeSlider, 0 );
|
||
|
if( notSoOldV != -1.f )
|
||
|
{
|
||
|
float d3dValueReally = -5000.f + ( 5000.f * notSoOldV );
|
||
|
|
||
|
float ourNewValue = (float)d3dValueReally / 100.f;
|
||
|
|
||
|
fCompPB->SetValue( (ParamID)kSoundVolumeSlider, 0, ourNewValue );
|
||
|
fCompPB->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, -1.f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float plBaseSoundEmitterComponent::IGetDigitalVolume( void ) const
|
||
|
{
|
||
|
return (float)pow( 10.f, fCompPB->GetFloat( (ParamID)kSoundVolumeSlider, 0 ) / 20.f );
|
||
|
}
|
||
|
|
||
|
#define OLD_MAX_ASS_CHUNK 0x5500
|
||
|
#define MAX_ASS_CHUNK 0x5501
|
||
|
|
||
|
IOResult plBaseSoundEmitterComponent::Save(ISave *isave)
|
||
|
{
|
||
|
IOResult res = plComponentBase::Save(isave);
|
||
|
if (res != IO_OK)
|
||
|
return res;
|
||
|
|
||
|
isave->BeginChunk(MAX_ASS_CHUNK);
|
||
|
ULONG nwrite;
|
||
|
|
||
|
UInt64 id = fSoundAssetId;
|
||
|
res = isave->Write(&id, sizeof(id), &nwrite);
|
||
|
if (res != IO_OK)
|
||
|
return res;
|
||
|
|
||
|
id = fCoverSoundAssetID;
|
||
|
res = isave->Write(&id, sizeof(id), &nwrite);
|
||
|
if (res != IO_OK)
|
||
|
return res;
|
||
|
|
||
|
isave->EndChunk();
|
||
|
|
||
|
return IO_OK;
|
||
|
}
|
||
|
|
||
|
IOResult plBaseSoundEmitterComponent::Load(ILoad *iload)
|
||
|
{
|
||
|
IOResult res = plComponentBase::Load(iload);
|
||
|
if (res != IO_OK)
|
||
|
return res;
|
||
|
|
||
|
while (IO_OK == (res = iload->OpenChunk()))
|
||
|
{
|
||
|
if (iload->CurChunkID() == OLD_MAX_ASS_CHUNK)
|
||
|
{
|
||
|
VARIANT tempVar;
|
||
|
ULONG nread;
|
||
|
res = iload->Read(&tempVar, sizeof(VARIANT), &nread);
|
||
|
fSoundAssetId = tempVar.decVal.Lo64;
|
||
|
}
|
||
|
// Secret AssMan value used for no good....
|
||
|
else if (iload->CurChunkID() == MAX_ASS_CHUNK)
|
||
|
{
|
||
|
ULONG nread;
|
||
|
UInt64 id;
|
||
|
res = iload->Read(&id, sizeof(id), &nread);
|
||
|
fSoundAssetId = id;
|
||
|
res = iload->Read(&id, sizeof(id), &nread);
|
||
|
fCoverSoundAssetID = id;
|
||
|
}
|
||
|
iload->CloseChunk();
|
||
|
if (res != IO_OK)
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
return IO_OK;
|
||
|
}
|
||
|
|
||
|
void plBaseSoundEmitterComponent::SetSoundAssetId( plBaseSoundEmitterComponent::WhichSound which, jvUniqueId assetId, const TCHAR *fileName )
|
||
|
{
|
||
|
if( which == kBaseSound )
|
||
|
{
|
||
|
fSoundAssetId = assetId;
|
||
|
|
||
|
fCompPB->SetValue( (ParamID)kSoundFileName, 0, (TCHAR *)fileName );
|
||
|
if( fCompPB->GetMap() )
|
||
|
fCompPB->GetMap()->Invalidate( (ParamID)kSoundFileName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hsAssert( false, "Setting a sound that isn't supported on this component" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
jvUniqueId plBaseSoundEmitterComponent::GetSoundAssetID( plBaseSoundEmitterComponent::WhichSound which )
|
||
|
{
|
||
|
if( which == kCoverSound )
|
||
|
return fCoverSoundAssetID;
|
||
|
else if( which == kBaseSound )
|
||
|
return fSoundAssetId;
|
||
|
|
||
|
hsAssert( false, "Getting a sound that isn't supported on this component" );
|
||
|
return fSoundAssetId;
|
||
|
}
|
||
|
|
||
|
void plBaseSoundEmitterComponent::IUpdateAssets( void )
|
||
|
{
|
||
|
if( fAssetsUpdated )
|
||
|
return;
|
||
|
|
||
|
if( !fSoundAssetId.IsEmpty() || !fCoverSoundAssetID.IsEmpty() )
|
||
|
{
|
||
|
MaxAssInterface *maxAssInterface = GetMaxAssInterface();
|
||
|
if( !maxAssInterface )
|
||
|
return;
|
||
|
|
||
|
// Download the latest version and retrieve the filename
|
||
|
char newfilename[ MAX_PATH ];
|
||
|
if(maxAssInterface->GetLatestVersionFile( fSoundAssetId, newfilename, MAX_PATH ) )
|
||
|
{
|
||
|
// AssetID overrides filename
|
||
|
fCompPB->SetValue( (ParamID)kSoundFileName, 0, newfilename );
|
||
|
}
|
||
|
|
||
|
fAssetsUpdated = true;
|
||
|
}
|
||
|
else
|
||
|
fAssetsUpdated = true;
|
||
|
}
|
||
|
|
||
|
TCHAR *plBaseSoundEmitterComponent::GetSoundFileName( plBaseSoundEmitterComponent::WhichSound which )
|
||
|
{
|
||
|
IUpdateAssets();
|
||
|
|
||
|
if( which == kBaseSound )
|
||
|
return fCompPB->GetStr( (ParamID)kSoundFileName );
|
||
|
|
||
|
hsAssert( false, "Getting a sound that isn't supported on this component" );
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
hsBool plBaseSoundEmitterComponent::DeInit( plMaxNode *node, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
fCreateGrouped = false;
|
||
|
fIndices.clear();
|
||
|
fValidNodes.clear();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool plBaseSoundEmitterComponent::SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
IConvertOldVolume();
|
||
|
/*
|
||
|
for (int i = 0; i < fIndices.Count(); i++)
|
||
|
delete(fIndices[i]);
|
||
|
fIndices.SetCountAndZero(0);
|
||
|
*/
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void plBaseSoundEmitterComponent::SetCreateGrouped( plMaxNode *baseNode, int commonSoundIdx )
|
||
|
{
|
||
|
fIndices[ baseNode ] = commonSoundIdx;
|
||
|
fCreateGrouped = true;
|
||
|
}
|
||
|
|
||
|
bool plBaseSoundEmitterComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
if( GetSoundFileName( kBaseSound ) == nil )
|
||
|
{
|
||
|
pErrMsg->Set(true, "Sound 3D FileName Error", "The Sound 3D component %s is missing a filename.", node->GetName()).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hsBool plBaseSoundEmitterComponent::PreConvert( plMaxNode *node, plErrorMsg *pErrMsg, Class_ID classToConvert )
|
||
|
{
|
||
|
const char* dbgNodeName = node->GetName();
|
||
|
fValidNodes[node] = IValidate(node, pErrMsg);
|
||
|
if (!fValidNodes[node])
|
||
|
return false;
|
||
|
|
||
|
node->SetForceLocal(true);
|
||
|
|
||
|
const plAudioInterface *ai = node->GetSceneObject()->GetAudioInterface();
|
||
|
if (!ai)
|
||
|
{
|
||
|
ai = TRACKED_NEW plAudioInterface;
|
||
|
plKey pAiKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), (hsKeyedObject*)ai,node->GetKey()->GetUoid().GetLocation(), node->GetLoadMask());
|
||
|
hsgResMgr::ResMgr()->AddViaNotify(pAiKey, TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef);
|
||
|
}
|
||
|
if (!ai->GetAudible())
|
||
|
{
|
||
|
plAudible *pAudible = TRACKED_NEW plWinAudible;
|
||
|
// Add a key for it
|
||
|
plKey key = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pAudible, node->GetKey()->GetUoid().GetLocation(), node->GetLoadMask());
|
||
|
|
||
|
plIntRefMsg* pMsg = TRACKED_NEW plIntRefMsg(node->GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kAudible);
|
||
|
hsgResMgr::ResMgr()->AddViaNotify(pAudible->GetKey(), pMsg, plRefFlags::kActiveRef );
|
||
|
|
||
|
pAudible->SetSceneNode(node->GetRoomKey());
|
||
|
}
|
||
|
|
||
|
if( !fCreateGrouped )
|
||
|
fIndices[ node ] = node->GetNextSoundIdx();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void plBaseSoundEmitterComponent::IGrabFadeValues( plSound *sound )
|
||
|
{
|
||
|
if( fCompPB->GetInt( (ParamID)kSndFadeInEnable, 0 ) != 0 )
|
||
|
{
|
||
|
// Fade in is enabled; set the params
|
||
|
plSound::plFadeParams::Type type;
|
||
|
|
||
|
hsScalar len = (hsScalar)fCompPB->GetFloat( (ParamID)kSndFadeInLength, 0 );
|
||
|
|
||
|
switch( fCompPB->GetInt( (ParamID)kSndFadeInType, 0 ) )
|
||
|
{
|
||
|
case kSndFadeTypeLinear: type = plSound::plFadeParams::kLinear; break;
|
||
|
case kSndFadeTypeLogarithmic: type = plSound::plFadeParams::kLogarithmic; break;
|
||
|
case kSndFadeTypeExponential: type = plSound::plFadeParams::kExponential; break;
|
||
|
}
|
||
|
|
||
|
sound->SetFadeInEffect( type, len );
|
||
|
}
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kSndFadeOutEnable, 0 ) != 0 )
|
||
|
{
|
||
|
// Fade out is enabled; set the params
|
||
|
plSound::plFadeParams::Type type;
|
||
|
|
||
|
hsScalar len = (hsScalar)fCompPB->GetFloat( (ParamID)kSndFadeOutLength, 0 );
|
||
|
|
||
|
switch( fCompPB->GetInt( (ParamID)kSndFadeOutType, 0 ) )
|
||
|
{
|
||
|
case kSndFadeTypeLinear: type = plSound::plFadeParams::kLinear; break;
|
||
|
case kSndFadeTypeLogarithmic: type = plSound::plFadeParams::kLogarithmic; break;
|
||
|
case kSndFadeTypeExponential: type = plSound::plFadeParams::kExponential; break;
|
||
|
}
|
||
|
|
||
|
sound->SetFadeOutEffect( type, len );
|
||
|
}
|
||
|
|
||
|
// sound->SetFadedVolume( (hsScalar)fCompPB->GetFloat( kSndFadedVolume, 0 ) );
|
||
|
}
|
||
|
|
||
|
void plBaseSoundEmitterComponent::IGrabSoftRegion( plSound *sound, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
// Do the soft volume, if there is one
|
||
|
if( fCompPB->GetInt( (ParamID)kSndSoftRegionEnable, 0 ) != 0 )
|
||
|
{
|
||
|
plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent( fCompPB->GetINode( (ParamID)kSndSoftRegion ) );
|
||
|
if( softComp != nil )
|
||
|
{
|
||
|
plKey softKey = softComp->GetSoftVolume();
|
||
|
if( softKey != nil )
|
||
|
{
|
||
|
// Make sure we set checkListener on the sucker
|
||
|
plSoftVolume *vol = plSoftVolume::ConvertNoRef( softKey->GetObjectPtr() );
|
||
|
if( vol != nil )
|
||
|
{
|
||
|
vol->SetCheckListener();
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( softKey, TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, 0, plSound::kSoftRegion ), plRefFlags::kActiveRef );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pErrMsg->Set(true, "Sound Emitter Error", "The Sound emitter component %s is checked to use a soft region, but no soft region is specified. Ignoring setting.", GetINode()->GetName() ).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UInt32 plBaseSoundEmitterComponent::ICalcSourceBufferFlags( void ) const
|
||
|
{
|
||
|
UInt32 bufferFlags = 0;
|
||
|
|
||
|
if( IHasWaveformProps() )
|
||
|
{
|
||
|
if( fCompPB->GetInt( (ParamID)kSndAllowChannelSelect ) )
|
||
|
{
|
||
|
if( fCompPB->GetInt( (ParamID)kSndChannelSelect ) )
|
||
|
bufferFlags = plSoundBuffer::kOnlyRightChannel;
|
||
|
else
|
||
|
bufferFlags = plSoundBuffer::kOnlyLeftChannel;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bufferFlags;
|
||
|
}
|
||
|
|
||
|
plSoundBuffer *plBaseSoundEmitterComponent::GetSourceBuffer( const char *fileName, plMaxNode *srcNode, UInt32 srcBufferFlags )
|
||
|
{
|
||
|
plSoundBuffer* sb = IGetSourceBuffer(fileName, srcNode, srcBufferFlags);
|
||
|
|
||
|
const char* plasmaDir = plMaxConfig::GetClientPath();
|
||
|
if (plasmaDir)
|
||
|
{
|
||
|
char sfxPath[MAX_PATH];
|
||
|
sprintf(sfxPath, "%ssfx\\%s", plasmaDir, plFileUtils::GetFileName(fileName));
|
||
|
|
||
|
// Export any localized versions as well
|
||
|
for (int i = 0; i < plLocalization::GetNumLocales(); i++)
|
||
|
{
|
||
|
char localName[MAX_PATH];
|
||
|
if (plLocalization::ExportGetLocalized(sfxPath, i, localName))
|
||
|
{
|
||
|
IGetSourceBuffer(localName, srcNode, srcBufferFlags);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sb;
|
||
|
}
|
||
|
|
||
|
plSoundBuffer *plBaseSoundEmitterComponent::IGetSourceBuffer( const char *fileName, plMaxNode *srcNode, UInt32 srcBufferFlags )
|
||
|
{
|
||
|
plKey key;
|
||
|
char keyName[ MAX_PATH ];
|
||
|
char fullPath[ MAX_PATH ];
|
||
|
|
||
|
|
||
|
strcpy( keyName, fileName );
|
||
|
::PathStripPath( keyName );
|
||
|
|
||
|
// TEMP HACK until we get packed sounds:
|
||
|
// Given the source filename, we check to see if it's in our plasma game directory. If not, or if
|
||
|
// it's out of date, we copy it over. We'll truncate the filename inside plSoundBuffer when we're ready.
|
||
|
|
||
|
const char *plasmaDir = plMaxConfig::GetClientPath();
|
||
|
if( plasmaDir != nil )
|
||
|
{
|
||
|
strcpy( fullPath, plasmaDir );
|
||
|
strcat( fullPath, "sfx\\" );
|
||
|
|
||
|
// Before we finish our path, make sure that directory EXISTS
|
||
|
plFileUtils::CreateDir( fullPath );
|
||
|
|
||
|
// Now finish the path...
|
||
|
strcat( fullPath, keyName );
|
||
|
|
||
|
// Check filestamp
|
||
|
WIN32_FILE_ATTRIBUTE_DATA oldFileAttrib, newFileAttrib;
|
||
|
BOOL oldOK, newOK;
|
||
|
|
||
|
oldOK = GetFileAttributesEx( fileName, GetFileExInfoStandard, &oldFileAttrib );
|
||
|
newOK = GetFileAttributesEx( fullPath, GetFileExInfoStandard, &newFileAttrib );
|
||
|
|
||
|
if( oldOK && newOK )
|
||
|
{
|
||
|
// Only copy if the file is newer
|
||
|
if( ::CompareFileTime( &oldFileAttrib.ftLastWriteTime, &newFileAttrib.ftLastWriteTime ) > 0 )
|
||
|
{
|
||
|
::CopyFile( fileName, fullPath, FALSE );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Can't compare, so either there was an error or the target file doesn't exist. Copy no matter what.
|
||
|
::CopyFile( fileName, fullPath, FALSE );
|
||
|
}
|
||
|
|
||
|
// Point to our new sound file
|
||
|
fileName = fullPath;
|
||
|
}
|
||
|
|
||
|
// Additional info for the keyName--need some flag mangling, specifically for the left/right channel mangling
|
||
|
if( srcBufferFlags & plSoundBuffer::kOnlyLeftChannel )
|
||
|
strcat( keyName, ":L" );
|
||
|
else if( srcBufferFlags & plSoundBuffer::kOnlyRightChannel )
|
||
|
strcat( keyName, ":R" );
|
||
|
|
||
|
key = srcNode->FindPageKey( plSoundBuffer::Index(), keyName );
|
||
|
if( key != nil )
|
||
|
return plSoundBuffer::ConvertNoRef( key->GetObjectPtr() );
|
||
|
|
||
|
// Not yet created, so make a new one
|
||
|
plSoundBuffer *buffer = TRACKED_NEW plSoundBuffer( fileName, srcBufferFlags );
|
||
|
if( !buffer->IsValid() )
|
||
|
{
|
||
|
// Invalid, so delete and return nil
|
||
|
delete buffer;
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
// We'll put it in a location parallel to the age, say, (age,district,"sounds")
|
||
|
plLocation loc = srcNode->GetLocation();
|
||
|
// plKey roomKey = hsgResMgr::ResMgr()->NameToLoc( loc.GetAge(), loc.GetChapter(), "sounds" );
|
||
|
// TEMP HACK FOR NOW, until we actually finish implementing this--just hide them in the same file
|
||
|
// plKey roomKey = hsgResMgr::ResMgr()->FindLocation( loc.GetAge(), loc.GetChapter(), loc.GetPage() );
|
||
|
|
||
|
// The buffer may be shared across multiple sources. We could or together the LoadMasks of all
|
||
|
// the nodes that use it, or we can just go with the default loadmask of Always load, and
|
||
|
// count on it never getting dragged into memory if nothing that references it does.
|
||
|
hsgResMgr::ResMgr()->NewKey( keyName, buffer, srcNode->GetLocation());
|
||
|
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
//// LookupLatestAsset ///////////////////////////////////////////////////////
|
||
|
// Does a find in AssetMan for the given sound file and makes sure it's
|
||
|
// copied over to the AssetMan directory, returning the path to said asset.
|
||
|
// Returns true if found, false if not.
|
||
|
|
||
|
hsBool plBaseSoundEmitterComponent::LookupLatestAsset( const char *waveName, char *retPath, plErrorMsg *errMsg )
|
||
|
{
|
||
|
MaxAssInterface* assetMan = GetMaxAssInterface();
|
||
|
if( assetMan == nil )
|
||
|
return false; // No AssetMan available
|
||
|
|
||
|
// Try to find it in assetMan
|
||
|
jvUniqueId assetId;
|
||
|
if (assetMan->FindAssetByFilename(waveName, assetId))
|
||
|
{
|
||
|
// Get the latest version
|
||
|
char assetPath[MAX_PATH];
|
||
|
if (!assetMan->GetLatestVersionFile(assetId, assetPath, sizeof(assetPath)))
|
||
|
{
|
||
|
errMsg->Set( true, "SoundEmitter Convert Error",
|
||
|
"Unable to update wave file '%s' because AssetMan was unable to get the latest version. Using local copy instead.", waveName ).Show();
|
||
|
errMsg->Set( false );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Copy the string over and go
|
||
|
hsStrcpy( retPath, assetPath );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
plSoundBuffer *plBaseSoundEmitterComponent::IProcessSourceBuffer( plMaxNode *maxNode, plErrorMsg *errMsg )
|
||
|
{
|
||
|
char *fileName = GetSoundFileName( kBaseSound );
|
||
|
if( fileName == nil )
|
||
|
return nil;
|
||
|
|
||
|
plSoundBuffer *srcBuffer = GetSourceBuffer( fileName, maxNode, ICalcSourceBufferFlags() );
|
||
|
if( srcBuffer == nil )
|
||
|
{
|
||
|
IShowError( kSrcBufferInvalid, "The file specified for the sound 3D component %s is invalid. "
|
||
|
"This emitter will not be exported.", GetINode()->GetName(), errMsg );
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
return srcBuffer;
|
||
|
}
|
||
|
|
||
|
void plBaseSoundEmitterComponent::UpdateSoundFileSelection( void )
|
||
|
{
|
||
|
plSoundBuffer *baseBuffer = nil;
|
||
|
|
||
|
|
||
|
// Attempt to load the sound in
|
||
|
if( GetSoundFileName( kBaseSound ) == nil )
|
||
|
{
|
||
|
// Disable this feature by default
|
||
|
fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( IAllowStereoFiles() )
|
||
|
{
|
||
|
// We allow stereo files, so we don't want to allow stereo->mono select
|
||
|
fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
baseBuffer = TRACKED_NEW plSoundBuffer( GetSoundFileName( kBaseSound ) );
|
||
|
if( baseBuffer != nil && baseBuffer->IsValid() )
|
||
|
{
|
||
|
// Update our stereo channel selection if necessary
|
||
|
if( baseBuffer->GetHeader().fNumChannels == 1 )
|
||
|
{
|
||
|
fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 1 );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
// Disable this feature by default
|
||
|
fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( baseBuffer != nil )
|
||
|
delete baseBuffer;
|
||
|
}
|
||
|
|
||
|
hsScalar plBaseSoundEmitterComponent::GetSoundVolume( void ) const
|
||
|
{
|
||
|
return IGetDigitalVolume();
|
||
|
}
|
||
|
|
||
|
//// UpdateCategories ///////////////////////////////////////////////////////////////////////////
|
||
|
// Loads the given combo box with category selections and sets the ParamID for the category parameter.
|
||
|
// Returns false if there are no categories to choose for this component
|
||
|
|
||
|
hsBool plBaseSoundEmitterComponent::UpdateCategories( HWND dialogBox, int &categoryID, ParamID ¶mID )
|
||
|
{
|
||
|
HWND comboBox = GetDlgItem( dialogBox, IDC_SND_CATEGORY );
|
||
|
char **cats;
|
||
|
int *catEnums;
|
||
|
int i, currCat, idx, currIdx;
|
||
|
|
||
|
|
||
|
// Get our list of cats
|
||
|
if( !IGetCategoryList( cats, catEnums ) )
|
||
|
return false;
|
||
|
|
||
|
// We get two categories for this one: Background Music (default) and Ambience
|
||
|
ComboBox_ResetContent( comboBox );
|
||
|
|
||
|
currCat = fCompPB->GetInt( (ParamID)kSndCategory );
|
||
|
currIdx = -1;
|
||
|
|
||
|
for( i = 0; cats[ i ][ 0 ] != 0; i++ )
|
||
|
{
|
||
|
idx = ComboBox_AddString( comboBox, cats[ i ] );
|
||
|
ComboBox_SetItemData( comboBox, idx, catEnums[ i ] );
|
||
|
if( catEnums[ i ] == currCat )
|
||
|
currIdx = idx;
|
||
|
}
|
||
|
|
||
|
if( currIdx != -1 )
|
||
|
ComboBox_SetCurSel( comboBox, currIdx );
|
||
|
else
|
||
|
{
|
||
|
// Option not found in our list, reset to a valid option
|
||
|
ComboBox_SetCurSel( comboBox, 0 );
|
||
|
fCompPB->SetValue( (ParamID)kSndCategory, 0, catEnums[ 0 ] );
|
||
|
}
|
||
|
|
||
|
// Return info
|
||
|
paramID = (ParamID)kSndCategory;
|
||
|
categoryID = IDC_SND_CATEGORY;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
SegmentMap *GetCompWaveSegmentMap(const char *file)
|
||
|
{
|
||
|
if( file == nil )
|
||
|
return nil;
|
||
|
|
||
|
return GetWaveSegmentMap( file, nil );
|
||
|
|
||
|
/*
|
||
|
const char *path = plMaxConfig::GetClientPath();
|
||
|
if (file && path)
|
||
|
{
|
||
|
char fullpath[MAX_PATH];
|
||
|
sprintf(fullpath, "%sSfx\\%s", path, file);
|
||
|
return GetWaveSegmentMap(fullpath, nil);
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
//// ISetBaseParameters /////////////////////////////////////////////////////////////////////////
|
||
|
// Sets up parameters on the given sound based on the common paramblock values
|
||
|
|
||
|
void plBaseSoundEmitterComponent::ISetBaseParameters( plSound *destSound, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
// Make sure our category is valid before we set it
|
||
|
int i, cat = fCompPB->GetInt( (ParamID)kSndCategory );
|
||
|
char **cats;
|
||
|
int *catEnums;
|
||
|
|
||
|
if( IGetCategoryList( cats, catEnums ) )
|
||
|
{
|
||
|
for( i = 0; cats[ i ][ 0 ] != 0; i++ )
|
||
|
{
|
||
|
if( catEnums[ i ] == cat )
|
||
|
break;
|
||
|
}
|
||
|
if( cats[ i ][ 0 ] == 0 )
|
||
|
cat = catEnums[ 0 ];
|
||
|
}
|
||
|
destSound->SetType( cat );
|
||
|
|
||
|
destSound->SetVolume( IGetDigitalVolume() );
|
||
|
destSound->SetProperty( plSound::kPropAutoStart, fCompPB->GetInt( (ParamID)kSoundAutoStartCkBx ) );
|
||
|
IGrabFadeValues( destSound );
|
||
|
if( fCompPB->GetInt( (ParamID)kSoundLoopCkBx ) )
|
||
|
{
|
||
|
destSound->SetProperty( plSound::kPropLooping, true );
|
||
|
|
||
|
const char *loop = fCompPB->GetStr((ParamID)kSoundLoopName);
|
||
|
if (loop && loop[0] != '\0')
|
||
|
{
|
||
|
SegmentMap *segMap = GetCompWaveSegmentMap( GetSoundFileName( kBaseSound ) );
|
||
|
if (segMap && segMap->find(loop) != segMap->end())
|
||
|
{
|
||
|
SegmentSpec *spec = (*segMap)[loop];
|
||
|
// sound->SetLoopPoints(spec->fStart, spec->fEnd);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
destSound->SetProperty( plSound::kPropLooping, false );
|
||
|
}
|
||
|
|
||
|
//// AddToAnim //////////////////////////////////////////////////////////////////////////////////
|
||
|
// Support for animated volumes
|
||
|
|
||
|
hsBool plBaseSoundEmitterComponent::AddToAnim( plAGAnim *anim, plMaxNode *node )
|
||
|
{
|
||
|
hsBool result = false;
|
||
|
plController *ctl;
|
||
|
hsControlConverter& cc = hsControlConverter::Instance();
|
||
|
|
||
|
hsScalar start, end;
|
||
|
if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME))
|
||
|
{
|
||
|
start = end = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
start = anim->GetStart();
|
||
|
end = anim->GetEnd();
|
||
|
}
|
||
|
|
||
|
ctl = cc.MakeScalarController( fCompPB->GetController( (ParamID)kSoundVolumeSlider ), node, start, end );
|
||
|
if( ctl != nil )
|
||
|
{
|
||
|
// Better only do this when the sound component is applied to only one object...
|
||
|
if( fIndices.size() != 1 )
|
||
|
{
|
||
|
delete ctl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
std::map<plMaxNode*, int>::iterator i = fIndices.begin();
|
||
|
plSoundVolumeApplicator *app = TRACKED_NEW plSoundVolumeApplicator( (*i).second );
|
||
|
app->SetChannelName(node->GetName());
|
||
|
plAnimComponentBase::SetupCtl( anim, ctl, app, node );
|
||
|
result = true;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Class that accesses the paramblock
|
||
|
|
||
|
struct indexinfo
|
||
|
{
|
||
|
indexinfo::indexinfo() { pNode = nil; fIndex = -1; }
|
||
|
plMaxNode* pNode;
|
||
|
int fIndex;
|
||
|
};
|
||
|
|
||
|
int GetSoundNameAndIdx(plComponentBase *comp, plMaxNodeBase *node, const char*& name)
|
||
|
{
|
||
|
int idx = -1;
|
||
|
if( ( comp->ClassID() == SOUND_3D_COMPONENT_ID ||
|
||
|
comp->ClassID() == BGND_MUSIC_COMPONENT_ID ||
|
||
|
comp->ClassID() == GUI_SOUND_COMPONENT_ID ) && node->CanConvert())
|
||
|
{
|
||
|
idx = ((plBaseSoundEmitterComponent *)comp)->GetSoundIdx((plMaxNode*)node);
|
||
|
}
|
||
|
if(node->CanConvert())
|
||
|
name = idx < 0 ? nil : comp->GetINode()->GetName();
|
||
|
else
|
||
|
name = nil;
|
||
|
return idx;
|
||
|
}
|
||
|
|
||
|
int plAudioComp::GetSoundModIdx(plComponentBase *comp, plMaxNode *node)
|
||
|
{
|
||
|
if( comp->ClassID() == SOUND_3D_COMPONENT_ID ||
|
||
|
comp->ClassID() == BGND_MUSIC_COMPONENT_ID ||
|
||
|
comp->ClassID() == GUI_SOUND_COMPONENT_ID )
|
||
|
return ((plBaseSoundEmitterComponent *)comp)->GetSoundIdx(node);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
bool plAudioComp::IsLocalOnly( plComponentBase *comp )
|
||
|
{
|
||
|
if( comp->ClassID() == SOUND_3D_COMPONENT_ID ||
|
||
|
comp->ClassID() == BGND_MUSIC_COMPONENT_ID ||
|
||
|
comp->ClassID() == GUI_SOUND_COMPONENT_ID )
|
||
|
return ((plBaseSoundEmitterComponent *)comp)->IsLocalOnly() ? true : false;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool plAudioComp::IsSoundComponent(plComponentBase *comp)
|
||
|
{
|
||
|
Class_ID id = comp->ClassID();
|
||
|
|
||
|
if( id == SOUND_3D_COMPONENT_ID ||
|
||
|
id == BGND_MUSIC_COMPONENT_ID ||
|
||
|
id == GUI_SOUND_COMPONENT_ID )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
class plAudioBaseComponentProc : public plLCBoxComponentProc
|
||
|
{
|
||
|
|
||
|
protected:
|
||
|
|
||
|
void IConvertOldVolume( IParamBlock2 *pb )
|
||
|
{
|
||
|
int oldVol = pb->GetInt( (ParamID)kOldSoundVolumeSlider, 0 );
|
||
|
if( oldVol != 4999 )
|
||
|
{
|
||
|
float v = (float)( oldVol - 5000 ) / 5000.f;
|
||
|
pb->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, v );
|
||
|
pb->SetValue( (ParamID)kOldSoundVolumeSlider, 0, 4999 );
|
||
|
}
|
||
|
|
||
|
// Shut up.
|
||
|
float notSoOldV = pb->GetFloat( (ParamID)kNotSoOldSoundVolumeSlider, 0 );
|
||
|
if( notSoOldV != -1.f )
|
||
|
{
|
||
|
float d3dValueReally = -5000.f + ( 5000.f * notSoOldV );
|
||
|
|
||
|
float ourNewValue = (float)d3dValueReally / 100.f;
|
||
|
|
||
|
pb->SetValue( (ParamID)kSoundVolumeSlider, 0, ourNewValue );
|
||
|
pb->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, -1.f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IGetNewLocalFileName( plBaseSoundEmitterComponent *soundComponent, plBaseSoundEmitterComponent::WhichSound which )
|
||
|
{
|
||
|
TCHAR fileName[ MAX_PATH ], dirName[ MAX_PATH ], *name;
|
||
|
|
||
|
|
||
|
name = soundComponent->GetSoundFileName( which );
|
||
|
if( name != nil )
|
||
|
strcpy( fileName, name );
|
||
|
else
|
||
|
strcpy( fileName, _T( "" ) );
|
||
|
|
||
|
strcpy( dirName, fileName );
|
||
|
::PathRemoveFileSpec( dirName );
|
||
|
|
||
|
OPENFILENAME ofn = {0};
|
||
|
ofn.lStructSize = sizeof( OPENFILENAME );
|
||
|
ofn.hwndOwner = GetCOREInterface()->GetMAXHWnd();
|
||
|
ofn.lpstrFilter = "WAV Files (*.wav)\0*.wav\0Windows Media Audio Files (*.wma)\0*.wma\0OGG Vorbis Files (*.ogg)\0*.ogg\0";
|
||
|
ofn.lpstrFile = fileName;
|
||
|
ofn.nMaxFile = sizeof( fileName );
|
||
|
ofn.lpstrInitialDir = dirName;
|
||
|
ofn.lpstrTitle = "Choose a sound file";
|
||
|
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
|
||
|
ofn.lpstrDefExt = "wav";
|
||
|
|
||
|
if( GetOpenFileName( &ofn ) )
|
||
|
{
|
||
|
jvUniqueId emptyId;
|
||
|
soundComponent->SetSoundAssetId( which, emptyId, fileName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IUpdateSoundButton( plBaseSoundEmitterComponent *soundComponent, HWND hDlg, int dlgBtnItemToSet, plBaseSoundEmitterComponent::WhichSound which )
|
||
|
{
|
||
|
ICustButton *custButton;
|
||
|
TCHAR fileName[ MAX_PATH ];
|
||
|
|
||
|
|
||
|
custButton = GetICustButton( GetDlgItem( hDlg, dlgBtnItemToSet ) );
|
||
|
if( custButton != nil )
|
||
|
{
|
||
|
TCHAR *origName = soundComponent->GetSoundFileName( which );
|
||
|
|
||
|
if( origName != nil && strlen( origName ) > 0 )
|
||
|
{
|
||
|
strcpy( fileName, origName );
|
||
|
::PathStripPath( fileName );
|
||
|
custButton->SetText( fileName );
|
||
|
}
|
||
|
else
|
||
|
custButton->SetText( _T( "<None>" ) );
|
||
|
|
||
|
ReleaseICustButton( custButton );
|
||
|
}
|
||
|
|
||
|
soundComponent->UpdateSoundFileSelection();
|
||
|
}
|
||
|
|
||
|
void ISelectSoundFile( plBaseSoundEmitterComponent *soundComponent, HWND hDlg, int dlgBtnItemToSet, plBaseSoundEmitterComponent::WhichSound which )
|
||
|
{
|
||
|
MaxAssInterface* maxAssInterface = GetMaxAssInterface();
|
||
|
|
||
|
// if we have the assetman plug-in, then try to use it, unless shift is held down
|
||
|
if( maxAssInterface && !( GetKeyState( VK_SHIFT ) & 0x8000 ) )
|
||
|
{
|
||
|
jvUniqueId assetId = soundComponent->GetSoundAssetID(which);
|
||
|
|
||
|
char fileName[MAX_PATH];
|
||
|
if (maxAssInterface->OpenSoundDlg(assetId, fileName, MAX_PATH))
|
||
|
{
|
||
|
// Set asset ID and filename
|
||
|
soundComponent->SetSoundAssetId(which, assetId, fileName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IGetNewLocalFileName( soundComponent, which );
|
||
|
}
|
||
|
|
||
|
// Update the button now
|
||
|
if( hDlg != nil )
|
||
|
IUpdateSoundButton( soundComponent, hDlg, dlgBtnItemToSet, which );
|
||
|
}
|
||
|
|
||
|
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
|
||
|
{
|
||
|
plBaseSoundEmitterComponent *soundComp = (plBaseSoundEmitterComponent *)map->GetParamBlock()->GetOwner();
|
||
|
|
||
|
|
||
|
switch( msg )
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
CheckDlgButton(hWnd, IDC_SND_TRACKVIEW, soundComp->fAllowUnhide ? BST_CHECKED : BST_UNCHECKED );
|
||
|
return true;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if( LOWORD( wParam ) == IDC_SND_TRACKVIEW )
|
||
|
{
|
||
|
soundComp->fAllowUnhide = ( IsDlgButtonChecked( hWnd, IDC_SND_TRACKVIEW ) == BST_CHECKED );
|
||
|
plComponentShow::Update();
|
||
|
return true;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
//// Helper accessors and dialog procs ////
|
||
|
|
||
|
static plSingleCompSelProc gSoundSoftVolumeSelProc( kSndSoftRegion, IDC_COMP_SOUNDREGION_CHOOSE_VOLUME, "Select a soft region to use for the sound" );
|
||
|
|
||
|
// When one of our parameters that is a ref changes, send out the component ref
|
||
|
// changed message. Normally, messages from component refs are ignored since
|
||
|
// they pass along all the messages of the ref, which generates a lot of false
|
||
|
// converts.
|
||
|
class plSoundSoftVolAccessor : public PBAccessor
|
||
|
{
|
||
|
public:
|
||
|
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
|
||
|
{
|
||
|
if( id == kSndSoftRegion )
|
||
|
{
|
||
|
plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent*)owner;
|
||
|
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static plSoundSoftVolAccessor gSoundSoftVolAccessor;
|
||
|
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kSndSharedParams,
|
||
|
kS3DBaseParams,
|
||
|
kS3DSoftVolumeParams,
|
||
|
kSoundFadeParams,
|
||
|
kSndWaveformParams,
|
||
|
kSndEAXParams
|
||
|
};
|
||
|
|
||
|
//// Shared ParamBlock for All Sounds ///////////////////////////////////////////////////////////
|
||
|
|
||
|
#define sSoundSharedPBHeader(s) kSndSharedParams, IDD_COMP_SOUNDBASE, s##, 0, 0, &gSoundCompProc, \
|
||
|
kSoundFadeParams, IDD_COMP_SOUND_FADEPARAMS, IDS_COMP_SOUNDFADEPARAMS, 0, 0, &gSoundFadeParamsProc
|
||
|
|
||
|
static ParamBlockDesc2 sSoundSharedPB
|
||
|
(
|
||
|
plComponent::kBlkComp + 2, _T("Sound Shared Params"), 0, nil, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp,
|
||
|
|
||
|
2, // Number of rollouts
|
||
|
kSndSharedParams, IDD_COMP_SOUNDBASE, IDS_COMP_SOUNDBASE, 0, 0, nil,
|
||
|
kSoundFadeParams, IDD_COMP_SOUND_FADEPARAMS, IDS_COMP_SOUNDFADEPARAMS, 0, 0, nil,
|
||
|
|
||
|
// params
|
||
|
|
||
|
/// Version # (currently 0, won't use until we know everyone has paramblocks with this in it)
|
||
|
kSndVersionCount, _T(""), TYPE_INT, 0, 0,
|
||
|
p_range, 0, 10000,
|
||
|
p_default, 0,
|
||
|
end,
|
||
|
|
||
|
kSoundFileName, _T("fileName"), TYPE_STRING, 0, 0,
|
||
|
end,
|
||
|
|
||
|
kSoundAutoStartCkBx, _T("autoStart"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kSndSharedParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND3D_AUTOSTART_CKBX,
|
||
|
end,
|
||
|
|
||
|
kSoundLoopCkBx, _T("loop"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kSndSharedParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND3D_LOOPCHKBOX,
|
||
|
end,
|
||
|
kSoundLoopName, _T("loopName"), TYPE_STRING, 0, 0,
|
||
|
end,
|
||
|
|
||
|
kSndDisableLOD, _T("disableLOD"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kSndSharedParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND_DISABLELOD,
|
||
|
end,
|
||
|
|
||
|
kSoundVolumeSlider, _T("volume"), TYPE_FLOAT, P_ANIMATABLE, IDS_SND_VOLUME,
|
||
|
p_ui, kSndSharedParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_COMP_SOUND3D_SLIDERVIEWER, IDC_COMP_SOUND3D_VOLSLIDER, 4,
|
||
|
p_range, -48.0f, 0.f,
|
||
|
p_default, 0.f,
|
||
|
end,
|
||
|
|
||
|
kNotSoOldSoundVolumeSlider, _T(""), TYPE_FLOAT, 0, 0,
|
||
|
end,
|
||
|
|
||
|
kOldSoundVolumeSlider, _T(""), TYPE_INT, 0, 0,
|
||
|
end,
|
||
|
|
||
|
kSndCategory, _T("category"), TYPE_INT, 0, 0,
|
||
|
p_range, plSound::kStartType, plSound::kNumTypes - 1,
|
||
|
p_default, plSound::kSoundFX,
|
||
|
end,
|
||
|
|
||
|
/// Fade Parameters rollout
|
||
|
kSndFadeInEnable, _T("fadeInEnable"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kSoundFadeParams, TYPE_SINGLECHEKBOX, IDC_SOUND3D_INENABLE,
|
||
|
end,
|
||
|
|
||
|
kSndFadeInType, _T("fadeInType"), TYPE_INT, 0, 0,
|
||
|
p_default, 0,
|
||
|
end,
|
||
|
|
||
|
kSndFadeInLength, _T("fadeInLength"), TYPE_FLOAT, 0,0,
|
||
|
p_ui, kSoundFadeParams, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_SOUND3D_INLENGTH, IDC_SOUND3D_INLENGTHSPIN, 1.0f,
|
||
|
p_default, 1.f,
|
||
|
end,
|
||
|
|
||
|
kSndFadeOutEnable, _T("fadeOutEnable"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kSoundFadeParams, TYPE_SINGLECHEKBOX, IDC_SOUND3D_OUTENABLE,
|
||
|
end,
|
||
|
|
||
|
kSndFadeOutType, _T("fadeOutType"), TYPE_INT, 0, 0,
|
||
|
p_default, 0,
|
||
|
end,
|
||
|
|
||
|
kSndFadeOutLength, _T("fadeOutLength"), TYPE_FLOAT, 0,0,
|
||
|
p_ui, kSoundFadeParams, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_SOUND3D_OUTLENGTH, IDC_SOUND3D_OUTLENGTHSPIN, 1.0f,
|
||
|
p_default, 1.f,
|
||
|
end,
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
//// ParamBlock Macros for Waveform Properties Rollout ///////////////////////////////////////////
|
||
|
|
||
|
#define sSndWaveformPropsHeader kSndWaveformParams, IDD_COMP_SOUNDSRC, IDS_COMP_SOUNDSRC, 0, 0, NULL
|
||
|
|
||
|
#define sSndWaveformPropsParamTemplate \
|
||
|
\
|
||
|
kSndAllowChannelSelect, _T( "allowChannelSelect" ), TYPE_BOOL, 0, 0, \
|
||
|
p_default, 0, \
|
||
|
p_ui, kSndWaveformParams, TYPE_SINGLECHEKBOX, IDC_SND_ISSTEREO_HIDDEN, \
|
||
|
p_enable_ctrls, 1, kSndChannelSelect, \
|
||
|
end, \
|
||
|
\
|
||
|
/* Channel select for stereo sources */ \
|
||
|
kSndChannelSelect, _T( "sourceChannel" ), TYPE_INT, 0, 0, \
|
||
|
p_ui, kSndWaveformParams, TYPE_RADIO, 2, IDC_SND_CHANSRC1, IDC_SND_CHANSRC2, \
|
||
|
p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kSndPriority, _T("sndPriority"), TYPE_INT, 0, 0, \
|
||
|
p_range, 0, 10, \
|
||
|
p_ui, kSndWaveformParams, TYPE_SPINNER, EDITTYPE_INT, IDC_SND_PRIORITY, IDC_SND_PRIORITY_SPIN, 1.f, \
|
||
|
p_default, 0, \
|
||
|
end
|
||
|
|
||
|
//// Enums Source EAX Properties Rollout ////////////////////////////////////////////////////////
|
||
|
|
||
|
enum EAXRefs
|
||
|
{
|
||
|
kEAXEnabled = 128,
|
||
|
kEAXRoom,
|
||
|
kEAXRoomHF,
|
||
|
kEAXRoomAuto,
|
||
|
kEAXRoomHFAuto,
|
||
|
kEAXOutsideVolHF,
|
||
|
kEAXAirAbsorptionFact,
|
||
|
kEAXRoomRolloffFact,
|
||
|
kEAXDopplerFact,
|
||
|
kEAXRolloffFact,
|
||
|
|
||
|
kEAXEnableOcclusion,
|
||
|
kEAXOcclusionRegion,
|
||
|
kEAXStartOcclusion,
|
||
|
kEAXStartOcclusionLFRatio,
|
||
|
kEAXStartOcclusionRoomRatio,
|
||
|
kEAXStartOcclusionDirectRatio,
|
||
|
kEAXEndOcclusion,
|
||
|
kEAXEndOcclusionLFRatio,
|
||
|
kEAXEndOcclusionRoomRatio,
|
||
|
kEAXEndOcclusionDirectRatio,
|
||
|
kEAXWhichOccSwapped,
|
||
|
kEAXTempOcclusion,
|
||
|
kEAXTempOcclusionLFRatio,
|
||
|
kEAXTempOcclusionRoomRatio,
|
||
|
kEAXTempOcclusionDirectRatio,
|
||
|
kEAXTempOccSwapper
|
||
|
};
|
||
|
|
||
|
//// DialogProc for Source EAX Properties Rollout ///////////////////////////////////////////////
|
||
|
|
||
|
class plEAXPropsDlgProc : public plSingleCompSelProc
|
||
|
{
|
||
|
IParamBlock2 *fLastBlockSwapped;
|
||
|
|
||
|
void ISwapOutOcclusion( IParamBlock2 *pb )
|
||
|
{
|
||
|
if( pb == nil )
|
||
|
return;
|
||
|
|
||
|
if( pb->GetInt( (ParamID)kEAXWhichOccSwapped ) == 0 )
|
||
|
{
|
||
|
// Swap out to start values
|
||
|
pb->SetValue( (ParamID)kEAXStartOcclusion, 0, pb->GetInt( (ParamID)kEAXTempOcclusion ) );
|
||
|
pb->SetValue( (ParamID)kEAXStartOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionLFRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXStartOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionRoomRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXStartOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionDirectRatio ) );
|
||
|
}
|
||
|
else if( pb->GetInt( (ParamID)kEAXWhichOccSwapped ) == 1 )
|
||
|
{
|
||
|
// Swap out to end values
|
||
|
pb->SetValue( (ParamID)kEAXEndOcclusion, 0, pb->GetInt( (ParamID)kEAXTempOcclusion ) );
|
||
|
pb->SetValue( (ParamID)kEAXEndOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionLFRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXEndOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionRoomRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXEndOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionDirectRatio ) );
|
||
|
}
|
||
|
|
||
|
// Set to "none swapped"
|
||
|
pb->SetValue( (ParamID)kEAXWhichOccSwapped, 0, (int)-1 );
|
||
|
|
||
|
fLastBlockSwapped = nil;
|
||
|
}
|
||
|
|
||
|
void ISwapInOcclusion( IParamBlock2 *pb, int which )
|
||
|
{
|
||
|
if( pb == nil )
|
||
|
return;
|
||
|
|
||
|
if( which == 0 )
|
||
|
{
|
||
|
// Swap in from start values
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusion, 0, pb->GetInt( (ParamID)kEAXStartOcclusion ) );
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXStartOcclusionLFRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXStartOcclusionRoomRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXStartOcclusionDirectRatio ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Swap in from end values
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusion, 0, pb->GetInt( (ParamID)kEAXEndOcclusion ) );
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXEndOcclusionLFRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXEndOcclusionRoomRatio ) );
|
||
|
pb->SetValue( (ParamID)kEAXTempOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXEndOcclusionDirectRatio ) );
|
||
|
}
|
||
|
|
||
|
pb->SetValue( (ParamID)kEAXWhichOccSwapped, 0, (int)which );
|
||
|
if( pb->GetMap() != nil )
|
||
|
pb->GetMap()->UpdateUI( 0 );
|
||
|
|
||
|
fLastBlockSwapped = pb;
|
||
|
}
|
||
|
|
||
|
class plOccPreset
|
||
|
{
|
||
|
public:
|
||
|
char *fName;
|
||
|
Int16 fOcc;
|
||
|
float fLFRatio;
|
||
|
float fRoomRatio;
|
||
|
};
|
||
|
|
||
|
plOccPreset fPresets[ 9 ];
|
||
|
|
||
|
void ILoadOccPresets( HWND hDlg )
|
||
|
{
|
||
|
HWND combo = GetDlgItem( hDlg, IDC_EAX_OCCPRESET );
|
||
|
int i;
|
||
|
|
||
|
|
||
|
ComboBox_ResetContent( combo );
|
||
|
for( i = 0; i < 9; i++ )
|
||
|
ComboBox_AddString( combo, fPresets[ i ].fName );
|
||
|
|
||
|
ComboBox_SetCurSel( combo, 0 );
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
void FlushSwappedPBs( void )
|
||
|
{
|
||
|
if( fLastBlockSwapped != nil )
|
||
|
ISwapOutOcclusion( fLastBlockSwapped );
|
||
|
}
|
||
|
|
||
|
plEAXPropsDlgProc() : plSingleCompSelProc( kEAXOcclusionRegion, IDC_EAX_OCCREGION, "Select the soft region to blend these EAX occlusion properties" )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
// Annoyingly, the EAX headers don't have a convenient array, just some #defines
|
||
|
static char occNames[][ 64 ] = { "Single window", "Double window", "Thin door", "Thick door",
|
||
|
"Wood wall", "Brick wall", "Stone wall", "Curtain" };
|
||
|
Int16 occValues[] = { EAX_MATERIAL_SINGLEWINDOW, EAX_MATERIAL_DOUBLEWINDOW, EAX_MATERIAL_THINDOOR,
|
||
|
EAX_MATERIAL_THICKDOOR, EAX_MATERIAL_WOODWALL, EAX_MATERIAL_BRICKWALL,
|
||
|
EAX_MATERIAL_STONEWALL, EAX_MATERIAL_CURTAIN };
|
||
|
float occLFValues[] = { EAX_MATERIAL_SINGLEWINDOWLF, EAX_MATERIAL_DOUBLEWINDOWLF, EAX_MATERIAL_THINDOORLF,
|
||
|
EAX_MATERIAL_THICKDOORLF, EAX_MATERIAL_WOODWALLLF, EAX_MATERIAL_BRICKWALLLF,
|
||
|
EAX_MATERIAL_STONEWALLLF, EAX_MATERIAL_CURTAINLF };
|
||
|
Int16 occRoomValues[] = { EAX_MATERIAL_SINGLEWINDOWROOMRATIO, EAX_MATERIAL_DOUBLEWINDOWROOMRATIO, EAX_MATERIAL_THINDOORROOMRATIO,
|
||
|
EAX_MATERIAL_THICKDOORROOMRATIO, EAX_MATERIAL_WOODWALLROOMRATIO, EAX_MATERIAL_BRICKWALLROOMRATIO,
|
||
|
EAX_MATERIAL_STONEWALLROOMRATIO, EAX_MATERIAL_CURTAINROOMRATIO };
|
||
|
|
||
|
for( i = 1; i < 9; i++ )
|
||
|
{
|
||
|
fPresets[ i ].fName = occNames[ i - 1 ];
|
||
|
fPresets[ i ].fOcc = occValues[ i - 1 ];
|
||
|
fPresets[ i ].fLFRatio = occLFValues[ i - 1 ];
|
||
|
fPresets[ i ].fRoomRatio = occRoomValues[ i - 1 ];
|
||
|
}
|
||
|
fPresets[ 0 ].fName = "None";
|
||
|
fPresets[ 0 ].fOcc = 0;
|
||
|
fPresets[ 0 ].fLFRatio = 0.25f;
|
||
|
fPresets[ 0 ].fRoomRatio = 1.5f;
|
||
|
}
|
||
|
|
||
|
void DeleteThis() {}
|
||
|
|
||
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
IParamBlock2 *pblock = map->GetParamBlock();
|
||
|
|
||
|
switch( msg )
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
pblock->SetValue( (ParamID)kEAXTempOccSwapper, 0, (int)0 );
|
||
|
ISwapInOcclusion( pblock, 0 );
|
||
|
ILoadOccPresets( hWnd );
|
||
|
break;
|
||
|
|
||
|
case WM_DESTROY:
|
||
|
ISwapOutOcclusion( pblock );
|
||
|
return 0;
|
||
|
|
||
|
case WM_SHOWWINDOW:
|
||
|
if( wParam )
|
||
|
{
|
||
|
pblock->SetValue( (ParamID)kEAXTempOccSwapper, 0, (int)0 );
|
||
|
ISwapInOcclusion( pblock, 0 );
|
||
|
}
|
||
|
else
|
||
|
ISwapOutOcclusion( pblock );
|
||
|
return 0;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if( LOWORD( wParam ) == IDC_EAX_STARTOCC || LOWORD( wParam ) == IDC_EAX_ENDOCC )
|
||
|
{
|
||
|
// Our radio button to switch between start and end values was hit. So swap out the values
|
||
|
// from the temp ones
|
||
|
ISwapOutOcclusion( pblock );
|
||
|
ISwapInOcclusion( pblock, ( LOWORD( wParam ) == IDC_EAX_STARTOCC ) ? 0 : 1 );
|
||
|
return true;
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_EAX_OCCPRESET && HIWORD( wParam ) == CBN_SELCHANGE )
|
||
|
{
|
||
|
HWND combo = GetDlgItem( hWnd, IDC_EAX_OCCPRESET );
|
||
|
int idx = ComboBox_GetCurSel( combo );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
// Load from presets
|
||
|
pblock->SetValue( (ParamID)kEAXTempOcclusion, 0, (int)fPresets[ idx ].fOcc );
|
||
|
pblock->SetValue( (ParamID)kEAXTempOcclusionLFRatio, 0, fPresets[ idx ].fLFRatio );
|
||
|
pblock->SetValue( (ParamID)kEAXTempOcclusionRoomRatio, 0, fPresets[ idx ].fRoomRatio );
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
return plSingleCompSelProc::DlgProc( t, map, hWnd, msg, wParam, lParam );
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
static plEAXPropsDlgProc sEAXPropsDlgProc;
|
||
|
|
||
|
//// ParamBlock for Source EAX Properties Rollout ///////////////////////////////////////////////
|
||
|
// Note: we can't make this a real ParamBlock and do P_INCLUDE_PARAMS because, in Discreet's
|
||
|
// amazing method of doing things, we can't INCLUDE more than one ParamBlock in any other PB.
|
||
|
// So either we chain them together here (and thus make them dependent on one another, which
|
||
|
// is lame) or we just make the whole damned thing a #define, which is all P_INCLUDE_PARAMS
|
||
|
// really does anyway.
|
||
|
|
||
|
#define sSndEAXPropsParamHeader kSndEAXParams, IDD_COMP_EAXBUFFER, IDS_COMP_EAXBUFFER, 0, APPENDROLL_CLOSED, &sEAXPropsDlgProc
|
||
|
//static ParamBlockDesc2 sSndEAXPropsParamTemplate
|
||
|
//(
|
||
|
/// Main def
|
||
|
// plComponent::kBlkComp + 1, _T("sndEAXProps"), 0, nil, P_AUTO_UI + P_MULTIMAP + P_AUTO_CONSTRUCT, plComponent::kRefComp,
|
||
|
|
||
|
// 1,
|
||
|
// kSndEAXParams, IDD_COMP_EAXBUFFER, IDS_COMP_EAXBUFFER, 0, 0, nil,
|
||
|
|
||
|
#define sSndEAXPropsParamTemplate \
|
||
|
\
|
||
|
kEAXEnabled, _T("eaxEnabled"), TYPE_BOOL, 0, 0, \
|
||
|
p_default, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ENABLE, \
|
||
|
p_enable_ctrls, 10, kEAXRoom, kEAXRoomHF, kEAXRoomAuto, kEAXRoomHFAuto, \
|
||
|
kEAXOutsideVolHF, kEAXAirAbsorptionFact, kEAXRoomRolloffFact, kEAXDopplerFact, kEAXRolloffFact, \
|
||
|
kEAXEnableOcclusion, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXRoom, _T("eaxRoom"), TYPE_INT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_ROOM_EDIT, IDC_EAX_ROOM, 8, \
|
||
|
p_range, -10000, 1000, \
|
||
|
p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXRoomHF, _T("eaxRoomHF"), TYPE_INT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_ROOMHF_EDIT, IDC_EAX_ROOMHF, 8, \
|
||
|
p_range, -10000, 0, \
|
||
|
p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXRoomAuto, _T( "eaxRoomAuto" ), TYPE_BOOL, 0, 0, \
|
||
|
p_default, 1, \
|
||
|
p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ROOMAUTO, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXRoomHFAuto, _T( "eaxRoomHFAuto" ), TYPE_BOOL, 0, 0, \
|
||
|
p_default, 1, \
|
||
|
p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ROOMHFAUTO, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXOutsideVolHF, _T("eaxOutsideVolHF"), TYPE_INT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_OUTSIDEVOLHF_EDIT, IDC_EAX_OUTSIDEVOLHF, 8, \
|
||
|
p_range, -10000, 0, \
|
||
|
p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXAirAbsorptionFact, _T("eaxAirAbsorptionFact"), TYPE_FLOAT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_AIRABSORPTFACT_EDIT, IDC_EAX_AIRABSORPTFACT, 8, \
|
||
|
p_range, 0.f, 10.f, \
|
||
|
p_default, 1.f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXRoomRolloffFact, _T("eaxRoomRolloffFact"), TYPE_FLOAT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_ROOMROLLOFFFACT_EDIT, IDC_EAX_ROOMROLLOFFFACT, 8, \
|
||
|
p_range, 0.f, 10.f, \
|
||
|
p_default, 0.f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXDopplerFact, _T("eaxDopplerFact"), TYPE_FLOAT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_DOPPLERFACT_EDIT, IDC_EAX_DOPPLERFACT, 8, \
|
||
|
p_range, 0.f, 10.f, \
|
||
|
p_default, 0.f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXRolloffFact, _T("eaxRolloffFact"), TYPE_FLOAT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_ROLLOFFFACT_EDIT, IDC_EAX_ROLLOFFFACT, 8, \
|
||
|
p_range, 0.f, 10.f, \
|
||
|
p_default, 0.f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXEnableOcclusion, _T("eaxEnableOcclusion"), TYPE_BOOL, 0, 0, \
|
||
|
p_default, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ENABLEOCCLUSION, \
|
||
|
p_enable_ctrls, 6, kEAXOcclusionRegion, kEAXTempOcclusion, kEAXTempOcclusionLFRatio, kEAXTempOcclusionRoomRatio, \
|
||
|
kEAXTempOcclusionDirectRatio, kEAXTempOccSwapper, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXOcclusionRegion, _T("eaxOcclusionRegion"), TYPE_INODE, 0, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXStartOcclusion, _T("eaxStartOcclusion"), TYPE_INT, 0, 0, \
|
||
|
p_range, -10000, 0, p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXStartOcclusionLFRatio, _T("eaxStartOccLFRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_range, 0.f, 1.f, p_default, 0.25f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXStartOcclusionRoomRatio, _T("eaxStartOccRoomRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_range, 0.f, 10.f, p_default, 1.5f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXStartOcclusionDirectRatio, _T("eaxStartOccDirectRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_range, 0.f, 10.f, p_default, 1.0f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXEndOcclusion, _T("eaxEndOcclusion"), TYPE_INT, 0, 0, \
|
||
|
p_range, -10000, 0, p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXEndOcclusionLFRatio, _T("eaxEndOccLFRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_range, 0.f, 1.f, p_default, 0.25f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXEndOcclusionRoomRatio, _T("eaxEndOccRoomRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_range, 0.f, 10.f, p_default, 1.5f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXEndOcclusionDirectRatio, _T("eaxEndOccDirectRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_range, 0.f, 10.f, p_default, 1.0f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXWhichOccSwapped, _T("eaxWhichOccSwapped"), TYPE_INT, 0, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXTempOccSwapper, _T("eaxOccSwapper"), TYPE_INT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_RADIO, 2, IDC_EAX_STARTOCC, IDC_EAX_ENDOCC, \
|
||
|
p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXTempOcclusion, _T("eaxTempOcclusion"), TYPE_INT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_OCCLUSION_EDIT, IDC_EAX_OCCLUSION, 8, \
|
||
|
p_range, -10000, 0, p_default, 0, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXTempOcclusionLFRatio, _T("eaxTempOccLFRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_OCCLFRATIO_EDIT, IDC_EAX_OCCLFRATIO, 8, \
|
||
|
p_range, 0.f, 1.f, p_default, 0.25f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXTempOcclusionRoomRatio, _T("eaxTempOccRoomRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_OCCROOMRATIO_EDIT, IDC_EAX_OCCROOMRATIO, 8, \
|
||
|
p_range, 0.f, 10.f, p_default, 1.5f, \
|
||
|
end, \
|
||
|
\
|
||
|
kEAXTempOcclusionDirectRatio, _T("eaxTempOccDirectRatio"), TYPE_FLOAT, 0, 0, \
|
||
|
p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_OCCDIRECTRATIO_EDIT, IDC_EAX_OCCDIRECTRATIO, 8, \
|
||
|
p_range, 0.f, 10.f, p_default, 1.0f, \
|
||
|
end
|
||
|
|
||
|
// , end
|
||
|
//);
|
||
|
|
||
|
|
||
|
void plBaseSoundEmitterComponent::IGrabEAXParams( plSound *sound, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
sEAXPropsDlgProc.FlushSwappedPBs();
|
||
|
|
||
|
plEAXSourceSettings &settings = sound->GetEAXSettings();
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kEAXEnabled ) )
|
||
|
{
|
||
|
settings.Enable( true );
|
||
|
settings.SetRoomParams( fCompPB->GetInt( (ParamID)kEAXRoom ), fCompPB->GetInt( (ParamID)kEAXRoomHF ),
|
||
|
fCompPB->GetInt( (ParamID)kEAXRoomAuto ), fCompPB->GetInt( (ParamID)kEAXRoomHFAuto ) );
|
||
|
settings.SetOutsideVolHF( fCompPB->GetInt( (ParamID)kEAXOutsideVolHF ) );
|
||
|
settings.SetFactors( fCompPB->GetFloat( (ParamID)kEAXAirAbsorptionFact ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXRoomRolloffFact ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXDopplerFact ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXRolloffFact ) );
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kEAXEnableOcclusion ) )
|
||
|
{
|
||
|
settings.GetSoftStarts().SetOcclusion( fCompPB->GetInt( (ParamID)kEAXStartOcclusion ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXStartOcclusionLFRatio ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXStartOcclusionRoomRatio ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXStartOcclusionDirectRatio ) );
|
||
|
settings.GetSoftEnds().SetOcclusion( fCompPB->GetInt( (ParamID)kEAXEndOcclusion ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXEndOcclusionLFRatio ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXEndOcclusionRoomRatio ),
|
||
|
fCompPB->GetFloat( (ParamID)kEAXEndOcclusionDirectRatio ) );
|
||
|
|
||
|
plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent( fCompPB->GetINode( (ParamID)kEAXOcclusionRegion ) );
|
||
|
if( softComp != nil )
|
||
|
{
|
||
|
plKey softKey = softComp->GetSoftVolume();
|
||
|
if( softKey != nil )
|
||
|
{
|
||
|
// Make sure we set checkListener on the sucker
|
||
|
plSoftVolume *vol = plSoftVolume::ConvertNoRef( softKey->GetObjectPtr() );
|
||
|
if( vol != nil )
|
||
|
{
|
||
|
vol->SetCheckListener();
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( softKey, TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, 0, plSound::kRefSoftOcclusionRegion ), plRefFlags::kActiveRef );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pErrMsg->Set(true, "Sound Emitter Error", "The Sound emitter component %s is checked to use an occlusion soft region, but no soft region is specified. Ignoring setting.", GetINode()->GetName() ).Show();
|
||
|
pErrMsg->Set(false);
|
||
|
settings.GetSoftStarts().Reset();
|
||
|
settings.GetSoftEnds().Reset();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
settings.GetSoftStarts().Reset();
|
||
|
settings.GetSoftEnds().Reset();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
settings.Enable( false );
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// SoundEmitter Component
|
||
|
//
|
||
|
//
|
||
|
|
||
|
/*
|
||
|
class plBaseComponentProc : public ParamMap2UserDlgProc
|
||
|
{
|
||
|
protected:
|
||
|
void ILoadComboBox(HWND hComboBox, const char *names[])
|
||
|
{
|
||
|
SendMessage(hComboBox, CB_RESETCONTENT, 0, 0);
|
||
|
for (int i = 0; names[i]; i++)
|
||
|
SendMessage(hComboBox, CB_ADDSTRING, 0, (LPARAM)names[i]);
|
||
|
SendMessage(hComboBox, CB_SETCURSEL, 0, 0);
|
||
|
}
|
||
|
|
||
|
void ILoadListBox(HWND hListBox, IParamBlock2 *pb, int param, const char *names[])
|
||
|
{
|
||
|
SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
|
||
|
for (int i = 0; i < pb->Count(param); i++)
|
||
|
{
|
||
|
int idx = pb->GetInt(param, 0, i);
|
||
|
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)names[idx]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IConvertOldVolume( IParamBlock2 *pb )
|
||
|
{
|
||
|
int oldVol = pb->GetInt( kOldSoundVolumeSlider, 0 );
|
||
|
if( oldVol != 4999 )
|
||
|
{
|
||
|
float v = (float)( oldVol - 5000 ) / 5000.f;
|
||
|
pb->SetValue( kSoundVolumeSlider, 0, v );
|
||
|
pb->SetValue( kOldSoundVolumeSlider, 0, 4999 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
*/
|
||
|
class plSound3DEmitterComponent : public plBaseSoundEmitterComponent
|
||
|
{
|
||
|
public:
|
||
|
plSound3DEmitterComponent();
|
||
|
virtual ~plSound3DEmitterComponent();
|
||
|
|
||
|
// Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
virtual hsBool IsLocalOnly( void ) const { if( fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ) return true; else return false; }
|
||
|
|
||
|
|
||
|
virtual hsBool ConvertGrouped( plMaxNode *baseNode, hsTArray<plBaseSoundEmitterComponent *> &groupArray, plErrorMsg *pErrMsg );
|
||
|
|
||
|
protected:
|
||
|
|
||
|
bool IValidate(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
virtual hsBool IAllowStereoFiles( void ) const { return false; }
|
||
|
|
||
|
void ISetParameters( plWin32Sound *destSound, plErrorMsg *pErrMsg );
|
||
|
|
||
|
virtual hsBool IGetCategoryList( char **&catList, int *&catKonstantList );
|
||
|
};
|
||
|
|
||
|
class plSoundComponentProc : public plAudioBaseComponentProc
|
||
|
{
|
||
|
hsBool fHandleCategory;
|
||
|
int fCategoryCtrlID;
|
||
|
ParamID fCategoryParamID;
|
||
|
|
||
|
public:
|
||
|
void DeleteThis() {}
|
||
|
|
||
|
void ILoadLoops(HWND hLoop, IParamBlock2 *pb)
|
||
|
{
|
||
|
SendMessage(hLoop, CB_RESETCONTENT, 0, 0);
|
||
|
SendMessage(hLoop, CB_ADDSTRING, 0, (LPARAM)"(Entire Sound)");
|
||
|
|
||
|
const char *loop = pb->GetStr(kSoundLoopName);
|
||
|
if (!loop)
|
||
|
loop = "";
|
||
|
|
||
|
SegmentMap *segMap = GetCompWaveSegmentMap(pb->GetStr(kSoundFileName));
|
||
|
|
||
|
if (segMap)
|
||
|
{
|
||
|
for (SegmentMap::iterator it = segMap->begin(); it != segMap->end(); it++)
|
||
|
{
|
||
|
SegmentSpec *spec = it->second;
|
||
|
int idx = SendMessage(hLoop, CB_ADDSTRING, 0, (LPARAM)spec->fName);
|
||
|
SendMessage(hLoop, CB_SETITEMDATA, idx, 1);
|
||
|
|
||
|
if (!strcmp(spec->fName, loop))
|
||
|
SendMessage(hLoop, CB_SETCURSEL, idx, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
DeleteSegmentMap(segMap);
|
||
|
}
|
||
|
|
||
|
if (SendMessage(hLoop, CB_GETCURSEL, 0, 0) == CB_ERR)
|
||
|
SendMessage(hLoop, CB_SETCURSEL, 0, 0);
|
||
|
}
|
||
|
|
||
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch( msg )
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
IParamBlock2 *pblock = map->GetParamBlock();
|
||
|
|
||
|
plSound3DEmitterComponent *soundComp = (plSound3DEmitterComponent *)map->GetParamBlock()->GetOwner();
|
||
|
hsAssert( soundComp != nil, "Problem trying to select a sound file" );
|
||
|
|
||
|
IUpdateSoundButton( soundComp, hWnd, IDC_COMP_SOUND3D_FILENAME_BTN, plBaseSoundEmitterComponent::kBaseSound );
|
||
|
|
||
|
IConvertOldVolume( pblock );
|
||
|
|
||
|
fHandleCategory = soundComp->UpdateCategories( hWnd, fCategoryCtrlID, fCategoryParamID );
|
||
|
}
|
||
|
|
||
|
{
|
||
|
ILoadLoops(GetDlgItem(hWnd, IDC_LOOP_COMBO), map->GetParamBlock());
|
||
|
#if 0
|
||
|
map->SetTooltip(kSoundFileName, true, _T("A sound file name."));
|
||
|
map->SetTooltip(kLoopBegin, true, _T("The distance, in feet, at which the sound begins to be less audible."));
|
||
|
map->SetTooltip(kLoopEnd, true, _T("The distance, in feet, at which the sound is no longer audible."));
|
||
|
map->SetTooltip(kSoundLoopSegBeg2, true, _T("The distance, in feet, at which the sound begins to be less audible."));
|
||
|
map->SetTooltip(kSoundLoopSegEnd2, true, _T("The distance, in feet, at which the sound is no longer audible."));
|
||
|
map->SetTooltip(kSoundAutoStartCkBx, true, _T("Check to play the sound file upon game start."));
|
||
|
map->SetTooltip(kSoundLoopSegBegDDList, true, _T("The time, keyframe or percentage at which looping is to begin."));
|
||
|
map->SetTooltip(kSoundLoopSegEndDDList, true, _T("The time, keyframe or percentage at which looping is to end."));
|
||
|
#endif
|
||
|
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_LOOP_COMBO)
|
||
|
{
|
||
|
int idx = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
|
||
|
if (idx == CB_ERR || SendMessage((HWND)lParam, CB_GETITEMDATA, idx, 0) == 0)
|
||
|
map->GetParamBlock()->SetValue((ParamID)kSoundLoopName, 0, "");
|
||
|
else
|
||
|
{
|
||
|
char buf[256];
|
||
|
SendMessage((HWND)lParam, CB_GETLBTEXT, idx, (LPARAM)buf);
|
||
|
map->GetParamBlock()->SetValue((ParamID)kSoundLoopName, 0, buf);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_COMP_SOUND3D_FILENAME_BTN )
|
||
|
{
|
||
|
plSound3DEmitterComponent *soundComp = (plSound3DEmitterComponent *)map->GetParamBlock()->GetOwner();
|
||
|
hsAssert( soundComp != nil, "Problem trying to select a sound file" );
|
||
|
|
||
|
ISelectSoundFile( soundComp, hWnd, IDC_COMP_SOUND3D_FILENAME_BTN, plBaseSoundEmitterComponent::kBaseSound );
|
||
|
}
|
||
|
else if( fHandleCategory && LOWORD( wParam ) == fCategoryCtrlID )
|
||
|
{
|
||
|
HWND ctrl = GetDlgItem( hWnd, fCategoryCtrlID );
|
||
|
int idx = ComboBox_GetCurSel( ctrl );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
int cat = ComboBox_GetItemData( ctrl, idx );
|
||
|
map->GetParamBlock()->SetValue( (ParamID)fCategoryParamID, 0, cat );
|
||
|
}
|
||
|
else
|
||
|
map->GetParamBlock()->SetValue( (ParamID)fCategoryParamID, 0, (int)0 );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return plAudioBaseComponentProc::DlgProc( t, map, hWnd, msg, wParam, lParam );
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
class plSoundFadeParamsDlgProc : public plAudioBaseComponentProc
|
||
|
{
|
||
|
public:
|
||
|
void DeleteThis() {}
|
||
|
|
||
|
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
|
||
|
{
|
||
|
const char *types[] = { "Linear", "Logarithmic", "Exponential", NULL };
|
||
|
IParamBlock2 *pb = map->GetParamBlock();
|
||
|
BOOL enable;
|
||
|
|
||
|
|
||
|
switch( msg )
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
// Load fade types
|
||
|
|
||
|
ILoadComboBox( GetDlgItem( hWnd, IDC_SOUND3D_INTYPE ), types );
|
||
|
ILoadComboBox( GetDlgItem( hWnd, IDC_SOUND3D_OUTTYPE ), types );
|
||
|
|
||
|
SendDlgItemMessage( hWnd, IDC_SOUND3D_INTYPE, CB_SETCURSEL, (WPARAM)pb->GetInt( (ParamID)kSndFadeInType, 0 ), 0 );
|
||
|
SendDlgItemMessage( hWnd, IDC_SOUND3D_OUTTYPE, CB_SETCURSEL, (WPARAM)pb->GetInt( (ParamID)kSndFadeOutType, 0 ), 0 );
|
||
|
|
||
|
enable = pb->GetInt( (ParamID)kSndFadeInEnable, 0 ) ? TRUE : FALSE;
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INTYPE ), enable );
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INLENGTH ), enable );
|
||
|
|
||
|
enable = pb->GetInt( (ParamID)kSndFadeOutEnable, 0 ) ? TRUE : FALSE;
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTTYPE ), enable );
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTLENGTH ), enable );
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if( HIWORD( wParam ) == CBN_SELCHANGE )
|
||
|
{
|
||
|
if( LOWORD( wParam ) == IDC_SOUND3D_INTYPE )
|
||
|
pb->SetValue( (ParamID)kSndFadeInType, 0, (int)SendDlgItemMessage( hWnd, IDC_SOUND3D_INTYPE, CB_GETCURSEL, 0, 0 ) );
|
||
|
else if( LOWORD( wParam ) == IDC_SOUND3D_OUTTYPE )
|
||
|
pb->SetValue( (ParamID)kSndFadeOutType, 0, (int)SendDlgItemMessage( hWnd, IDC_SOUND3D_OUTTYPE, CB_GETCURSEL, 0, 0 ) );
|
||
|
}
|
||
|
|
||
|
else if( LOWORD( wParam ) == IDC_SOUND3D_INENABLE )
|
||
|
{
|
||
|
// Enable/disable controls manually
|
||
|
enable = pb->GetInt( (ParamID)kSndFadeInEnable, 0 ) ? TRUE : FALSE;
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INTYPE ), enable );
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INLENGTH ), enable );
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_SOUND3D_OUTENABLE )
|
||
|
{
|
||
|
// Enable/disable controls manually
|
||
|
enable = pb->GetInt( (ParamID)kSndFadeOutEnable, 0 ) ? TRUE : FALSE;
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTTYPE ), enable );
|
||
|
EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTLENGTH ), enable );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// For the paramblock below.
|
||
|
static plSoundComponentProc gSoundCompProc;
|
||
|
static plSoundFadeParamsDlgProc gSoundFadeParamsProc;
|
||
|
|
||
|
//Max desc stuff necessary below.
|
||
|
CLASS_DESC(plSound3DEmitterComponent, gSound3DEmitterDesc, "Sound 3D", "Sound3D", COMP_TYPE_AUDIO, SOUND_3D_COMPONENT_ID)
|
||
|
|
||
|
ParamBlockDesc2 gSound3DEmitterBk
|
||
|
(
|
||
|
plComponent::kBlkComp, _T("3D Sound"), 0, &gSound3DEmitterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
|
||
|
|
||
|
6, // Number of rollouts
|
||
|
sSoundSharedPBHeader(IDS_COMP_SOUNDBASE),
|
||
|
kS3DBaseParams, IDD_COMP_SOUND3D, IDS_COMP_SOUND3DS, 0, 0, &gSoundCompProc,
|
||
|
kS3DSoftVolumeParams, IDD_COMP_SOUND_SOFTPARAMS, IDS_COMP_SOUNDSOFTPARAMS, 0, 0, &gSoundSoftVolumeSelProc,
|
||
|
sSndWaveformPropsHeader,
|
||
|
sSndEAXPropsParamHeader,
|
||
|
|
||
|
// Included paramblock
|
||
|
&sSoundSharedPB,
|
||
|
|
||
|
// Waveform props define
|
||
|
sSndWaveformPropsParamTemplate,
|
||
|
|
||
|
// params
|
||
|
|
||
|
kSndIsLocalOnly, _T("noNetworkSynch"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_SND_LOCALONLY,
|
||
|
end,
|
||
|
|
||
|
|
||
|
kMinFallOffRad, _T("minFalloff"), TYPE_INT, 0, 0,
|
||
|
p_range, 1, 1000000000,
|
||
|
p_default, 1,
|
||
|
p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_POS_INT,
|
||
|
IDC_COMP_SOUND3D_EDIT3, IDC_COMP_SOUND3D_SPIN3, SPIN_AUTOSCALE,
|
||
|
end,
|
||
|
|
||
|
kMaxFallOffRad, _T("maxFalloff"), TYPE_INT, 0, 0,
|
||
|
p_range, 1, 1000000000,
|
||
|
p_default, 1000000000,
|
||
|
p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_POS_INT,
|
||
|
IDC_COMP_SOUND3D_EDIT4, IDC_COMP_SOUND3D_SPIN4, SPIN_AUTOSCALE,
|
||
|
end,
|
||
|
|
||
|
kSoundConeBool, _T("SoundCone"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_enable_ctrls, 3, kSoundIConeAngle, kSoundOConeAngle, kSoundOConeVolumeSlider,
|
||
|
p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND3D_CONEEFFECT_CKBX,
|
||
|
end,
|
||
|
|
||
|
|
||
|
kSoundIConeAngle, _T("insideConeAngle"), TYPE_INT, 0, 0,
|
||
|
p_range, 0, 360,
|
||
|
p_default, 360,
|
||
|
p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_INT,
|
||
|
IDC_COMP_SOUND3D_ICONEANGLE_EDIT, IDC_COMP_SOUND3D_ICONEANGLE_SPIN, 1.0f,
|
||
|
end,
|
||
|
|
||
|
kSoundOConeAngle, _T("outsideConeAngle"), TYPE_INT, 0, 0,
|
||
|
p_range, 0, 360,
|
||
|
p_default, 360,
|
||
|
p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_INT,
|
||
|
IDC_COMP_SOUND3D_OCONEANGLE_EDIT, IDC_COMP_SOUND3D_OCONEANGLE_SPIN, 1.0f,
|
||
|
end,
|
||
|
|
||
|
kSoundOConeVolumeSlider, _T("outsideConeVolSlider"), TYPE_INT, 0, 0,
|
||
|
p_ui, kS3DBaseParams, TYPE_SLIDER, EDITTYPE_INT, IDC_COMP_SOUND3D_SLIDERVIEWER2, IDC_COMP_SOUND3D_VOLSLIDER2, 4,
|
||
|
p_range, 5000, 10000,
|
||
|
p_default, 5000,
|
||
|
end,
|
||
|
|
||
|
/// Soft Region/Volume Parameters rollout
|
||
|
kSndSoftRegionEnable, _T( "enableSoftRegion" ), TYPE_BOOL, 0, 0,
|
||
|
p_ui, kS3DSoftVolumeParams, TYPE_SINGLECHEKBOX, IDC_SOUND_SOFTENABLE,
|
||
|
p_default, FALSE,
|
||
|
end,
|
||
|
|
||
|
kSndSoftRegion, _T("softRegion"), TYPE_INODE, 0, 0,
|
||
|
p_prompt, IDS_COMP_SOUNDSOFTSELECT,
|
||
|
p_accessor, &gSoundSoftVolAccessor,
|
||
|
end,
|
||
|
|
||
|
kSndIncidental, _T("isIncidental"), TYPE_INT, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_SND_INCIDENTAL,
|
||
|
end,
|
||
|
|
||
|
sSndEAXPropsParamTemplate, // it's a #define
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
|
||
|
plSound3DEmitterComponent::plSound3DEmitterComponent()
|
||
|
{
|
||
|
fClassDesc = &gSound3DEmitterDesc;
|
||
|
fClassDesc->MakeAutoParamBlocks(this);
|
||
|
}
|
||
|
|
||
|
plSound3DEmitterComponent::~plSound3DEmitterComponent()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//// IGetCategoryList ///////////////////////////////////////////////////////////////////////////
|
||
|
// Returns a list of the categories and konstants supported for this type of sound
|
||
|
|
||
|
hsBool plSound3DEmitterComponent::IGetCategoryList( char **&catList, int *&catKonstantList )
|
||
|
{
|
||
|
static char *cats[] = { "Background Music", "Ambience", "Sound FX", "GUI", "NPC Voice", "" };
|
||
|
static int catEnums[] = { plSound::kBackgroundMusic, plSound::kAmbience, plSound::kSoundFX, plSound::kGUISound, plSound::kNPCVoices };
|
||
|
|
||
|
catList = cats;
|
||
|
catKonstantList = catEnums;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool plSound3DEmitterComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::SetupProperties( pNode, pErrMsg );
|
||
|
}
|
||
|
|
||
|
bool plSound3DEmitterComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::IValidate( node, pErrMsg );
|
||
|
}
|
||
|
|
||
|
hsBool plSound3DEmitterComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::PreConvert( node, pErrMsg, SOUND_3D_COMPONENT_ID );
|
||
|
}
|
||
|
|
||
|
void plSound3DEmitterComponent::ISetParameters( plWin32Sound *destSound, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
ISetBaseParameters( destSound, pErrMsg );
|
||
|
|
||
|
int min = fCompPB->GetInt( (ParamID)kMinFallOffRad );
|
||
|
int max = fCompPB->GetInt( (ParamID)kMaxFallOffRad );
|
||
|
float Vol = IGetDigitalVolume();
|
||
|
|
||
|
int OutVol, innerCone, outerCone;
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kSoundConeBool ) )
|
||
|
{
|
||
|
OutVol = fCompPB->GetInt( (ParamID)kSoundOConeVolumeSlider );
|
||
|
innerCone = fCompPB->GetInt( (ParamID)kSoundIConeAngle );
|
||
|
outerCone = fCompPB->GetInt( (ParamID)kSoundOConeAngle );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
OutVol = 0;
|
||
|
innerCone = 360;
|
||
|
outerCone = 360;
|
||
|
}
|
||
|
|
||
|
destSound->SetMax(max);
|
||
|
destSound->SetMin(min);
|
||
|
destSound->SetOuterVolume(OutVol - 10000);
|
||
|
destSound->SetConeAngles(innerCone, outerCone);
|
||
|
destSound->SetProperty( plSound::kPropLocalOnly, fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ? true : false );
|
||
|
destSound->SetPriority( fCompPB->GetInt( (ParamID)kSndPriority ) );
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kSndIncidental ) )
|
||
|
{
|
||
|
destSound->SetProperty( plSound::kPropIncidental, true );
|
||
|
|
||
|
// Refactor the priority, since incidental priorities are a different range
|
||
|
int pri = fCompPB->GetInt( (ParamID)kSndPriority );
|
||
|
pri = pri < 1 ? 1 : pri;
|
||
|
destSound->SetPriority( pri );
|
||
|
}
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kSndDisableLOD ) )
|
||
|
{
|
||
|
// Force LOD off on this sound
|
||
|
destSound->SetProperty( plSound::kPropDisableLOD, true );
|
||
|
}
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kSndChannelSelect ) )
|
||
|
destSound->SetChannelSelect( plWin32Sound::kRightChannel );
|
||
|
else
|
||
|
destSound->SetChannelSelect( plWin32Sound::kLeftChannel );
|
||
|
|
||
|
IGrabSoftRegion( destSound, pErrMsg );
|
||
|
IGrabEAXParams( destSound, pErrMsg );
|
||
|
}
|
||
|
|
||
|
hsBool plSound3DEmitterComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
if (!fValidNodes[node])
|
||
|
return false;
|
||
|
|
||
|
if( fCreateGrouped )
|
||
|
return true;
|
||
|
|
||
|
char* fileName = GetSoundFileName( kBaseSound );
|
||
|
|
||
|
int fIndex = -1;
|
||
|
if (fIndices.find(node) != fIndices.end())
|
||
|
fIndex = fIndices[node];
|
||
|
|
||
|
plSoundBuffer *srcBuffer = IProcessSourceBuffer( node, pErrMsg );
|
||
|
if( srcBuffer == nil )
|
||
|
return false;
|
||
|
|
||
|
const plAudioInterface* ai = node->GetSceneObject()->GetAudioInterface();
|
||
|
plWinAudible* pAudible = (plWinAudible*)ai->GetAudible();
|
||
|
|
||
|
|
||
|
char keyName[ 256 ];
|
||
|
sprintf( keyName, "%s", GetINode()->GetName());
|
||
|
|
||
|
plWin32Sound *sound = nil;
|
||
|
|
||
|
if (!strcmp(node->GetName(), "LinkSoundSource"))
|
||
|
sound = TRACKED_NEW plWin32LinkSound;
|
||
|
else
|
||
|
{
|
||
|
#if 0
|
||
|
sound = TRACKED_NEW plWin32StaticSound;
|
||
|
#else
|
||
|
/// New method, still in testing: any sounds over 4 seconds get made into streaming sounds
|
||
|
if( srcBuffer->GetDataLengthInSecs() > 4.f )
|
||
|
sound = TRACKED_NEW plWin32StreamingSound;
|
||
|
else
|
||
|
sound = TRACKED_NEW plWin32StaticSound;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
hsgResMgr::ResMgr()->NewKey(keyName, sound, node->GetLocation(), node->GetLoadMask());
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( srcBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef );
|
||
|
|
||
|
if( pAudible->AddSound( sound, fIndex, true ) )
|
||
|
{
|
||
|
ISetParameters( sound, pErrMsg );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Converts an array of components into a single grouped sound
|
||
|
hsBool plSound3DEmitterComponent::ConvertGrouped( plMaxNode *baseNode, hsTArray<plBaseSoundEmitterComponent *> &groupArray, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
char keyName[ 256 ];
|
||
|
|
||
|
|
||
|
if( !fValidNodes[ baseNode ] || !fCreateGrouped )
|
||
|
return false;
|
||
|
|
||
|
|
||
|
// First, we need to grab the sound buffers from ALL the components and merge them into one big buffer.
|
||
|
// Also build up an array of positions to feed to our groupedSound later.
|
||
|
// Also also assign all the components the audioInterface index (will be the same one, so we need to
|
||
|
// allocate it here).
|
||
|
// Also also also build up a volume array parallel to startPoses that represents the individual volume
|
||
|
// setting for each sound in the group
|
||
|
hsTArray<UInt32> startPoses;
|
||
|
hsTArray<hsScalar> volumes;
|
||
|
hsLargeArray<UInt8> mergedData;
|
||
|
int i;
|
||
|
plWAVHeader mergedHeader;
|
||
|
|
||
|
for( i = 0; i < groupArray.GetCount(); i++ )
|
||
|
{
|
||
|
// Make sure they're all 3D sounds...
|
||
|
if( groupArray[ i ]->ClassID() != SOUND_3D_COMPONENT_ID )
|
||
|
{
|
||
|
char msg[ 512 ];
|
||
|
sprintf( msg, "The sound component %s isn't a 3D sound, which is necessary for making grouped sounds. "
|
||
|
"Make sure all the sounds in this group are 3D sounds.", groupArray[ i ]->GetINode()->GetName() );
|
||
|
IShowError( kSrcBufferInvalid, msg, baseNode->GetName(), pErrMsg );
|
||
|
|
||
|
// Attempt to recover
|
||
|
startPoses.Append( mergedData.GetCount() );
|
||
|
volumes.Append( 1.f );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Grab the buffer for this sound directly from the original source
|
||
|
char *fileName = groupArray[ i ]->GetSoundFileName( kBaseSound );
|
||
|
|
||
|
plSoundBuffer *buffer = TRACKED_NEW plSoundBuffer( fileName );
|
||
|
if( !buffer->IsValid() || !buffer->EnsureInternal() )
|
||
|
{
|
||
|
// OK, because some *cough* machines are completely stupid and don't load AssetMan scenes with
|
||
|
// AssetMan plugins, we need to do a really stupid fallback search to the current exporting directory.
|
||
|
const char *plasmaDir = plMaxConfig::GetClientPath();
|
||
|
bool worked = false;
|
||
|
if( plasmaDir != nil )
|
||
|
{
|
||
|
char newPath[ MAX_PATH ], *c;
|
||
|
strcpy( newPath, plasmaDir );
|
||
|
strcat( newPath, "sfx\\" );
|
||
|
|
||
|
c = strrchr( fileName, '\\' );
|
||
|
if( c == nil )
|
||
|
c = strrchr( fileName, '/' );
|
||
|
if( c == nil )
|
||
|
c = fileName;
|
||
|
else
|
||
|
c++;
|
||
|
strcat( newPath, c );
|
||
|
|
||
|
// Got a path to try, so try it!
|
||
|
delete buffer;
|
||
|
buffer = TRACKED_NEW plSoundBuffer( newPath );
|
||
|
if( buffer->IsValid() && buffer->EnsureInternal() )
|
||
|
worked = true;
|
||
|
}
|
||
|
|
||
|
if( !worked )
|
||
|
{
|
||
|
char msg[ 512 ];
|
||
|
sprintf( msg, "The sound file %s cannot be loaded for component %s.", fileName, groupArray[ i ]->GetINode()->GetName() );
|
||
|
IShowError( kSrcBufferInvalid, msg, baseNode->GetName(), pErrMsg );
|
||
|
delete buffer;
|
||
|
|
||
|
// Attempt to recover
|
||
|
startPoses.Append( mergedData.GetCount() );
|
||
|
volumes.Append( 1.f );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get a header (they should all be the same)
|
||
|
if( i == 0 )
|
||
|
mergedHeader = buffer->GetHeader();
|
||
|
else
|
||
|
{
|
||
|
if( memcmp( &mergedHeader, &buffer->GetHeader(), sizeof( mergedHeader ) ) != 0 )
|
||
|
{
|
||
|
char msg[ 512 ];
|
||
|
sprintf( msg, "The format for sound file %s does not match the format for the other grouped sounds on node %s. "
|
||
|
"Make sure the sounds are all the same format.", fileName, baseNode->GetName() );
|
||
|
IShowError( kMergeSourceFormatMismatch, msg, baseNode->GetName(), pErrMsg );
|
||
|
delete buffer;
|
||
|
|
||
|
// Attempt to recover
|
||
|
startPoses.Append( mergedData.GetCount() );
|
||
|
volumes.Append( 1.f );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Grab the data from this buffer and merge it
|
||
|
// HACK: SetCount() won't copy the old data over, Expand() won't up the use count, so do
|
||
|
// an expand-and-setCount combo.
|
||
|
UInt32 pos = mergedData.GetCount();
|
||
|
startPoses.Append( pos );
|
||
|
mergedData.Expand( pos + buffer->GetDataLength() );
|
||
|
mergedData.SetCount( pos + buffer->GetDataLength() );
|
||
|
memcpy( &mergedData[ pos ], buffer->GetData(), buffer->GetDataLength() );
|
||
|
|
||
|
delete buffer;
|
||
|
|
||
|
// Also keep track of what the volume should be for this particular sound
|
||
|
volumes.Append( groupArray[ i ]->GetSoundVolume() );
|
||
|
}
|
||
|
|
||
|
/// We got a merged buffer, so make a plSoundBuffer from it
|
||
|
int index = -1;
|
||
|
if( fIndices.find( baseNode ) != fIndices.end() )
|
||
|
index = fIndices[ baseNode ];
|
||
|
|
||
|
sprintf( keyName, "%s_MergedSound", GetINode()->GetName() );
|
||
|
|
||
|
plKey buffKey = baseNode->FindPageKey( plSoundBuffer::Index(), keyName );
|
||
|
if( buffKey != nil )
|
||
|
plPluginResManager::ResMgr()->NukeKeyAndObject( buffKey );
|
||
|
|
||
|
// Create a new one...
|
||
|
plSoundBuffer *mergedBuffer = TRACKED_NEW plSoundBuffer();
|
||
|
mergedBuffer->SetInternalData( mergedHeader, mergedData.GetCount(), mergedData.AcquireArray() );
|
||
|
mergedData.Reset();
|
||
|
// The buffer may be shared across multiple sources. We could or together the LoadMasks of all
|
||
|
// the nodes that use it, or we can just go with the default loadmask of Always load, and
|
||
|
// count on it never getting dragged into memory if nothing that references it does.
|
||
|
hsgResMgr::ResMgr()->NewKey( keyName, mergedBuffer, baseNode->GetLocation() );
|
||
|
|
||
|
|
||
|
/// We got the sound buffer, now just create a groupedSound for it
|
||
|
|
||
|
const plAudioInterface* ai = baseNode->GetSceneObject()->GetAudioInterface();
|
||
|
plWinAudible* pAudible = (plWinAudible*)ai->GetAudible();
|
||
|
|
||
|
sprintf( keyName, "%s", GetINode()->GetName());
|
||
|
plWin32GroupedSound *sound = TRACKED_NEW plWin32GroupedSound;
|
||
|
sound->SetPositionArray( startPoses.GetCount(), startPoses.AcquireArray(), volumes.AcquireArray() );
|
||
|
sound->SetProperty( plSound::kPropLoadOnlyOnCall, true );
|
||
|
|
||
|
hsgResMgr::ResMgr()->NewKey( keyName, sound, baseNode->GetLocation(), baseNode->GetLoadMask() );
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( mergedBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef );
|
||
|
|
||
|
if( pAudible->AddSound( sound, index, true ) )
|
||
|
{
|
||
|
// Just use the first component
|
||
|
plSound3DEmitterComponent *grpComp = (plSound3DEmitterComponent *)groupArray[ 0 ];
|
||
|
grpComp->ISetParameters( sound, pErrMsg );
|
||
|
}
|
||
|
|
||
|
/// All done!
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Background Music Component
|
||
|
//
|
||
|
|
||
|
class plBackgroundMusicComponent : public plBaseSoundEmitterComponent
|
||
|
{
|
||
|
public:
|
||
|
plBackgroundMusicComponent();
|
||
|
virtual ~plBackgroundMusicComponent();
|
||
|
|
||
|
// Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
virtual hsBool IsLocalOnly( void ) const { if( fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ) return true; else return false; }
|
||
|
|
||
|
protected:
|
||
|
virtual UInt32 ICalcSourceBufferFlags() const;
|
||
|
|
||
|
bool IValidate(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
virtual hsBool IGetCategoryList( char **&catList, int *&catKonstantList );
|
||
|
};
|
||
|
|
||
|
//Max desc stuff necessary below.
|
||
|
CLASS_DESC(plBackgroundMusicComponent, gBgndMusicEmitterDesc, "Nonspatial Sound", "NonspatSound", COMP_TYPE_AUDIO, BGND_MUSIC_COMPONENT_ID)
|
||
|
|
||
|
|
||
|
ParamBlockDesc2 gBgndMusicEmitterBk
|
||
|
(
|
||
|
plComponent::kBlkComp, _T("Bgnd Music"), 0, &gBgndMusicEmitterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
|
||
|
|
||
|
5, // Number of rollouts
|
||
|
sSoundSharedPBHeader(IDS_COMP_SOUNDBASE),
|
||
|
kS3DBaseParams, IDD_COMP_SOUNDBGND, IDS_COMP_SOUNDBGND, 0, 0, &gSoundCompProc,
|
||
|
sSndWaveformPropsHeader,
|
||
|
kS3DSoftVolumeParams, IDD_COMP_SOUND_SOFTPARAMS, IDS_COMP_SOUNDSOFTPARAMS, 0, 0, &gSoundSoftVolumeSelProc,
|
||
|
|
||
|
// Included paramblock
|
||
|
&sSoundSharedPB,
|
||
|
|
||
|
// Waveform props define
|
||
|
sSndWaveformPropsParamTemplate,
|
||
|
|
||
|
// params
|
||
|
|
||
|
kSndIsLocalOnly, _T("noNetworkSynch"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_SND_LOCALONLY,
|
||
|
end,
|
||
|
|
||
|
kSndStreamCompressed, _T("stream"), TYPE_BOOL, 0, 0,
|
||
|
p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_CHECK_STREAM,
|
||
|
end,
|
||
|
|
||
|
/// Soft Region/Volume Parameters rollout
|
||
|
kSndSoftRegionEnable, _T( "enableSoftRegion" ), TYPE_BOOL, 0, 0,
|
||
|
p_ui, kS3DSoftVolumeParams, TYPE_SINGLECHEKBOX, IDC_SOUND_SOFTENABLE,
|
||
|
p_default, FALSE,
|
||
|
end,
|
||
|
|
||
|
kSndSoftRegion, _T("softRegion"), TYPE_INODE, 0, 0,
|
||
|
p_prompt, IDS_COMP_SOUNDSOFTSELECT,
|
||
|
p_accessor, &gSoundSoftVolAccessor,
|
||
|
end,
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
|
||
|
plBackgroundMusicComponent::plBackgroundMusicComponent()
|
||
|
{
|
||
|
fClassDesc = &gBgndMusicEmitterDesc;
|
||
|
fClassDesc->MakeAutoParamBlocks(this);
|
||
|
}
|
||
|
|
||
|
plBackgroundMusicComponent::~plBackgroundMusicComponent()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool plBackgroundMusicComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::SetupProperties( pNode, pErrMsg );
|
||
|
}
|
||
|
|
||
|
UInt32 plBackgroundMusicComponent::ICalcSourceBufferFlags() const
|
||
|
{
|
||
|
UInt32 ourFlags = 0;
|
||
|
if (fCompPB->GetInt(kSndStreamCompressed))
|
||
|
ourFlags |= plSoundBuffer::kStreamCompressed;
|
||
|
|
||
|
return plBaseSoundEmitterComponent::ICalcSourceBufferFlags() | ourFlags;
|
||
|
}
|
||
|
|
||
|
bool plBackgroundMusicComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::IValidate( node, pErrMsg );
|
||
|
}
|
||
|
|
||
|
hsBool plBackgroundMusicComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::PreConvert( node, pErrMsg, BGND_MUSIC_COMPONENT_ID );
|
||
|
}
|
||
|
|
||
|
hsBool plBackgroundMusicComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
if (!fValidNodes[node])
|
||
|
return false;
|
||
|
|
||
|
char* fileName = GetSoundFileName( kBaseSound );
|
||
|
|
||
|
int fIndex = -1;
|
||
|
if (fIndices.find(node) != fIndices.end())
|
||
|
fIndex = fIndices[node];
|
||
|
|
||
|
const plAudioInterface* ai = node->GetSceneObject()->GetAudioInterface();
|
||
|
plWinAudible* pAudible = (plWinAudible*)ai->GetAudible();
|
||
|
|
||
|
plSoundBuffer *srcBuffer = IProcessSourceBuffer( node, pErrMsg );
|
||
|
if( srcBuffer == nil )
|
||
|
return false;
|
||
|
|
||
|
char keyName[ 256 ];
|
||
|
sprintf( keyName, "%s_Win32BgndSnd", GetINode()->GetName() );
|
||
|
plWin32Sound *sound = nil;
|
||
|
|
||
|
if( srcBuffer->GetDataLengthInSecs() > 4.f )
|
||
|
sound = TRACKED_NEW plWin32StreamingSound;
|
||
|
else
|
||
|
sound = TRACKED_NEW plWin32StaticSound;
|
||
|
|
||
|
hsgResMgr::ResMgr()->NewKey(keyName, sound, node->GetLocation(), node->GetLoadMask());
|
||
|
|
||
|
srcBuffer->SetFlag( plSoundBuffer::kAlwaysExternal );
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( srcBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef );
|
||
|
|
||
|
if (pAudible->AddSound( sound, fIndex, false))
|
||
|
{
|
||
|
ISetBaseParameters( sound, pErrMsg );
|
||
|
|
||
|
sound->SetProperty( plSound::kPropLocalOnly, fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ? true : false );
|
||
|
sound->SetPriority( fCompPB->GetInt( (ParamID)kSndPriority ) );
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kSndDisableLOD ) )
|
||
|
{
|
||
|
// Force LOD off on this sound
|
||
|
sound->SetProperty( plSound::kPropDisableLOD, true );
|
||
|
}
|
||
|
|
||
|
IGrabSoftRegion( sound, pErrMsg );
|
||
|
sound->GetEAXSettings().Enable( false );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//// IGetCategoryList ///////////////////////////////////////////////////////////////////////////
|
||
|
// Returns a list of the categories and konstants supported for this type of sound
|
||
|
|
||
|
hsBool plBackgroundMusicComponent::IGetCategoryList( char **&catList, int *&catKonstantList )
|
||
|
{
|
||
|
static char *cats[] = { "Background Music", "Ambience", "Sound FX", "GUI", "NPC Voice", "" };
|
||
|
static int catEnums[] = { plSound::kBackgroundMusic, plSound::kAmbience, plSound::kSoundFX, plSound::kGUISound, plSound::kNPCVoices };
|
||
|
|
||
|
catList = cats;
|
||
|
catKonstantList = catEnums;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// GUI Sound Component
|
||
|
//
|
||
|
|
||
|
class plGUISoundComponent : public plBaseSoundEmitterComponent
|
||
|
{
|
||
|
public:
|
||
|
plGUISoundComponent();
|
||
|
virtual ~plGUISoundComponent();
|
||
|
|
||
|
// Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
virtual void UpdateSoundFileSelection( void ) { ; }
|
||
|
|
||
|
protected:
|
||
|
|
||
|
bool IValidate(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
virtual hsBool IGetCategoryList( char **&catList, int *&catKonstantList );
|
||
|
|
||
|
virtual hsBool IHasWaveformProps( void ) const { return false; }
|
||
|
};
|
||
|
|
||
|
//Max desc stuff necessary below.
|
||
|
CLASS_DESC(plGUISoundComponent, gGUISoundEmitterDesc, "GUI Sound", "GUISound", COMP_TYPE_AUDIO, GUI_SOUND_COMPONENT_ID)
|
||
|
|
||
|
|
||
|
ParamBlockDesc2 gGUISoundEmitterBk
|
||
|
(
|
||
|
plComponent::kBlkComp, _T("GUI Sound"), 0, &gGUISoundEmitterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
|
||
|
|
||
|
2, // Number of rollouts
|
||
|
sSoundSharedPBHeader(IDS_COMP_SOUNDGUI),
|
||
|
|
||
|
// Included paramblock
|
||
|
&sSoundSharedPB,
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
|
||
|
plGUISoundComponent::plGUISoundComponent()
|
||
|
{
|
||
|
fClassDesc = &gGUISoundEmitterDesc;
|
||
|
fClassDesc->MakeAutoParamBlocks(this);
|
||
|
}
|
||
|
|
||
|
plGUISoundComponent::~plGUISoundComponent()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//// IGetCategoryList ///////////////////////////////////////////////////////////////////////////
|
||
|
// Returns a list of the categories and konstants supported for this type of sound
|
||
|
|
||
|
hsBool plGUISoundComponent::IGetCategoryList( char **&catList, int *&catKonstantList )
|
||
|
{
|
||
|
static char *cats[] = { "GUI", "" };
|
||
|
static int catEnums[] = { plSound::kGUISound };
|
||
|
|
||
|
catList = cats;
|
||
|
catKonstantList = catEnums;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool plGUISoundComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::SetupProperties( pNode, pErrMsg );
|
||
|
}
|
||
|
|
||
|
bool plGUISoundComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::IValidate( node, pErrMsg );
|
||
|
}
|
||
|
|
||
|
hsBool plGUISoundComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
return plBaseSoundEmitterComponent::PreConvert( node, pErrMsg, GUI_SOUND_COMPONENT_ID );
|
||
|
}
|
||
|
|
||
|
hsBool plGUISoundComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
if (!fValidNodes[node])
|
||
|
return false;
|
||
|
|
||
|
char* fileName = GetSoundFileName( kBaseSound );
|
||
|
|
||
|
int fIndex = -1;
|
||
|
if (fIndices.find(node) != fIndices.end())
|
||
|
fIndex = fIndices[node];
|
||
|
|
||
|
const plAudioInterface* ai = node->GetSceneObject()->GetAudioInterface();
|
||
|
plWinAudible* pAudible = (plWinAudible*)ai->GetAudible();
|
||
|
|
||
|
plSoundBuffer *srcBuffer = GetSourceBuffer( fileName, node, ICalcSourceBufferFlags() );
|
||
|
if( srcBuffer == nil )
|
||
|
{
|
||
|
pErrMsg->Set( true, node->GetName(), "The file specified for the sound 3D component %s is invalid. This emitter will not be exported.", GetINode()->GetName() ).Show();
|
||
|
pErrMsg->Set( false );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
char keyName[ 256 ];
|
||
|
sprintf( keyName, "%s_Win32GUISound", GetINode()->GetName() );
|
||
|
|
||
|
plWin32StaticSound *sound = TRACKED_NEW plWin32StaticSound;
|
||
|
hsgResMgr::ResMgr()->NewKey(keyName, sound, node->GetLocation(), node->GetLoadMask());
|
||
|
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( srcBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef );
|
||
|
|
||
|
if (pAudible->AddSound( sound, fIndex, false))
|
||
|
{
|
||
|
ISetBaseParameters( sound, pErrMsg );
|
||
|
|
||
|
sound->SetProperty( plSound::kPropLocalOnly, true ); // GUI sounds are always local-only
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kSndDisableLOD ) )
|
||
|
{
|
||
|
// Force LOD off on this sound
|
||
|
sound->SetProperty( plSound::kPropDisableLOD, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// EAX Listener Soft Region component
|
||
|
//
|
||
|
|
||
|
class plEAXListenerComponent : public plComponent
|
||
|
{
|
||
|
public:
|
||
|
enum
|
||
|
{
|
||
|
kRefSoftRegion,
|
||
|
kRefWhichSettings,
|
||
|
kRefPreset,
|
||
|
kRefCustFile,
|
||
|
|
||
|
// The following are the parameters for the listener as defined in eax.h, minus the panning
|
||
|
kRefEnvironmentSize, // float
|
||
|
kRefEnvironmentDiffusion, // float
|
||
|
kRefRoom, // long
|
||
|
kRefRoomHF, // long
|
||
|
kRefRoomLF, // long
|
||
|
kRefDecayTime, // float
|
||
|
kRefDecayHFRatio, // float
|
||
|
kRefDecayLFRatio, // float
|
||
|
kRefReflections, // long
|
||
|
kRefReflectionsDelay, // float
|
||
|
// panning goes here
|
||
|
kRefReverb, // long
|
||
|
kRefReverbDelay, // float
|
||
|
// Reverb pan
|
||
|
kRefEchoTime, // float
|
||
|
kRefEchoDepth,
|
||
|
kRefModulationTime,
|
||
|
kRefModulationDepth,
|
||
|
kRefAirAbsorptionHF,
|
||
|
kRefHFReference,
|
||
|
kRefLFReference,
|
||
|
kRefRoomRolloffFactor,
|
||
|
kRefFlags, // unsigned long
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
plEAXListenerComponent();
|
||
|
void DeleteThis() { delete 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 SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg);
|
||
|
|
||
|
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *errMsg);
|
||
|
hsBool Convert(plMaxNode *node, plErrorMsg *errMsg);
|
||
|
|
||
|
const char *GetCustFileName( void ) const;
|
||
|
void SetCustFile( const char *path );
|
||
|
};
|
||
|
|
||
|
// When one of our parameters that is a ref changes, send out the component ref
|
||
|
// changed message. Normally, messages from component refs are ignored since
|
||
|
// they pass along all the messages of the ref, which generates a lot of false
|
||
|
// converts.
|
||
|
class plEAXListenerAccessor : public PBAccessor
|
||
|
{
|
||
|
public:
|
||
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
|
||
|
{
|
||
|
if (id == plEAXListenerComponent::kRefSoftRegion )
|
||
|
{
|
||
|
plEAXListenerComponent *comp = (plEAXListenerComponent*)owner;
|
||
|
comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
static plEAXListenerAccessor gEAXListenerAccessor;
|
||
|
|
||
|
//// DialogProc for EAXListenerComponent ////////////////////////////////////////////////////////
|
||
|
|
||
|
class plEAXListenerDlgProc : public plSingleCompSelProc
|
||
|
{
|
||
|
protected:
|
||
|
|
||
|
hsBool IGetCustFileName( plEAXListenerComponent *listenerComp )
|
||
|
{
|
||
|
TCHAR fileName[ MAX_PATH ], dirName[ MAX_PATH ];
|
||
|
|
||
|
|
||
|
const char *name = listenerComp->GetCustFileName();
|
||
|
if( name != nil )
|
||
|
strcpy( fileName, name );
|
||
|
else
|
||
|
strcpy( fileName, _T( "" ) );
|
||
|
|
||
|
strcpy( dirName, fileName );
|
||
|
::PathRemoveFileSpec( dirName );
|
||
|
|
||
|
OPENFILENAME ofn = {0};
|
||
|
ofn.lStructSize = sizeof( OPENFILENAME );
|
||
|
ofn.hwndOwner = GetCOREInterface()->GetMAXHWnd();
|
||
|
ofn.lpstrFilter = "EAX Preset Files (*.eax)\0*.eax\0All Files\0*.*\0";
|
||
|
ofn.lpstrFile = fileName;
|
||
|
ofn.nMaxFile = sizeof( fileName );
|
||
|
ofn.lpstrInitialDir = dirName;
|
||
|
ofn.lpstrTitle = "Choose a sound file";
|
||
|
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
|
||
|
ofn.lpstrDefExt = "eax";
|
||
|
|
||
|
if( GetOpenFileName( &ofn ) )
|
||
|
{
|
||
|
listenerComp->SetCustFile( fileName );
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
public:
|
||
|
|
||
|
plEAXListenerDlgProc()
|
||
|
: plSingleCompSelProc( plEAXListenerComponent::kRefSoftRegion, IDC_EAX_SOFTREGION, "Select the soft region to apply these EAX listener properties to" )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
|
||
|
{
|
||
|
int i;
|
||
|
IParamBlock2 *pb = map->GetParamBlock();
|
||
|
|
||
|
|
||
|
switch ( msg )
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
|
||
|
// Load the preset combo with preset names
|
||
|
HWND comboBox = GetDlgItem( hWnd, IDC_EAX_PRESET_COMBO );
|
||
|
ComboBox_ResetContent( comboBox );
|
||
|
|
||
|
for( i = 0; i < /*sizeof( EAX30_ORIGINAL_PRESETS )
|
||
|
/ sizeof( EAXLISTENERPROPERTIES )*/26 ; i++ )
|
||
|
ComboBox_AddString( comboBox, EAX30_ORIGINAL_PRESET_NAMES[ i ] );
|
||
|
|
||
|
ComboBox_SetCurSel( comboBox, pb->GetInt( (ParamID)plEAXListenerComponent::kRefPreset ) );
|
||
|
|
||
|
ICustButton *custButton = GetICustButton( GetDlgItem( hWnd, IDC_EAX_CUSTFILE ) );
|
||
|
if( custButton != nil )
|
||
|
{
|
||
|
custButton->SetText( pb->GetStr( (ParamID)plEAXListenerComponent::kRefCustFile ) );
|
||
|
ReleaseICustButton( custButton );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if( LOWORD( wParam ) == IDC_EAX_PRESET_COMBO )
|
||
|
{
|
||
|
int sel = SendDlgItemMessage( hWnd, IDC_EAX_PRESET_COMBO, CB_GETCURSEL, 0, 0 );
|
||
|
if( sel != CB_ERR )
|
||
|
pb->SetValue( (ParamID)plEAXListenerComponent::kRefPreset, 0, sel );
|
||
|
return true;
|
||
|
}
|
||
|
if( ( HIWORD( wParam ) == BN_CLICKED ) && LOWORD( wParam ) == IDC_EAX_CUSTFILE )
|
||
|
{
|
||
|
// Get the file to load
|
||
|
plEAXListenerComponent *comp = (plEAXListenerComponent *)map->GetParamBlock()->GetOwner();
|
||
|
if( IGetCustFileName( comp ) )
|
||
|
{
|
||
|
ICustButton *custButton = GetICustButton( GetDlgItem( hWnd, IDC_EAX_CUSTFILE ) );
|
||
|
if( custButton != nil )
|
||
|
{
|
||
|
custButton->SetText( pb->GetStr( (ParamID)plEAXListenerComponent::kRefCustFile ) );
|
||
|
ReleaseICustButton( custButton );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return plSingleCompSelProc::DlgProc( t, map, hWnd, msg, wParam, lParam );
|
||
|
}
|
||
|
|
||
|
void DeleteThis() {}
|
||
|
};
|
||
|
|
||
|
static plEAXListenerDlgProc gEAXListenerDlgProc;
|
||
|
|
||
|
//Max desc stuff necessary below.
|
||
|
CLASS_DESC(plEAXListenerComponent, gEAXListenerDesc, "EAX Listener", "EAXListener", COMP_TYPE_AUDIO, EAX_LISTENER_COMPONENT_ID)
|
||
|
|
||
|
ParamBlockDesc2 gEAXListenerBlk
|
||
|
( // KLUDGE: not the defined block ID, but kept for backwards compat.
|
||
|
plComponent::kBlkComp, _T("EAXListener"), 0, &gEAXListenerDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
||
|
|
||
|
IDD_COMP_EAXLISTENER, IDS_COMP_EAXLISTENER, 0, 0, &gEAXListenerDlgProc,
|
||
|
|
||
|
plEAXListenerComponent::kRefSoftRegion, _T("SoftRegion"), TYPE_INODE, 0, 0,
|
||
|
p_accessor, &gEAXListenerAccessor,
|
||
|
end,
|
||
|
|
||
|
plEAXListenerComponent::kRefWhichSettings, _T("whichSettings"), TYPE_INT, 0, 0,
|
||
|
p_ui, TYPE_RADIO, 2, IDC_EAX_PRESET, IDC_EAX_CUSTOM,
|
||
|
p_default, 0,
|
||
|
end,
|
||
|
|
||
|
plEAXListenerComponent::kRefPreset, _T("preset"), TYPE_INT, 0, 0,
|
||
|
p_default, 0,
|
||
|
end,
|
||
|
|
||
|
// This is just a label for now, so the users know what file the presets came from
|
||
|
plEAXListenerComponent::kRefCustFile, _T("custFile"), TYPE_STRING, 0, 0,
|
||
|
p_default, _T(""),
|
||
|
end,
|
||
|
|
||
|
// EAX listener params (should be private)
|
||
|
plEAXListenerComponent::kRefEnvironmentSize, _T(""), TYPE_FLOAT, 0, 0, end, // float
|
||
|
plEAXListenerComponent::kRefEnvironmentDiffusion, _T(""), TYPE_FLOAT, 0, 0, end,// float
|
||
|
plEAXListenerComponent::kRefRoom, _T(""), TYPE_INT, 0, 0, end,// long
|
||
|
plEAXListenerComponent::kRefRoomHF, _T(""), TYPE_INT, 0, 0, end,// long
|
||
|
plEAXListenerComponent::kRefRoomLF, _T(""), TYPE_INT, 0, 0, end,// long
|
||
|
plEAXListenerComponent::kRefDecayTime, _T(""), TYPE_FLOAT, 0, 0, end,// float
|
||
|
plEAXListenerComponent::kRefDecayHFRatio, _T(""), TYPE_FLOAT, 0, 0, end,// float
|
||
|
plEAXListenerComponent::kRefDecayLFRatio, _T(""), TYPE_FLOAT, 0, 0, end,// float
|
||
|
plEAXListenerComponent::kRefReflections, _T(""), TYPE_INT, 0, 0, end,// long
|
||
|
plEAXListenerComponent::kRefReflectionsDelay, _T(""), TYPE_FLOAT, 0, 0, end,// float
|
||
|
// panning goes here
|
||
|
plEAXListenerComponent::kRefReverb, _T(""), TYPE_INT, 0, 0, end,// long
|
||
|
plEAXListenerComponent::kRefReverbDelay, _T(""), TYPE_FLOAT, 0, 0, end,// float
|
||
|
// Reverb pan
|
||
|
plEAXListenerComponent::kRefEchoTime, _T(""), TYPE_FLOAT, 0, 0, end,// float
|
||
|
plEAXListenerComponent::kRefEchoDepth, _T(""), TYPE_FLOAT, 0, 0, end,
|
||
|
plEAXListenerComponent::kRefModulationTime, _T(""), TYPE_FLOAT, 0, 0, end,
|
||
|
plEAXListenerComponent::kRefModulationDepth, _T(""), TYPE_FLOAT, 0, 0, end,
|
||
|
plEAXListenerComponent::kRefAirAbsorptionHF, _T(""), TYPE_FLOAT, 0, 0, end,
|
||
|
plEAXListenerComponent::kRefHFReference, _T(""), TYPE_FLOAT, 0, 0, end,
|
||
|
plEAXListenerComponent::kRefLFReference, _T(""), TYPE_FLOAT, 0, 0, end,
|
||
|
plEAXListenerComponent::kRefRoomRolloffFactor, _T(""), TYPE_FLOAT, 0, 0, end,
|
||
|
plEAXListenerComponent::kRefFlags, _T(""), TYPE_INT, 0, 0, end,// unsigned long
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
plEAXListenerComponent::plEAXListenerComponent()
|
||
|
{
|
||
|
fClassDesc = &gEAXListenerDesc;
|
||
|
fClassDesc->MakeAutoParamBlocks(this);
|
||
|
}
|
||
|
|
||
|
hsBool plEAXListenerComponent::Convert(plMaxNode *node, plErrorMsg *errMsg)
|
||
|
{
|
||
|
if( !fCompPB->GetINode((ParamID)kRefSoftRegion) )
|
||
|
return true;
|
||
|
|
||
|
plSceneObject* sceneObj = node->GetSceneObject();
|
||
|
if( !sceneObj )
|
||
|
return true;
|
||
|
/*
|
||
|
plLightInfo* li = plLightInfo::ConvertNoRef(sceneObj->GetGenericInterface(plLightInfo::Index()));
|
||
|
if( !li )
|
||
|
return true;
|
||
|
*/
|
||
|
plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent( fCompPB->GetINode( (ParamID)kRefSoftRegion ) );
|
||
|
if( !softComp )
|
||
|
return true;
|
||
|
|
||
|
plKey softKey = softComp->GetSoftVolume();
|
||
|
if( !softKey )
|
||
|
return true;
|
||
|
|
||
|
// Create a listener mod to handle these things
|
||
|
plEAXListenerMod *listener = TRACKED_NEW plEAXListenerMod();
|
||
|
node->AddModifier( listener, IGetUniqueName(node) );
|
||
|
|
||
|
// Add the soft region
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( softKey, TRACKED_NEW plGenRefMsg( listener->GetKey(), plRefMsg::kOnCreate, 0, plEAXListenerMod::kRefSoftRegion ), plRefFlags::kActiveRef );
|
||
|
|
||
|
// Set up the parameters of the listener mod
|
||
|
EAXLISTENERPROPERTIES *listenerProps = listener->GetListenerProps();
|
||
|
if( fCompPB->GetInt( (ParamID)kRefWhichSettings ) == 0 )
|
||
|
{
|
||
|
// Set params based on a preset
|
||
|
listener->SetFromPreset( fCompPB->GetInt( (ParamID)kRefPreset ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Get the raw params
|
||
|
listenerProps->flEnvironmentSize = fCompPB->GetFloat( (ParamID)kRefEnvironmentSize );
|
||
|
listenerProps->flEnvironmentDiffusion = fCompPB->GetFloat( (ParamID)kRefEnvironmentDiffusion );
|
||
|
listenerProps->lRoom = fCompPB->GetInt( (ParamID)kRefRoom );
|
||
|
listenerProps->lRoomHF = fCompPB->GetInt( (ParamID)kRefRoomHF );
|
||
|
listenerProps->lRoomLF = fCompPB->GetInt( (ParamID)kRefRoomLF );
|
||
|
listenerProps->flDecayTime = fCompPB->GetFloat( (ParamID)kRefDecayTime );
|
||
|
listenerProps->flDecayHFRatio = fCompPB->GetFloat( (ParamID)kRefDecayHFRatio );
|
||
|
listenerProps->flDecayLFRatio = fCompPB->GetFloat( (ParamID)kRefDecayLFRatio );
|
||
|
listenerProps->lReflections = fCompPB->GetInt( (ParamID)kRefReflections );
|
||
|
listenerProps->flReflectionsDelay = fCompPB->GetFloat( (ParamID)kRefReflectionsDelay );
|
||
|
//listenerProps->vReflectionsPan; // early reflections panning vector
|
||
|
listenerProps->lReverb = fCompPB->GetInt( (ParamID)kRefReverb ); // late reverberation level relative to room effect
|
||
|
listenerProps->flReverbDelay = fCompPB->GetFloat( (ParamID)kRefReverbDelay );
|
||
|
//listenerProps->vReverbPan; // late reverberation panning vector
|
||
|
listenerProps->flEchoTime = fCompPB->GetFloat( (ParamID)kRefEchoTime );
|
||
|
listenerProps->flEchoDepth = fCompPB->GetFloat( (ParamID)kRefEchoDepth );
|
||
|
listenerProps->flModulationTime = fCompPB->GetFloat( (ParamID)kRefModulationTime );
|
||
|
listenerProps->flModulationDepth = fCompPB->GetFloat( (ParamID)kRefModulationDepth );
|
||
|
listenerProps->flAirAbsorptionHF = fCompPB->GetFloat( (ParamID)kRefAirAbsorptionHF );
|
||
|
listenerProps->flHFReference = fCompPB->GetFloat( (ParamID)kRefHFReference );
|
||
|
listenerProps->flLFReference = fCompPB->GetFloat( (ParamID)kRefLFReference );
|
||
|
listenerProps->flRoomRolloffFactor = fCompPB->GetFloat( (ParamID)kRefRoomRolloffFactor );
|
||
|
listenerProps->ulFlags = fCompPB->GetInt( (ParamID)kRefFlags );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hsBool plEAXListenerComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg)
|
||
|
{
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool plEAXListenerComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
||
|
{
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const char *plEAXListenerComponent::GetCustFileName( void ) const
|
||
|
{
|
||
|
return (const char *)fCompPB->GetStr( (ParamID)kRefCustFile );
|
||
|
}
|
||
|
|
||
|
void plEAXListenerComponent::SetCustFile( const char *path )
|
||
|
{
|
||
|
char file[ MAX_PATH ];
|
||
|
int i;
|
||
|
hsUNIXStream presetFile;
|
||
|
|
||
|
// Map of PB values to file entries
|
||
|
struct FilePBMap
|
||
|
{
|
||
|
char *fKeyword;
|
||
|
ParamID fParamID;
|
||
|
UInt8 fType; // 0 is int, 1 is float for now
|
||
|
} myMap[] = {
|
||
|
{ "flEnvironmentSize", kRefEnvironmentSize, 1 },
|
||
|
{ "flEnvironmentDiffusion", kRefEnvironmentDiffusion, 1 },
|
||
|
{ "lRoom", kRefRoom, 0 },
|
||
|
{ "lRoomHF", kRefRoomHF, 0 },
|
||
|
{ "lRoomLF", kRefRoomLF, 0 },
|
||
|
{ "flDecayTime", kRefDecayTime, 1 },
|
||
|
{ "flDecayHFRatio", kRefDecayHFRatio, 1 },
|
||
|
{ "flDecayLFRatio", kRefDecayLFRatio, 1 },
|
||
|
{ "lReflections", kRefReflections, 0 },
|
||
|
{ "flReflectionsDelay", kRefReflectionsDelay, 1 },
|
||
|
{ "lReverb", kRefReverb, 0 },
|
||
|
{ "flReverbDelay", kRefReverbDelay, 1 },
|
||
|
{ "flEchoTime", kRefEchoTime, 1 },
|
||
|
{ "flEchoDepth", kRefEchoDepth, 1 },
|
||
|
{ "flModulationTime", kRefModulationTime, 1 },
|
||
|
{ "flModulationDepth", kRefModulationDepth, 1 },
|
||
|
{ "flAirAbsorptionHF", kRefAirAbsorptionHF, 1 },
|
||
|
{ "flHFReference", kRefHFReference, 1 },
|
||
|
{ "flLFReference", kRefLFReference, 1 },
|
||
|
{ "flRoomRolloffFactor", kRefRoomRolloffFactor, 1 },
|
||
|
{ "dwFlags", kRefFlags, 0 },
|
||
|
{ nil, 0, 0 } };
|
||
|
|
||
|
// Read the file and set settings from it
|
||
|
if( !presetFile.Open( path, "rt" ) )
|
||
|
{
|
||
|
// Oops
|
||
|
hsAssert( false, "can't open file" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Loop and find our keywords
|
||
|
for( i = 0; myMap[ i ].fKeyword != nil && !presetFile.AtEnd(); )
|
||
|
{
|
||
|
char line[ 512 ];
|
||
|
|
||
|
// Read a line from the file until we find our keyword
|
||
|
presetFile.ReadLn( line, sizeof( line ) );
|
||
|
if( strstr( line, myMap[ i ].fKeyword ) == nil )
|
||
|
continue;
|
||
|
|
||
|
// Read the next line, with our value
|
||
|
presetFile.ReadLn( line, sizeof( line ) );
|
||
|
float value = atof( line );
|
||
|
if( myMap[ i ].fType == 0 )
|
||
|
fCompPB->SetValue( myMap[ i ].fParamID, 0, (int)value );
|
||
|
else
|
||
|
fCompPB->SetValue( myMap[ i ].fParamID, 0, (float)value );
|
||
|
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if( myMap[ i ].fKeyword != nil )
|
||
|
{
|
||
|
hsAssert( false, "Couldn't find all of the keywords in the settings file. Oh well" );
|
||
|
}
|
||
|
|
||
|
// All done!
|
||
|
presetFile.Close();
|
||
|
|
||
|
// Update our helper reminder string
|
||
|
_splitpath( path, nil, nil, file, nil );
|
||
|
fCompPB->SetValue( (ParamID)kRefCustFile, 0, file );
|
||
|
}
|
||
|
|
||
|
/// Obsolete SFX components (made obsolete by the new EAX support)
|
||
|
|
||
|
OBSOLETE_CLASS(plSoundReverbComponent, gSoundReverbDesc, "Audio Region", "AudioRegion", COMP_TYPE_AUDIO, Class_ID(0x50507200, 0x48651c4c))
|
||
|
OBSOLETE_CLASS(plSoundChorusModComponent,gSoundChorusModDesc , "Chorus Effect", "ChorusEffect", COMP_TYPE_AUDIO, Class_ID(0x10f91101, 0x28cb21b9))
|
||
|
OBSOLETE_CLASS(plSoundCompressorModComponent,gSoundCompressorModDesc , "Compressor Effect", "CompressEffect", COMP_TYPE_AUDIO, Class_ID(0x443d2167, 0x4ca42eb))
|
||
|
OBSOLETE_CLASS(plSoundDistortModComponent,gSoundDistortModDesc , "Distort Effect", "DistortEffect", COMP_TYPE_AUDIO, Class_ID(0x7cb45868, 0x61220227))
|
||
|
OBSOLETE_CLASS(plSoundEchoModComponent,gSoundEchoModDesc , "Echo Effect", "EchoEffect", COMP_TYPE_AUDIO,Class_ID(0x2948347e, 0x30ba0be3))
|
||
|
OBSOLETE_CLASS(plSoundFlangerModComponent,gSoundFlangerModDesc , "Flanger Effect", "FlangerEffect", COMP_TYPE_AUDIO, Class_ID(0x25034090, 0x361a08d7) )
|
||
|
OBSOLETE_CLASS(plSoundGargleModComponent,gSoundGargleModDesc , "Gargle Effect", "GargleEffect", COMP_TYPE_AUDIO, Class_ID(0x639b6a41, 0x24da2462))
|
||
|
OBSOLETE_CLASS(plSoundReverbModComponent,gSoundReverbModDesc , "Reverb Effect", "ReverbEffect", COMP_TYPE_AUDIO, Class_ID(0x1bef33fc, 0x5c763858))
|
||
|
|
||
|
|
||
|
|
||
|
#if 1 // Waiting... mf
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// RandomSound Component
|
||
|
//
|
||
|
//
|
||
|
|
||
|
plKey plAudioComp::GetRandomSoundKey(plComponentBase *comp, plMaxNode *node)
|
||
|
{
|
||
|
if (comp->ClassID() == RANDOM_SOUND_COMPONENT_ID)
|
||
|
{
|
||
|
plRandomSoundComponent *rndSnd = (plRandomSoundComponent*)comp;
|
||
|
if (rndSnd->fSoundMods.find(node) != rndSnd->fSoundMods.end())
|
||
|
return rndSnd->fSoundMods[node]->GetKey();
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||
|
enum
|
||
|
{
|
||
|
kAutoStart,
|
||
|
kSelectMode,
|
||
|
kDelayMode,
|
||
|
kMinDelay,
|
||
|
kMaxDelay,
|
||
|
kUseAll,
|
||
|
kGroupIdx,
|
||
|
kSoundList,
|
||
|
kGroupTotals,
|
||
|
kLastPick,
|
||
|
kCombineSounds
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kNormal = 0,
|
||
|
kNoRepeats,
|
||
|
kFullSetRepeat,
|
||
|
kFullSetStop,
|
||
|
kSequential
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kDelayFromStart = 0,
|
||
|
kDelayFromEnd,
|
||
|
kDelayInfinite
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kRandomSoundMain,
|
||
|
kRandomSoundGroup,
|
||
|
};
|
||
|
|
||
|
static const kMaxGroups = 10;
|
||
|
|
||
|
class plRandomSoundComponentProc : public ParamMap2UserDlgProc
|
||
|
{
|
||
|
public:
|
||
|
plRandomSoundComponentProc() {}
|
||
|
|
||
|
BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||
|
|
||
|
void DeleteThis() {}
|
||
|
void UpdateDisplay(IParamMap2 *pm);
|
||
|
virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { UpdateDisplay(pmap); }
|
||
|
};
|
||
|
|
||
|
static plRandomSoundComponentProc gRandomSoundComponentProc;
|
||
|
|
||
|
void plRandomSoundComponentProc::UpdateDisplay(IParamMap2 *pm)
|
||
|
{
|
||
|
HWND hWnd = pm->GetHWnd();
|
||
|
HWND hList = GetDlgItem(hWnd, IDC_COMP_RS_GROUPLIST);
|
||
|
IParamBlock2 *pb = pm->GetParamBlock();
|
||
|
plRandomSoundComponent *comp = (plRandomSoundComponent *)pb->GetOwner();
|
||
|
|
||
|
ListBox_ResetContent(hList);
|
||
|
int group = comp->GetCurGroupIdx();
|
||
|
int startIdx = comp->GetStartIndex(group);
|
||
|
int endIdx = comp->GetEndIndex(group);
|
||
|
|
||
|
while (startIdx < endIdx)
|
||
|
{
|
||
|
INode *curNode = pb->GetINode(ParamID(kSoundList), 0, startIdx);
|
||
|
if (curNode == nil)
|
||
|
{
|
||
|
comp->RemoveSound(startIdx);
|
||
|
endIdx--;
|
||
|
continue;
|
||
|
}
|
||
|
ListBox_AddString(hList, curNode->GetName());
|
||
|
startIdx++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL plRandomSoundComponentProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
IParamBlock2 *pb = pm->GetParamBlock();
|
||
|
HWND hList = GetDlgItem(hWnd, IDC_COMP_RS_GROUPLIST);
|
||
|
plRandomSoundComponent *comp = (plRandomSoundComponent *)pb->GetOwner();
|
||
|
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
//UpdateDisplay(pm);
|
||
|
return TRUE;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if (HIWORD(wParam) == BN_CLICKED)
|
||
|
{
|
||
|
if (LOWORD(wParam) == IDC_COMP_RS_GROUP_ADD)
|
||
|
{
|
||
|
std::vector<Class_ID> cids;
|
||
|
cids.push_back(SOUND_3D_COMPONENT_ID);
|
||
|
if (plPick::NodeRefKludge(pb, kLastPick, &cids, true, false))
|
||
|
comp->AddSelectedSound();
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
// Remove the currently selected material
|
||
|
else if (LOWORD(wParam) == IDC_COMP_RS_GROUP_REMOVE)
|
||
|
{
|
||
|
int curSel = ListBox_GetCurSel(hList);
|
||
|
if (curSel >= 0)
|
||
|
comp->RemoveSound(curSel);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//Max desc stuff necessary below.
|
||
|
CLASS_DESC(plRandomSoundComponent, gRandomSoundDesc, "Random Sound", "RandomSound", COMP_TYPE_AUDIO, RANDOM_SOUND_COMPONENT_ID)
|
||
|
|
||
|
//
|
||
|
// Block not necessary, kept for backwards compat.
|
||
|
//
|
||
|
|
||
|
ParamBlockDesc2 gRandomSoundBk
|
||
|
(
|
||
|
plComponent::kBlkComp, _T("RandomSound"), 0, &gRandomSoundDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp,
|
||
|
|
||
|
2,
|
||
|
kRandomSoundMain, IDD_COMP_RANDOMSOUND, IDS_COMP_RANDOMSOUNDS, 0, 0, NULL,
|
||
|
kRandomSoundGroup, IDD_COMP_RANDOMSOUND_GROUPS, IDS_COMP_RANDOMSOUNDS_GROUPS, 0, APPENDROLL_CLOSED, &gRandomSoundComponentProc,
|
||
|
|
||
|
// Main rollout
|
||
|
kAutoStart, _T("AutoStart"), TYPE_BOOL, 0, 0,
|
||
|
p_default, TRUE,
|
||
|
p_ui, kRandomSoundMain, TYPE_SINGLECHEKBOX, IDC_COMP_RS_AUTOSTART,
|
||
|
end,
|
||
|
|
||
|
kSelectMode, _T("SelectMode"), TYPE_INT, 0, 0,
|
||
|
p_ui, kRandomSoundMain, TYPE_RADIO, 5, IDC_RADIO_RS_NORMAL, IDC_RADIO_RS_NOREP, IDC_RADIO_RS_FSREP, IDC_RADIO_RS_FSSTOP, IDC_RADIO_RS_SEQ,
|
||
|
end,
|
||
|
|
||
|
kDelayMode, _T("DelayMode"), TYPE_INT, 0, 0,
|
||
|
p_ui, kRandomSoundMain, TYPE_RADIO, 3, IDC_RADIO_RS_DELAYSTART, IDC_RADIO_RS_DELAYEND, IDC_RADIO_RS_DELAYNEVER,
|
||
|
end,
|
||
|
|
||
|
kMinDelay, _T("MinDelay"), TYPE_FLOAT, 0, 0,
|
||
|
p_default, 0.0,
|
||
|
p_range, -500.0, 1000.0,
|
||
|
p_ui, kRandomSoundMain, TYPE_SPINNER, EDITTYPE_FLOAT,
|
||
|
IDC_COMP_RS_DELAYMIN, IDC_COMP_RS_DELAYMIN_SPIN, 1.0,
|
||
|
end,
|
||
|
|
||
|
kMaxDelay, _T("MaxDelay"), TYPE_FLOAT, 0, 0,
|
||
|
p_default, 0.0,
|
||
|
p_range, -500.0, 1000.0,
|
||
|
p_ui, kRandomSoundMain, TYPE_SPINNER, EDITTYPE_FLOAT,
|
||
|
IDC_COMP_RS_DELAYMAX, IDC_COMP_RS_DELAYMAX_SPIN, 0.1,
|
||
|
end,
|
||
|
|
||
|
// Group rollout
|
||
|
kUseAll, _T("UseAll"), TYPE_BOOL, 0, 0,
|
||
|
p_default, TRUE,
|
||
|
p_ui, kRandomSoundGroup, TYPE_SINGLECHEKBOX, IDC_COMP_RS_USEALL,
|
||
|
end,
|
||
|
|
||
|
kGroupIdx, _T("GroupIndex"), TYPE_INT, 0, 0,
|
||
|
p_default, 1,
|
||
|
p_range, 1, kMaxGroups,
|
||
|
p_ui, kRandomSoundGroup, TYPE_SPINNER, EDITTYPE_INT,
|
||
|
IDC_COMP_RS_GROUP, IDC_COMP_RS_GROUP_SPIN, 1.f,
|
||
|
end,
|
||
|
|
||
|
kSoundList, _T("Sounds"), TYPE_INODE_TAB, 0, 0, 0,
|
||
|
end,
|
||
|
|
||
|
kGroupTotals, _T("Totals"), TYPE_INT_TAB, kMaxGroups, 0, 0,
|
||
|
p_default, 0,
|
||
|
end,
|
||
|
|
||
|
kLastPick, _T("LastPick"), TYPE_INODE, 0, 0, // Temp storage space for the comp picker
|
||
|
end,
|
||
|
|
||
|
kCombineSounds, _T("combineSounds"), TYPE_BOOL, 0, 0,
|
||
|
p_default, FALSE,
|
||
|
p_ui, kRandomSoundGroup, TYPE_SINGLECHEKBOX, IDC_RAND_COMBINESOUNDS,
|
||
|
end,
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
plRandomSoundComponent::plRandomSoundComponent()
|
||
|
{
|
||
|
fClassDesc = &gRandomSoundDesc;
|
||
|
fClassDesc->MakeAutoParamBlocks(this);
|
||
|
}
|
||
|
|
||
|
int plRandomSoundComponent::GetCurGroupIdx()
|
||
|
{
|
||
|
return fCompPB->GetInt(ParamID(kGroupIdx)) - 1;
|
||
|
}
|
||
|
|
||
|
int plRandomSoundComponent::GetStartIndex(int group)
|
||
|
{
|
||
|
int result = 0;
|
||
|
int i;
|
||
|
for (i = 0; i < group; i++)
|
||
|
result += fCompPB->GetInt(ParamID(kGroupTotals), 0, i);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
int plRandomSoundComponent::GetEndIndex(int group)
|
||
|
{
|
||
|
return GetStartIndex(group) + fCompPB->GetInt(ParamID(kGroupTotals), 0, group);
|
||
|
}
|
||
|
|
||
|
void plRandomSoundComponent::AddSelectedSound()
|
||
|
{
|
||
|
int group = GetCurGroupIdx();
|
||
|
int soundIdx = GetEndIndex(group);
|
||
|
|
||
|
INode *node = fCompPB->GetINode(ParamID(kLastPick));
|
||
|
fCompPB->Insert(ParamID(kSoundList), soundIdx, 1, &node);
|
||
|
|
||
|
fCompPB->SetValue(ParamID(kGroupTotals), 0, fCompPB->GetInt(ParamID(kGroupTotals), 0, group) + 1, group);
|
||
|
}
|
||
|
|
||
|
void plRandomSoundComponent::RemoveSound(int index)
|
||
|
{
|
||
|
int group = GetCurGroupIdx();
|
||
|
int soundIdx = GetStartIndex(group) + index;
|
||
|
|
||
|
fCompPB->Delete(ParamID(kSoundList), soundIdx, 1);
|
||
|
fCompPB->SetValue(ParamID(kGroupTotals), 0, fCompPB->GetInt(ParamID(kGroupTotals), 0, group) - 1, group);
|
||
|
}
|
||
|
|
||
|
hsBool plRandomSoundComponent::ICheckForSounds(plMaxNode* node)
|
||
|
{
|
||
|
if (!node->CanConvert())
|
||
|
return false;
|
||
|
|
||
|
int nSounds = 0;
|
||
|
UInt32 numComp = node->NumAttachedComponents(false);
|
||
|
for(int i = 0; i < numComp; i++)
|
||
|
{
|
||
|
plComponentBase* comp = node->GetAttachedComponent(i);
|
||
|
if (plAudioComp::IsSoundComponent(comp))
|
||
|
nSounds++;
|
||
|
}
|
||
|
|
||
|
return nSounds > 0;
|
||
|
}
|
||
|
|
||
|
hsBool plRandomSoundComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
if( !ICheckForSounds(node) )
|
||
|
{
|
||
|
// Warning that there's no sounds to be played?
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
plRandomSoundMod* mod = fSoundMods[node];
|
||
|
plSound *pSound = nil;
|
||
|
const plAudioInterface* ai = nil;
|
||
|
plWinAudible* pAudible = nil;
|
||
|
|
||
|
|
||
|
if( fCompPB->GetInt((ParamID)kAutoStart) )
|
||
|
mod->SetState(0);
|
||
|
else
|
||
|
mod->SetState(plRandomSoundMod::kStopped);
|
||
|
|
||
|
UInt8 mode = plRandomSoundMod::kNormal;
|
||
|
switch( fCompPB->GetInt((ParamID)kSelectMode) )
|
||
|
{
|
||
|
// random, repeats okay, play until stopped - Normal
|
||
|
case kNormal:
|
||
|
mode = plRandomSoundMod::kNormal;
|
||
|
break;
|
||
|
// random, no repeats, play until stopped - NoRepeats
|
||
|
case kNoRepeats:
|
||
|
mode = plRandomSoundMod::kNoRepeats;
|
||
|
break;
|
||
|
// random, play full cycle before repeating - FullSetRepeat
|
||
|
case kFullSetRepeat:
|
||
|
mode = plRandomSoundMod::kCoverall | plRandomSoundMod::kNoRepeats;
|
||
|
break;
|
||
|
// random, play full cycle, then stop - FullSetStop
|
||
|
case kFullSetStop:
|
||
|
mode = plRandomSoundMod::kCoverall | plRandomSoundMod::kOneCycle | plRandomSoundMod::kNoRepeats;
|
||
|
break;
|
||
|
case kSequential:
|
||
|
mode = plRandomSoundMod::kSequential;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch( fCompPB->GetInt((ParamID)kDelayMode) )
|
||
|
{
|
||
|
case kDelayFromStart:
|
||
|
break;
|
||
|
case kDelayFromEnd:
|
||
|
mode |= plRandomSoundMod::kDelayFromEnd;
|
||
|
break;
|
||
|
case kDelayInfinite:
|
||
|
mode |= plRandomSoundMod::kOneCmd;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
mod->SetMode(mode);
|
||
|
|
||
|
float minDel = fCompPB->GetFloat((ParamID)kMinDelay);
|
||
|
float maxDel = fCompPB->GetFloat((ParamID)kMaxDelay);
|
||
|
if( minDel > maxDel )
|
||
|
{
|
||
|
float t = maxDel;
|
||
|
maxDel = minDel;
|
||
|
minDel = t;
|
||
|
}
|
||
|
|
||
|
mod->SetMinDelay(minDel);
|
||
|
mod->SetMaxDelay(maxDel);
|
||
|
|
||
|
node->AddModifier(mod, IGetUniqueName(node));
|
||
|
|
||
|
|
||
|
if (!fCompPB->GetInt(ParamID(kUseAll))) // Actually using separate groups
|
||
|
{
|
||
|
ai = node->GetSceneObject()->GetAudioInterface();
|
||
|
pAudible = (plWinAudible*)ai->GetAudible();
|
||
|
hsTArray<plBaseSoundEmitterComponent *> comps;
|
||
|
|
||
|
plRandomSoundModGroup *groups = TRACKED_NEW plRandomSoundModGroup[kMaxGroups];
|
||
|
int i;
|
||
|
int numSoFar = 0;
|
||
|
for (i = 0; i < kMaxGroups; i++)
|
||
|
{
|
||
|
int numSounds = fCompPB->GetInt(ParamID(kGroupTotals), 0, i);
|
||
|
if( numSounds == 0 )
|
||
|
{
|
||
|
groups[i].fGroupedIdx = -1;
|
||
|
groups[i].fNumSounds = 0;
|
||
|
groups[i].fIndices = nil;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
groups[i].fIndices = TRACKED_NEW UInt16[numSounds];
|
||
|
|
||
|
hsTArray<UInt16> indices;
|
||
|
int j;
|
||
|
|
||
|
if( !fCompPB->GetInt( (ParamID)kCombineSounds ) )
|
||
|
{
|
||
|
for (j = 0; j < numSounds; j++)
|
||
|
{
|
||
|
plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kSoundList), 0, numSoFar + j);
|
||
|
if (compNode)
|
||
|
{
|
||
|
plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent *)compNode->ConvertToComponent();
|
||
|
int idx = comp->GetSoundIdx((plMaxNode*)node);
|
||
|
if (idx >= 0)
|
||
|
{
|
||
|
indices.Append(idx);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
groups[i].fNumSounds = indices.GetCount();
|
||
|
for (j = 0; j < indices.GetCount(); j++)
|
||
|
{
|
||
|
groups[i].fIndices[j] = indices[j];
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Build array of components to give to ConvertGrouped()
|
||
|
for (j = 0; j < numSounds; j++)
|
||
|
{
|
||
|
plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kSoundList), 0, numSoFar + j);
|
||
|
if (compNode)
|
||
|
{
|
||
|
plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent *)compNode->ConvertToComponent();
|
||
|
comps.Append( comp );
|
||
|
// Stupid, i know. Leave me alone, PG is playing.
|
||
|
indices.Append( comps.GetCount() - 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get index from first (should be the same for all of 'em)
|
||
|
groups[i].fGroupedIdx = comps[ 0 ]->GetSoundIdx( (plMaxNode *)node );
|
||
|
groups[i].fNumSounds = indices.GetCount();
|
||
|
for (j = 0; j < indices.GetCount(); j++)
|
||
|
{
|
||
|
groups[i].fIndices[j] = indices[ j ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
numSoFar += groups[i].fNumSounds;
|
||
|
}
|
||
|
mod->SetGroupInfo(kMaxGroups, groups);
|
||
|
|
||
|
if( fCompPB->GetInt( (ParamID)kCombineSounds ) )
|
||
|
{
|
||
|
// Convert (use pointer to first comp so we get the virtual call)
|
||
|
if( !comps[ 0 ]->ConvertGrouped( node, comps, pErrMsg ) )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Non-grouped random sounds - give priority to each sound
|
||
|
else
|
||
|
{
|
||
|
ai = node->GetSceneObject()->GetAudioInterface();
|
||
|
pAudible = (plWinAudible*)ai->GetAudible();
|
||
|
int numSounds = pAudible->GetNumSounds();
|
||
|
|
||
|
if(numSounds == 0) return true;
|
||
|
|
||
|
pSound = pAudible->GetSound(0); // Get sound ptr
|
||
|
int highestPriority = pSound->GetPriority();
|
||
|
|
||
|
// Distance to lowest priority
|
||
|
int distToLowest = 9 - highestPriority;
|
||
|
if( distToLowest <= 0) distToLowest = 1; // just incase
|
||
|
|
||
|
for( int i = 0; i < numSounds; i++)
|
||
|
{
|
||
|
pSound = pAudible->GetSound(i); // Get sound ptr
|
||
|
|
||
|
// Give the first random sound highest priority
|
||
|
if(i == 0)
|
||
|
pSound->SetPriority(highestPriority);
|
||
|
|
||
|
else
|
||
|
{
|
||
|
pSound->SetPriority(highestPriority+((i-1)%distToLowest)+1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hsBool plRandomSoundComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
if (ICheckForSounds(pNode))
|
||
|
{
|
||
|
plRandomSoundMod* mod = TRACKED_NEW plRandomSoundMod;
|
||
|
hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), mod, pNode->GetLocation());
|
||
|
fSoundMods[pNode] = mod;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
|
||
|
// of properties on the MaxNode, as it's still indeterminant.
|
||
|
hsBool plRandomSoundComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
|
||
|
{
|
||
|
fSoundMods.clear();
|
||
|
|
||
|
// Tell some (all?) of the sound components we point to that they're going to be
|
||
|
// grouped sounds instead
|
||
|
if( fCompPB->GetInt( (ParamID)kCombineSounds ) )
|
||
|
{
|
||
|
if (!fCompPB->GetInt(ParamID(kUseAll))) // Actually using separate groups
|
||
|
{
|
||
|
// Get a sound index to assign to all the components, since they get the same one as a grouped sound
|
||
|
int idx = pNode->GetNextSoundIdx();
|
||
|
|
||
|
int i, numSoFar = 0;
|
||
|
for (i = 0; i < kMaxGroups; i++)
|
||
|
{
|
||
|
int numSounds = fCompPB->GetInt(ParamID(kGroupTotals), 0, i);
|
||
|
|
||
|
if( numSounds <= 0 )
|
||
|
continue;
|
||
|
|
||
|
int j;
|
||
|
for (j = 0; j < numSounds; j++)
|
||
|
{
|
||
|
plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kSoundList), 0, numSoFar + j);
|
||
|
if (compNode)
|
||
|
{
|
||
|
plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent *)compNode->ConvertToComponent();
|
||
|
comp->SetCreateGrouped( pNode, idx );
|
||
|
}
|
||
|
}
|
||
|
numSoFar += numSounds;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#endif // Waiting... mf
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
/// Physics Sound Group Component ///////////////////////////////////////////////////////////////
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
class plPhysicsSndGroupCompProc;
|
||
|
class plPhysicsSndGroupComp : public plComponent
|
||
|
{
|
||
|
protected:
|
||
|
|
||
|
friend class plPhysicsSndGroupCompProc;
|
||
|
|
||
|
public:
|
||
|
plPhysicsSndGroupComp();
|
||
|
void DeleteThis() { delete this; }
|
||
|
|
||
|
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
|
||
|
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
|
||
|
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
|
||
|
|
||
|
enum Refs
|
||
|
{
|
||
|
kRefGroup,
|
||
|
kRefImpactSoundsOld,
|
||
|
kRefSlideSoundsOld,
|
||
|
kRefDummyPickNode,
|
||
|
kRefImpactSounds,
|
||
|
kRefSlideSounds,
|
||
|
};
|
||
|
};
|
||
|
|
||
|
class plPhysicsSndGroupCompProc : public ParamMap2UserDlgProc
|
||
|
{
|
||
|
public:
|
||
|
plPhysicsSndGroupCompProc() {}
|
||
|
|
||
|
BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||
|
|
||
|
void DeleteThis() {}
|
||
|
virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { }
|
||
|
|
||
|
protected:
|
||
|
|
||
|
void IInitList( HWND hList, int currSel, hsBool allowAll )
|
||
|
{
|
||
|
int i, toSet = -1;
|
||
|
struct plSndGrp
|
||
|
{
|
||
|
char name[ 64 ];
|
||
|
int group;
|
||
|
} groups[] = { { "Metal", plPhysicalSndGroup::kMetal },
|
||
|
{ "Grass", plPhysicalSndGroup::kGrass },
|
||
|
{ "Wood", plPhysicalSndGroup::kWood },
|
||
|
{ "Stone", plPhysicalSndGroup::kWood + 1 },
|
||
|
{ "Water", plPhysicalSndGroup::kWood + 2 },
|
||
|
{ "Bone", plPhysicalSndGroup::kWood + 3 },
|
||
|
{ "Dirt", plPhysicalSndGroup::kWood + 4 },
|
||
|
{ "Rug", plPhysicalSndGroup::kWood + 5 },
|
||
|
{ "Cone", plPhysicalSndGroup::kWood + 6 },
|
||
|
{ "User 1", plPhysicalSndGroup::kWood + 7 },
|
||
|
{ "User 2", plPhysicalSndGroup::kWood + 8 },
|
||
|
{ "User 3", plPhysicalSndGroup::kWood + 9 },
|
||
|
{ "", plPhysicalSndGroup::kNone } };
|
||
|
|
||
|
SendMessage( hList, CB_RESETCONTENT, 0, 0 );
|
||
|
|
||
|
if( allowAll )
|
||
|
{
|
||
|
int idx = SendMessage( hList, CB_ADDSTRING, 0, (LPARAM)"* All *" );
|
||
|
SendMessage( hList, CB_SETITEMDATA, idx, (LPARAM)-1 );
|
||
|
if( currSel == -1 )
|
||
|
toSet = idx;
|
||
|
}
|
||
|
|
||
|
for( i = 0; groups[ i ].group != plPhysicalSndGroup::kNone; i++ )
|
||
|
{
|
||
|
int idx = SendMessage( hList, CB_ADDSTRING, 0, (LPARAM)groups[ i ].name );
|
||
|
SendMessage( hList, CB_SETITEMDATA, idx, (LPARAM)groups[ i ].group );
|
||
|
|
||
|
if( groups[ i ].group == currSel )
|
||
|
toSet = idx;
|
||
|
}
|
||
|
|
||
|
if( toSet != -1 )
|
||
|
SendMessage( hList, CB_SETCURSEL, toSet, 0 );
|
||
|
}
|
||
|
|
||
|
void IUpdateBtns( HWND hWnd, int idx, plPhysicsSndGroupComp *comp )
|
||
|
{
|
||
|
// Update da buttons
|
||
|
if( idx == -1 )
|
||
|
idx = 0;
|
||
|
|
||
|
INode *impact = IGet( comp->GetParamBlock( 0 ), plPhysicsSndGroupComp::kRefImpactSounds, idx );
|
||
|
::SetWindowText( GetDlgItem( hWnd, IDC_SND_IMPACT ), ( impact != nil ) ? impact->GetName() : "<none>" );
|
||
|
|
||
|
INode *slide = IGet( comp->GetParamBlock( 0 ), plPhysicsSndGroupComp::kRefSlideSounds, idx );
|
||
|
::SetWindowText( GetDlgItem( hWnd, IDC_SND_SLIDE ), ( slide != nil ) ? slide->GetName() : "<none>" );
|
||
|
}
|
||
|
|
||
|
void ISet( IParamBlock2 *pb, ParamID which, int idx, INode *node )
|
||
|
{
|
||
|
if( pb->Count( which ) <= idx )
|
||
|
{
|
||
|
pb->SetCount( (ParamID)which, idx + 1 );
|
||
|
pb->Resize( (ParamID)which, idx + 1 );
|
||
|
}
|
||
|
|
||
|
if( idx == -1 )
|
||
|
{
|
||
|
pb->SetCount( (ParamID)which, plPhysicalSndGroup::kWood + 9 );
|
||
|
pb->Resize( which, plPhysicalSndGroup::kWood + 9 );
|
||
|
int i;
|
||
|
for( i = 0; i < plPhysicalSndGroup::kWood + 9; i++ )
|
||
|
pb->SetValue( which, 0, node, i );
|
||
|
}
|
||
|
else
|
||
|
pb->SetValue( which, 0, node, idx );
|
||
|
}
|
||
|
|
||
|
INode *IGet( IParamBlock2 *pb, ParamID which, int idx )
|
||
|
{
|
||
|
if( pb->Count( which ) <= idx )
|
||
|
return nil;
|
||
|
|
||
|
return pb->GetINode( which, 0, idx );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static plPhysicsSndGroupCompProc gPhysicsSndGroupCompProc;
|
||
|
|
||
|
|
||
|
BOOL plPhysicsSndGroupCompProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
IParamBlock2 *pb = pm->GetParamBlock();
|
||
|
HWND hList = GetDlgItem( hWnd, IDC_SND_GROUP );
|
||
|
HWND hAgainst = GetDlgItem( hWnd, IDC_SND_AGAINST );
|
||
|
plPhysicsSndGroupComp *comp = (plPhysicsSndGroupComp *)pb->GetOwner();
|
||
|
|
||
|
switch( msg )
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
IInitList( GetDlgItem( hWnd, IDC_SND_GROUP ), pb->GetInt( plPhysicsSndGroupComp::kRefGroup ), false );
|
||
|
IInitList( GetDlgItem( hWnd, IDC_SND_AGAINST ), -1, true );
|
||
|
|
||
|
int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 );
|
||
|
IUpdateBtns( hWnd, idx, comp );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
if( HIWORD( wParam ) == CBN_SELCHANGE )
|
||
|
{
|
||
|
if( LOWORD( wParam ) == IDC_SND_GROUP )
|
||
|
{
|
||
|
int idx = SendMessage( hList, CB_GETCURSEL, 0, 0 );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
pb->SetValue( (ParamID)plPhysicsSndGroupComp::kRefGroup, 0, (int)SendMessage( hList, CB_GETITEMDATA, idx, 0 ) );
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_SND_AGAINST )
|
||
|
{
|
||
|
int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 );
|
||
|
IUpdateBtns( hWnd, idx, comp );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_SND_CLEAR_IMPACT )
|
||
|
{
|
||
|
int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 );
|
||
|
if( idx == -1 )
|
||
|
{
|
||
|
pb->Resize( (ParamID)plPhysicsSndGroupComp::kRefImpactSounds, 0 );
|
||
|
}
|
||
|
else
|
||
|
ISet( pb, plPhysicsSndGroupComp::kRefImpactSounds, idx, nil );
|
||
|
IUpdateBtns( hWnd, idx, comp );
|
||
|
}
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_SND_CLEAR_SLIDE )
|
||
|
{
|
||
|
int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 );
|
||
|
if( idx == -1 )
|
||
|
pb->Resize( (ParamID)plPhysicsSndGroupComp::kRefSlideSounds, 0 );
|
||
|
else
|
||
|
ISet( pb, plPhysicsSndGroupComp::kRefSlideSounds, idx, nil );
|
||
|
IUpdateBtns( hWnd, idx, comp );
|
||
|
}
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_SND_IMPACT )
|
||
|
{
|
||
|
int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 );
|
||
|
|
||
|
std::vector<Class_ID> cids;
|
||
|
cids.push_back( RANDOM_SOUND_COMPONENT_ID );
|
||
|
if( plPick::NodeRefKludge( pb, plPhysicsSndGroupComp::kRefDummyPickNode, &cids, true, false ) )
|
||
|
ISet( comp->GetParamBlock( 0 ), plPhysicsSndGroupComp::kRefImpactSounds, idx, pb->GetINode( plPhysicsSndGroupComp::kRefDummyPickNode ) );
|
||
|
|
||
|
IUpdateBtns( hWnd, idx, comp );
|
||
|
}
|
||
|
}
|
||
|
else if( LOWORD( wParam ) == IDC_SND_SLIDE )
|
||
|
{
|
||
|
int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 );
|
||
|
if( idx != CB_ERR )
|
||
|
{
|
||
|
idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 );
|
||
|
|
||
|
std::vector<Class_ID> cids;
|
||
|
cids.push_back( RANDOM_SOUND_COMPONENT_ID );
|
||
|
if( plPick::NodeRefKludge( pb, plPhysicsSndGroupComp::kRefDummyPickNode, &cids, true, false ) )
|
||
|
ISet( pb, plPhysicsSndGroupComp::kRefSlideSounds, idx, pb->GetINode( plPhysicsSndGroupComp::kRefDummyPickNode ) );
|
||
|
|
||
|
IUpdateBtns( hWnd, idx, comp );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Simple accessor
|
||
|
class plPhysicsSndGroupAccessor : public PBAccessor
|
||
|
{
|
||
|
public:
|
||
|
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
|
||
|
{
|
||
|
if( id == plPhysicsSndGroupComp::kRefImpactSounds || id == plPhysicsSndGroupComp::kRefSlideSounds )
|
||
|
{
|
||
|
plPhysicsSndGroupComp *comp = (plPhysicsSndGroupComp *)owner;
|
||
|
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
static plPhysicsSndGroupAccessor glPhysicsSndGroupAccessor;
|
||
|
|
||
|
//Max desc stuff necessary below.
|
||
|
CLASS_DESC(plPhysicsSndGroupComp, gPhysSndGrpDesc, "Physics Sound Group", "PhysSndGroup", COMP_TYPE_AUDIO, SOUND_PHYS_COMP_ID)
|
||
|
|
||
|
ParamBlockDesc2 gPhysSndGrpBk
|
||
|
(
|
||
|
plComponent::kBlkComp, _T("PhysSndGroup"), 0, &gPhysSndGrpDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
||
|
|
||
|
IDD_COMP_SOUNDPHYS, IDS_COMP_SOUNDPHYS, 0, 0, &gPhysicsSndGroupCompProc,
|
||
|
|
||
|
plPhysicsSndGroupComp::kRefGroup, _T("Group"), TYPE_INT, 0, 0,
|
||
|
p_default, (int)plPhysicalSndGroup::kNone,
|
||
|
end,
|
||
|
|
||
|
plPhysicsSndGroupComp::kRefDummyPickNode, _T( "Dummy" ), TYPE_INODE, 0, 0,
|
||
|
end,
|
||
|
|
||
|
plPhysicsSndGroupComp::kRefImpactSounds, _T("Impacts"), TYPE_INODE_TAB, 0, 0, 0,
|
||
|
// p_accessor, glPhysicsSndGroupAccessor,
|
||
|
end,
|
||
|
|
||
|
plPhysicsSndGroupComp::kRefSlideSounds, _T("Slides"), TYPE_INODE_TAB, 0, 0, 0,
|
||
|
// p_accessor, glPhysicsSndGroupAccessor,
|
||
|
end,
|
||
|
|
||
|
end
|
||
|
);
|
||
|
|
||
|
plPhysicsSndGroupComp::plPhysicsSndGroupComp()
|
||
|
{
|
||
|
fClassDesc = &gPhysSndGrpDesc;
|
||
|
fClassDesc->MakeAutoParamBlocks(this);
|
||
|
}
|
||
|
|
||
|
hsBool plPhysicsSndGroupComp::Convert( plMaxNode *node, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
plMaxNode *pNode;
|
||
|
plKey RandSoundKey;
|
||
|
|
||
|
// Try to grab the SI from the current scene object. This'll have the pointer we want
|
||
|
plSceneObject *obj = node->GetSceneObject();
|
||
|
if( obj != nil )
|
||
|
{
|
||
|
const plSimulationInterface* si = obj->GetSimulationInterface();
|
||
|
if (si)
|
||
|
{
|
||
|
// Create a new sound group
|
||
|
plPhysicalSndGroup *grp = TRACKED_NEW plPhysicalSndGroup( fCompPB->GetInt( (ParamID)kRefGroup ) );
|
||
|
hsgResMgr::ResMgr()->NewKey( IGetUniqueName( node ), grp, node->GetLocation(), node->GetLoadMask() );
|
||
|
|
||
|
// Convert each sound into a plWin32StaticSound and store onto the sound group
|
||
|
int i;
|
||
|
for( i = 0; i < fCompPB->Count( (ParamID)kRefImpactSounds ); i++ )
|
||
|
{
|
||
|
plMaxNode *targNode = (plMaxNode *)fCompPB->GetINode( (ParamID)kRefImpactSounds, 0, i );
|
||
|
if( targNode != nil )
|
||
|
{
|
||
|
plComponentBase *comp = targNode->ConvertToComponent();
|
||
|
if( comp != nil )
|
||
|
{
|
||
|
// Check root node for random sound component
|
||
|
RandSoundKey = plAudioComp::GetRandomSoundKey( comp, node );
|
||
|
if(RandSoundKey)
|
||
|
grp->AddImpactSound( i, RandSoundKey );
|
||
|
|
||
|
// If not in root node check children
|
||
|
else
|
||
|
{
|
||
|
for(int j = 0; j < node->NumChildren(); j++)
|
||
|
{
|
||
|
pNode = (plMaxNode *)node->GetChildNode(j);
|
||
|
RandSoundKey = plAudioComp::GetRandomSoundKey( comp, pNode );
|
||
|
if(!RandSoundKey) continue;
|
||
|
|
||
|
grp->AddImpactSound( i, RandSoundKey );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < fCompPB->Count( (ParamID)kRefSlideSounds ); i++ )
|
||
|
{
|
||
|
plMaxNode *targNode = (plMaxNode *)fCompPB->GetINode( (ParamID)kRefSlideSounds, 0, i );
|
||
|
if( targNode != nil )
|
||
|
{
|
||
|
plComponentBase *comp = targNode->ConvertToComponent();
|
||
|
if( comp != nil )
|
||
|
{
|
||
|
// Check root node for random sound component
|
||
|
RandSoundKey = plAudioComp::GetRandomSoundKey( comp, node );
|
||
|
if(RandSoundKey)
|
||
|
grp->AddSlideSound( i, RandSoundKey );
|
||
|
else
|
||
|
{
|
||
|
for(int j = 0; j < node->NumChildren(); j++)
|
||
|
{
|
||
|
pNode = (plMaxNode *)node->GetChildNode(j);
|
||
|
RandSoundKey = plAudioComp::GetRandomSoundKey( comp, pNode );
|
||
|
if(!RandSoundKey) continue;
|
||
|
|
||
|
grp->AddSlideSound( i, RandSoundKey );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Attach the sound group to the physical
|
||
|
hsgResMgr::ResMgr()->AddViaNotify( grp->GetKey(), TRACKED_NEW plGenRefMsg( si->GetPhysical()->GetKey(), plRefMsg::kOnCreate, 0, plPXPhysical::kPhysRefSndGroup ), plRefFlags::kActiveRef );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hsBool plPhysicsSndGroupComp::PreConvert( plMaxNode *pNode, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hsBool plPhysicsSndGroupComp::SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|