mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
367 lines
11 KiB
C++
367 lines
11 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "T3D/sfx/sfx3DWorld.h"
|
|
#include "T3D/portal.h"
|
|
#include "T3D/shapeBase.h"
|
|
#include "ts/tsShapeInstance.h"
|
|
#include "sfx/sfxSystem.h"
|
|
#include "math/mBox.h"
|
|
#include "core/module.h"
|
|
#include "T3D/gameBase/gameConnection.h"
|
|
|
|
|
|
//#define DEBUG_SPEW
|
|
|
|
|
|
MODULE_BEGIN( SFX3D )
|
|
|
|
MODULE_INIT_AFTER( Scene )
|
|
MODULE_SHUTDOWN_BEFORE( Scene )
|
|
|
|
MODULE_INIT
|
|
{
|
|
if( !Con::getBoolVariable( "$SFX::noSFXWorld", false ) )
|
|
gSFX3DWorld = new SFX3DWorld;
|
|
}
|
|
|
|
MODULE_SHUTDOWN
|
|
{
|
|
if( gSFX3DWorld )
|
|
SAFE_DELETE( gSFX3DWorld );
|
|
}
|
|
|
|
MODULE_END;
|
|
|
|
|
|
SFX3DWorld* gSFX3DWorld;
|
|
|
|
|
|
//=============================================================================
|
|
// SFX3DObject.
|
|
//=============================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFX3DObject::SFX3DObject()
|
|
: Parent(NULL, NULL)
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFX3DObject::SFX3DObject( SFX3DWorld* world, SceneObject* object )
|
|
: Parent( world, object )
|
|
{
|
|
if( mObject->isOccludingSound() )
|
|
setFlags( SFXObjectOccluder );
|
|
if( dynamic_cast< Portal* >( mObject ) )
|
|
setFlags( SFXObjectPortal );
|
|
if( object->getSoundAmbience() )
|
|
setFlags( SFXObjectZone );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DObject::getEarTransform( MatrixF& transform ) const
|
|
{
|
|
// If it's not a ShapeBase, just use the object transform.
|
|
ShapeBase* shape = dynamic_cast< ShapeBase* >( mObject );
|
|
if ( !shape )
|
|
{
|
|
transform = mObject->getTransform();
|
|
return;
|
|
}
|
|
|
|
// It it's ShapeBase, use the earNode transform if one was defined.
|
|
// Otherwise, use the camera transform.
|
|
TSShapeInstance* shapeInstance = shape->getShapeInstance();
|
|
if ( !shapeInstance )
|
|
{
|
|
// Just in case.
|
|
GameConnection* connection = dynamic_cast<GameConnection *>(NetConnection::getConnectionToServer());
|
|
if ( !connection || !connection->getControlCameraTransform( 0.0f, &transform ) )
|
|
transform = mObject->getTransform();
|
|
return;
|
|
}
|
|
|
|
ShapeBaseData* datablock = dynamic_cast< ShapeBaseData* >( shape->getDataBlock() );
|
|
AssertFatal( datablock, "SFX3DObject::getEarTransform() - shape without ShapeBaseData datablock!" );
|
|
|
|
// Get the transform for the ear node.
|
|
|
|
const S32 earNode = datablock->earNode;
|
|
|
|
if ( earNode != -1 && earNode != datablock->eyeNode )
|
|
{
|
|
transform = shape->getTransform();
|
|
transform *= shapeInstance->mNodeTransforms[ earNode ];
|
|
}
|
|
else
|
|
{
|
|
GameConnection* connection = dynamic_cast<GameConnection *>(NetConnection::getConnectionToServer());
|
|
if ( !connection || !connection->getControlCameraTransform( 0.0f, &transform ) )
|
|
transform = mObject->getTransform();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DObject::getReferenceCenter( F32 position[ 3 ] ) const
|
|
{
|
|
MatrixF transform;
|
|
getEarTransform( transform );
|
|
Point3F pos = transform.getPosition();
|
|
|
|
AssertFatal( TypeTraits< F32 >::MIN <= pos.x && pos.x <= TypeTraits< F32 >::MAX,
|
|
"SFX3DObject::getReferenceCenter - invalid float in reference center X position" );
|
|
AssertFatal( TypeTraits< F32 >::MIN <= pos.y && pos.y <= TypeTraits< F32 >::MAX,
|
|
"SFX3DObject::getReferenceCenter - invalid float in reference center Y position" );
|
|
AssertFatal( TypeTraits< F32 >::MIN <= pos.z && pos.z <= TypeTraits< F32 >::MAX,
|
|
"SFX3DObject::getReferenceCenter - invalid float in reference center Z position" );
|
|
|
|
dMemcpy( position, &pos.x, sizeof( F32 ) * 3 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DObject::getBounds( F32 minBounds[ 3 ], F32 maxBounds[ 3 ] ) const
|
|
{
|
|
getRealBounds( minBounds, maxBounds );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DObject::getRealBounds( F32 minBounds[ 3 ], F32 maxBounds[ 3 ] ) const
|
|
{
|
|
const Box3F& worldBox = mObject->getWorldBox();
|
|
dMemcpy( minBounds, &worldBox.minExtents.x, sizeof( F32 ) * 3 );
|
|
dMemcpy( maxBounds, &worldBox.maxExtents.x, sizeof( F32 ) * 3 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFXAmbience* SFX3DObject::getAmbience() const
|
|
{
|
|
return mObject->getSoundAmbience();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool SFX3DObject::containsPoint( const F32 point[ 3 ] ) const
|
|
{
|
|
return mObject->containsPoint( *( reinterpret_cast< const Point3F* >( &point[ 0 ] ) ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
String SFX3DObject::describeSelf() const
|
|
{
|
|
return mObject->describeSelf();
|
|
}
|
|
|
|
//=============================================================================
|
|
// SFX3DWorld.
|
|
//=============================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFX3DWorld::SFX3DWorld()
|
|
: Parent( true, TYPEMASK )
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool SFX3DWorld::_isTrackableObject( SceneObject* object ) const
|
|
{
|
|
if( !Parent::_isTrackableObject( object ) )
|
|
return false;
|
|
|
|
// We are only interested in occluders, zones, and portals.
|
|
|
|
if( object->isOccludingSound() )
|
|
return true;
|
|
else if( object->getSoundAmbience() )
|
|
return true;
|
|
else if( dynamic_cast< Portal* >( object ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DWorld::update()
|
|
{
|
|
mSFXWorld.update();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DWorld::registerObject( SceneObject* object )
|
|
{
|
|
if( !_isTrackableObject( object ) )
|
|
return;
|
|
|
|
// Construct a new scene object link.
|
|
|
|
SFX3DObject* sfxObject = mChunker.alloc();
|
|
constructInPlace( sfxObject, this, object );
|
|
|
|
// Register it with the SFX world.
|
|
|
|
#ifdef DEBUG_SPEW
|
|
Platform::outputDebugString( "[SFX3DWorld] Registering %i:%s as 0x%x",
|
|
object->getId(), object->getClassName(), sfxObject );
|
|
#endif
|
|
|
|
mSFXWorld.registerObject( sfxObject );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DWorld::unregisterObject( SceneObject* object )
|
|
{
|
|
SFX3DObject* sfxObject = dynamic_cast< SFX3DObject* >( SFX3DObject::getLinkForTracker( this, object ) );
|
|
if( !sfxObject )
|
|
return;
|
|
|
|
#ifdef DEBUG_SPEW
|
|
Platform::outputDebugString( "[SFX3DWorld] Unregistering %i:%s (was 0x%x)",
|
|
object->getId(), object->getClassName(), sfxObject );
|
|
#endif
|
|
|
|
// Remove the object from the SFX world.
|
|
|
|
if( sfxObject->isListener() )
|
|
mSFXWorld.setReferenceObject( NULL );
|
|
else
|
|
mSFXWorld.unregisterObject( sfxObject );
|
|
|
|
// Destroy the scene object link.
|
|
|
|
destructInPlace( sfxObject );
|
|
mChunker.free( sfxObject );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DWorld::updateObject( SceneObjectLink* object )
|
|
{
|
|
SFX3DObject* sfxObject = dynamic_cast< SFX3DObject* >( object );
|
|
AssertFatal( sfxObject, "SFX3DWorld::updateObject - invalid object type" );
|
|
|
|
mSFXWorld.updateObject( sfxObject );
|
|
|
|
// If this is the listener object, update its
|
|
// properties on the SFX system.
|
|
|
|
if( sfxObject->isListener() )
|
|
{
|
|
SFXListenerProperties listener;
|
|
sfxObject->getEarTransform( listener.getTransform() );
|
|
listener.getVelocity() = sfxObject->getObject()->getVelocity();
|
|
|
|
SFX->setListener( 0, listener );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SceneObject* SFX3DWorld::getListener() const
|
|
{
|
|
SFX3DObject* object = mSFXWorld.getReferenceObject();
|
|
if( !object )
|
|
return NULL;
|
|
|
|
return object->getObject();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DWorld::setListener( SceneObject* object )
|
|
{
|
|
SFX3DObject* oldListener = mSFXWorld.getReferenceObject();
|
|
|
|
// If it's the same object as our current listener,
|
|
// return.
|
|
|
|
if( oldListener && oldListener->getObject() == object )
|
|
return;
|
|
|
|
// Create a SFX3DObject for the given SceneObject.
|
|
|
|
SFX3DObject* sfxObject = NULL;
|
|
if( object )
|
|
{
|
|
AssertFatal( !dynamic_cast< SFX3DObject* >( SFX3DObject::getLinkForTracker( this, object ) ),
|
|
"SFX3DWorld::setListener - listener objects must not be registered for tracking" );
|
|
|
|
sfxObject = mChunker.alloc();
|
|
constructInPlace( sfxObject, this, object );
|
|
sfxObject->setFlags( SFXObjectListener );
|
|
}
|
|
|
|
#ifdef DEBUG_SPEW
|
|
if( object )
|
|
Platform::outputDebugString( "[SFX3DWorld] Listener is now %i:%s (%s)",
|
|
object->getId(), object->getClassName(), object->getName() );
|
|
else
|
|
Platform::outputDebugString( "[SFX3DWorld] Unsetting listener" );
|
|
#endif
|
|
|
|
// Make this object the center of our SFX world.
|
|
|
|
mSFXWorld.setReferenceObject( sfxObject );
|
|
|
|
// Remove the tracking links from the old listener so we
|
|
// don't see further updates on it.
|
|
|
|
if( oldListener )
|
|
{
|
|
destructInPlace( oldListener );
|
|
mChunker.free( oldListener );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DWorld::notifyChanged( SceneObject* object )
|
|
{
|
|
SFX3DObject* sfxObject = dynamic_cast< SFX3DObject* >( SFX3DObject::getLinkForTracker( this, object ) );
|
|
|
|
if( !sfxObject && _isTrackableObject( object ) )
|
|
registerObject( object );
|
|
else if( sfxObject && !_isTrackableObject( object ) )
|
|
unregisterObject( object );
|
|
else if( sfxObject )
|
|
mSFXWorld.notifyChanged( sfxObject );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFX3DWorld::debugDump()
|
|
{
|
|
mSFXWorld.debugDump();
|
|
}
|