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/reflector.h"
# include "console/consoleTypes.h"
# include "gfx/gfxCubemap.h"
# include "gfx/gfxDebugEvent.h"
# include "gfx/gfxTransformSaver.h"
2025-12-22 10:29:01 +00:00
# include "gfx/util/gfxFrustumSaver.h"
2012-09-19 15:15:01 +00:00
# include "scene/sceneManager.h"
# include "scene/sceneRenderState.h"
# include "core/stream/bitStream.h"
# include "scene/reflectionManager.h"
# include "gui/3d/guiTSControl.h"
# include "ts/tsShapeInstance.h"
# include "gfx/gfxOcclusionQuery.h"
# include "lighting/lightManager.h"
# include "lighting/shadowMap/lightShadowMap.h"
# include "math/mathUtils.h"
# include "math/util/frustum.h"
# include "gfx/screenshot.h"
2016-05-21 18:52:41 +00:00
# include "postFx/postEffectManager.h"
2012-09-19 15:15:01 +00:00
extern ColorI gCanvasClearColor ;
//-------------------------------------------------------------------------
// ReflectorDesc
//-------------------------------------------------------------------------
IMPLEMENT_CO_DATABLOCK_V1 ( ReflectorDesc ) ;
ConsoleDocClass ( ReflectorDesc ,
" @brief A datablock which defines performance and quality properties for "
" dynamic reflections. \n \n "
" ReflectorDesc is not itself a reflection and does not render reflections. "
" It is a dummy class for holding and exposing to the user a set of "
" reflection related properties. Objects which support dynamic reflections "
" may then reference a ReflectorDesc. \n \n "
" @tsexample \n "
" datablock ReflectorDesc( ExampleReflectorDesc ) \n "
" { \n "
" texSize = 256; \n "
" nearDist = 0.1; \n "
" farDist = 500; \n "
" objectTypeMask = 0xFFFFFFFF; \n "
" detailAdjust = 1.0; \n "
" priority = 1.0; \n "
" maxRateMs = 0; \n "
" useOcclusionQuery = true; \n "
" }; \n "
" @endtsexample \n "
" @see ShapeBaseData::cubeReflectorDesc \n "
" @ingroup enviroMisc "
) ;
ReflectorDesc : : ReflectorDesc ( )
{
texSize = 256 ;
nearDist = 0.1f ;
farDist = 1000.0f ;
objectTypeMask = 0xFFFFFFFF ;
detailAdjust = 1.0f ;
priority = 1.0f ;
maxRateMs = 15 ;
useOcclusionQuery = true ;
}
ReflectorDesc : : ~ ReflectorDesc ( )
{
}
void ReflectorDesc : : initPersistFields ( )
{
2023-01-27 07:13:15 +00:00
docsURL ;
2012-09-19 15:15:01 +00:00
addGroup ( " ReflectorDesc " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " texSize " , TypeRangedS32 , Offset ( texSize , ReflectorDesc ) , & CommonValidators : : PositiveInt ,
2012-09-19 15:15:01 +00:00
" Size in pixels of the (square) reflection texture. For a cubemap "
" this value is interpreted as size of each face. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " nearDist " , TypeRangedF32 , Offset ( nearDist , ReflectorDesc ) , & CommonValidators : : PositiveFloat ,
2012-09-19 15:15:01 +00:00
" Near plane distance to use when rendering this reflection. Adjust "
" this to limit self-occlusion artifacts. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " farDist " , TypeRangedF32 , Offset ( farDist , ReflectorDesc ) , & CommonValidators : : PositiveFloat ,
2012-09-19 15:15:01 +00:00
" Far plane distance to use when rendering reflections. " ) ;
2025-03-09 16:53:23 +00:00
addField ( " objectTypeMask " , TypeGameTypeMasksType , Offset ( objectTypeMask , ReflectorDesc ) ,
2012-09-19 15:15:01 +00:00
" Object types which render into this reflection. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " detailAdjust " , TypeRangedF32 , Offset ( detailAdjust , ReflectorDesc ) , & CommonValidators : : PositiveFloat ,
2012-09-19 15:15:01 +00:00
" Scale applied to lod calculation of objects rendering into "
" this reflection ( modulates $pref::TS::detailAdjust ). " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " priority " , TypeRangedF32 , Offset ( priority , ReflectorDesc ) , & CommonValidators : : PositiveFloat ,
2012-09-19 15:15:01 +00:00
" Priority for updating this reflection, relative to others. " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " maxRateMs " , TypeRangedS32 , Offset ( maxRateMs , ReflectorDesc ) , & CommonValidators : : PositiveInt ,
2012-09-19 15:15:01 +00:00
" If less than maxRateMs has elapsed since this relfection was last "
" updated, then do not update it again. This 'skip' can be disabled by "
" setting maxRateMs to zero. " ) ;
addField ( " useOcclusionQuery " , TypeBool , Offset ( useOcclusionQuery , ReflectorDesc ) ,
" If available on the device use HOQs to determine if the reflective object "
" is visible before updating its reflection. " ) ;
endGroup ( " ReflectorDesc " ) ;
Parent : : initPersistFields ( ) ;
}
void ReflectorDesc : : packData ( BitStream * stream )
{
Parent : : packData ( stream ) ;
stream - > write ( texSize ) ;
stream - > write ( nearDist ) ;
stream - > write ( farDist ) ;
stream - > write ( objectTypeMask ) ;
stream - > write ( detailAdjust ) ;
stream - > write ( priority ) ;
stream - > write ( maxRateMs ) ;
stream - > writeFlag ( useOcclusionQuery ) ;
}
void ReflectorDesc : : unpackData ( BitStream * stream )
{
Parent : : unpackData ( stream ) ;
stream - > read ( & texSize ) ;
stream - > read ( & nearDist ) ;
stream - > read ( & farDist ) ;
stream - > read ( & objectTypeMask ) ;
stream - > read ( & detailAdjust ) ;
stream - > read ( & priority ) ;
stream - > read ( & maxRateMs ) ;
useOcclusionQuery = stream - > readFlag ( ) ;
}
bool ReflectorDesc : : preload ( bool server , String & errorStr )
{
if ( ! Parent : : preload ( server , errorStr ) )
return false ;
return true ;
}
//-------------------------------------------------------------------------
// ReflectorBase
//-------------------------------------------------------------------------
ReflectorBase : : ReflectorBase ( )
{
mEnabled = false ;
mOccluded = false ;
mIsRendering = false ;
mDesc = NULL ;
mObject = NULL ;
mOcclusionQuery = GFX - > createOcclusionQuery ( ) ;
mQueryPending = false ;
2014-11-20 05:35:56 +00:00
score = 0.0f ;
lastUpdateMs = 0 ;
2012-09-19 15:15:01 +00:00
}
ReflectorBase : : ~ ReflectorBase ( )
{
delete mOcclusionQuery ;
}
void ReflectorBase : : unregisterReflector ( )
{
if ( mEnabled )
{
REFLECTMGR - > unregisterReflector ( this ) ;
mEnabled = false ;
}
}
F32 ReflectorBase : : calcScore ( const ReflectParams & params )
{
PROFILE_SCOPE ( ReflectorBase_calcScore ) ;
// First check the occlusion query to see if we're hidden.
if ( mDesc - > useOcclusionQuery & &
mOcclusionQuery )
{
GFXOcclusionQuery : : OcclusionQueryStatus status = mOcclusionQuery - > getStatus ( false ) ;
if ( status = = GFXOcclusionQuery : : Waiting )
{
mQueryPending = true ;
// Don't change mOccluded since we don't know yet, use the value
// from last frame.
}
else
{
mQueryPending = false ;
if ( status = = GFXOcclusionQuery : : Occluded )
mOccluded = true ;
else if ( status = = GFXOcclusionQuery : : NotOccluded )
mOccluded = false ;
}
}
// If we're disabled for any reason then there
// is nothing more left to do.
if ( ! mEnabled | |
mOccluded | |
params . culler . isCulled ( mObject - > getWorldBox ( ) ) )
{
score = 0 ;
return score ;
}
// This mess is calculating a score based on LOD.
/*
F32 sizeWS = getMax ( object - > getWorldBox ( ) . len_z ( ) , 0.001f ) ;
Point3F cameraOffset = params . culler . getPosition ( ) - object - > getPosition ( ) ;
F32 dist = getMax ( cameraOffset . len ( ) , 0.01f ) ;
F32 worldToScreenScaleY = ( params . culler . getNearDist ( ) * params . viewportExtent . y ) /
( params . culler . getNearTop ( ) - params . culler . getNearBottom ( ) ) ;
F32 sizeSS = sizeWS / dist * worldToScreenScaleY ;
*/
if ( mDesc - > priority = = - 1.0f )
{
score = 1000.0f ;
return score ;
}
F32 lodFactor = 1.0f ; //sizeSS;
F32 maxRate = getMax ( ( F32 ) mDesc - > maxRateMs , 1.0f ) ;
U32 delta = params . startOfUpdateMs - lastUpdateMs ;
F32 timeFactor = getMax ( ( F32 ) delta / maxRate - 1.0f , 0.0f ) ;
score = mDesc - > priority * timeFactor * lodFactor ;
return score ;
}
//-------------------------------------------------------------------------
// CubeReflector
//-------------------------------------------------------------------------
CubeReflector : : CubeReflector ( )
: mLastTexSize ( 0 )
{
}
void CubeReflector : : registerReflector ( SceneObject * object ,
ReflectorDesc * desc )
{
if ( mEnabled )
return ;
mEnabled = true ;
mObject = object ;
mDesc = desc ;
REFLECTMGR - > registerReflector ( this ) ;
}
void CubeReflector : : unregisterReflector ( )
{
if ( ! mEnabled )
return ;
REFLECTMGR - > unregisterReflector ( this ) ;
mEnabled = false ;
}
2020-10-19 05:53:09 +00:00
void CubeReflector : : updateReflection ( const ReflectParams & params , Point3F explicitPostion )
2012-09-19 15:15:01 +00:00
{
GFXDEBUGEVENT_SCOPE ( CubeReflector_UpdateReflection , ColorI : : WHITE ) ;
mIsRendering = true ;
// Setup textures and targets...
S32 texDim = mDesc - > texSize ;
texDim = getMax ( texDim , 32 ) ;
// Protect against the reflection texture being bigger
// than the current game back buffer.
texDim = getMin ( texDim , params . viewportExtent . x ) ;
texDim = getMin ( texDim , params . viewportExtent . y ) ;
bool texResize = ( texDim ! = mLastTexSize ) ;
const GFXFormat reflectFormat = REFLECTMGR - > getReflectFormat ( ) ;
if ( texResize | |
2018-03-14 02:26:27 +00:00
mCubemap . isNull ( ) | |
mCubemap - > getFormat ( ) ! = reflectFormat )
2012-09-19 15:15:01 +00:00
{
2025-12-22 10:29:01 +00:00
mCubemap . set ( texDim , texDim , reflectFormat , & GFXCubemapRenderTargetProfile , " CubeReflector::updateReflection " , 0 ) ;
2012-09-19 15:15:01 +00:00
}
2018-03-14 02:26:27 +00:00
if ( mRenderTarget . isNull ( ) )
mRenderTarget = GFX - > allocRenderToTextureTarget ( ) ;
2012-09-19 15:15:01 +00:00
F32 oldVisibleDist = gClientSceneGraph - > getVisibleDistance ( ) ;
gClientSceneGraph - > setVisibleDistance ( mDesc - > farDist ) ;
2024-12-04 21:57:32 +00:00
F32 detailAdjustBackup = TSShapeInstance : : smDetailAdjust ;
TSShapeInstance : : smDetailAdjust * = mDesc - > detailAdjust ;
2025-01-07 15:44:19 +00:00
// store current matrices
2025-12-22 10:29:01 +00:00
GFXFrustumSaver fsaver ;
2025-01-07 15:44:19 +00:00
GFXTransformSaver saver ;
2025-12-22 10:29:01 +00:00
{
// set projection to 90 degrees vertical and horizontal
F32 left , right , top , bottom ;
MathUtils : : makeFrustum ( & left , & right , & top , & bottom , M_HALFPI_F , 1.0f , mDesc - > nearDist ) ;
GFX - > setFrustum ( left , right , bottom , top , mDesc - > nearDist , mDesc - > farDist ) ;
}
2024-12-04 21:57:32 +00:00
2025-01-07 15:44:19 +00:00
GFX - > pushActiveRenderTarget ( ) ;
2025-12-22 10:29:01 +00:00
for ( S32 i = 0 ; i < 6 ; i + + ) {
2024-12-04 21:57:32 +00:00
updateFace ( params , i , explicitPostion ) ;
}
2025-01-07 15:44:19 +00:00
GFX - > popActiveRenderTarget ( ) ;
2012-09-19 15:15:01 +00:00
2024-12-04 21:57:32 +00:00
TSShapeInstance : : smDetailAdjust = detailAdjustBackup ;
2024-04-18 06:56:52 +00:00
mCubemap - > generateMipMaps ( ) ;
2012-09-19 15:15:01 +00:00
gClientSceneGraph - > setVisibleDistance ( oldVisibleDist ) ;
mIsRendering = false ;
mLastTexSize = texDim ;
}
2020-10-19 05:53:09 +00:00
void CubeReflector : : updateFace ( const ReflectParams & params , U32 faceidx , Point3F explicitPostion )
2012-09-19 15:15:01 +00:00
{
GFXDEBUGEVENT_SCOPE ( CubeReflector_UpdateFace , ColorI : : WHITE ) ;
// Standard view that will be overridden below.
2024-12-04 21:57:32 +00:00
VectorF target = VectorF : : Zero ;
VectorF eye = VectorF : : Zero ;
if ( explicitPostion = = Point3F : : Max )
{
eye = mObject - > getPosition ( ) ;
}
else
{
eye = explicitPostion ;
}
2025-01-07 15:44:19 +00:00
// Standard view that will be overridden below.
VectorF vLookatPt ( 0.0f , 0.0f , 0.0f ) , vUpVec ( 0.0f , 0.0f , 0.0f ) , vRight ( 0.0f , 0.0f , 0.0f ) ;
2012-09-19 15:15:01 +00:00
2025-01-07 15:44:19 +00:00
switch ( faceidx )
2012-09-19 15:15:01 +00:00
{
2025-01-07 15:44:19 +00:00
case 0 : // D3DCUBEMAP_FACE_POSITIVE_X:
vLookatPt = VectorF ( 1.0f , 0.0f , 0.0f ) ;
vUpVec = VectorF ( 0.0f , 1.0f , 0.0f ) ;
2012-09-19 15:15:01 +00:00
break ;
2025-01-07 15:44:19 +00:00
case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X:
vLookatPt = VectorF ( - 1.0f , 0.0f , 0.0f ) ;
vUpVec = VectorF ( 0.0f , 1.0f , 0.0f ) ;
2012-09-19 15:15:01 +00:00
break ;
2025-01-07 15:44:19 +00:00
case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y:
vLookatPt = VectorF ( 0.0f , 1.0f , 0.0f ) ;
vUpVec = VectorF ( 0.0f , 0.0f , - 1.0f ) ;
2012-09-19 15:15:01 +00:00
break ;
2025-01-07 15:44:19 +00:00
case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y:
vLookatPt = VectorF ( 0.0f , - 1.0f , 0.0f ) ;
vUpVec = VectorF ( 0.0f , 0.0f , 1.0f ) ;
2012-09-19 15:15:01 +00:00
break ;
2025-01-07 15:44:19 +00:00
case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z:
vLookatPt = VectorF ( 0.0f , 0.0f , 1.0f ) ;
vUpVec = VectorF ( 0.0f , 1.0f , 0.0f ) ;
2012-09-19 15:15:01 +00:00
break ;
case 5 : // D3DCUBEMAP_FACE_NEGATIVE_Z:
2025-01-07 15:44:19 +00:00
vLookatPt = VectorF ( 0.0f , 0.0f , - 1.0f ) ;
vUpVec = VectorF ( 0.0f , 1.0f , 0.0f ) ;
2012-09-19 15:15:01 +00:00
break ;
}
// create camera matrix
2025-01-07 15:44:19 +00:00
MatrixF lightMatrix ( true ) ;
lightMatrix . LookAt ( eye , vLookatPt , vUpVec ) ;
lightMatrix . inverse ( ) ;
2012-09-19 15:15:01 +00:00
2025-01-07 15:44:19 +00:00
GFX - > setWorldMatrix ( lightMatrix ) ;
2017-07-09 20:15:30 +00:00
GFX - > clearTextureStateImmediate ( 0 ) ;
2025-01-07 15:44:19 +00:00
S32 texDim = mDesc - > texSize ;
texDim = getMax ( texDim , 32 ) ;
2025-12-22 10:29:01 +00:00
mRenderTarget - > attachTexture ( GFXTextureTarget : : Color0 , mCubemap , 0 , 0 , faceidx ) ; // Setup textures and targets...
2025-01-07 15:44:19 +00:00
mRenderTarget - > attachTexture ( GFXTextureTarget : : DepthStencil , LightShadowMap : : _getDepthTarget ( texDim , texDim ) ) ;
2024-12-04 21:57:32 +00:00
2025-12-22 10:29:01 +00:00
GFX - > setActiveRenderTarget ( mRenderTarget , true ) ;
2025-01-07 15:44:19 +00:00
GFX - > clear ( GFXClearStencil | GFXClearTarget | GFXClearZBuffer , gCanvasClearColor , 1.0f , 0 ) ;
2012-09-19 15:15:01 +00:00
SceneRenderState reflectRenderState
(
gClientSceneGraph ,
SPT_Reflect ,
2025-12-22 10:29:01 +00:00
SceneCameraState : : fromGFXWithViewport ( GFX - > getViewport ( ) )
2012-09-19 15:15:01 +00:00
) ;
reflectRenderState . getMaterialDelegate ( ) . bind ( REFLECTMGR , & ReflectionManager : : getReflectionMaterial ) ;
2025-01-07 15:44:19 +00:00
reflectRenderState . setDiffuseCameraTransform ( lightMatrix ) ; //params.query->headMatrix );
// We don't use a special clipping projection, but still need to initialize
// this for objects like SkyBox which will use it during a reflect pass.
gClientSceneGraph - > setNonClipProjection ( GFX - > getProjectionMatrix ( ) ) ;
2012-09-19 15:15:01 +00:00
// render scene
2013-11-07 20:07:16 +00:00
LIGHTMGR - > registerGlobalLights ( & reflectRenderState . getCullingFrustum ( ) , false ) ;
2012-09-19 15:15:01 +00:00
gClientSceneGraph - > renderSceneNoLights ( & reflectRenderState , mDesc - > objectTypeMask ) ;
LIGHTMGR - > unregisterAllLights ( ) ;
// Clean up.
2018-03-14 02:26:27 +00:00
mRenderTarget - > resolve ( ) ;
2012-09-19 15:15:01 +00:00
}
F32 CubeReflector : : calcFaceScore ( const ReflectParams & params , U32 faceidx )
{
if ( Parent : : calcScore ( params ) < = 0.0f )
return score ;
VectorF vLookatPt ( 0.0f , 0.0f , 0.0f ) ;
switch ( faceidx )
{
case 0 : // D3DCUBEMAP_FACE_POSITIVE_X:
vLookatPt = VectorF ( 1.0f , 0.0f , 0.0f ) ;
break ;
case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X:
vLookatPt = VectorF ( - 1.0f , 0.0f , 0.0f ) ;
break ;
case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y:
vLookatPt = VectorF ( 0.0f , 1.0f , 0.0f ) ;
break ;
case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y:
vLookatPt = VectorF ( 0.0f , - 1.0f , 0.0f ) ;
break ;
case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z:
vLookatPt = VectorF ( 0.0f , 0.0f , 1.0f ) ;
break ;
case 5 : // D3DCUBEMAP_FACE_NEGATIVE_Z:
vLookatPt = VectorF ( 0.0f , 0.0f , - 1.0f ) ;
break ;
}
VectorF cameraDir ;
params . query - > cameraMatrix . getColumn ( 1 , & cameraDir ) ;
F32 dot = mDot ( cameraDir , - vLookatPt ) ;
dot = getMax ( ( dot + 1.0f ) / 2.0f , 0.1f ) ;
score * = dot ;
return score ;
}
F32 CubeReflector : : CubeFaceReflector : : calcScore ( const ReflectParams & params )
{
score = cube - > calcFaceScore ( params , faceIdx ) ;
mOccluded = cube - > isOccluded ( ) ;
return score ;
}
//-------------------------------------------------------------------------
// PlaneReflector
//-------------------------------------------------------------------------
void PlaneReflector : : registerReflector ( SceneObject * object ,
ReflectorDesc * desc )
{
mEnabled = true ;
mObject = object ;
mDesc = desc ;
mLastDir = Point3F : : One ;
mLastPos = Point3F : : Max ;
REFLECTMGR - > registerReflector ( this ) ;
}
F32 PlaneReflector : : calcScore ( const ReflectParams & params )
{
if ( Parent : : calcScore ( params ) < = 0.0f | | score > = 1000.0f )
return score ;
// The planar reflection is view dependent to score it
// higher if the view direction and/or position has changed.
// Get the current camera info.
VectorF camDir = params . query - > cameraMatrix . getForwardVector ( ) ;
Point3F camPos = params . query - > cameraMatrix . getPosition ( ) ;
// Scale up the score based on the view direction change.
F32 dot = mDot ( camDir , mLastDir ) ;
dot = ( 1.0f - dot ) * 1000.0f ;
score + = dot * mDesc - > priority ;
// Also account for the camera movement.
score + = ( camPos - mLastPos ) . lenSquared ( ) * mDesc - > priority ;
return score ;
}
2024-04-18 18:57:29 +00:00
void PlaneReflector : : updateReflection ( const ReflectParams & params , Point3F explicitPostion )
2012-09-19 15:15:01 +00:00
{
PROFILE_SCOPE ( PlaneReflector_updateReflection ) ;
GFXDEBUGEVENT_SCOPE ( PlaneReflector_updateReflection , ColorI : : WHITE ) ;
mIsRendering = true ;
S32 texDim = mDesc - > texSize ;
texDim = getMax ( texDim , 32 ) ;
// Protect against the reflection texture being bigger
// than the current game back buffer.
texDim = getMin ( texDim , params . viewportExtent . x ) ;
texDim = getMin ( texDim , params . viewportExtent . y ) ;
2016-06-08 21:50:10 +00:00
S32 currentTarget = params . eyeId > = 0 ? params . eyeId : 0 ;
const Point2I texSize = Point2I ( texDim , texDim ) ;
2012-09-19 15:15:01 +00:00
2016-06-08 21:50:10 +00:00
bool texResize = ( texSize ! = mLastTexSize ) ;
mLastTexSize = texSize ;
2012-09-19 15:15:01 +00:00
if ( texResize | |
2016-06-08 21:50:10 +00:00
innerReflectTex [ currentTarget ] . isNull ( ) | |
innerReflectTex [ currentTarget ] - > getSize ( ) ! = texSize | |
2012-09-19 15:15:01 +00:00
reflectTex - > getFormat ( ) ! = REFLECTMGR - > getReflectFormat ( ) )
2014-11-08 01:02:30 +00:00
{
2016-06-08 21:50:10 +00:00
innerReflectTex [ currentTarget ] = REFLECTMGR - > allocRenderTarget ( texSize ) ;
2014-11-08 01:02:30 +00:00
}
2012-09-19 15:15:01 +00:00
2016-06-08 21:50:10 +00:00
if ( texResize | | depthBuff . isNull ( ) )
{
depthBuff = LightShadowMap : : _getDepthTarget ( texSize . x , texSize . y ) ;
}
reflectTex = innerReflectTex [ currentTarget ] ;
2012-09-19 15:15:01 +00:00
// store current matrices
GFXTransformSaver saver ;
Frustum frustum ;
2016-06-04 11:24:26 +00:00
S32 stereoTarget = GFX - > getCurrentStereoTarget ( ) ;
if ( stereoTarget ! = - 1 )
{
2016-06-08 21:50:10 +00:00
MathUtils : : makeFovPortFrustum ( & frustum , false , params . query - > nearPlane , params . query - > farPlane , params . query - > fovPort [ stereoTarget ] ) ;
2016-06-04 11:24:26 +00:00
}
else
{
2016-06-08 21:50:10 +00:00
Point2I viewport ( params . viewportExtent ) ;
if ( GFX - > getCurrentRenderStyle ( ) = = GFXDevice : : RS_StereoSideBySide )
{
viewport . x * = 0.5f ;
}
F32 aspectRatio = F32 ( viewport . x ) / F32 ( viewport . y ) ;
frustum . set ( false , params . query - > fov , aspectRatio , params . query - > nearPlane , params . query - > farPlane ) ;
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 ( frustum ) ;
GFX - > setFrustum ( frustum ) ;
// Store the last view info for scoring.
mLastDir = params . query - > cameraMatrix . getForwardVector ( ) ;
mLastPos = params . query - > cameraMatrix . getPosition ( ) ;
2013-10-25 05:57:38 +00:00
setGFXMatrices ( params . query - > cameraMatrix ) ;
2012-09-19 15:15:01 +00:00
// Adjust the detail amount
F32 detailAdjustBackup = TSShapeInstance : : smDetailAdjust ;
TSShapeInstance : : smDetailAdjust * = mDesc - > detailAdjust ;
if ( reflectTarget . isNull ( ) )
reflectTarget = GFX - > allocRenderToTextureTarget ( ) ;
2016-06-08 21:50:10 +00:00
reflectTarget - > attachTexture ( GFXTextureTarget : : Color0 , innerReflectTex [ currentTarget ] ) ;
2012-09-19 15:15:01 +00:00
reflectTarget - > attachTexture ( GFXTextureTarget : : DepthStencil , depthBuff ) ;
GFX - > pushActiveRenderTarget ( ) ;
2016-05-17 23:18:02 +00:00
GFX - > setActiveRenderTarget ( reflectTarget ) ;
2012-09-19 15:15:01 +00:00
U32 objTypeFlag = - 1 ;
2013-10-25 05:57:38 +00:00
SceneCameraState reflectCameraState = SceneCameraState : : fromGFX ( ) ;
LIGHTMGR - > registerGlobalLights ( & reflectCameraState . getFrustum ( ) , false ) ;
2012-09-19 15:15:01 +00:00
// Since we can sometime be rendering a reflection for 1 or 2 frames before
// it gets updated do to the lag associated with getting the results from
// a HOQ we can sometimes see into parts of the reflection texture that
// have nothing but clear color ( eg. under the water ).
// To make this look less crappy use the ambient color of the sun.
//
// In the future we may want to fix this instead by having the scatterSky
// render a skirt or something in its lower half.
//
2017-06-23 16:36:20 +00:00
LinearColorF clearColor = gClientSceneGraph - > getAmbientLightColor ( ) ;
2023-04-15 02:13:28 +00:00
GFX - > clear ( GFXClearZBuffer | GFXClearStencil | GFXClearTarget , clearColor , 0.0f , 0 ) ;
2012-09-19 15:15:01 +00:00
2013-10-25 05:57:38 +00:00
if ( GFX - > getCurrentRenderStyle ( ) = = GFXDevice : : RS_StereoSideBySide )
{
// Store previous values
RectI originalVP = GFX - > getViewport ( ) ;
2016-05-21 18:52:41 +00:00
MatrixF origNonClipProjection = gClientSceneGraph - > getNonClipProjection ( ) ;
PFXFrameState origPFXState = PFXMGR - > getFrameState ( ) ;
2013-10-25 05:57:38 +00:00
2016-06-08 21:50:10 +00:00
MatrixF inverseEyeTransforms [ 2 ] ;
Frustum gfxFrustum ;
// Calculate viewport based on texture size
RectI stereoViewports [ 2 ] ;
stereoViewports [ 0 ] = params . query - > stereoViewports [ 0 ] ;
stereoViewports [ 1 ] = params . query - > stereoViewports [ 1 ] ;
stereoViewports [ 0 ] . extent . x = stereoViewports [ 1 ] . extent . x = texSize . x / 2 ;
stereoViewports [ 0 ] . extent . y = stereoViewports [ 1 ] . extent . y = texSize . y ;
stereoViewports [ 0 ] . point . x = 0 ;
stereoViewports [ 1 ] . point . x = stereoViewports [ 0 ] . extent . x ;
2013-10-25 05:57:38 +00:00
2015-05-06 22:07:48 +00:00
// Calculate world transforms for eyes
inverseEyeTransforms [ 0 ] = params . query - > eyeTransforms [ 0 ] ;
inverseEyeTransforms [ 1 ] = params . query - > eyeTransforms [ 1 ] ;
inverseEyeTransforms [ 0 ] . inverse ( ) ;
inverseEyeTransforms [ 1 ] . inverse ( ) ;
2016-06-08 21:50:10 +00:00
//
2015-05-06 22:07:48 +00:00
// Render left half of display
2016-06-08 21:50:10 +00:00
//
2013-10-25 05:57:38 +00:00
2016-06-08 21:50:10 +00:00
GFX - > setViewport ( stereoViewports [ 0 ] ) ;
GFX - > setCurrentStereoTarget ( 0 ) ;
MathUtils : : makeFovPortFrustum ( & gfxFrustum , params . query - > ortho , params . query - > nearPlane , params . query - > farPlane , params . query - > fovPort [ 0 ] ) ;
gfxFrustum . update ( ) ;
2013-10-25 05:57:38 +00:00
GFX - > setFrustum ( gfxFrustum ) ;
2015-05-06 22:07:48 +00:00
setGFXMatrices ( params . query - > eyeTransforms [ 0 ] ) ;
2013-10-25 05:57:38 +00:00
2016-06-08 21:50:10 +00:00
SceneRenderState renderStateLeft
(
gClientSceneGraph ,
SPT_Reflect ,
SceneCameraState : : fromGFX ( )
) ;
2013-10-25 05:57:38 +00:00
renderStateLeft . setSceneRenderStyle ( SRS_SideBySide ) ;
renderStateLeft . getMaterialDelegate ( ) . bind ( REFLECTMGR , & ReflectionManager : : getReflectionMaterial ) ;
2016-06-08 21:50:10 +00:00
renderStateLeft . setDiffuseCameraTransform ( params . query - > headMatrix ) ;
//renderStateLeft.disableAdvancedLightingBins(true);
2013-10-25 05:57:38 +00:00
gClientSceneGraph - > renderSceneNoLights ( & renderStateLeft , objTypeFlag ) ;
2016-06-08 21:50:10 +00:00
//
2013-10-25 05:57:38 +00:00
// Render right half of display
2016-06-08 21:50:10 +00:00
//
2015-05-06 22:07:48 +00:00
2016-06-08 21:50:10 +00:00
GFX - > setViewport ( stereoViewports [ 1 ] ) ;
GFX - > setCurrentStereoTarget ( 1 ) ;
MathUtils : : makeFovPortFrustum ( & gfxFrustum , params . query - > ortho , params . query - > nearPlane , params . query - > farPlane , params . query - > fovPort [ 1 ] ) ;
gfxFrustum . update ( ) ;
2013-10-25 05:57:38 +00:00
GFX - > setFrustum ( gfxFrustum ) ;
2015-05-06 22:07:48 +00:00
setGFXMatrices ( params . query - > eyeTransforms [ 1 ] ) ;
2013-10-25 05:57:38 +00:00
2016-06-08 21:50:10 +00:00
SceneRenderState renderStateRight
(
gClientSceneGraph ,
SPT_Reflect ,
SceneCameraState : : fromGFX ( )
) ;
2013-10-25 05:57:38 +00:00
renderStateRight . setSceneRenderStyle ( SRS_SideBySide ) ;
renderStateRight . getMaterialDelegate ( ) . bind ( REFLECTMGR , & ReflectionManager : : getReflectionMaterial ) ;
2016-05-17 23:18:02 +00:00
renderStateRight . setDiffuseCameraTransform ( params . query - > headMatrix ) ;
2016-06-08 21:50:10 +00:00
//renderStateRight.disableAdvancedLightingBins(true);
2013-10-25 05:57:38 +00:00
gClientSceneGraph - > renderSceneNoLights ( & renderStateRight , objTypeFlag ) ;
// Restore previous values
2016-06-08 21:50:10 +00:00
GFX - > setFrustum ( frustum ) ;
2013-10-25 05:57:38 +00:00
GFX - > setViewport ( originalVP ) ;
2016-05-21 18:52:41 +00:00
gClientSceneGraph - > setNonClipProjection ( origNonClipProjection ) ;
PFXMGR - > setFrameState ( origPFXState ) ;
2016-06-08 21:50:10 +00:00
GFX - > setCurrentStereoTarget ( - 1 ) ;
2013-10-25 05:57:38 +00:00
}
else
{
SceneRenderState reflectRenderState
(
gClientSceneGraph ,
SPT_Reflect ,
SceneCameraState : : fromGFX ( )
) ;
reflectRenderState . getMaterialDelegate ( ) . bind ( REFLECTMGR , & ReflectionManager : : getReflectionMaterial ) ;
2016-05-17 23:18:02 +00:00
reflectRenderState . setDiffuseCameraTransform ( params . query - > headMatrix ) ;
2013-10-25 05:57:38 +00:00
gClientSceneGraph - > renderSceneNoLights ( & reflectRenderState , objTypeFlag ) ;
}
2012-09-19 15:15:01 +00:00
LIGHTMGR - > unregisterAllLights ( ) ;
// Clean up.
reflectTarget - > resolve ( ) ;
GFX - > popActiveRenderTarget ( ) ;
2016-06-08 21:50:10 +00:00
# ifdef DEBUG_REFLECT_TEX
static U32 reflectStage = 0 ;
char buf [ 128 ] ; dSprintf ( buf , 128 , " F: \\ REFLECT-OUT%i.PNG " , reflectStage ) ;
//reflectTex->dumpToDisk("PNG", buf);
reflectStage + + ;
if ( reflectStage > 1 ) reflectStage = 0 ;
# endif
2012-09-19 15:15:01 +00:00
// Restore detail adjust amount.
TSShapeInstance : : smDetailAdjust = detailAdjustBackup ;
mIsRendering = false ;
}
2013-10-25 05:57:38 +00:00
void PlaneReflector : : setGFXMatrices ( const MatrixF & camTrans )
{
if ( objectSpace )
{
// set up camera transform relative to object
MatrixF invObjTrans = mObject - > getRenderTransform ( ) ;
invObjTrans . inverse ( ) ;
MatrixF relCamTrans = invObjTrans * camTrans ;
MatrixF camReflectTrans = getCameraReflection ( relCamTrans ) ;
2018-03-14 02:26:27 +00:00
MatrixF objTrans = mObject - > getRenderTransform ( ) * camReflectTrans ;
objTrans . inverse ( ) ;
2013-10-25 05:57:38 +00:00
2018-03-14 02:26:27 +00:00
GFX - > setWorldMatrix ( objTrans ) ;
2013-10-25 05:57:38 +00:00
// use relative reflect transform for modelview since clip plane is in object space
2018-03-14 02:26:27 +00:00
objTrans = camReflectTrans ;
objTrans . inverse ( ) ;
2013-10-25 05:57:38 +00:00
// set new projection matrix
gClientSceneGraph - > setNonClipProjection ( ( MatrixF & ) GFX - > getProjectionMatrix ( ) ) ;
2018-03-14 02:26:27 +00:00
MatrixF clipProj = getFrustumClipProj ( objTrans ) ;
2013-10-25 05:57:38 +00:00
GFX - > setProjectionMatrix ( clipProj ) ;
}
else
{
// set world mat from new camera view
MatrixF camReflectTrans = getCameraReflection ( camTrans ) ;
camReflectTrans . inverse ( ) ;
GFX - > setWorldMatrix ( camReflectTrans ) ;
// set new projection matrix
gClientSceneGraph - > setNonClipProjection ( ( MatrixF & ) GFX - > getProjectionMatrix ( ) ) ;
MatrixF clipProj = getFrustumClipProj ( camReflectTrans ) ;
GFX - > setProjectionMatrix ( clipProj ) ;
}
}
MatrixF PlaneReflector : : getCameraReflection ( const MatrixF & camTrans )
2012-09-19 15:15:01 +00:00
{
Point3F normal = refplane ;
// Figure out new cam position
Point3F camPos = camTrans . getPosition ( ) ;
F32 dist = refplane . distToPlane ( camPos ) ;
Point3F newCamPos = camPos - normal * dist * 2.0 ;
// Figure out new look direction
Point3F i , j , k ;
camTrans . getColumn ( 0 , & i ) ;
camTrans . getColumn ( 1 , & j ) ;
camTrans . getColumn ( 2 , & k ) ;
i = MathUtils : : reflect ( i , normal ) ;
j = MathUtils : : reflect ( j , normal ) ;
k = MathUtils : : reflect ( k , normal ) ;
//mCross( i, j, &k );
MatrixF newTrans ( true ) ;
newTrans . setColumn ( 0 , i ) ;
newTrans . setColumn ( 1 , j ) ;
newTrans . setColumn ( 2 , k ) ;
newTrans . setPosition ( newCamPos ) ;
return newTrans ;
}
2013-08-04 21:58:59 +00:00
inline F32 sgn ( F32 a )
2012-09-19 15:15:01 +00:00
{
if ( a > 0.0F ) return ( 1.0F ) ;
if ( a < 0.0F ) return ( - 1.0F ) ;
return ( 0.0F ) ;
}
MatrixF PlaneReflector : : getFrustumClipProj ( MatrixF & modelview )
{
static MatrixF rotMat ( EulerF ( static_cast < F32 > ( M_PI / 2.f ) , 0.0 , 0.0 ) ) ;
static MatrixF invRotMat ( EulerF ( - static_cast < F32 > ( M_PI / 2.f ) , 0.0 , 0.0 ) ) ;
MatrixF revModelview = modelview ;
revModelview = rotMat * revModelview ; // add rotation to modelview because it needs to be removed from projection
// rotate clip plane into modelview space
Point4F clipPlane ;
Point3F pnt = refplane * - ( refplane . d + 0.0 ) ;
Point3F norm = refplane ;
revModelview . mulP ( pnt ) ;
revModelview . mulV ( norm ) ;
norm . normalize ( ) ;
clipPlane . set ( norm . x , norm . y , norm . z , - mDot ( pnt , norm ) ) ;
// Manipulate projection matrix
//------------------------------------------------------------------------
MatrixF proj = GFX - > getProjectionMatrix ( ) ;
2023-04-15 02:13:28 +00:00
proj . reverseProjection ( ) ; // convert back into normal depth space from reversed, otherwise we have to figure out how to convert this modification into inverted depth space
2012-09-19 15:15:01 +00:00
proj . mul ( invRotMat ) ; // reverse rotation imposed by Torque
proj . transpose ( ) ; // switch to row-major order
// Calculate the clip-space corner point opposite the clipping plane
// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
// transform it into camera space by multiplying it
// by the inverse of the projection matrix
2016-06-08 21:50:10 +00:00
Vector4F q ;
2012-09-19 15:15:01 +00:00
q . x = sgn ( clipPlane . x ) / proj ( 0 , 0 ) ;
q . y = sgn ( clipPlane . y ) / proj ( 1 , 1 ) ;
q . z = - 1.0F ;
q . w = ( 1.0F - proj ( 2 , 2 ) ) / proj ( 3 , 2 ) ;
F32 a = 1.0 / ( clipPlane . x * q . x + clipPlane . y * q . y + clipPlane . z * q . z + clipPlane . w * q . w ) ;
Vector4F c = clipPlane * a ;
2023-04-15 02:13:28 +00:00
// [ZREV] This was a hack to handle OGL using -1 to 1 as its Z range
2012-09-19 15:15:01 +00:00
// CodeReview [ags 1/23/08] Come up with a better way to deal with this.
2023-04-15 02:13:28 +00:00
//if(GFX->getAdapterType() == OpenGL)
// c.z += 1.0f;
2012-09-19 15:15:01 +00:00
// Replace the third column of the projection matrix
proj . setColumn ( 2 , c ) ;
proj . transpose ( ) ; // convert back to column major order
proj . mul ( rotMat ) ; // restore Torque rotation
2023-04-15 02:13:28 +00:00
proj . reverseProjection ( ) ; // convert back to reversed depth space
2012-09-19 15:15:01 +00:00
return proj ;
2019-07-15 05:38:53 +00:00
}