Torque3D/Engine/source/postFx/postEffect.cpp

2120 lines
66 KiB
C++
Raw Normal View History

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 "postFx/postEffect.h"
#include "console/engineAPI.h"
#include "core/stream/fileStream.h"
#include "core/strings/stringUnit.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "math/util/frustum.h"
#include "math/mathUtils.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/gfxStringEnumTranslate.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/gfxDebugEvent.h"
#include "gfx/util/screenspace.h"
#include "gfx/sim/gfxStateBlockData.h"
#include "scene/sceneRenderState.h"
#include "shaderGen/shaderGenVars.h"
#include "lighting/lightInfo.h"
#include "lighting/lightManager.h"
#include "materials/materialManager.h"
#include "materials/shaderData.h"
#include "postFx/postEffectManager.h"
#include "postFx/postEffectVis.h"
using namespace Torque;
2012-09-19 15:15:01 +00:00
ConsoleDocClass( PostEffect,
"@brief A fullscreen shader effect.\n\n"
"@section PFXTextureIdentifiers\n\n"
"@ingroup Rendering\n"
);
IMPLEMENT_CALLBACK( PostEffect, onAdd, void, (), (),
"Called when this object is first created and registered."
);
IMPLEMENT_CALLBACK( PostEffect, preProcess, void, (), (),
"Called when an effect is processed but before textures are bound. This "
"allows the user to change texture related paramaters or macros at runtime.\n"
"@tsexample\n"
"function SSAOPostFx::preProcess( %this )\n"
"{\n"
" if ( $SSAOPostFx::quality !$= %this.quality )\n"
" {\n"
" %this.quality = mClamp( mRound( $SSAOPostFx::quality ), 0, 2 );\n"
" \n"
" %this.setShaderMacro( \"QUALITY\", %this.quality );\n"
" }\n"
" %this.targetScale = $SSAOPostFx::targetScale;\n"
"}\n"
"@endtsexample\n"
"@see setShaderConst\n"
"@see setShaderMacro"
);
IMPLEMENT_CALLBACK( PostEffect, setShaderConsts, void, (), (),
"Called immediate before processing this effect. This is the user's chance "
"to set the value of shader uniforms (constants).\n"
"@see setShaderConst"
);
IMPLEMENT_CALLBACK( PostEffect, onEnabled, bool, (), (),
"Called when this effect becomes enabled. If the user returns false from "
"this callback the effect will not be enabled.\n"
"@return True to allow this effect to be enabled."
);
IMPLEMENT_CALLBACK( PostEffect, onDisabled, void, (), (),
"Called when this effect becomes disabled."
);
ImplementEnumType( PFXRenderTime,
"When to process this effect during the frame.\n"
"@ingroup Rendering\n\n")
{ PFXBeforeBin, "PFXBeforeBin", "Before a RenderInstManager bin.\n" },
{ PFXAfterBin, "PFXAfterBin", "After a RenderInstManager bin.\n" },
{ PFXAfterDiffuse, "PFXAfterDiffuse", "After the diffuse rendering pass.\n" },
{ PFXEndOfFrame, "PFXEndOfFrame", "When the end of the frame is reached.\n" },
{ PFXTexGenOnDemand, "PFXTexGenOnDemand", "This PostEffect is not processed by the manager. It will generate its texture when it is requested.\n" }
EndImplementEnumType;
ImplementEnumType( PFXTargetClear,
"Describes when the target texture should be cleared\n"
"@ingroup Rendering\n\n")
{ PFXTargetClear_None, "PFXTargetClear_None", "Never clear the PostEffect target.\n" },
{ PFXTargetClear_OnCreate, "PFXTargetClear_OnCreate", "Clear once on create.\n" },
{ PFXTargetClear_OnDraw, "PFXTargetClear_OnDraw", "Clear before every draw.\n" },
EndImplementEnumType;
ImplementEnumType( PFXTargetViewport,
"Specifies how the viewport should be set up for a PostEffect's target.\n"
"@note Applies to both the diffuse target and the depth target (if defined).\n"
"@ingroup Rendering\n\n")
{ PFXTargetViewport_TargetSize, "PFXTargetViewport_TargetSize", "Set viewport to match target size (default).\n" },
{ PFXTargetViewport_GFXViewport, "PFXTargetViewport_GFXViewport", "Use the current GFX viewport (scaled to match target size).\n" },
{ PFXTargetViewport_NamedInTexture0, "PFXTargetViewport_NamedInTexture0", "Use the input texture 0 if it is named (scaled to match target size), otherwise revert to PFXTargetViewport_TargetSize if there is none.\n" },
EndImplementEnumType;
2012-09-19 15:15:01 +00:00
GFXImplementVertexFormat( PFXVertex )
{
addElement( "POSITION", GFXDeclType_Float3 );
addElement( "TEXCOORD", GFXDeclType_Float2, 0 );
addElement( "TEXCOORD", GFXDeclType_Float3, 1 );
};
GFX_ImplementTextureProfile( PostFxTargetProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::RenderTarget |
GFXTextureProfile::Pooled,
GFXTextureProfile::NONE );
2012-09-19 15:15:01 +00:00
IMPLEMENT_CONOBJECT(PostEffect);
GFX_ImplementTextureProfile( PostFxTextureProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::Static | GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap,
GFXTextureProfile::NONE );
2012-09-19 15:15:01 +00:00
GFX_ImplementTextureProfile( PostFxTextureSRGBProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::Static | GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap | GFXTextureProfile::SRGB,
GFXTextureProfile::NONE);
GFX_ImplementTextureProfile( VRTextureProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::RenderTarget |
GFXTextureProfile::NoMipmap,
GFXTextureProfile::NONE );
GFX_ImplementTextureProfile( VRDepthProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::NoMipmap |
GFXTextureProfile::ZTarget,
GFXTextureProfile::NONE );
2012-09-19 15:15:01 +00:00
void PostEffect::EffectConst::set( const String &newVal )
{
if ( mStringVal == newVal )
return;
mStringVal = newVal;
mDirty = true;
mValueType = StringType;
}
void PostEffect::EffectConst::set(const F32 &newVal)
{
if (mFloatVal == newVal)
return;
mFloatVal = newVal;
mDirty = true;
mValueType = FloatType;
}
void PostEffect::EffectConst::set(const int& newVal)
{
if (mIntVal == newVal)
return;
mIntVal = newVal;
mDirty = true;
mValueType = IntType;
}
void PostEffect::EffectConst::set(const Point4F &newVal)
{
if (mPointVal == newVal)
return;
mPointVal = newVal;
mDirty = true;
mValueType = PointType;
}
void PostEffect::EffectConst::set(const MatrixF &newVal)
{
if (mMatrixVal == newVal)
return;
mMatrixVal = newVal;
mDirty = true;
mValueType = MatrixType;
}
void PostEffect::EffectConst::set(const Vector<Point4F> &newVal)
{
//if (mPointArrayVal == newVal)
// return;
mPointArrayVal = newVal;
mDirty = true;
mValueType = PointArrayType;
}
void PostEffect::EffectConst::set(const Vector<MatrixF> &newVal)
{
//if (mMatrixArrayVal == newVal)
// return;
mMatrixArrayVal = newVal;
mDirty = true;
mValueType = MatrixArrayType;
2012-09-19 15:15:01 +00:00
}
void PostEffect::EffectConst::setToBuffer( GFXShaderConstBufferRef buff )
{
// Nothing to do if the value hasn't changed.
if ( !mDirty )
return;
mDirty = false;
// If we don't have a handle... get it now.
if ( !mHandle )
mHandle = buff->getShader()->getShaderConstHandle( mName );
// If the handle isn't valid then we're done.
if ( !mHandle->isValid() )
return;
const GFXShaderConstType type = mHandle->getType();
// For now, we're only going
// to support float4 arrays.
// Expand to other types as necessary.
U32 arraySize = mHandle->getArraySize();
if (mValueType == StringType)
2014-11-08 08:42:01 +00:00
{
const char *strVal = mStringVal.c_str();
if (type == GFXSCT_Int)
{
S32 val;
Con::setData(TypeS32, &val, 0, 1, &strVal);
buff->set(mHandle, val);
}
else if (type == GFXSCT_Float)
{
F32 val;
Con::setData(TypeF32, &val, 0, 1, &strVal);
buff->set(mHandle, val);
}
else if (type == GFXSCT_Float2)
{
Point2F val;
Con::setData(TypePoint2F, &val, 0, 1, &strVal);
buff->set(mHandle, val);
}
else if (type == GFXSCT_Float3)
{
Point3F val;
Con::setData(TypePoint3F, &val, 0, 1, &strVal);
buff->set(mHandle, val);
}
else if (type == GFXSCT_Float4)
{
Point4F val;
if (arraySize > 1)
{
// Do array setup!
//U32 unitCount = StringUnit::getUnitCount( strVal, "\t" );
//AssertFatal( unitCount == arraySize, "" );
String tmpString;
Vector<Point4F> valArray;
for (U32 i = 0; i < arraySize; i++)
{
tmpString = StringUnit::getUnit(strVal, i, "\t");
valArray.increment();
const char *tmpCStr = tmpString.c_str();
Con::setData(TypePoint4F, &valArray.last(), 0, 1, &tmpCStr);
}
AlignedArray<Point4F> rectData(valArray.size(), sizeof(Point4F), (U8*)valArray.address(), false);
buff->set(mHandle, rectData);
}
else
{
// Do regular setup.
Con::setData(TypePoint4F, &val, 0, 1, &strVal);
buff->set(mHandle, val);
}
}
else
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
}
2014-11-08 08:42:01 +00:00
}
else if (mValueType == FloatType)
2012-09-19 15:15:01 +00:00
{
if (type == GFXSCT_Float)
{
buff->set(mHandle, mFloatVal);
}
else
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
}
}
else if (mValueType == IntType)
{
if (type == GFXSCT_Int)
{
buff->set(mHandle, mIntVal);
}
else
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
}
2012-09-19 15:15:01 +00:00
}
else if (mValueType == PointType)
2012-09-19 15:15:01 +00:00
{
if (type == GFXSCT_Float2)
{
buff->set(mHandle, Point2F(mPointVal.x, mPointVal.y));
}
else if (type == GFXSCT_Float3)
{
buff->set(mHandle, Point3F(mPointVal.x, mPointVal.y, mPointVal.z));
}
else if (type == GFXSCT_Float4)
{
buff->set(mHandle, mPointVal);
}
else
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
}
2012-09-19 15:15:01 +00:00
}
else if (mValueType == MatrixType)
2012-09-19 15:15:01 +00:00
{
if (type == GFXSCT_Float4x4)
{
buff->set(mHandle, mMatrixVal);
}
else
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
}
2012-09-19 15:15:01 +00:00
}
else if (mValueType == PointArrayType)
2012-09-19 15:15:01 +00:00
{
if (type == GFXSCT_Float4)
2012-09-19 15:15:01 +00:00
{
if (arraySize != mPointArrayVal.size())
2012-09-19 15:15:01 +00:00
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer PointArrayType, attempted to feed an array that does not match the uniform array's size!");
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
return;
2012-09-19 15:15:01 +00:00
}
AlignedArray<Point4F> alignedVal = AlignedArray<Point4F>(arraySize, sizeof(Point4F), (U8*)mPointArrayVal.address(), false);
buff->set(mHandle, alignedVal);
2012-09-19 15:15:01 +00:00
}
else
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
2012-09-19 15:15:01 +00:00
}
}
else if (mValueType == MatrixArrayType)
2014-11-08 08:42:01 +00:00
{
if (type == GFXSCT_Float4x4)
{
if (arraySize != mMatrixArrayVal.size())
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer MatrixArrayType, attempted to feed an array that does not match the uniform array's size!");
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
return;
}
buff->set(mHandle, mMatrixArrayVal.address(), arraySize);
}
else
{
#if TORQUE_DEBUG
const char* err = avar("PostEffect::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
Con::errorf(err);
GFXAssertFatal(0, err);
#endif
}
2014-11-08 08:42:01 +00:00
}
2012-09-19 15:15:01 +00:00
}
//-------------------------------------------------------------------------
// PostEffect
//-------------------------------------------------------------------------
PostEffect::PostEffect()
: mRenderTime( PFXAfterDiffuse ),
mRenderPriority( 1.0 ),
mEnabled( false ),
mStateBlockData( NULL ),
mUpdateShader( true ),
mSkip( false ),
2012-09-19 15:15:01 +00:00
mAllowReflectPass( false ),
mTargetClear( PFXTargetClear_None ),
mTargetScale( Point2F::One ),
mTargetViewport( PFXTargetViewport_TargetSize ),
2012-09-19 15:15:01 +00:00
mTargetSize( Point2I::Zero ),
mTargetFormat( GFXFormatR8G8B8A8 ),
mTargetClearColor( LinearColorF::BLACK ),
2012-09-19 15:15:01 +00:00
mOneFrameOnly( false ),
mOnThisFrame( true ),
mRTSizeSC( NULL ),
mIsValid( false ),
mShaderReloadKey( 0 ),
2012-09-19 15:15:01 +00:00
mOneOverRTSizeSC( NULL ),
mViewportOffsetSC( NULL ),
mTargetViewportSC( NULL ),
2012-09-19 15:15:01 +00:00
mFogDataSC( NULL ),
mFogColorSC( NULL ),
mEyePosSC( NULL ),
mMatWorldToScreenSC( NULL ),
mMatScreenToWorldSC( NULL ),
mMatPrevScreenToWorldSC( NULL ),
mNearFarSC( NULL ),
mInvNearFarSC( NULL ),
mWorldToScreenScaleSC( NULL ),
mProjectionOffsetSC( NULL ),
2012-09-19 15:15:01 +00:00
mWaterColorSC( NULL ),
mWaterFogDataSC( NULL ),
mAmbientColorSC( NULL ),
mWaterFogPlaneSC( NULL ),
mWaterDepthGradMaxSC( NULL ),
mScreenSunPosSC( NULL ),
mLightDirectionSC( NULL ),
mCameraForwardSC( NULL ),
mAccumTimeSC( NULL ),
mDeltaTimeSC( NULL ),
2018-11-01 23:08:45 +00:00
mInvCameraMatSC( NULL ),
mMatCameraToWorldSC( NULL),
mInvCameraTransSC(NULL),
mMatCameraToScreenSC(NULL),
mMatScreenToCameraSC(NULL)
2012-09-19 15:15:01 +00:00
{
dMemset( mTexSRGB, 0, sizeof(bool) * NumTextures);
2012-09-19 15:15:01 +00:00
dMemset( mActiveTextures, 0, sizeof( GFXTextureObject* ) * NumTextures );
dMemset( mActiveNamedTarget, 0, sizeof( NamedTexTarget* ) * NumTextures );
dMemset( mActiveTextureViewport, 0, sizeof( RectI ) * NumTextures );
dMemset( mTexSizeSC, 0, sizeof( GFXShaderConstHandle* ) * NumTextures );
dMemset( mRenderTargetParamsSC, 0, sizeof( GFXShaderConstHandle* ) * NumTextures );
for (U32 i = 0; i < NumTextures; i++)
{
INIT_IMAGEASSET_ARRAY(Texture, PostFxTextureProfile, i);
}
2012-09-19 15:15:01 +00:00
}
PostEffect::~PostEffect()
{
EffectConstTable::Iterator iter = mEffectConsts.begin();
for ( ; iter != mEffectConsts.end(); iter++ )
delete iter->value;
}
void PostEffect::initPersistFields()
{
addField( "shader", TypeRealString, Offset( mShaderName, PostEffect ),
"Name of a GFXShaderData for this effect." );
addField( "stateBlock", TYPEID<GFXStateBlockData>(), Offset( mStateBlockData, PostEffect ),
"Name of a GFXStateBlockData for this effect." );
addField( "target", TypeRealString, Offset( mTargetName, PostEffect ),
"String identifier of this effect's target texture.\n"
"@see PFXTextureIdentifiers" );
addField( "targetDepthStencil", TypeRealString, Offset( mTargetDepthStencilName, PostEffect ),
"Optional string identifier for this effect's target depth/stencil texture.\n"
"@see PFXTextureIdentifiers" );
addField( "targetScale", TypePoint2F, Offset( mTargetScale, PostEffect ),
"If targetSize is zero this is used to set a relative size from the current target." );
addField( "targetSize", TypePoint2I, Offset( mTargetSize, PostEffect ),
"If non-zero this is used as the absolute target size." );
addField( "targetFormat", TypeGFXFormat, Offset( mTargetFormat, PostEffect ),
"Format of the target texture, not applicable if writing to the backbuffer." );
addField( "targetClearColor", TypeColorF, Offset( mTargetClearColor, PostEffect ),
"Color to which the target texture is cleared before rendering." );
addField( "targetClear", TYPEID< PFXTargetClear >(), Offset( mTargetClear, PostEffect ),
"Describes when the target texture should be cleared." );
addField( "targetViewport", TYPEID< PFXTargetViewport >(), Offset( mTargetViewport, PostEffect ),
"Specifies how the viewport should be set up for a target texture." );
INITPERSISTFIELD_IMAGEASSET_ARRAY(Texture, NumTextures, PostEffect, "Input textures to this effect ( samplers ).\n"
"@see PFXTextureIdentifiers");
2012-09-19 15:15:01 +00:00
addField("textureSRGB", TypeBool, Offset(mTexSRGB, PostEffect), NumTextures,
"Set input texture to be sRGB");
2012-09-19 15:15:01 +00:00
addField( "renderTime", TYPEID< PFXRenderTime >(), Offset( mRenderTime, PostEffect ),
"When to process this effect during the frame." );
addField( "renderBin", TypeRealString, Offset( mRenderBin, PostEffect ),
"Name of a renderBin, used if renderTime is PFXBeforeBin or PFXAfterBin." );
addField( "renderPriority", TypeF32, Offset( mRenderPriority, PostEffect ),
"PostEffects are processed in DESCENDING order of renderPriority if more than one has the same renderBin/Time." );
addField( "allowReflectPass", TypeBool, Offset( mAllowReflectPass, PostEffect ),
"Is this effect processed during reflection render passes." );
addProtectedField( "enabled", TypeBool, Offset( mEnabled, PostEffect),
2012-09-19 15:15:01 +00:00
&PostEffect::_setIsEnabled, &defaultProtectedGetFn,
"Is the effect on." );
addField( "onThisFrame", TypeBool, Offset( mOnThisFrame, PostEffect ),
"Allows you to turn on a PostEffect for only a single frame." );
addField( "oneFrameOnly", TypeBool, Offset( mOneFrameOnly, PostEffect ),
"Allows you to turn on a PostEffect for only a single frame." );
addField( "skip", TypeBool, Offset( mSkip, PostEffect ),
"Skip processing of this PostEffect and its children even if its parent "
"is enabled. Parent and sibling PostEffects in the chain are still processed." );
Parent::initPersistFields();
}
bool PostEffect::onAdd()
{
if( !Parent::onAdd() )
return false;
LightManager::smActivateSignal.notify( this, &PostEffect::_onLMActivate );
mUpdateShader = true;
// Grab the script path.
Torque::Path scriptPath( Con::getVariable( "$Con::File" ) );
scriptPath.setFileName( String::EmptyString );
scriptPath.setExtension( String::EmptyString );
// Find additional textures
for (S32 i = 0; i < NumTextures; i++)
2012-09-19 15:15:01 +00:00
{
mTextureType[i] = NormalTextureType;
String texFilename = getTexture(i);
// Skip empty stages or ones with variable or target names.
if (texFilename.isEmpty() ||
texFilename[0] == '$' ||
texFilename[0] == '#')
continue;
mTextureProfile[i] = (mTexSRGB[i]) ? &PostFxTextureSRGBProfile : &PostFxTextureProfile;
_setTexture(texFilename, i);
2012-09-19 15:15:01 +00:00
}
// Is the target a named target?
if ( mTargetName.isNotEmpty() && mTargetName[0] == '#' )
{
mNamedTarget.registerWithName( mTargetName.substr( 1 ) );
mNamedTarget.getTextureDelegate().bind( this, &PostEffect::_getTargetTexture );
}
if ( mTargetDepthStencilName.isNotEmpty() && mTargetDepthStencilName[0] == '#' )
mNamedTargetDepthStencil.registerWithName( mTargetDepthStencilName.substr( 1 ) );
if (mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered())
GFXTextureManager::addEventDelegate( this, &PostEffect::_onTextureEvent );
// Call onAdd in script
onAdd_callback();
// Should we start enabled?
if ( mEnabled )
{
mEnabled = false;
enable();
}
getSet()->addObject( this );
return true;
}
void PostEffect::onRemove()
{
Parent::onRemove();
PFXMGR->_removeEffect( this );
LightManager::smActivateSignal.remove( this, &PostEffect::_onLMActivate );
mShader = NULL;
_cleanTargets();
if ( mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered() )
GFXTextureManager::removeEventDelegate( this, &PostEffect::_onTextureEvent );
if ( mNamedTarget.isRegistered() )
{
mNamedTarget.unregister();
mNamedTarget.getTextureDelegate().clear();
}
if ( mNamedTargetDepthStencil.isRegistered() )
mNamedTargetDepthStencil.unregister();
}
void PostEffect::_updateScreenGeometry( const Frustum &frustum,
GFXVertexBufferHandle<PFXVertex> *outVB )
{
outVB->set( GFX, 4, GFXBufferTypeVolatile );
const Point3F *frustumPoints = frustum.getPoints();
const Point3F& cameraPos = frustum.getPosition();
// Perform a camera offset. We need to manually perform this offset on the postFx's
// polygon, which is at the far plane.
const Point2F& projOffset = frustum.getProjectionOffset();
Point3F cameraOffsetPos = cameraPos;
if(!projOffset.isZero())
{
// First we need to calculate the offset at the near plane. The projOffset
// given above can be thought of a percent as it ranges from 0..1 (or 0..-1).
F32 nearOffset = frustum.getNearRight() * projOffset.x;
// Now given the near plane distance from the camera we can solve the right
// triangle and calcuate the SIN theta for the offset at the near plane.
// SIN theta = x/y
F32 sinTheta = nearOffset / frustum.getNearDist();
// Finally, we can calcuate the offset at the far plane, which is where our sun (or vector)
// light's polygon is drawn.
F32 farOffset = frustum.getFarDist() * sinTheta;
// We can now apply this far plane offset to the far plane itself, which then compensates
// for the project offset.
MatrixF camTrans = frustum.getTransform();
VectorF offset = camTrans.getRightVector();
offset *= farOffset;
cameraOffsetPos += offset;
}
2012-09-19 15:15:01 +00:00
PFXVertex *vert = outVB->lock();
2016-03-20 11:52:11 +00:00
vert->point.set(-1.0, 1.0, 0.0);
vert->texCoord.set(0.0f, 0.0f);
vert->wsEyeRay = frustumPoints[Frustum::FarTopLeft] - cameraOffsetPos;
2012-09-19 15:15:01 +00:00
vert++;
2016-03-20 11:52:11 +00:00
vert->point.set(1.0, 1.0, 0.0);
vert->texCoord.set(1.0f, 0.0f);
vert->wsEyeRay = frustumPoints[Frustum::FarTopRight] - cameraOffsetPos;
2012-09-19 15:15:01 +00:00
vert++;
2016-03-20 11:52:11 +00:00
vert->point.set(-1.0, -1.0, 0.0);
vert->texCoord.set(0.0f, 1.0f);
vert->wsEyeRay = frustumPoints[Frustum::FarBottomLeft] - cameraOffsetPos;
vert++;
vert->point.set(1.0, -1.0, 0.0);
vert->texCoord.set(1.0f, 1.0f);
vert->wsEyeRay = frustumPoints[Frustum::FarBottomRight] - cameraOffsetPos;
2012-09-19 15:15:01 +00:00
vert++;
outVB->unlock();
}
void PostEffect::_setupStateBlock( const SceneRenderState *state )
{
if ( mStateBlock.isNull() )
{
GFXStateBlockDesc desc;
if ( mStateBlockData )
desc = mStateBlockData->getState();
mStateBlock = GFX->createStateBlock( desc );
}
GFX->setStateBlock( mStateBlock );
}
void PostEffect::_setupConstants( const SceneRenderState *state )
{
// Alloc the const buffer.
if ( mShaderConsts.isNull() )
{
mShaderConsts = mShader->allocConstBuffer();
mRTSizeSC = mShader->getShaderConstHandle( "$targetSize" );
mOneOverRTSizeSC = mShader->getShaderConstHandle( "$oneOverTargetSize" );
mRTRatioSC = mShader->getShaderConstHandle("$targetRatio");
for (U32 i = 0; i < NumTextures; i++)
{
mTexSizeSC[i] = mShader->getShaderConstHandle(String::ToString("$texSize%d", i));
mRenderTargetParamsSC[i] = mShader->getShaderConstHandle(String::ToString("$rtParams%d",i));
}
2012-09-19 15:15:01 +00:00
mTargetViewportSC = mShader->getShaderConstHandle( "$targetViewport" );
2012-09-19 15:15:01 +00:00
mFogDataSC = mShader->getShaderConstHandle( ShaderGenVars::fogData );
mFogColorSC = mShader->getShaderConstHandle( ShaderGenVars::fogColor );
mEyePosSC = mShader->getShaderConstHandle( ShaderGenVars::eyePosWorld );
mNearFarSC = mShader->getShaderConstHandle( "$nearFar" );
mInvNearFarSC = mShader->getShaderConstHandle( "$invNearFar" );
mWorldToScreenScaleSC = mShader->getShaderConstHandle( "$worldToScreenScale" );
mMatWorldToScreenSC = mShader->getShaderConstHandle( "$matWorldToScreen" );
mMatScreenToWorldSC = mShader->getShaderConstHandle( "$matScreenToWorld" );
mMatPrevScreenToWorldSC = mShader->getShaderConstHandle( "$matPrevScreenToWorld" );
mProjectionOffsetSC = mShader->getShaderConstHandle( "$projectionOffset" );
2012-09-19 15:15:01 +00:00
mWaterColorSC = mShader->getShaderConstHandle( "$waterColor" );
mAmbientColorSC = mShader->getShaderConstHandle( "$ambientColor" );
mWaterFogDataSC = mShader->getShaderConstHandle( "$waterFogData" );
mWaterFogPlaneSC = mShader->getShaderConstHandle( "$waterFogPlane" );
mWaterDepthGradMaxSC = mShader->getShaderConstHandle( "$waterDepthGradMax" );
mScreenSunPosSC = mShader->getShaderConstHandle( "$screenSunPos" );
mLightDirectionSC = mShader->getShaderConstHandle( "$lightDirection" );
mCameraForwardSC = mShader->getShaderConstHandle( "$camForward" );
mAccumTimeSC = mShader->getShaderConstHandle( "$accumTime" );
mDeltaTimeSC = mShader->getShaderConstHandle( "$deltaTime" );
mInvCameraMatSC = mShader->getShaderConstHandle( "$invCameraMat" );
2018-11-01 23:08:45 +00:00
mMatCameraToWorldSC = mShader->getShaderConstHandle("$cameraToWorld");
mInvCameraTransSC = mShader->getShaderConstHandle("$invCameraTrans");
mMatCameraToScreenSC = mShader->getShaderConstHandle("$cameraToScreen");
mMatScreenToCameraSC = mShader->getShaderConstHandle("$screenToCamera");
2012-09-19 15:15:01 +00:00
}
// Set up shader constants for source image size
if ( mRTSizeSC->isValid() )
{
const Point2I &resolution = GFX->getActiveRenderTarget()->getSize();
Point2F pixelShaderConstantData;
pixelShaderConstantData.x = resolution.x;
pixelShaderConstantData.y = resolution.y;
mShaderConsts->set( mRTSizeSC, pixelShaderConstantData );
}
if ( mOneOverRTSizeSC->isValid() )
{
const Point2I &resolution = GFX->getActiveRenderTarget()->getSize();
Point2F oneOverTargetSize( 1.0f / (F32)resolution.x, 1.0f / (F32)resolution.y );
mShaderConsts->set( mOneOverRTSizeSC, oneOverTargetSize );
}
if (mRTRatioSC->isValid())
{
const Point2I& resolution = GFX->getActiveRenderTarget()->getSize();
mShaderConsts->set(mRTRatioSC, (F32)resolution.x/ (F32)resolution.y);
}
2012-09-19 15:15:01 +00:00
// Set up additional textures
Point2F texSizeConst;
for( U32 i = 0; i < NumTextures; i++ )
{
if( !mActiveTextures[i] )
continue;
if ( mTexSizeSC[i]->isValid() )
{
texSizeConst.x = (F32)mActiveTextures[i]->getWidth();
texSizeConst.y = (F32)mActiveTextures[i]->getHeight();
mShaderConsts->set( mTexSizeSC[i], texSizeConst );
}
}
for ( U32 i = 0; i < NumTextures; i++ )
{
if ( !mRenderTargetParamsSC[i]->isValid() )
continue;
Point4F rtParams( Point4F::One );
if ( mActiveTextures[i] )
{
const Point3I &targetSz = mActiveTextures[i]->getSize();
RectI targetVp = mActiveTextureViewport[i];
ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams);
}
mShaderConsts->set( mRenderTargetParamsSC[i], rtParams );
}
// Target viewport (in target space)
if ( mTargetViewportSC->isValid() )
{
const Point2I& targetSize = GFX->getActiveRenderTarget()->getSize();
Point3I size(targetSize.x, targetSize.y, 0);
const RectI& viewport = GFX->getViewport();
Point2F offset((F32)viewport.point.x / (F32)targetSize.x, (F32)viewport.point.y / (F32)targetSize.y );
Point2F scale((F32)viewport.extent.x / (F32)targetSize.x, (F32)viewport.extent.y / (F32)targetSize.y );
Point4F targetParams;
2017-05-28 21:51:31 +00:00
targetParams.x = offset.x;
targetParams.y = offset.y;
targetParams.z = offset.x + scale.x;
targetParams.w = offset.y + scale.y;
mShaderConsts->set( mTargetViewportSC, targetParams );
}
2012-09-19 15:15:01 +00:00
// Set the fog data.
if ( mFogDataSC->isValid() )
{
const FogData &data = state->getSceneManager()->getFogData();
Point3F params;
params.x = data.density;
params.y = data.densityOffset;
if ( !mIsZero( data.atmosphereHeight ) )
params.z = 1.0f / data.atmosphereHeight;
else
params.z = 0.0f;
mShaderConsts->set( mFogDataSC, params );
}
const PFXFrameState &thisFrame = PFXMGR->getFrameState();
if ( mMatWorldToScreenSC->isValid() )
{
// Screen space->world space
MatrixF tempMat = thisFrame.cameraToScreen;
tempMat.mul( thisFrame.worldToCamera );
tempMat.fullInverse();
tempMat.transpose();
// Support using these matrices as float3x3 or float4x4...
mShaderConsts->set( mMatWorldToScreenSC, tempMat, mMatWorldToScreenSC->getType() );
}
if ( mMatScreenToWorldSC->isValid() )
{
// World space->screen space
MatrixF tempMat = thisFrame.cameraToScreen;
tempMat.mul( thisFrame.worldToCamera );
tempMat.transpose();
// Support using these matrices as float3x3 or float4x4...
mShaderConsts->set( mMatScreenToWorldSC, tempMat, mMatScreenToWorldSC->getType() );
}
if ( mMatPrevScreenToWorldSC->isValid() )
{
const PFXFrameState &lastFrame = PFXMGR->getLastFrameState();
// Previous frame world space->screen space
MatrixF tempMat = lastFrame.cameraToScreen;
tempMat.mul( lastFrame.worldToCamera );
tempMat.transpose();
mShaderConsts->set( mMatPrevScreenToWorldSC, tempMat );
}
if (mAmbientColorSC->isValid() && state)
2012-09-19 15:15:01 +00:00
{
const LinearColorF &sunlight = state->getAmbientLightColor();
2012-09-19 15:15:01 +00:00
Point3F ambientColor( sunlight.red, sunlight.green, sunlight.blue );
mShaderConsts->set( mAmbientColorSC, ambientColor );
}
2018-11-01 23:08:45 +00:00
if (mMatCameraToWorldSC->isValid())
{
MatrixF tempMat = thisFrame.worldToCamera;
tempMat.inverse();
mShaderConsts->set(mMatCameraToWorldSC, tempMat);
}
if (mInvCameraTransSC->isValid())
{
MatrixF mat = state->getCameraTransform();
mat.fullInverse();
mShaderConsts->set(mInvCameraTransSC, mat, mInvCameraTransSC->getType());
}
//Projection Matrix
if (mMatCameraToScreenSC->isValid())
{
MatrixF tempMat = thisFrame.cameraToScreen;
mShaderConsts->set(mMatCameraToScreenSC, tempMat, mMatCameraToScreenSC->getType());
}
//Inverse Projection Matrix
if (mMatScreenToCameraSC->isValid())
{
MatrixF tempMat = thisFrame.cameraToScreen;
tempMat.fullInverse();
mShaderConsts->set(mMatScreenToCameraSC, tempMat, mMatScreenToCameraSC->getType());
}
2012-09-19 15:15:01 +00:00
mShaderConsts->setSafe( mAccumTimeSC, MATMGR->getTotalTime() );
mShaderConsts->setSafe( mDeltaTimeSC, MATMGR->getDeltaTime() );
// Now set all the constants that are dependent on the scene state.
if ( state )
{
mShaderConsts->setSafe( mEyePosSC, state->getDiffuseCameraPosition() );
mShaderConsts->setSafe( mNearFarSC, Point2F( state->getNearPlane(), state->getFarPlane() ) );
mShaderConsts->setSafe( mInvNearFarSC, Point2F( 1.0f / state->getNearPlane(), 1.0f / state->getFarPlane() ) );
mShaderConsts->setSafe( mWorldToScreenScaleSC, state->getWorldToScreenScale() );
mShaderConsts->setSafe( mProjectionOffsetSC, state->getCameraFrustum().getProjectionOffset() );
2012-09-19 15:15:01 +00:00
mShaderConsts->setSafe( mFogColorSC, state->getSceneManager()->getFogData().color );
if ( mWaterColorSC->isValid() )
{
LinearColorF color( state->getSceneManager()->getWaterFogData().color );
2012-09-19 15:15:01 +00:00
mShaderConsts->set( mWaterColorSC, color );
}
if ( mWaterFogDataSC->isValid() )
{
const WaterFogData &data = state->getSceneManager()->getWaterFogData();
Point4F params( data.density, data.densityOffset, data.wetDepth, data.wetDarkening );
mShaderConsts->set( mWaterFogDataSC, params );
}
if ( mWaterFogPlaneSC->isValid() )
{
const PlaneF &plane = state->getSceneManager()->getWaterFogData().plane;
mShaderConsts->set( mWaterFogPlaneSC, plane );
}
if ( mWaterDepthGradMaxSC->isValid() )
{
mShaderConsts->set( mWaterDepthGradMaxSC, state->getSceneManager()->getWaterFogData().depthGradMax );
}
if ( mScreenSunPosSC->isValid() )
{
// Grab our projection matrix
// from the frustum.
Frustum frust = state->getCameraFrustum();
2012-09-19 15:15:01 +00:00
MatrixF proj( true );
frust.getProjectionMatrix( &proj );
// Grab the ScatterSky world matrix.
MatrixF camMat = state->getCameraTransform();
camMat.inverse();
MatrixF tmp( true );
tmp = camMat;
tmp.setPosition( Point3F( 0, 0, 0 ) );
Point3F sunPos( 0, 0, 0 );
// Get the light manager and sun light object.
LightInfo *sunLight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType );
// Grab the light direction and scale
// by the ScatterSky radius to get the world
// space sun position.
const VectorF &lightDir = sunLight->getDirection();
Point3F lightPos( lightDir.x * (6378.0f * 1000.0f),
lightDir.y * (6378.0f * 1000.0f),
lightDir.z * (6378.0f * 1000.0f) );
RectI viewPort = GFX->getViewport();
2012-09-19 15:15:01 +00:00
// Get the screen space sun position.
MathUtils::mProjectWorldToScreen(lightPos, &sunPos, viewPort, tmp, proj);
2012-09-19 15:15:01 +00:00
// And normalize it to the 0 to 1 range.
sunPos.x -= (F32)viewPort.point.x;
sunPos.y -= (F32)viewPort.point.y;
sunPos.x /= (F32)viewPort.extent.x;
sunPos.y /= (F32)viewPort.extent.y;
2012-09-19 15:15:01 +00:00
mShaderConsts->set( mScreenSunPosSC, Point2F( sunPos.x, sunPos.y ) );
}
if ( mLightDirectionSC->isValid() )
{
LightInfo *sunLight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType );
const VectorF &lightDir = sunLight->getDirection();
mShaderConsts->set( mLightDirectionSC, lightDir );
}
if ( mCameraForwardSC->isValid() )
{
const MatrixF &camMat = state->getCameraTransform();
VectorF camFwd( 0, 0, 0 );
camMat.getColumn( 1, &camFwd );
mShaderConsts->set( mCameraForwardSC, camFwd );
}
if ( mInvCameraMatSC->isValid() )
{
MatrixF mat = state->getCameraTransform();
mat.inverse();
mShaderConsts->set( mInvCameraMatSC, mat, mInvCameraMatSC->getType() );
}
} // if ( state )
// Set EffectConsts - specified from script
// If our shader has reloaded since last frame we must mark all
// EffectConsts dirty so they will be reset.
if ( mShader->getReloadKey() != mShaderReloadKey )
{
mShaderReloadKey = mShader->getReloadKey();
EffectConstTable::Iterator iter = mEffectConsts.begin();
for ( ; iter != mEffectConsts.end(); iter++ )
2014-11-08 08:42:01 +00:00
{
2012-09-19 15:15:01 +00:00
iter->value->mDirty = true;
2014-11-08 08:42:01 +00:00
iter->value->mHandle = NULL;
}
2012-09-19 15:15:01 +00:00
}
// Doesn't look like anyone is using this anymore.
// But if we do want to pass this info to script,
// we should do so in the same way as I am doing below.
/*
Point2F texSizeScriptConst( 0, 0 );
String buffer;
if ( mActiveTextures[0] )
{
texSizeScriptConst.x = (F32)mActiveTextures[0]->getWidth();
texSizeScriptConst.y = (F32)mActiveTextures[0]->getHeight();
dSscanf( buffer.c_str(), "%g %g", texSizeScriptConst.x, texSizeScriptConst.y );
}
*/
{
PROFILE_SCOPE( PostEffect_SetShaderConsts );
// Pass some data about the current render state to script.
//
// TODO: This is pretty messy... it should go away. This info
// should be available from some other script accessible method
// or field which isn't PostEffect specific.
//
if ( state )
{
Con::setFloatVariable( "$Param::NearDist", state->getNearPlane() );
Con::setFloatVariable( "$Param::FarDist", state->getFarPlane() );
}
setShaderConsts_callback();
}
EffectConstTable::Iterator iter = mEffectConsts.begin();
for ( ; iter != mEffectConsts.end(); iter++ )
iter->value->setToBuffer( mShaderConsts );
}
void PostEffect::_setupTexture( U32 stage, GFXTexHandle &inputTex, const RectI *inTexViewport )
{
const String &texFilename = getTexture( stage );
2012-09-19 15:15:01 +00:00
GFXTexHandle theTex;
NamedTexTarget *namedTarget = NULL;
RectI viewport = GFX->getViewport();
if ( texFilename.compare( "$inTex", 0, String::NoCase ) == 0 )
{
theTex = inputTex;
if ( inTexViewport )
{
viewport = *inTexViewport;
}
else if ( theTex )
{
viewport.set( 0, 0, theTex->getWidth(), theTex->getHeight() );
}
}
else if ( texFilename.compare( "$backBuffer", 0, String::NoCase ) == 0 )
{
theTex = PFXMGR->getBackBufferTex();
// Always use the GFX viewport when reading from the backbuffer
}
else if ( texFilename.isNotEmpty() && texFilename[0] == '#' )
{
namedTarget = NamedTexTarget::find( texFilename.c_str() + 1 );
if ( namedTarget )
{
theTex = namedTarget->getTexture( 0 );
viewport = namedTarget->getViewport();
}
}
else
{
theTex = mTexture[ stage ];
2012-09-19 15:15:01 +00:00
if ( theTex )
viewport.set( 0, 0, theTex->getWidth(), theTex->getHeight() );
}
mActiveTextures[ stage ] = theTex;
mActiveNamedTarget[ stage ] = namedTarget;
mActiveTextureViewport[ stage ] = viewport;
if ( theTex.isValid() )
GFX->setTexture( stage, theTex );
}
void PostEffect::_setupCubemapTexture(U32 stage, GFXCubemapHandle &inputTex)
{
RectI viewport = GFX->getViewport();
mActiveTextures[stage] = nullptr;
mActiveNamedTarget[stage] = nullptr;
mActiveTextureViewport[stage] = viewport;
if (inputTex.isValid())
GFX->setCubeTexture(stage, inputTex);
}
void PostEffect::_setupCubemapArrayTexture(U32 stage, GFXCubemapArrayHandle &inputTex)
{
RectI viewport = GFX->getViewport();
mActiveTextures[stage] = nullptr;
mActiveNamedTarget[stage] = nullptr;
mActiveTextureViewport[stage] = viewport;
if (inputTex.isValid())
GFX->setCubeArrayTexture(stage, inputTex);
}
2012-09-19 15:15:01 +00:00
void PostEffect::_setupTransforms()
{
// Set everything to identity.
GFX->setWorldMatrix( MatrixF::Identity );
GFX->setProjectionMatrix( MatrixF::Identity );
}
void PostEffect::_setupTarget( const SceneRenderState *state, bool *outClearTarget )
{
if ( mNamedTarget.isRegistered() ||
mTargetName.compare( "$outTex", 0, String::NoCase ) == 0 )
{
// Size it relative to the texture of the first stage or
// if NULL then use the current target.
Point2I targetSize;
// If we have an absolute target size then use that.
if ( !mTargetSize.isZero() )
targetSize = mTargetSize;
// Else generate a relative size using the target scale.
else if ( mActiveTextures[ 0 ] )
{
const Point3I &texSize = mActiveTextures[ 0 ]->getSize();
targetSize.set( texSize.x * mTargetScale.x,
texSize.y * mTargetScale.y );
}
else
{
GFXTarget *oldTarget = GFX->getActiveRenderTarget();
const Point2I &oldTargetSize = oldTarget->getSize();
targetSize.set( oldTargetSize.x * mTargetScale.x,
oldTargetSize.y * mTargetScale.y );
}
// Make sure its at least 1x1.
targetSize.setMax( Point2I::One );
if ( mNamedTarget.isRegistered() ||
!mTargetTex ||
mTargetTex.getWidthHeight() != targetSize )
{
mTargetTex.set( targetSize.x, targetSize.y, mTargetFormat,
&PostFxTargetProfile, "PostEffect::_setupTarget" );
if ( mTargetClear == PFXTargetClear_OnCreate )
*outClearTarget = true;
if(mTargetViewport == PFXTargetViewport_GFXViewport)
{
// We may need to scale the GFX viewport to fit within
// our target texture size
GFXTarget *oldTarget = GFX->getActiveRenderTarget();
const Point2I &oldTargetSize = oldTarget->getSize();
Point2F scale(targetSize.x / F32(oldTargetSize.x), targetSize.y / F32(oldTargetSize.y));
const RectI &viewport = GFX->getViewport();
mNamedTarget.setViewport( RectI( viewport.point.x*scale.x, viewport.point.y*scale.y, viewport.extent.x*scale.x, viewport.extent.y*scale.y ) );
}
else if(mTargetViewport == PFXTargetViewport_NamedInTexture0 && mActiveNamedTarget[0] && mActiveNamedTarget[0]->getTexture())
{
// Scale the named input texture's viewport to match our target
const Point3I &namedTargetSize = mActiveNamedTarget[0]->getTexture()->getSize();
Point2F scale(targetSize.x / F32(namedTargetSize.x), targetSize.y / F32(namedTargetSize.y));
const RectI &viewport = mActiveNamedTarget[0]->getViewport();
mNamedTarget.setViewport( RectI( viewport.point.x*scale.x, viewport.point.y*scale.y, viewport.extent.x*scale.x, viewport.extent.y*scale.y ) );
}
else
{
// PFXTargetViewport_TargetSize
mNamedTarget.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) );
}
2012-09-19 15:15:01 +00:00
}
}
else
mTargetTex = NULL;
// Do we have a named depthStencil target?
if ( mNamedTargetDepthStencil.isRegistered() )
{
// Size it relative to the texture of the first stage or
// if NULL then use the current target.
Point2I targetSize;
// If we have an absolute target size then use that.
if ( !mTargetSize.isZero() )
targetSize = mTargetSize;
// Else generate a relative size using the target scale.
else if ( mActiveTextures[ 0 ] )
{
const Point3I &texSize = mActiveTextures[ 0 ]->getSize();
targetSize.set( texSize.x * mTargetScale.x,
texSize.y * mTargetScale.y );
}
else
{
GFXTarget *oldTarget = GFX->getActiveRenderTarget();
const Point2I &oldTargetSize = oldTarget->getSize();
targetSize.set( oldTargetSize.x * mTargetScale.x,
oldTargetSize.y * mTargetScale.y );
}
// Make sure its at least 1x1.
targetSize.setMax( Point2I::One );
if ( mNamedTargetDepthStencil.isRegistered() &&
mTargetDepthStencil.getWidthHeight() != targetSize )
{
mTargetDepthStencil.set( targetSize.x, targetSize.y, GFXFormatD24S8,
&GFXZTargetProfile, "PostEffect::_setupTarget" );
2012-09-19 15:15:01 +00:00
if ( mTargetClear == PFXTargetClear_OnCreate )
*outClearTarget = true;
if(mTargetViewport == PFXTargetViewport_GFXViewport)
{
// We may need to scale the GFX viewport to fit within
// our target texture size
GFXTarget *oldTarget = GFX->getActiveRenderTarget();
const Point2I &oldTargetSize = oldTarget->getSize();
Point2F scale(targetSize.x / F32(oldTargetSize.x), targetSize.y / F32(oldTargetSize.y));
const RectI &viewport = GFX->getViewport();
mNamedTargetDepthStencil.setViewport( RectI( viewport.point.x*scale.x, viewport.point.y*scale.y, viewport.extent.x*scale.x, viewport.extent.y*scale.y ) );
}
else if(mTargetViewport == PFXTargetViewport_NamedInTexture0 && mActiveNamedTarget[0] && mActiveNamedTarget[0]->getTexture())
{
// Scale the named input texture's viewport to match our target
const Point3I &namedTargetSize = mActiveNamedTarget[0]->getTexture()->getSize();
Point2F scale(targetSize.x / F32(namedTargetSize.x), targetSize.y / F32(namedTargetSize.y));
const RectI &viewport = mActiveNamedTarget[0]->getViewport();
mNamedTargetDepthStencil.setViewport( RectI( viewport.point.x*scale.x, viewport.point.y*scale.y, viewport.extent.x*scale.x, viewport.extent.y*scale.y ) );
}
else
{
// PFXTargetViewport_TargetSize
mNamedTargetDepthStencil.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) );
}
2012-09-19 15:15:01 +00:00
}
}
else
mTargetDepthStencil = NULL;
if ( mTargetClear == PFXTargetClear_OnDraw )
*outClearTarget = true;
if ( !mTarget && (mTargetTex || mTargetDepthStencil) )
mTarget = GFX->allocRenderToTextureTarget();
}
void PostEffect::_cleanTargets( bool recurse )
{
mTargetTex = NULL;
mTargetDepthStencil = NULL;
mTarget = NULL;
if ( !recurse )
return;
// Clear the children too!
for ( U32 i = 0; i < size(); i++ )
{
PostEffect *effect = (PostEffect*)(*this)[i];
effect->_cleanTargets( true );
}
}
void PostEffect::process( const SceneRenderState *state,
GFXTexHandle &inOutTex,
const RectI *inTexViewport )
{
// If the shader is forced to be skipped... then skip.
if ( mSkip )
return;
// Skip out if we don't support reflection passes.
if ( state && state->isReflectPass() && !mAllowReflectPass )
return;
if ( mOneFrameOnly && !mOnThisFrame )
return;
// Check requirements if the shader needs updating.
if ( mUpdateShader )
{
_checkRequirements();
// Clear the targets if we failed passing
// the requirements at this time.
if ( !mIsValid )
_cleanTargets( true );
}
// If we're not valid then we cannot render.
if ( !mIsValid )
return;
GFXDEBUGEVENT_SCOPE_EX( PostEffect_Process, ColorI::GREEN, avar("PostEffect: %s", getName()) );
preProcess_callback();
GFXTransformSaver saver;
// Set the textures.
for (U32 i = 0; i < NumTextures; i++)
{
if (mTextureType[i] == NormalTextureType)
_setupTexture(i, inOutTex, inTexViewport);
else if (mTextureType[i] == CubemapType)
_setupCubemapTexture(i, mCubemapTextures[i]);
else if (mTextureType[i] == CubemapArrayType)
_setupCubemapArrayTexture(i, mCubemapArrayTextures[i]);
}
2012-09-19 15:15:01 +00:00
_setupStateBlock( state ) ;
_setupTransforms();
bool clearTarget = false;
_setupTarget( state, &clearTarget );
if ( mTargetTex || mTargetDepthStencil )
{
const RectI &oldViewport = GFX->getViewport();
GFXTarget *oldTarget = GFX->getActiveRenderTarget();
2012-09-19 15:15:01 +00:00
GFX->pushActiveRenderTarget();
mTarget->attachTexture( GFXTextureTarget::Color0, mTargetTex );
// Set the right depth stencil target.
if ( !mTargetDepthStencil && mTargetTex.getWidthHeight() == GFX->getActiveRenderTarget()->getSize() )
mTarget->attachTexture( GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil );
else
mTarget->attachTexture( GFXTextureTarget::DepthStencil, mTargetDepthStencil );
// Set the render target but not its viewport. We'll do that below.
GFX->setActiveRenderTarget( mTarget, false );
if(mNamedTarget.isRegistered())
{
// Always use the name target's viewport, if available. It was set up in _setupTarget().
GFX->setViewport(mNamedTarget.getViewport());
}
else if(mTargetViewport == PFXTargetViewport_GFXViewport)
{
// Go with the current viewport as scaled against our render target.
const Point2I &oldTargetSize = oldTarget->getSize();
const Point2I &targetSize = mTarget->getSize();
Point2F scale(targetSize.x / F32(oldTargetSize.x), targetSize.y / F32(oldTargetSize.y));
GFX->setViewport( RectI( oldViewport.point.x*scale.x, oldViewport.point.y*scale.y, oldViewport.extent.x*scale.x, oldViewport.extent.y*scale.y ) );
}
else if(mTargetViewport == PFXTargetViewport_NamedInTexture0 && mActiveNamedTarget[0] && mActiveNamedTarget[0]->getTexture())
{
// Go with the first input texture, if it is named. Scale the named input texture's viewport to match our target
const Point3I &namedTargetSize = mActiveNamedTarget[0]->getTexture()->getSize();
const Point2I &targetSize = mTarget->getSize();
Point2F scale(targetSize.x / F32(namedTargetSize.x), targetSize.y / F32(namedTargetSize.y));
const RectI &viewport = mActiveNamedTarget[0]->getViewport();
GFX->setViewport( RectI( viewport.point.x*scale.x, viewport.point.y*scale.y, viewport.extent.x*scale.x, viewport.extent.y*scale.y ) );
}
else
{
// Default to using the whole target as the viewport
GFX->setViewport( RectI( Point2I::Zero, mTarget->getSize() ) );
}
2012-09-19 15:15:01 +00:00
}
if ( clearTarget )
GFX->clear( GFXClearTarget, mTargetClearColor, 1.f, 0 );
// Setup the shader and constants.
if ( mShader )
{
2014-11-08 08:42:01 +00:00
GFX->setShader( mShader );
2012-09-19 15:15:01 +00:00
_setupConstants( state );
GFX->setShaderConstBuffer( mShaderConsts );
}
else
GFX->setupGenericShaders();
2012-09-19 15:15:01 +00:00
Frustum frustum;
if ( state )
frustum = state->getCameraFrustum();
2012-09-19 15:15:01 +00:00
else
{
// If we don't have a scene state then setup
// a dummy frustum... you better not be depending
// on this being related to the camera in any way.
frustum = Frustum();
}
GFXVertexBufferHandle<PFXVertex> vb;
_updateScreenGeometry( frustum, &vb );
// Draw it.
GFX->setVertexBuffer( vb );
2016-03-20 11:52:11 +00:00
GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
2012-09-19 15:15:01 +00:00
// Allow PostEffecVis to hook in.
PFXVIS->onPFXProcessed( this );
if ( mTargetTex || mTargetDepthStencil )
{
mTarget->resolve();
GFX->popActiveRenderTarget();
}
else
{
// We wrote to the active back buffer, so release
// the current texture copy held by the manager.
//
// This ensures a new copy is made.
PFXMGR->releaseBackBufferTex();
}
// Return and release our target texture.
inOutTex = mTargetTex;
if ( !mNamedTarget.isRegistered() )
mTargetTex = NULL;
// Restore the transforms before the children
// are processed as it screws up the viewport.
saver.restore();
// Now process my children.
iterator i = begin();
for ( ; i != end(); i++ )
{
PostEffect *effect = static_cast<PostEffect*>(*i);
effect->process( state, inOutTex );
}
if ( mOneFrameOnly )
mOnThisFrame = false;
}
bool PostEffect::_setIsEnabled( void *object, const char *index, const char *data )
{
bool enabled = dAtob( data );
if ( enabled )
static_cast<PostEffect*>( object )->enable();
else
static_cast<PostEffect*>( object )->disable();
// Always return false from a protected field.
return false;
}
void PostEffect::enable()
{
// Don't add TexGen PostEffects to the PostEffectManager!
if ( mRenderTime == PFXTexGenOnDemand )
return;
// Ignore it if its already enabled.
if ( mEnabled )
return;
mEnabled = true;
// We cannot really enable the effect
// until its been registed.
if ( !isProperlyAdded() )
return;
// If the enable callback returns 'false' then
// leave the effect disabled.
if ( !onEnabled_callback() )
{
mEnabled = false;
return;
}
PFXMGR->_addEffect( this );
}
void PostEffect::disable()
{
if ( !mEnabled )
return;
mEnabled = false;
_cleanTargets( true );
if ( isProperlyAdded() )
{
PFXMGR->_removeEffect( this );
onDisabled_callback();
}
}
void PostEffect::reload()
{
// Reload the shader if we have one or mark it
// for updating when its processed next.
if ( mShader )
mShader->reload();
else
mUpdateShader = true;
// Null stateblock so it is reloaded.
mStateBlock = NULL;
// Call reload on any children
// this PostEffect may have.
for ( U32 i = 0; i < size(); i++ )
{
PostEffect *effect = (PostEffect*)(*this)[i];
effect->reload();
}
}
void PostEffect::setTexture( U32 index, const String &texFilePath )
{
// Set the new texture name.
mTextureName[index] = texFilePath;
mTexture[index].free();
2012-09-19 15:15:01 +00:00
// Skip empty stages or ones with variable or target names.
if ( texFilePath.isEmpty() ||
texFilePath[0] == '$' ||
texFilePath[0] == '#' )
return;
mTextureProfile[index] = (mTexSRGB[index])? &PostFxTextureSRGBProfile : &PostFxTextureProfile;
_setTexture(texFilePath, index);
mTextureType[index] = NormalTextureType;
}
void PostEffect::setTexture(U32 index, const GFXTexHandle& texHandle)
{
// Set the new texture name.
2021-11-16 18:40:22 +00:00
mTextureName[index] = StringTable->EmptyString();
mTexture[index].free();
// Skip empty stages or ones with variable or target names.
if (!texHandle.isValid())
return;
// Try to load the texture.
mTexture[index] = texHandle;
mTextureType[index] = NormalTextureType;
}
void PostEffect::setCubemapTexture(U32 index, const GFXCubemapHandle &cubemapHandle)
{
// Set the new texture name.
mCubemapTextures[index].free();
// Skip empty stages or ones with variable or target names.
if (cubemapHandle.isNull())
return;
// Try to load the texture.
mCubemapTextures[index] = cubemapHandle;
mTextureType[index] = CubemapType;
}
void PostEffect::setCubemapArrayTexture(U32 index, const GFXCubemapArrayHandle &cubemapArrayHandle)
{
// Set the new texture name.
mCubemapArrayTextures[index].free();
// Skip empty stages or ones with variable or target names.
if (cubemapArrayHandle.isNull())
return;
// Try to load the texture.
mCubemapArrayTextures[index] = cubemapArrayHandle;
mTextureType[index] = CubemapArrayType;
2012-09-19 15:15:01 +00:00
}
void PostEffect::setShaderConst( const String &name, const String &val )
{
PROFILE_SCOPE( PostEffect_SetShaderConst );
EffectConstTable::Iterator iter = mEffectConsts.find( name );
if ( iter == mEffectConsts.end() )
{
EffectConst *newConst = new EffectConst( name, val );
iter = mEffectConsts.insertUnique( name, newConst );
}
iter->value->set( val );
}
void PostEffect::setShaderConst(const String &name, const F32 &val)
{
PROFILE_SCOPE(PostEffect_SetShaderConst_Float);
EffectConstTable::Iterator iter = mEffectConsts.find(name);
if (iter == mEffectConsts.end())
{
EffectConst *newConst = new EffectConst(name, val);
iter = mEffectConsts.insertUnique(name, newConst);
}
iter->value->set(val);
}
void PostEffect::setShaderConst(const String& name, const int& val)
{
PROFILE_SCOPE(PostEffect_SetShaderConst_Int);
EffectConstTable::Iterator iter = mEffectConsts.find(name);
if (iter == mEffectConsts.end())
{
EffectConst* newConst = new EffectConst(name, val);
iter = mEffectConsts.insertUnique(name, newConst);
}
iter->value->set(val);
}
void PostEffect::setShaderConst(const String &name, const Point4F &val)
{
PROFILE_SCOPE(PostEffect_SetShaderConst_Point);
EffectConstTable::Iterator iter = mEffectConsts.find(name);
if (iter == mEffectConsts.end())
{
EffectConst *newConst = new EffectConst(name, val);
iter = mEffectConsts.insertUnique(name, newConst);
}
iter->value->set(val);
}
void PostEffect::setShaderConst(const String &name, const MatrixF &val)
{
PROFILE_SCOPE(PostEffect_SetShaderConst_Matrix);
EffectConstTable::Iterator iter = mEffectConsts.find(name);
if (iter == mEffectConsts.end())
{
EffectConst *newConst = new EffectConst(name, val);
iter = mEffectConsts.insertUnique(name, newConst);
}
iter->value->set(val);
}
void PostEffect::setShaderConst(const String &name, const Vector<Point4F> &val)
{
PROFILE_SCOPE(PostEffect_SetShaderConst_PointArray);
EffectConstTable::Iterator iter = mEffectConsts.find(name);
if (iter == mEffectConsts.end())
{
EffectConst *newConst = new EffectConst(name, val);
iter = mEffectConsts.insertUnique(name, newConst);
}
iter->value->set(val);
}
void PostEffect::setShaderConst(const String &name, const Vector<MatrixF> &val)
{
PROFILE_SCOPE(PostEffect_SetShaderConst_MatrixArray);
EffectConstTable::Iterator iter = mEffectConsts.find(name);
if (iter == mEffectConsts.end())
{
EffectConst *newConst = new EffectConst(name, val);
iter = mEffectConsts.insertUnique(name, newConst);
}
iter->value->set(val);
}
2012-09-19 15:15:01 +00:00
F32 PostEffect::getAspectRatio() const
{
const Point2I &rtSize = GFX->getActiveRenderTarget()->getSize();
return (F32)rtSize.x / (F32)rtSize.y;
}
void PostEffect::_checkRequirements()
{
// This meets requirements if its shader loads
// properly, we can find all the input textures,
// and its formats are supported.
mIsValid = false;
mUpdateShader = false;
mShader = NULL;
2014-11-08 08:42:01 +00:00
mShaderConsts = NULL;
EffectConstTable::Iterator iter = mEffectConsts.begin();
for ( ; iter != mEffectConsts.end(); iter++ )
{
iter->value->mDirty = true;
iter->value->mHandle = NULL;
}
2012-09-19 15:15:01 +00:00
// First make sure the target format is supported.
if ( mNamedTarget.isRegistered() )
{
Vector<GFXFormat> formats;
formats.push_back( mTargetFormat );
GFXFormat format = GFX->selectSupportedFormat( &PostFxTargetProfile,
formats,
true,
false,
false );
// If we didn't get our format out then its unsupported!
if ( format != mTargetFormat )
return;
}
// Gather macros specified on this PostEffect.
Vector<GFXShaderMacro> macros( mShaderMacros );
// Now check the input named targets and make sure
// they exist... else we're invalid.
for ( U32 i=0; i < NumTextures; i++ )
{
if (mTextureType[i] == NormalTextureType)
2012-09-19 15:15:01 +00:00
{
const String &texFilename = mTextureName[i];
if (texFilename.isNotEmpty() && texFilename[0] == '#')
2019-02-11 06:17:53 +00:00
{
NamedTexTarget *namedTarget = NamedTexTarget::find(texFilename.c_str() + 1);
if (!namedTarget)
{
return;
}
// Grab the macros for shader initialization.
namedTarget->getShaderMacros(&macros);
2019-02-11 06:17:53 +00:00
}
2012-09-19 15:15:01 +00:00
}
}
// Finally find and load the shader.
ShaderData *shaderData;
if ( Sim::findObject( mShaderName, shaderData ) )
if ( shaderData->getPixVersion() <= GFX->getPixelShaderVersion() )
mShader = shaderData->getShader( macros );
// If we didn't get a shader... we're done.
if ( !mShader )
return;
// If we got here then we're valid.
mIsValid = true;
}
bool PostEffect::dumpShaderDisassembly( String &outFilename ) const
{
String data;
if ( !mShader || !mShader->getDisassembly( data ) )
return false;
outFilename = FS::MakeUniquePath( "", "ShaderDisassembly", "txt" );
FileStream *fstream = FileStream::createAndOpen( outFilename, Torque::FS::File::Write );
if ( !fstream )
return false;
fstream->write( data );
fstream->close();
delete fstream;
return true;
}
SimSet* PostEffect::getSet() const
{
SimSet *set;
if ( !Sim::findObject( "PFXSet", set ) )
{
set = new SimSet();
set->registerObject( "PFXSet" );
Sim::getRootGroup()->addObject( set );
}
return set;
}
void PostEffect::setShaderMacro( const String &name, const String &value )
{
// Check to see if we already have this macro.
Vector<GFXShaderMacro>::iterator iter = mShaderMacros.begin();
for ( ; iter != mShaderMacros.end(); iter++ )
{
if ( iter->name == name )
{
if ( iter->value != value )
{
iter->value = value;
mUpdateShader = true;
}
return;
}
}
// Add a new macro.
mShaderMacros.increment();
mShaderMacros.last().name = name;
mShaderMacros.last().value = value;
mUpdateShader = true;
}
bool PostEffect::removeShaderMacro( const String &name )
{
Vector<GFXShaderMacro>::iterator iter = mShaderMacros.begin();
for ( ; iter != mShaderMacros.end(); iter++ )
{
if ( iter->name == name )
{
mShaderMacros.erase( iter );
mUpdateShader = true;
return true;
}
}
return false;
}
void PostEffect::clearShaderMacros()
{
if ( mShaderMacros.empty() )
return;
mShaderMacros.clear();
mUpdateShader = true;
}
GFXTextureObject* PostEffect::_getTargetTexture( U32 )
{
// A TexGen PostEffect will generate its texture now if it
// has not already.
if ( mRenderTime == PFXTexGenOnDemand &&
( !mTargetTex || mUpdateShader ) )
{
GFXTexHandle chainTex;
process( NULL, chainTex );
// TODO: We should add a conditional copy
// to a non-RT texture here to reduce the
// amount of non-swappable RTs in use.
}
return mTargetTex.getPointer();
}
DefineEngineMethod( PostEffect, reload, void, (),,
"Reloads the effect shader and textures." )
{
return object->reload();
}
DefineEngineMethod( PostEffect, enable, void, (),,
"Enables the effect." )
{
object->enable();
}
DefineEngineMethod( PostEffect, disable, void, (),,
"Disables the effect." )
{
object->disable();
}
DefineEngineMethod( PostEffect, toggle, bool, (),,
"Toggles the effect between enabled / disabled.\n"
"@return True if effect is enabled." )
{
if ( object->isEnabled() )
object->disable();
else
object->enable();
return object->isEnabled();
}
DefineEngineMethod( PostEffect, isEnabled, bool, (),,
"@return True if the effect is enabled." )
{
return object->isEnabled();
}
DefineEngineMethod( PostEffect, setTexture, void, ( S32 index, const char *filePath ),,
"This is used to set the texture file and load the texture on a running effect. "
"If the texture file is not different from the current file nothing is changed. If "
"the texture cannot be found a null texture is assigned.\n"
"@param index The texture stage index.\n"
"@param filePath The file name of the texture to set.\n" )
{
if ( index > -1 && index < PostEffect::NumTextures )
object->setTexture( index, filePath );
}
DefineEngineMethod( PostEffect, setShaderConst, void, ( const char* name, const char* value ),,
"Sets the value of a uniform defined in the shader. This will usually "
"be called within the setShaderConsts callback. Array type constants are "
"not supported.\n"
"@param name Name of the constanst, prefixed with '$'.\n"
"@param value Value to set, space seperate values with more than one element.\n"
"@tsexample\n"
"function MyPfx::setShaderConsts( %this )\n"
"{\n"
" // example float4 uniform\n"
" %this.setShaderConst( \"$colorMod\", \"1.0 0.9 1.0 1.0\" );\n"
" // example float1 uniform\n"
" %this.setShaderConst( \"$strength\", \"3.0\" );\n"
" // example integer uniform\n"
" %this.setShaderConst( \"$loops\", \"5\" );"
"}\n"
"@endtsexample" )
{
object->setShaderConst( name, value );
}
DefineEngineMethod( PostEffect, getAspectRatio, F32, (),,
"@return Width over height of the backbuffer." )
{
return object->getAspectRatio();
}
DefineEngineMethod( PostEffect, dumpShaderDisassembly, String, (),,
"Dumps this PostEffect shader's disassembly to a temporary text file.\n"
"@return Full path to the dumped file or an empty string if failed." )
{
String fileName;
object->dumpShaderDisassembly( fileName );
return fileName;
}
DefineEngineMethod( PostEffect, setShaderMacro, void, ( const char* key, const char* value ), ( "" ),
"Adds a macro to the effect's shader or sets an existing one's value. "
"This will usually be called within the onAdd or preProcess callback.\n"
"@param key lval of the macro."
"@param value rval of the macro, or may be empty."
"@tsexample\n"
"function MyPfx::onAdd( %this )\n"
"{\n"
" %this.setShaderMacro( \"NUM_SAMPLES\", \"10\" );\n"
" %this.setShaderMacro( \"HIGH_QUALITY_MODE\" );\n"
" \n"
" // In the shader looks like... \n"
" // #define NUM_SAMPLES 10\n"
" // #define HIGH_QUALITY_MODE\n"
"}\n"
"@endtsexample" )
{
object->setShaderMacro( key, value );
}
DefineEngineMethod( PostEffect, removeShaderMacro, void, ( const char* key ),,
"Remove a shader macro. This will usually be called within the preProcess callback.\n"
"@param key Macro to remove." )
{
object->removeShaderMacro( key );
}
DefineEngineMethod( PostEffect, clearShaderMacros, void, (),,
"Remove all shader macros." )
{
object->clearShaderMacros();
}
DefineEngineFunction( dumpRandomNormalMap, void, (),,
"Creates a 64x64 normal map texture filled with noise. The texture is saved "
"to randNormTex.png in the location of the game executable.\n\n"
"@ingroup GFX")
{
GFXTexHandle tex;
tex.set( 64, 64, GFXFormatR8G8B8A8, &GFXTexturePersistentProfile, "" );
2012-09-19 15:15:01 +00:00
GFXLockedRect *rect = tex.lock();
U8 *f = rect->bits;
for ( U32 i = 0; i < 64*64; i++, f += 4 )
{
VectorF vec;
vec.x = mRandF( -1.0f, 1.0f );
vec.y = mRandF( -1.0f, 1.0f );
vec.z = mRandF( -1.0f, 1.0f );
vec.normalizeSafe();
f[0] = U8_MAX * ( ( 1.0f + vec.x ) * 0.5f );
f[1] = U8_MAX * ( ( 1.0f + vec.y ) * 0.5f );
f[2] = U8_MAX * ( ( 1.0f + vec.z ) * 0.5f );
f[3] = U8_MAX;
}
tex.unlock();
String path = Torque::FS::MakeUniquePath( "", "randNormTex", "png" );
tex->dumpToDisk( "png", path );
}