2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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"
2016-06-04 11:24:26 +00:00
# include "math/mathUtils.h"
2012-09-19 15:15:01 +00:00
# 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 ,
The final step (barring any overlooked missing bits, requested refactors, and of course, rolling in dependencies already submitted as PRs) consists of:
renderPrePassMgr.cpp related:
A) shifting .addFeature( MFT_XYZ); calls from ProcessedShaderMaterial::_determineFeatures to ProcessedPrePassMaterial::_determineFeatures
B) mimicking the "// set the XXX if different" entries from RenderMeshMgr::render in RenderPrePassMgr::render
C) fleshing out ProcessedPrePassMaterial::getNumStages() so that it shares a 1:1 correlation with ProcessedShaderMaterial::getNumStages()
D) causing inline void Swizzle<T, mapLength>::ToBuffer( void *destination, const void *source, const dsize_t size ) to silently fail rather than fatally assert if a source or destination buffer is not yet ready to be filled. (support for #customTarget scripted render targets)
Reflections:
A) removing reflectRenderState.disableAdvancedLightingBins(true); entries. this would otherwise early out from prepass and provide no color data whatsoever.
B) removing the fd.features.addFeature( MFT_ForwardShading ); entry forcing all materials to be forward lit when reflected.
C) 2 things best described bluntly as working hacks:
C1) when reflected, a scattersky is rotated PI along it's z then x axis in order to draw properly.
C2) along similar lines, in terraincellmaterial, we shut off culling if it's a prepass material.
Skies: scattersky is given a pair of rotations for reflection purposes, all sky objects are given a z value for depth testing.
2016-02-16 08:50:49 +00:00
GFXTextureProfile : : PreserveSize | GFXTextureProfile : : RenderTarget | GFXTextureProfile : : Pooled ,
2014-04-06 22:03:52 +00:00
GFXTextureProfile : : NONE ) ;
2012-09-19 15:15:01 +00:00
GFX_ImplementTextureProfile ( RefractTextureProfile ,
GFXTextureProfile : : DiffuseMap ,
GFXTextureProfile : : PreserveSize |
GFXTextureProfile : : RenderTarget |
GFXTextureProfile : : Pooled ,
2014-04-06 22:03:52 +00:00
GFXTextureProfile : : NONE ) ;
2012-09-19 15:15:01 +00:00
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 ( )
2017-04-11 04:45:02 +00:00
: mReflectFormat ( GFXFormatR8G8B8A8 ) ,
mUpdateRefract ( true ) ,
2014-11-20 05:35:56 +00:00
mLastUpdateMs ( 0 )
2012-09-19 15:15:01 +00:00
{
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 "
2016-06-08 21:50:10 +00:00
" @ingroup Rendering " ) ;
2012-09-19 15:15:01 +00:00
Con : : addVariable ( " $pref::Reflect::frameLimitMS " , TypeS32 , & ReflectionManager : : smFrameReflectionMS , " ReflectionManager tries not to spend more than this amount of time updating reflections per frame. \n "
2016-06-08 21:50:10 +00:00
" @ingroup Rendering " ) ;
2012-09-19 15:15:01 +00:00
}
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 ;
2016-06-04 11:24:26 +00:00
2016-06-08 21:50:10 +00:00
// jamesu - normally we just need a frustum which covers the current ports, however for SBS mode
// we need something which covers both viewports.
2016-06-04 11:24:26 +00:00
S32 stereoTarget = GFX - > getCurrentStereoTarget ( ) ;
if ( stereoTarget ! = - 1 )
{
2016-06-08 21:50:10 +00:00
// 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 ) ;
2016-06-04 11:24:26 +00:00
}
else
{
2016-06-08 21:50:10 +00:00
// Normal culling
culler . set ( false ,
query . fov ,
( F32 ) resolution . x / ( F32 ) resolution . y ,
query . nearPlane ,
query . farPlane ,
query . cameraMatrix ) ;
2016-06-04 11:24:26 +00:00
}
2012-09-19 15:15:01 +00:00
// 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 ;
2016-06-08 21:50:10 +00:00
refparams . eyeId = stereoTarget ;
2012-09-19 15:15:01 +00:00
// 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 ) ;
2016-06-08 21:50:10 +00:00
if ( stereoTarget ! = 0 ) // only update MS if we're not rendering the left eye in separate mode
{
( * reflectorIter ) - > lastUpdateMs = startOfUpdateMs ;
}
2012-09-19 15:15:01 +00:00
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
2014-03-15 10:51:36 +00:00
U32 numEnabled = mReflectors . size ( ) ;
U32 totalElapsed = mTimer - > getElapsedMs ( ) ;
2012-09-19 15:15:01 +00:00
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__ ) ) ;
}
2012-11-08 21:00:52 +00:00
GFXTextureObject * ReflectionManager : : getRefractTex ( bool forceUpdate )
2012-09-19 15:15:01 +00:00
{
GFXTarget * target = GFX - > getActiveRenderTarget ( ) ;
GFXFormat targetFormat = target - > getFormat ( ) ;
const Point2I & targetSize = target - > getSize ( ) ;
2016-03-20 11:52:11 +00:00
// D3D11 needs to be the same size as the active target
2017-05-28 21:51:31 +00:00
U32 desWidth = targetSize . x ;
U32 desHeight = targetSize . y ;
2012-09-19 15:15:01 +00:00
if ( mRefractTex . isNull ( ) | |
mRefractTex - > getWidth ( ) ! = desWidth | |
mRefractTex - > getHeight ( ) ! = desHeight | |
mRefractTex - > getFormat ( ) ! = targetFormat )
{
mRefractTex . set ( desWidth , desHeight , targetFormat , & RefractTextureProfile , " mRefractTex " ) ;
mUpdateRefract = true ;
}
2012-11-08 21:00:52 +00:00
if ( forceUpdate | | mUpdateRefract )
2012-09-19 15:15:01 +00:00
{
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 :
2013-10-31 20:54:22 +00:00
case GFXDevice : : deStartOfField :
2012-09-19 15:15:01 +00:00
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 ) ;
}