mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
316 lines
9.1 KiB
C++
316 lines
9.1 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 "platform/platform.h"
|
|
#include "forest/forestWindMgr.h"
|
|
|
|
#include "platform/profiler.h"
|
|
#include "core/tAlgorithm.h"
|
|
#include "core/module.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "math/mathUtils.h"
|
|
#include "T3D/gameBase/gameConnection.h"
|
|
#include "forest/forest.h"
|
|
#include "forest/forestWindAccumulator.h"
|
|
#include "T3D/fx/particleEmitter.h"
|
|
|
|
|
|
MODULE_BEGIN( ForestWindMgr )
|
|
|
|
MODULE_INIT
|
|
{
|
|
ManagedSingleton< ForestWindMgr >::createSingleton();
|
|
ForestWindMgr::initConsole();
|
|
}
|
|
|
|
MODULE_SHUTDOWN
|
|
{
|
|
ManagedSingleton< ForestWindMgr >::deleteSingleton();
|
|
}
|
|
|
|
MODULE_END;
|
|
|
|
|
|
ForestWindMgr::WindAdvanceSignal ForestWindMgr::smAdvanceSignal;
|
|
|
|
F32 ForestWindMgr::smWindEffectRadius = 25.0f;
|
|
|
|
|
|
ForestWindMgr::ForestWindMgr()
|
|
{
|
|
setProcessTicks( true );
|
|
mSources = new IdToWindMap();
|
|
mPrevSources = new IdToWindMap();
|
|
}
|
|
|
|
ForestWindMgr::~ForestWindMgr()
|
|
{
|
|
IdToWindMap::Iterator sourceIter = mSources->begin();
|
|
for (; sourceIter != mSources->end(); ++sourceIter)
|
|
delete (*sourceIter).value;
|
|
|
|
delete mSources;
|
|
delete mPrevSources;
|
|
}
|
|
|
|
void ForestWindMgr::initConsole()
|
|
{
|
|
Con::addVariable( "$pref::windEffectRadius", TypeF32, &smWindEffectRadius, "Radius to affect the wind.\n"
|
|
"@ingroup Rendering\n");
|
|
}
|
|
|
|
void ForestWindMgr::addEmitter( ForestWindEmitter *emitter )
|
|
{
|
|
mEmitters.push_back( emitter );
|
|
}
|
|
|
|
void ForestWindMgr::removeEmitter( ForestWindEmitter *emitter )
|
|
{
|
|
ForestWindEmitterList::iterator iter = T3D::find( mEmitters.begin(),
|
|
mEmitters.end(),
|
|
emitter );
|
|
|
|
AssertFatal( iter != mEmitters.end(),
|
|
"SpeedTreeWindMgr::removeEmitter() - Bad emitter!" );
|
|
|
|
mEmitters.erase( iter );
|
|
}
|
|
|
|
void ForestWindMgr::processTick()
|
|
{
|
|
const F32 timeDelta = 0.032f;
|
|
|
|
if ( mEmitters.empty() )
|
|
return;
|
|
|
|
PROFILE_SCOPE(ForestWindMgr_AdvanceTime);
|
|
|
|
// Advance all ForestWinds.
|
|
{
|
|
PROFILE_SCOPE(ForestWindMgr_AdvanceTime_ForestWind_ProcessTick);
|
|
|
|
ForestWindEmitterList::iterator iter = mEmitters.begin();
|
|
for ( ; iter != mEmitters.end(); iter++ )
|
|
{
|
|
if ( (*iter)->getWind() &&
|
|
(*iter)->isEnabled() )
|
|
{
|
|
(*iter)->updateMountPosition();
|
|
|
|
ForestWind *wind = (*iter)->getWind();
|
|
if ( wind )
|
|
wind->processTick();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assign the new global wind value used by the particle system.
|
|
{
|
|
ForestWindEmitter *pWindEmitter = getGlobalWind();
|
|
if ( pWindEmitter == NULL )
|
|
ParticleEmitter::setWindVelocity( Point3F::Zero );
|
|
else
|
|
{
|
|
ForestWind *pWind = pWindEmitter->getWind();
|
|
ParticleEmitter::setWindVelocity( pWind->getDirection() * pWind->getStrength() );
|
|
}
|
|
}
|
|
|
|
// Get the game connection and camera object
|
|
// in order to retrieve the camera position.
|
|
GameConnection *conn = GameConnection::getConnectionToServer();
|
|
if ( !conn )
|
|
return;
|
|
|
|
GameBase *cam = conn->getCameraObject();
|
|
if ( !cam )
|
|
return;
|
|
|
|
const Point3F &camPos = cam->getPosition();
|
|
|
|
|
|
|
|
// Gather TreePlacementInfo for trees near the camera.
|
|
{
|
|
PROFILE_SCOPE( ForestWindMgr_AdvanceTime_GatherTreePlacementInfo );
|
|
|
|
smAdvanceSignal.trigger( camPos, smWindEffectRadius, &mPlacementInfo );
|
|
}
|
|
|
|
// Prepare to build a new local source map.
|
|
{
|
|
PROFILE_SCOPE( ForestWindMgr_AdvanceTime_SwapSources );
|
|
AssertFatal( mPrevSources->isEmpty(), "prev sources not empty!" );
|
|
|
|
T3D::swap( mSources, mPrevSources );
|
|
|
|
AssertFatal( mSources->isEmpty(), "swap failed!" );
|
|
}
|
|
|
|
// Update wind for each TreePlacementInfo
|
|
{
|
|
PROFILE_SCOPE( ForestWindMgr_AdvanceTime_UpdateWind );
|
|
|
|
for( S32 i = 0; i < mPlacementInfo.size(); i++ )
|
|
{
|
|
const TreePlacementInfo &info = mPlacementInfo[i];
|
|
updateWind( camPos, info, timeDelta );
|
|
}
|
|
|
|
mPlacementInfo.clear();
|
|
}
|
|
|
|
// Clean up any accumulators in the
|
|
// previous local source map.
|
|
{
|
|
PROFILE_SCOPE( ForestWindMgr_AdvanceTime_Cleanup );
|
|
|
|
IdToWindMap::Iterator sourceIter = mPrevSources->begin();
|
|
for (; sourceIter != mPrevSources->end(); ++sourceIter)
|
|
{
|
|
ForestWindAccumulator *accum = (*sourceIter).value;
|
|
|
|
AssertFatal( accum, "Got null accumulator!" );
|
|
delete accum;
|
|
}
|
|
|
|
mPrevSources->clear();
|
|
}
|
|
}
|
|
|
|
ForestWindAccumulator* ForestWindMgr::getLocalWind( ForestItemKey key )
|
|
{
|
|
PROFILE_SCOPE( ForestWindMgr_getLocalWind );
|
|
|
|
ForestWindAccumulator *accumulator = NULL;
|
|
IdToWindMap::Iterator iter = mSources->find( key );
|
|
if ( iter != mSources->end() )
|
|
accumulator = iter->value;
|
|
|
|
if ( accumulator )
|
|
return accumulator;
|
|
else
|
|
{
|
|
/*
|
|
ForestWindEmitterList::iterator iter = mEmitters.begin();
|
|
for ( ; iter != mEmitters.end(); iter++ )
|
|
{
|
|
if ( (*iter)->getWind() &&
|
|
(*iter)->isEnabled() &&
|
|
!(*iter)->isLocalWind() )
|
|
{
|
|
return (*iter)->getWind();
|
|
}
|
|
}
|
|
*/
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
ForestWindEmitter* ForestWindMgr::getGlobalWind()
|
|
{
|
|
ForestWindEmitterList::iterator itr = mEmitters.begin();
|
|
for ( ; itr != mEmitters.end(); itr++ )
|
|
{
|
|
if ( !(*itr)->isRadialEmitter() )
|
|
return *itr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ForestWindMgr::updateWind( const Point3F &camPos,
|
|
const TreePlacementInfo &info,
|
|
F32 timeDelta )
|
|
{
|
|
PROFILE_SCOPE(ForestWindMgr_updateWind);
|
|
|
|
// See if we have the blended source available.
|
|
ForestWindAccumulator *blendDest = NULL;
|
|
{
|
|
IdToWindMap::Iterator iter = mPrevSources->find( info.itemKey );
|
|
if ( iter != mPrevSources->end() )
|
|
{
|
|
blendDest = iter->value;
|
|
mPrevSources->erase( iter );
|
|
}
|
|
}
|
|
|
|
// Get some stuff we'll need for finding the emitters.
|
|
F32 treeHeight = info.scale * info.dataBlock->getObjBox().len_z();
|
|
Point3F top = info.pos;
|
|
top.z += treeHeight;
|
|
if ( blendDest )
|
|
top += ( 1.0f / info.scale ) * blendDest->getDirection();
|
|
|
|
// Go thru the emitters to accumulate the total wind force.
|
|
VectorF windForce( 0, 0, 0 );
|
|
|
|
F32 time = Sim::getCurrentTime() / 1000.0f;
|
|
|
|
ForestWindEmitterList::iterator iter = mEmitters.begin();
|
|
for ( ; iter != mEmitters.end(); iter++ )
|
|
{
|
|
ForestWindEmitter *emitter = (*iter);
|
|
|
|
// If disabled or no wind object... skip it.
|
|
if ( !emitter->isEnabled() || !emitter->getWind() )
|
|
continue;
|
|
|
|
ForestWind *wind = emitter->getWind();
|
|
|
|
F32 strength = wind->getStrength();
|
|
|
|
if ( emitter->isRadialEmitter() )
|
|
{
|
|
Point3F closest = MathUtils::mClosestPointOnSegment( info.pos, top, emitter->getPosition() );
|
|
Point3F dir = closest - emitter->getPosition();
|
|
F32 lenSquared = dir.lenSquared();
|
|
if ( lenSquared > emitter->getWindRadiusSquared() )
|
|
continue;
|
|
|
|
dir *= 1.0f / mSqrt( lenSquared );
|
|
|
|
F32 att = lenSquared / emitter->getWindRadiusSquared();
|
|
strength *= 1.0f - att;
|
|
windForce += dir * strength;
|
|
}
|
|
else
|
|
{
|
|
F32 d = mDot( info.pos, Point3F::One ); //PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) );
|
|
//F32 d = PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) );
|
|
F32 scale = 1.0f + ( mSin( d + ( time / 10.0 ) ) * 0.5f );
|
|
windForce += wind->getDirection() * strength * scale;
|
|
}
|
|
}
|
|
|
|
// If we need a accumulator then we also need to presimulate.
|
|
if ( !blendDest )
|
|
{
|
|
blendDest = new ForestWindAccumulator( info );
|
|
blendDest->presimulate( windForce, 4.0f / TickSec );
|
|
}
|
|
else
|
|
blendDest->updateWind( windForce, timeDelta );
|
|
|
|
mSources->insertUnique( info.itemKey, blendDest );
|
|
} |