123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- //-----------------------------------------------------------------------------
- // 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 "scene/reflectionManager.h"
- #include "platform/profiler.h"
- #include "platform/platformTimer.h"
- #include "console/consoleTypes.h"
- #include "core/tAlgorithm.h"
- #include "math/mMathFn.h"
- #include "math/mathUtils.h"
- #include "T3D/gameBase/gameConnection.h"
- #include "ts/tsShapeInstance.h"
- #include "gui/3d/guiTSControl.h"
- #include "scene/sceneManager.h"
- #include "gfx/gfxDebugEvent.h"
- #include "gfx/gfxStringEnumTranslate.h"
- #include "gfx/screenshot.h"
- #include "core/module.h"
- #include "scene/reflectionMatHook.h"
- #include "console/engineAPI.h"
- MODULE_BEGIN( ReflectionManager )
- MODULE_INIT
- {
- ManagedSingleton< ReflectionManager >::createSingleton();
- ReflectionManager::initConsole();
- }
-
- MODULE_SHUTDOWN
- {
- ManagedSingleton< ReflectionManager >::deleteSingleton();
- }
- MODULE_END;
- GFX_ImplementTextureProfile( ReflectRenderTargetProfile,
- GFXTextureProfile::DiffuseMap,
- GFXTextureProfile::PreserveSize | GFXTextureProfile::RenderTarget | GFXTextureProfile::SRGB | GFXTextureProfile::Pooled,
- GFXTextureProfile::NONE );
- GFX_ImplementTextureProfile( RefractTextureProfile,
- GFXTextureProfile::DiffuseMap,
- GFXTextureProfile::PreserveSize |
- GFXTextureProfile::RenderTarget |
- GFXTextureProfile::SRGB |
- GFXTextureProfile::Pooled,
- GFXTextureProfile::NONE );
- static S32 QSORT_CALLBACK compareReflectors( const void *a, const void *b )
- {
- const ReflectorBase *A = *((ReflectorBase**)a);
- const ReflectorBase *B = *((ReflectorBase**)b);
-
- F32 dif = B->score - A->score;
- return (S32)mFloor( dif );
- }
- U32 ReflectionManager::smFrameReflectionMS = 10;
- F32 ReflectionManager::smRefractTexScale = 0.5f;
- ReflectionManager::ReflectionManager()
- : mReflectFormat( GFXFormatR8G8B8A8_SRGB ),
- mUpdateRefract( true ),
- mLastUpdateMs( 0 )
- {
- mTimer = PlatformTimer::create();
- GFXDevice::getDeviceEventSignal().notify( this, &ReflectionManager::_handleDeviceEvent );
- }
- void ReflectionManager::initConsole()
- {
- Con::addVariable( "$pref::Reflect::refractTexScale", TypeF32, &ReflectionManager::smRefractTexScale, "RefractTex has dimensions equal to the active render target scaled in both x and y by this float.\n"
- "@ingroup Rendering");
- Con::addVariable( "$pref::Reflect::frameLimitMS", TypeS32, &ReflectionManager::smFrameReflectionMS, "ReflectionManager tries not to spend more than this amount of time updating reflections per frame.\n"
- "@ingroup Rendering");
- }
- ReflectionManager::~ReflectionManager()
- {
- SAFE_DELETE( mTimer );
- AssertFatal( mReflectors.size() == 0, "ReflectionManager, some reflectors were left nregistered!" );
- GFXDevice::getDeviceEventSignal().remove( this, &ReflectionManager::_handleDeviceEvent );
- }
- void ReflectionManager::registerReflector( ReflectorBase *reflector )
- {
- mReflectors.push_back_unique( reflector );
- }
- void ReflectionManager::unregisterReflector( ReflectorBase *reflector )
- {
- mReflectors.remove( reflector );
- }
- void ReflectionManager::update( F32 timeSlice,
- const Point2I &resolution,
- const CameraQuery &query )
- {
- GFXDEBUGEVENT_SCOPE( UpdateReflections, ColorI::WHITE );
- if ( mReflectors.empty() )
- return;
- PROFILE_SCOPE( ReflectionManager_Update );
- // Calculate our target time from the slice.
- U32 targetMs = timeSlice * smFrameReflectionMS;
- // Setup a culler for testing the
- // visibility of reflectors.
- Frustum culler;
- // jamesu - normally we just need a frustum which covers the current ports, however for SBS mode
- // we need something which covers both viewports.
- S32 stereoTarget = GFX->getCurrentStereoTarget();
- if (stereoTarget != -1)
- {
- // In this case we're rendering in stereo using a specific eye
- MathUtils::makeFovPortFrustum(&culler, false, query.nearPlane, query.farPlane, query.fovPort[stereoTarget], query.headMatrix);
- }
- else if (GFX->getCurrentRenderStyle() == GFXDevice::RS_StereoSideBySide)
- {
- // Calculate an ideal culling size here, we'll just assume double fov based on the first fovport based on
- // the head position.
- FovPort port = query.fovPort[0];
- F32 upSize = query.nearPlane * port.upTan;
- F32 downSize = query.nearPlane * port.downTan;
- F32 top = upSize;
- F32 bottom = -downSize;
- F32 fovInRadians = mAtan2((top - bottom) / 2.0f, query.nearPlane) * 3.0f;
- culler.set(false,
- fovInRadians,
- (F32)(query.stereoViewports[0].extent.x + query.stereoViewports[1].extent.x) / (F32)query.stereoViewports[0].extent.y,
- query.nearPlane,
- query.farPlane,
- query.headMatrix);
- }
- else
- {
- // Normal culling
- culler.set(false,
- query.fov,
- (F32)resolution.x / (F32)resolution.y,
- query.nearPlane,
- query.farPlane,
- query.cameraMatrix);
- }
- // Manipulate the frustum for tiled screenshots
- const bool screenShotMode = gScreenShot && gScreenShot->isPending();
- if ( screenShotMode )
- gScreenShot->tileFrustum( culler );
- // We use the frame time and not real time
- // here as this may be called multiple times
- // within a frame.
- U32 startOfUpdateMs = Platform::getVirtualMilliseconds();
- // Save this for interested parties.
- mLastUpdateMs = startOfUpdateMs;
- ReflectParams refparams;
- refparams.query = &query;
- refparams.viewportExtent = resolution;
- refparams.culler = culler;
- refparams.startOfUpdateMs = startOfUpdateMs;
- refparams.eyeId = stereoTarget;
- // Update the reflection score.
- ReflectorList::iterator reflectorIter = mReflectors.begin();
- for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
- (*reflectorIter)->calcScore( refparams );
- // Sort them by the score.
- dQsort( mReflectors.address(), mReflectors.size(), sizeof(ReflectorBase*), compareReflectors );
-
- // Update as many reflections as we can
- // within the target time limit.
- mTimer->getElapsedMs();
- mTimer->reset();
- U32 numUpdated = 0;
- reflectorIter = mReflectors.begin();
- for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
- {
- // We're sorted by score... so once we reach
- // a zero score we have nothing more to update.
- if ( (*reflectorIter)->score <= 0.0f && !screenShotMode )
- break;
- (*reflectorIter)->updateReflection( refparams );
- if (stereoTarget != 0) // only update MS if we're not rendering the left eye in separate mode
- {
- (*reflectorIter)->lastUpdateMs = startOfUpdateMs;
- }
- numUpdated++;
- // If we run out of update time then stop.
- if ( mTimer->getElapsedMs() > targetMs && !screenShotMode && (*reflectorIter)->score < 1000.0f )
- break;
- }
- // Set metric/debug related script variables...
- U32 numVisible = 0;
- U32 numOccluded = 0;
- reflectorIter = mReflectors.begin();
- for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
- {
- ReflectorBase *pReflector = (*reflectorIter);
- if ( pReflector->isOccluded() )
- numOccluded++;
- else
- numVisible++;
- }
- #ifdef TORQUE_GATHER_METRICS
- U32 numEnabled = mReflectors.size();
- U32 totalElapsed = mTimer->getElapsedMs();
- const GFXTextureProfileStats &stats = ReflectRenderTargetProfile.getStats();
-
- F32 mb = ( stats.activeBytes / 1024.0f ) / 1024.0f;
- char temp[256];
- dSprintf( temp, 256, "%s %d %0.2f\n",
- ReflectRenderTargetProfile.getName().c_str(),
- stats.activeCount,
- mb );
- Con::setVariable( "$Reflect::textureStats", temp );
- Con::setIntVariable( "$Reflect::renderTargetsAllocated", stats.allocatedTextures );
- Con::setIntVariable( "$Reflect::poolSize", stats.activeCount );
- Con::setIntVariable( "$Reflect::numObjects", numEnabled );
- Con::setIntVariable( "$Reflect::numVisible", numVisible );
- Con::setIntVariable( "$Reflect::numOccluded", numOccluded );
- Con::setIntVariable( "$Reflect::numUpdated", numUpdated );
- Con::setIntVariable( "$Reflect::elapsed", totalElapsed );
- #endif
- }
- GFXTexHandle ReflectionManager::allocRenderTarget( const Point2I &size )
- {
- return GFXTexHandle( size.x, size.y, mReflectFormat,
- &ReflectRenderTargetProfile,
- avar("%s() - mReflectTex (line %d)", __FUNCTION__, __LINE__) );
- }
- GFXTextureObject* ReflectionManager::getRefractTex( bool forceUpdate )
- {
- GFXTarget *target = GFX->getActiveRenderTarget();
- GFXFormat targetFormat = target->getFormat();
- const Point2I &targetSize = target->getSize();
- // D3D11 needs to be the same size as the active target
- U32 desWidth = targetSize.x;
- U32 desHeight = targetSize.y;
- if ( mRefractTex.isNull() ||
- mRefractTex->getWidth() != desWidth ||
- mRefractTex->getHeight() != desHeight ||
- mRefractTex->getFormat() != targetFormat )
- {
- mRefractTex.set( desWidth, desHeight, targetFormat, &RefractTextureProfile, "mRefractTex" );
- mUpdateRefract = true;
- }
- if ( forceUpdate || mUpdateRefract )
- {
- target->resolveTo( mRefractTex );
- mUpdateRefract = false;
- }
- return mRefractTex;
- }
- BaseMatInstance* ReflectionManager::getReflectionMaterial( BaseMatInstance *inMat ) const
- {
- // See if we have an existing material hook.
- ReflectionMaterialHook *hook = static_cast<ReflectionMaterialHook*>( inMat->getHook( ReflectionMaterialHook::Type ) );
- if ( !hook )
- {
- // Create a hook and initialize it using the incoming material.
- hook = new ReflectionMaterialHook;
- hook->init( inMat );
- inMat->addHook( hook );
- }
- return hook->getReflectMat();
- }
- bool ReflectionManager::_handleDeviceEvent( GFXDevice::GFXDeviceEventType evt )
- {
- switch( evt )
- {
- case GFXDevice::deStartOfFrame:
- case GFXDevice::deStartOfField:
- mUpdateRefract = true;
- break;
- case GFXDevice::deDestroy:
-
- mRefractTex = NULL;
- break;
-
- default:
- break;
- }
- return true;
- }
- DefineEngineFunction( setReflectFormat, void, ( GFXFormat format ),,
- "Set the reflection texture format.\n"
- "@ingroup GFX\n" )
- {
- REFLECTMGR->setReflectFormat( format );
- }
|