From b32e7688c2a2a8af3831fdf01719fbebe387e59d Mon Sep 17 00:00:00 2001 From: DavidWyand-GG Date: Tue, 9 Apr 2013 15:19:18 -0400 Subject: [PATCH] Side by side rendering - Side by side rendering implemented throughout the graphics pipeline. - New GuiTSCtrl renderStyle property is set to "stereo side by side" to activate. - You set an IDisplayDevice on the GameConnection to define any vertical FOV, projection offset, and stereo eye offset properties required for the stereo rendering (no display device included with this commit). - Full and Empty templates updated with correct scripts and shaders. --- Engine/source/T3D/gameBase/gameConnection.cpp | 8 +++ Engine/source/T3D/gameBase/gameConnection.h | 7 ++ Engine/source/T3D/gameFunctions.cpp | 43 ++++++++++- Engine/source/T3D/lightFlareData.cpp | 12 +++- Engine/source/gfx/gfxDevice.cpp | 3 + Engine/source/gfx/gfxDevice.h | 34 +++++++++ Engine/source/gui/3d/guiTSControl.cpp | 71 ++++++++++++++++--- Engine/source/gui/3d/guiTSControl.h | 22 ++++-- .../advanced/advancedLightBinManager.cpp | 35 ++++++++- .../advanced/advancedLightBinManager.h | 7 +- .../advanced/advancedLightManager.cpp | 2 +- Engine/source/math/util/frustum.cpp | 18 +++++ Engine/source/math/util/frustum.h | 20 ++++++ .../source/platform/output/IDisplayDevice.h | 44 ++++++++++++ Engine/source/postFx/postEffect.cpp | 37 ++++++++-- Engine/source/scene/sceneManager.cpp | 65 ++++++++++++++++- Engine/source/scene/sceneManager.h | 12 ++++ Engine/source/scene/sceneRenderState.cpp | 4 +- Engine/source/scene/sceneRenderState.h | 23 ++++++ .../core/scripts/client/postFx/lightRay.cs | 5 +- .../Empty/game/shaders/common/hlslStructs.h | 8 +++ .../lighting/advanced/farFrustumQuadV.hlsl | 4 +- .../lighting/advanced/vectorLightP.hlsl | 2 +- .../core/scripts/client/postFx/lightRay.cs | 5 +- .../Full/game/shaders/common/hlslStructs.h | 8 +++ .../lighting/advanced/farFrustumQuadV.hlsl | 4 +- .../lighting/advanced/vectorLightP.hlsl | 2 +- Tools/projectGenerator/modules/core.inc | 1 + 28 files changed, 465 insertions(+), 41 deletions(-) create mode 100644 Engine/source/platform/output/IDisplayDevice.h diff --git a/Engine/source/T3D/gameBase/gameConnection.cpp b/Engine/source/T3D/gameBase/gameConnection.cpp index e3f91e160..ede671d0e 100644 --- a/Engine/source/T3D/gameBase/gameConnection.cpp +++ b/Engine/source/T3D/gameBase/gameConnection.cpp @@ -219,6 +219,7 @@ GameConnection::GameConnection() // first person mFirstPerson = true; mUpdateFirstPerson = false; + clearDisplayDevice(); } GameConnection::~GameConnection() @@ -1729,6 +1730,13 @@ DefineEngineMethod( GameConnection, setControlObject, bool, (GameBase* ctrlObj), return true; } +DefineEngineMethod( GameConnection, clearDisplayDevice, void, (),, + "@brief Clear any display device.\n\n" + "A display device may define a number of properties that are used during rendering.\n\n") +{ + object->clearDisplayDevice(); +} + DefineEngineMethod( GameConnection, getControlObject, GameBase*, (),, "@brief On the server, returns the object that the client is controlling." "By default the control object is an instance of the Player class, but can also be an instance " diff --git a/Engine/source/T3D/gameBase/gameConnection.h b/Engine/source/T3D/gameBase/gameConnection.h index fc25ba25a..324a9c989 100644 --- a/Engine/source/T3D/gameBase/gameConnection.h +++ b/Engine/source/T3D/gameBase/gameConnection.h @@ -45,6 +45,7 @@ enum GameConnectionConstants DataBlockQueueCount = 16 }; +class IDisplayDevice; class SFXProfile; class MatrixF; class MatrixF; @@ -86,6 +87,8 @@ private: F32 mCameraFov; ///< Current camera fov (in degrees). F32 mCameraPos; ///< Current camera pos (0-1). F32 mCameraSpeed; ///< Camera in/out speed. + + IDisplayDevice* mDisplayDevice; ///< Optional client display device that imposes rendering properties. /// @} public: @@ -263,6 +266,10 @@ public: void setFirstPerson(bool firstPerson); + bool hasDisplayDevice() const { return mDisplayDevice != NULL; } + const IDisplayDevice* getDisplayDevice() const { return mDisplayDevice; } + void setDisplayDevice(IDisplayDevice* display) { mDisplayDevice = display; } + void clearDisplayDevice() { mDisplayDevice = NULL; } /// @} void detectLag(); diff --git a/Engine/source/T3D/gameFunctions.cpp b/Engine/source/T3D/gameFunctions.cpp index c3b3967a3..557607a44 100644 --- a/Engine/source/T3D/gameFunctions.cpp +++ b/Engine/source/T3D/gameFunctions.cpp @@ -32,7 +32,7 @@ #include "math/mEase.h" #include "core/module.h" #include "console/engineAPI.h" - +#include "platform/output/IDisplayDevice.h" static void RegisterGameFunctions(); static void Process3D(); @@ -82,6 +82,8 @@ static S32 gEaseBack = Ease::Back; static S32 gEaseBounce = Ease::Bounce; +extern bool gEditingMission; + extern void ShowInit(); //------------------------------------------------------------------------------ @@ -354,9 +356,44 @@ bool GameProcessCameraQuery(CameraQuery *query) sVisDistanceScale = mClampF( sVisDistanceScale, 0.01f, 1.0f ); query->farPlane = gClientSceneGraph->getVisibleDistance() * sVisDistanceScale; - F32 cameraFov; - if(!connection->getControlCameraFov(&cameraFov)) + // Provide some default values + query->projectionOffset = Point2F::Zero; + query->eyeOffset = Point3F::Zero; + + F32 cameraFov = 0.0f; + bool fovSet = false; + + // Try to use the connection's display deivce, if any, but only if the editor + // is not open + if(!gEditingMission && connection->hasDisplayDevice()) + { + const IDisplayDevice* display = connection->getDisplayDevice(); + + // The connection's display device may want to set the FOV + if(display->providesYFOV()) + { + cameraFov = mRadToDeg(display->getYFOV()); + fovSet = true; + } + + // The connection's display device may want to set the projection offset + if(display->providesProjectionOffset()) + { + query->projectionOffset = display->getProjectionOffset(); + } + + // The connection's display device may want to set the eye offset + if(display->providesEyeOffset()) + { + query->eyeOffset = display->getEyeOffset(); + } + } + + // Use the connection's FOV settings if requried + if(!fovSet && !connection->getControlCameraFov(&cameraFov)) + { return false; + } query->fov = mDegToRad(cameraFov); return true; diff --git a/Engine/source/T3D/lightFlareData.cpp b/Engine/source/T3D/lightFlareData.cpp index 8dfd9badc..5a3e0e322 100644 --- a/Engine/source/T3D/lightFlareData.cpp +++ b/Engine/source/T3D/lightFlareData.cpp @@ -278,7 +278,11 @@ bool LightFlareData::_testVisibility(const SceneRenderState *state, LightFlareSt // the last result. const Point3F &lightPos = flareState->lightMat.getPosition(); const RectI &viewport = GFX->getViewport(); - bool onScreen = MathUtils::mProjectWorldToScreen( lightPos, outLightPosSS, viewport, GFX->getWorldMatrix(), state->getSceneManager()->getNonClipProjection() ); + MatrixF projMatrix; + state->getFrustum().getProjectionMatrix(&projMatrix); + if( state->isReflectPass() ) + projMatrix = state->getSceneManager()->getNonClipProjection(); + bool onScreen = MathUtils::mProjectWorldToScreen( lightPos, outLightPosSS, viewport, GFX->getWorldMatrix(), projMatrix ); // It is onscreen, so raycast as a simple occlusion test. const LightInfo *lightInfo = flareState->lightInfo; @@ -452,13 +456,17 @@ void LightFlareData::prepRender( SceneRenderState *state, LightFlareState *flare Point3F oneOverViewportExtent( 1.0f / (F32)viewport.extent.x, 1.0f / (F32)viewport.extent.y, 0.0f ); // Really convert it to screen space. + lightPosSS.x -= viewport.point.x; lightPosSS.y -= viewport.point.y; lightPosSS *= oneOverViewportExtent; lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One; lightPosSS.y = -lightPosSS.y; lightPosSS.z = 0.0f; - Point3F flareVec( -lightPosSS ); + // Take any projection offset into account so that the point where the flare's + // elements converge is at the 'eye' point rather than the center of the viewport. + const Point2F& projOffset = state->getFrustum().getProjectionOffset(); + Point3F flareVec( -lightPosSS + Point3F(projOffset.x, projOffset.y, 0.0f) ); const F32 flareLength = flareVec.len(); if ( flareLength > 0.0f ) flareVec *= 1.0f / flareLength; diff --git a/Engine/source/gfx/gfxDevice.cpp b/Engine/source/gfx/gfxDevice.cpp index 4f5cae5d7..9e57bcc81 100644 --- a/Engine/source/gfx/gfxDevice.cpp +++ b/Engine/source/gfx/gfxDevice.cpp @@ -159,6 +159,9 @@ GFXDevice::GFXDevice() // misc mAllowRender = true; + mCurrentRenderStyle = RS_Standard; + mCurrentProjectionOffset = Point2F::Zero; + mStereoEyeOffset = Point3F::Zero; mCanCurrentlyRender = false; mInitialized = false; diff --git a/Engine/source/gfx/gfxDevice.h b/Engine/source/gfx/gfxDevice.h index ec9fd6797..e153d02c5 100644 --- a/Engine/source/gfx/gfxDevice.h +++ b/Engine/source/gfx/gfxDevice.h @@ -231,6 +231,13 @@ private: //-------------------------------------------------------------------------- // Core GFX interface //-------------------------------------------------------------------------- +public: + enum GFXDeviceRenderStyles + { + RS_Standard = 0, + RS_StereoSideBySide = (1<<0), + }; + private: /// Adapter for this device. @@ -254,6 +261,15 @@ protected: /// Set if we're in a mode where we want rendering to occur. bool mAllowRender; + /// The style of rendering that is to be performed, based on GFXDeviceRenderStyles + U32 mCurrentRenderStyle; + + /// The current projection offset. May be used during side-by-side rendering, for example. + Point2F mCurrentProjectionOffset; + + /// Eye offset used when using a stereo rendering style + Point3F mStereoEyeOffset; + /// This will allow querying to see if a device is initialized and ready to /// have operations performed on it. bool mInitialized; @@ -285,6 +301,24 @@ public: inline bool allowRender() const { return mAllowRender; } + /// Retrieve the current rendering style based on GFXDeviceRenderStyles + U32 getCurrentRenderStyle() const { return mCurrentRenderStyle; } + + /// Set the current rendering style, based on GFXDeviceRenderStyles + void setCurrentRenderStyle(U32 style) { mCurrentRenderStyle = style; } + + /// Set the current projection offset used during stereo rendering + const Point2F& getCurrentProjectionOffset() { return mCurrentProjectionOffset; } + + /// Get the current projection offset used during stereo rendering + void setCurrentProjectionOffset(const Point2F& offset) { mCurrentProjectionOffset = offset; } + + /// Get the current eye offset used during stereo rendering + const Point3F& getStereoEyeOffset() { return mStereoEyeOffset; } + + /// Set the current eye offset used during stereo rendering + void setStereoEyeOffset(const Point3F& offset) { mStereoEyeOffset = offset; } + GFXCardProfiler* getCardProfiler() const { return mCardProfiler; } /// Returns active graphics adapter type. diff --git a/Engine/source/gui/3d/guiTSControl.cpp b/Engine/source/gui/3d/guiTSControl.cpp index e1d2ecc0b..95dc960c3 100644 --- a/Engine/source/gui/3d/guiTSControl.cpp +++ b/Engine/source/gui/3d/guiTSControl.cpp @@ -53,6 +53,13 @@ ConsoleDocClass( GuiTSCtrl, U32 GuiTSCtrl::smFrameCount = 0; Vector GuiTSCtrl::smAwakeTSCtrls; +ImplementEnumType( GuiTSRenderStyles, + "Style of rendering for a GuiTSCtrl.\n\n" + "@ingroup Gui3D" ) + { GuiTSCtrl::RenderStyleStandard, "standard" }, + { GuiTSCtrl::RenderStyleStereoSideBySide, "stereo side by side" }, +EndImplementEnumType; + //----------------------------------------------------------------------------- @@ -131,6 +138,8 @@ GuiTSCtrl::GuiTSCtrl() mForceFOV = 0; mReflectPriority = 1.0f; + mRenderStyle = RenderStyleStandard; + mSaveModelview.identity(); mSaveProjection.identity(); mSaveViewport.set( 0, 0, 10, 10 ); @@ -142,6 +151,9 @@ GuiTSCtrl::GuiTSCtrl() mLastCameraQuery.farPlane = 10.0f; mLastCameraQuery.nearPlane = 0.01f; + mLastCameraQuery.projectionOffset = Point2F::Zero; + mLastCameraQuery.eyeOffset = Point3F::Zero; + mLastCameraQuery.ortho = false; } @@ -165,6 +177,9 @@ void GuiTSCtrl::initPersistFields() "The reflect update priorities of all visible GuiTSCtrls are added together and each control is assigned " "a share of the per-frame reflection update time according to its percentage of the total priority value." ); + addField("renderStyle", TYPEID< RenderStyles >(), Offset(mRenderStyle, GuiTSCtrl), + "Indicates how this control should render its contents." ); + endGroup( "Rendering" ); Parent::initPersistFields(); @@ -256,7 +271,9 @@ F32 GuiTSCtrl::calculateViewDistance(F32 radius) F32 fov = mLastCameraQuery.fov; F32 wwidth; F32 wheight; - F32 aspectRatio = F32(getWidth()) / F32(getHeight()); + F32 renderWidth = (mRenderStyle == RenderStyleStereoSideBySide) ? F32(getWidth())*0.5f : F32(getWidth()); + F32 renderHeight = F32(getHeight()); + F32 aspectRatio = renderWidth / renderHeight; // Use the FOV to calculate the viewport height scale // then generate the width scale from the aspect ratio. @@ -321,10 +338,27 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect) mLastCameraQuery.cameraMatrix.mul(rotMat); } + // Set up the appropriate render style + U32 prevRenderStyle = GFX->getCurrentRenderStyle(); + Point2F prevProjectionOffset = GFX->getCurrentProjectionOffset(); + Point3F prevEyeOffset = GFX->getStereoEyeOffset(); + if(mRenderStyle == RenderStyleStereoSideBySide) + { + GFX->setCurrentRenderStyle(GFXDevice::RS_StereoSideBySide); + GFX->setCurrentProjectionOffset(mLastCameraQuery.projectionOffset); + GFX->setStereoEyeOffset(mLastCameraQuery.eyeOffset); + } + else + { + GFX->setCurrentRenderStyle(GFXDevice::RS_Standard); + } + // set up the camera and viewport stuff: F32 wwidth; F32 wheight; - F32 aspectRatio = F32(getWidth()) / F32(getHeight()); + F32 renderWidth = (mRenderStyle == RenderStyleStereoSideBySide) ? F32(getWidth())*0.5f : F32(getWidth()); + F32 renderHeight = F32(getHeight()); + F32 aspectRatio = renderWidth / renderHeight; // Use the FOV to calculate the viewport height scale // then generate the width scale from the aspect ratio. @@ -339,16 +373,28 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect) wwidth = aspectRatio * wheight; } - F32 hscale = wwidth * 2.0f / F32(getWidth()); - F32 vscale = wheight * 2.0f / F32(getHeight()); - - F32 left = (updateRect.point.x - offset.x) * hscale - wwidth; - F32 right = (updateRect.point.x + updateRect.extent.x - offset.x) * hscale - wwidth; - F32 top = wheight - vscale * (updateRect.point.y - offset.y); - F32 bottom = wheight - vscale * (updateRect.point.y + updateRect.extent.y - offset.y); + F32 hscale = wwidth * 2.0f / renderWidth; + F32 vscale = wheight * 2.0f / renderHeight; Frustum frustum; - frustum.set( mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane ); + if(mRenderStyle == RenderStyleStereoSideBySide) + { + F32 left = 0.0f * hscale - wwidth; + F32 right = renderWidth * hscale - wwidth; + F32 top = wheight - vscale * 0.0f; + F32 bottom = wheight - vscale * renderHeight; + + frustum.set( mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane ); + } + else + { + F32 left = (updateRect.point.x - offset.x) * hscale - wwidth; + F32 right = (updateRect.point.x + updateRect.extent.x - offset.x) * hscale - wwidth; + F32 top = wheight - vscale * (updateRect.point.y - offset.y); + F32 bottom = wheight - vscale * (updateRect.point.y + updateRect.extent.y - offset.y); + + frustum.set( mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane ); + } // Manipulate the frustum for tiled screenshots const bool screenShotMode = gScreenShot && gScreenShot->isPending(); @@ -412,6 +458,11 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect) // we begin rendering the child controls. saver.restore(); + // Restore the render style and any stereo parameters + GFX->setCurrentRenderStyle(prevRenderStyle); + GFX->setCurrentProjectionOffset(prevProjectionOffset); + GFX->setStereoEyeOffset(prevEyeOffset); + // Allow subclasses to render 2D elements. GFX->setClipRect(updateRect); renderGui( offset, updateRect ); diff --git a/Engine/source/gui/3d/guiTSControl.h b/Engine/source/gui/3d/guiTSControl.h index 809e49c78..cd438c526 100644 --- a/Engine/source/gui/3d/guiTSControl.h +++ b/Engine/source/gui/3d/guiTSControl.h @@ -36,6 +36,8 @@ struct CameraQuery F32 nearPlane; F32 farPlane; F32 fov; + Point2F projectionOffset; + Point3F eyeOffset; bool ortho; MatrixF cameraMatrix; }; @@ -45,12 +47,17 @@ class GuiTSCtrl : public GuiContainer { typedef GuiContainer Parent; +public: + enum RenderStyles { + RenderStyleStandard = 0, + RenderStyleStereoSideBySide = (1<<0), + }; + +protected: static U32 smFrameCount; F32 mCameraZRot; F32 mForceFOV; -protected: - /// A list of GuiTSCtrl which are awake and /// most likely rendering. static Vector smAwakeTSCtrls; @@ -59,8 +66,11 @@ protected: /// update timeslice for this viewport to get. F32 mReflectPriority; - F32 mOrthoWidth; - F32 mOrthoHeight; + /// The current render type + U32 mRenderStyle; + + F32 mOrthoWidth; + F32 mOrthoHeight; MatrixF mSaveModelview; MatrixF mSaveProjection; @@ -150,4 +160,8 @@ public: DECLARE_DESCRIPTION( "Abstract base class for controls that render a 3D viewport." ); }; +typedef GuiTSCtrl::RenderStyles GuiTSRenderStyles; + +DefineEnumType( GuiTSRenderStyles ); + #endif // _GUITSCONTROL_H_ diff --git a/Engine/source/lighting/advanced/advancedLightBinManager.cpp b/Engine/source/lighting/advanced/advancedLightBinManager.cpp index 1aeef56ff..069f0e762 100644 --- a/Engine/source/lighting/advanced/advancedLightBinManager.cpp +++ b/Engine/source/lighting/advanced/advancedLightBinManager.cpp @@ -41,7 +41,6 @@ #include "math/util/matrixSet.h" #include "console/consoleTypes.h" - const RenderInstType AdvancedLightBinManager::RIT_LightInfo( "LightInfo" ); const String AdvancedLightBinManager::smBufferName( "lightinfo" ); @@ -49,7 +48,6 @@ ShadowFilterMode AdvancedLightBinManager::smShadowFilterMode = ShadowFilterMode_ bool AdvancedLightBinManager::smPSSMDebugRender = false; bool AdvancedLightBinManager::smUseSSAOMask = false; - ImplementEnumType( ShadowFilterMode, "The shadow filtering modes for Advanced Lighting shadows.\n" "@ingroup AdvancedLighting" ) @@ -221,7 +219,7 @@ void AdvancedLightBinManager::addLight( LightInfo *light ) curBin.push_back( lEntry ); } -void AdvancedLightBinManager::clear() +void AdvancedLightBinManager::clearAllLights() { Con::setIntVariable("lightMetrics::activeLights", mLightBin.size()); Con::setIntVariable("lightMetrics::culledLights", mNumLightsCulled); @@ -454,6 +452,33 @@ void AdvancedLightBinManager::_setupPerFrameParameters( const SceneRenderState * const Point3F *wsFrustumPoints = frustum.getPoints(); const Point3F& cameraPos = frustum.getPosition(); + // Perform a camera offset. We need to manually perform this offset on the sun (or vector) light'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; + } + // Now build the quad for drawing full-screen vector light // passes.... this is a volatile VB and updates every frame. FarFrustumQuadVert verts[4]; @@ -461,18 +486,22 @@ void AdvancedLightBinManager::_setupPerFrameParameters( const SceneRenderState * verts[0].point.set( wsFrustumPoints[Frustum::FarBottomLeft] - cameraPos ); invCam.mulP( wsFrustumPoints[Frustum::FarBottomLeft], &verts[0].normal ); verts[0].texCoord.set( -1.0, -1.0 ); + verts[0].tangent.set(wsFrustumPoints[Frustum::FarBottomLeft] - cameraOffsetPos); verts[1].point.set( wsFrustumPoints[Frustum::FarTopLeft] - cameraPos ); invCam.mulP( wsFrustumPoints[Frustum::FarTopLeft], &verts[1].normal ); verts[1].texCoord.set( -1.0, 1.0 ); + verts[1].tangent.set(wsFrustumPoints[Frustum::FarTopLeft] - cameraOffsetPos); verts[2].point.set( wsFrustumPoints[Frustum::FarTopRight] - cameraPos ); invCam.mulP( wsFrustumPoints[Frustum::FarTopRight], &verts[2].normal ); verts[2].texCoord.set( 1.0, 1.0 ); + verts[2].tangent.set(wsFrustumPoints[Frustum::FarTopRight] - cameraOffsetPos); verts[3].point.set( wsFrustumPoints[Frustum::FarBottomRight] - cameraPos ); invCam.mulP( wsFrustumPoints[Frustum::FarBottomRight], &verts[3].normal ); verts[3].texCoord.set( 1.0, -1.0 ); + verts[3].tangent.set(wsFrustumPoints[Frustum::FarBottomRight] - cameraOffsetPos); } mFarFrustumQuadVerts.set( GFX, 4 ); dMemcpy( mFarFrustumQuadVerts.lock(), verts, sizeof( verts ) ); diff --git a/Engine/source/lighting/advanced/advancedLightBinManager.h b/Engine/source/lighting/advanced/advancedLightBinManager.h index a8aa20659..ee2064885 100644 --- a/Engine/source/lighting/advanced/advancedLightBinManager.h +++ b/Engine/source/lighting/advanced/advancedLightBinManager.h @@ -107,12 +107,15 @@ public: // RenderBinManager virtual void render(SceneRenderState *); - virtual void clear(); + virtual void clear() {} virtual void sort() {} // Add a light to the bins void addLight( LightInfo *light ); + // Clear all lights from the bins + void clearAllLights(); + virtual bool setTargetSize(const Point2I &newTargetSize); // ConsoleObject interface @@ -220,7 +223,7 @@ protected: AdvancedLightBufferConditioner *mConditioner; - typedef GFXVertexPNT FarFrustumQuadVert; + typedef GFXVertexPNTT FarFrustumQuadVert; GFXVertexBufferHandle mFarFrustumQuadVerts; diff --git a/Engine/source/lighting/advanced/advancedLightManager.cpp b/Engine/source/lighting/advanced/advancedLightManager.cpp index 36be8db78..9a911acfb 100644 --- a/Engine/source/lighting/advanced/advancedLightManager.cpp +++ b/Engine/source/lighting/advanced/advancedLightManager.cpp @@ -438,7 +438,7 @@ void AdvancedLightManager::unregisterAllLights() Parent::unregisterAllLights(); if ( mLightBinManager ) - mLightBinManager->clear(); + mLightBinManager->clearAllLights(); } bool AdvancedLightManager::setTextureStage( const SceneData &sgData, diff --git a/Engine/source/math/util/frustum.cpp b/Engine/source/math/util/frustum.cpp index e5c6fd43b..7cff16e4c 100644 --- a/Engine/source/math/util/frustum.cpp +++ b/Engine/source/math/util/frustum.cpp @@ -76,6 +76,9 @@ Frustum::Frustum( bool isOrtho, mNumTiles = 1; mCurrTile.set(0,0); mTileOverlap.set(0.0f, 0.0f); + + mProjectionOffset.zero(); + mProjectionOffsetMatrix.identity(); } //----------------------------------------------------------------------------- @@ -433,12 +436,27 @@ void Frustum::mulL( const MatrixF& mat ) //----------------------------------------------------------------------------- +void Frustum::setProjectionOffset(const Point2F& offsetMat) +{ + mProjectionOffset = offsetMat; + mProjectionOffsetMatrix.identity(); + mProjectionOffsetMatrix.setPosition(Point3F(mProjectionOffset.x, mProjectionOffset.y, 0.0f)); +} + +//----------------------------------------------------------------------------- + void Frustum::getProjectionMatrix( MatrixF *proj, bool gfxRotate ) const { if (mIsOrtho) + { MathUtils::makeOrthoProjection(proj, mNearLeft, mNearRight, mNearTop, mNearBottom, mNearDist, mFarDist, gfxRotate); + proj->mulL(mProjectionOffsetMatrix); + } else + { MathUtils::makeProjection(proj, mNearLeft, mNearRight, mNearTop, mNearBottom, mNearDist, mFarDist, gfxRotate); + proj->mulL(mProjectionOffsetMatrix); + } } //----------------------------------------------------------------------------- diff --git a/Engine/source/math/util/frustum.h b/Engine/source/math/util/frustum.h index 4a756225c..869abcf47 100644 --- a/Engine/source/math/util/frustum.h +++ b/Engine/source/math/util/frustum.h @@ -246,6 +246,12 @@ class Frustum : public PolyhedronImpl< FrustumData > /// @} + /// Offset used for projection matrix calculations + Point2F mProjectionOffset; + + /// The calculated projection offset matrix + MatrixF mProjectionOffsetMatrix; + public: /// @name Constructors @@ -403,9 +409,23 @@ class Frustum : public PolyhedronImpl< FrustumData > /// points typically used for early rejection. const Box3F& getBounds() const { _update(); return mBounds; } + /// Get the offset used when calculating the projection matrix + const Point2F& getProjectionOffset() const { return mProjectionOffset; } + + /// Get the offset matrix used when calculating the projection matrix + const MatrixF& getProjectionOffsetMatrix() const { return mProjectionOffsetMatrix; } + + /// Set the offset used when calculating the projection matrix + void setProjectionOffset(const Point2F& offsetMat); + + /// Clear any offset used when calculating the projection matrix + void clearProjectionOffset() { mProjectionOffset.zero(); mProjectionOffsetMatrix.identity(); } + /// Generates a projection matrix from the frustum. void getProjectionMatrix( MatrixF *proj, bool gfxRotate=true ) const; + /// Will update the frustum if it is dirty + void update() { _update(); } /// @} /// @name Culling diff --git a/Engine/source/platform/output/IDisplayDevice.h b/Engine/source/platform/output/IDisplayDevice.h new file mode 100644 index 000000000..80e036a01 --- /dev/null +++ b/Engine/source/platform/output/IDisplayDevice.h @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +#ifndef _IDISPLAYDEVICE_H_ +#define _IDISPLAYDEVICE_H_ + +#include "console/consoleTypes.h" + +// Defines a custom display device that requires particular rendering settings +// in order for a scene to display correctly. + +class IDisplayDevice +{ +public: + virtual bool providesYFOV() const = 0; + virtual F32 getYFOV() const = 0; + + virtual bool providesEyeOffset() const = 0; + virtual const Point3F& getEyeOffset() const = 0; + + virtual bool providesProjectionOffset() const = 0; + virtual const Point2F& getProjectionOffset() const = 0; +}; + +#endif // _IDISPLAYDEVICE_H_ diff --git a/Engine/source/postFx/postEffect.cpp b/Engine/source/postFx/postEffect.cpp index dce0c9447..1dcfae1ed 100644 --- a/Engine/source/postFx/postEffect.cpp +++ b/Engine/source/postFx/postEffect.cpp @@ -435,26 +435,53 @@ void PostEffect::_updateScreenGeometry( const Frustum &frustum, 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; + } + PFXVertex *vert = outVB->lock(); vert->point.set( -1.0, -1.0, 0.0 ); vert->texCoord.set( 0.0f, 1.0f ); - vert->wsEyeRay = frustumPoints[Frustum::FarBottomLeft] - cameraPos; + vert->wsEyeRay = frustumPoints[Frustum::FarBottomLeft] - cameraOffsetPos; vert++; vert->point.set( -1.0, 1.0, 0.0 ); vert->texCoord.set( 0.0f, 0.0f ); - vert->wsEyeRay = frustumPoints[Frustum::FarTopLeft] - cameraPos; + vert->wsEyeRay = frustumPoints[Frustum::FarTopLeft] - cameraOffsetPos; vert++; vert->point.set( 1.0, 1.0, 0.0 ); vert->texCoord.set( 1.0f, 0.0f ); - vert->wsEyeRay = frustumPoints[Frustum::FarTopRight] - cameraPos; + vert->wsEyeRay = frustumPoints[Frustum::FarTopRight] - cameraOffsetPos; vert++; vert->point.set( 1.0, -1.0, 0.0 ); vert->texCoord.set( 1.0f, 1.0f ); - vert->wsEyeRay = frustumPoints[Frustum::FarBottomRight] - cameraPos; + vert->wsEyeRay = frustumPoints[Frustum::FarBottomRight] - cameraOffsetPos; vert++; outVB->unlock(); @@ -710,6 +737,8 @@ void PostEffect::_setupConstants( const SceneRenderState *state ) MathUtils::mProjectWorldToScreen( lightPos, &sunPos, GFX->getViewport(), tmp, proj ); // And normalize it to the 0 to 1 range. + sunPos.x -= (F32)GFX->getViewport().point.x; + sunPos.y -= (F32)GFX->getViewport().point.y; sunPos.x /= (F32)GFX->getViewport().extent.x; sunPos.y /= (F32)GFX->getViewport().extent.y; diff --git a/Engine/source/scene/sceneManager.cpp b/Engine/source/scene/sceneManager.cpp index 3e323efdb..5210d5345 100644 --- a/Engine/source/scene/sceneManager.cpp +++ b/Engine/source/scene/sceneManager.cpp @@ -233,7 +233,70 @@ void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, S // Render the scene. - renderSceneNoLights( renderState, objectMask, baseObject, baseZone ); + if(GFX->getCurrentRenderStyle() == GFXDevice::RS_StereoSideBySide) + { + // Store previous values + RectI originalVP = GFX->getViewport(); + MatrixF originalWorld = GFX->getWorldMatrix(); + + Point2F projOffset = GFX->getCurrentProjectionOffset(); + Point3F eyeOffset = GFX->getStereoEyeOffset(); + + // Render left half of display + RectI leftVP = originalVP; + leftVP.extent.x *= 0.5; + GFX->setViewport(leftVP); + + MatrixF leftWorldTrans(true); + leftWorldTrans.setPosition(Point3F(eyeOffset.x, eyeOffset.y, eyeOffset.z)); + MatrixF leftWorld(originalWorld); + leftWorld.mulL(leftWorldTrans); + GFX->setWorldMatrix(leftWorld); + + Frustum gfxFrustum = GFX->getFrustum(); + gfxFrustum.setProjectionOffset(Point2F(projOffset.x, projOffset.y)); + GFX->setFrustum(gfxFrustum); + + SceneCameraState cameraStateLeft = SceneCameraState::fromGFX(); + SceneRenderState renderStateLeft( this, renderState->getScenePassType(), cameraStateLeft ); + renderStateLeft.setSceneRenderStyle(SRS_SideBySide); + renderStateLeft.setSceneRenderField(0); + + renderSceneNoLights( &renderStateLeft, objectMask, baseObject, baseZone ); + + // Render right half of display + RectI rightVP = originalVP; + rightVP.extent.x *= 0.5; + rightVP.point.x += rightVP.extent.x; + GFX->setViewport(rightVP); + + MatrixF rightWorldTrans(true); + rightWorldTrans.setPosition(Point3F(-eyeOffset.x, eyeOffset.y, eyeOffset.z)); + MatrixF rightWorld(originalWorld); + rightWorld.mulL(rightWorldTrans); + GFX->setWorldMatrix(rightWorld); + + gfxFrustum = GFX->getFrustum(); + gfxFrustum.setProjectionOffset(Point2F(-projOffset.x, projOffset.y)); + GFX->setFrustum(gfxFrustum); + + SceneCameraState cameraStateRight = SceneCameraState::fromGFX(); + SceneRenderState renderStateRight( this, renderState->getScenePassType(), cameraStateRight ); + renderStateRight.setSceneRenderStyle(SRS_SideBySide); + renderStateRight.setSceneRenderField(1); + + renderSceneNoLights( &renderStateRight, objectMask, baseObject, baseZone ); + + // Restore previous values + GFX->setWorldMatrix(originalWorld); + gfxFrustum.clearProjectionOffset(); + GFX->setFrustum(gfxFrustum); + GFX->setViewport(originalVP); + } + else + { + renderSceneNoLights( renderState, objectMask, baseObject, baseZone ); + } // Trigger the post-render signal. diff --git a/Engine/source/scene/sceneManager.h b/Engine/source/scene/sceneManager.h index deb43fe25..00eb3bd10 100644 --- a/Engine/source/scene/sceneManager.h +++ b/Engine/source/scene/sceneManager.h @@ -89,6 +89,18 @@ enum ScenePassType }; +/// The type of scene render style +/// @see SceneRenderState +enum SceneRenderStyle +{ + /// The regular style of rendering + SRS_Standard, + + /// Side-by-side style rendering + SRS_SideBySide, +}; + + /// An object that manages the SceneObjects belonging to a scene. class SceneManager { diff --git a/Engine/source/scene/sceneRenderState.cpp b/Engine/source/scene/sceneRenderState.cpp index 8da65407d..0aeb1d273 100644 --- a/Engine/source/scene/sceneRenderState.cpp +++ b/Engine/source/scene/sceneRenderState.cpp @@ -44,7 +44,9 @@ SceneRenderState::SceneRenderState( SceneManager* sceneManager, mUsePostEffects( usePostEffects ), mDisableAdvancedLightingBins( false ), mRenderArea( view.getFrustum().getBounds() ), - mAmbientLightColor( sceneManager->getAmbientLightColor() ) + mAmbientLightColor( sceneManager->getAmbientLightColor() ), + mSceneRenderStyle( SRS_Standard ), + mRenderField( 0 ) { // Setup the default parameters for the screen metrics methods. mDiffuseCameraTransform = view.getViewWorldMatrix(); diff --git a/Engine/source/scene/sceneRenderState.h b/Engine/source/scene/sceneRenderState.h index e8503548c..7a66e40aa 100644 --- a/Engine/source/scene/sceneRenderState.h +++ b/Engine/source/scene/sceneRenderState.h @@ -69,6 +69,12 @@ class SceneRenderState /// The type of scene render pass we're doing. ScenePassType mScenePassType; + /// The render style being performed + SceneRenderStyle mSceneRenderStyle; + + /// When doing stereo rendering, the current field that is being rendered + S32 mRenderField; + /// The render pass which we are setting up with this scene state. RenderPassManager* mRenderPass; @@ -219,6 +225,23 @@ class SceneRenderState /// @} + /// @name Render Style + /// @{ + + /// Get the rendering style used for the scene + SceneRenderStyle getSceneRenderStyle() const { return mSceneRenderStyle; } + + /// Set the rendering style used for the scene + void setSceneRenderStyle(SceneRenderStyle style) { mSceneRenderStyle = style; } + + /// Get the stereo field being rendered + S32 getSceneRenderField() const { return mRenderField; } + + /// Set the stereo field being rendered + void setSceneRenderField(S32 field) { mRenderField = field; } + + /// @} + /// @name Transforms, projections, and viewports. /// @{ diff --git a/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs b/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs index b8b15adb7..5a6d7aa28 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs @@ -58,8 +58,9 @@ singleton PostEffect( LightRayPostFX ) isEnabled = false; allowReflectPass = false; - renderTime = "PFXAfterDiffuse"; - renderPriority = 0.1; + renderTime = "PFXBeforeBin"; + renderBin = "EditorBin"; + renderPriority = 10; shader = LightRayOccludeShader; stateBlock = LightRayStateBlock; diff --git a/Templates/Empty/game/shaders/common/hlslStructs.h b/Templates/Empty/game/shaders/common/hlslStructs.h index 7d66a0481..6a57e4db7 100644 --- a/Templates/Empty/game/shaders/common/hlslStructs.h +++ b/Templates/Empty/game/shaders/common/hlslStructs.h @@ -89,6 +89,14 @@ struct VertexIn_PNT float2 uv0 : TEXCOORD0; }; +struct VertexIn_PNTT +{ + float4 pos : POSITION; + float3 normal : NORMAL; + float3 tangent : TANGENT; + float2 uv0 : TEXCOORD0; +}; + struct VertexIn_PNCT { float4 pos : POSITION; diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl b/Templates/Empty/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl index bf6fa9619..08cf61285 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl @@ -24,7 +24,7 @@ #include "farFrustumQuad.hlsl" -FarFrustumQuadConnectV main( VertexIn_PNT IN, +FarFrustumQuadConnectV main( VertexIn_PNTT IN, uniform float4 rtParams0 ) { FarFrustumQuadConnectV OUT; @@ -36,7 +36,7 @@ FarFrustumQuadConnectV main( VertexIn_PNT IN, // Interpolators will generate eye rays the // from far-frustum corners. - OUT.wsEyeRay = IN.pos.xyz; + OUT.wsEyeRay = IN.tangent.xyz; OUT.vsEyeRay = IN.normal.xyz; return OUT; diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/vectorLightP.hlsl b/Templates/Empty/game/shaders/common/lighting/advanced/vectorLightP.hlsl index 326802966..4be1bc9bd 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/vectorLightP.hlsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/vectorLightP.hlsl @@ -73,7 +73,7 @@ float4 main( FarFrustumQuadConnectP IN, // Use eye ray to get ws pos float4 worldPos = float4(eyePosWorld + IN.wsEyeRay * depth, 1.0f); - + // Get the light attenuation. float dotNL = dot(-lightDirection, normal); diff --git a/Templates/Full/game/core/scripts/client/postFx/lightRay.cs b/Templates/Full/game/core/scripts/client/postFx/lightRay.cs index b8b15adb7..5a6d7aa28 100644 --- a/Templates/Full/game/core/scripts/client/postFx/lightRay.cs +++ b/Templates/Full/game/core/scripts/client/postFx/lightRay.cs @@ -58,8 +58,9 @@ singleton PostEffect( LightRayPostFX ) isEnabled = false; allowReflectPass = false; - renderTime = "PFXAfterDiffuse"; - renderPriority = 0.1; + renderTime = "PFXBeforeBin"; + renderBin = "EditorBin"; + renderPriority = 10; shader = LightRayOccludeShader; stateBlock = LightRayStateBlock; diff --git a/Templates/Full/game/shaders/common/hlslStructs.h b/Templates/Full/game/shaders/common/hlslStructs.h index 7d66a0481..6a57e4db7 100644 --- a/Templates/Full/game/shaders/common/hlslStructs.h +++ b/Templates/Full/game/shaders/common/hlslStructs.h @@ -89,6 +89,14 @@ struct VertexIn_PNT float2 uv0 : TEXCOORD0; }; +struct VertexIn_PNTT +{ + float4 pos : POSITION; + float3 normal : NORMAL; + float3 tangent : TANGENT; + float2 uv0 : TEXCOORD0; +}; + struct VertexIn_PNCT { float4 pos : POSITION; diff --git a/Templates/Full/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl b/Templates/Full/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl index bf6fa9619..08cf61285 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl @@ -24,7 +24,7 @@ #include "farFrustumQuad.hlsl" -FarFrustumQuadConnectV main( VertexIn_PNT IN, +FarFrustumQuadConnectV main( VertexIn_PNTT IN, uniform float4 rtParams0 ) { FarFrustumQuadConnectV OUT; @@ -36,7 +36,7 @@ FarFrustumQuadConnectV main( VertexIn_PNT IN, // Interpolators will generate eye rays the // from far-frustum corners. - OUT.wsEyeRay = IN.pos.xyz; + OUT.wsEyeRay = IN.tangent.xyz; OUT.vsEyeRay = IN.normal.xyz; return OUT; diff --git a/Templates/Full/game/shaders/common/lighting/advanced/vectorLightP.hlsl b/Templates/Full/game/shaders/common/lighting/advanced/vectorLightP.hlsl index 326802966..4be1bc9bd 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/vectorLightP.hlsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/vectorLightP.hlsl @@ -73,7 +73,7 @@ float4 main( FarFrustumQuadConnectP IN, // Use eye ray to get ws pos float4 worldPos = float4(eyePosWorld + IN.wsEyeRay * depth, 1.0f); - + // Get the light attenuation. float dotNL = dot(-lightDirection, normal); diff --git a/Tools/projectGenerator/modules/core.inc b/Tools/projectGenerator/modules/core.inc index 84cb1c016..a6f3cae5a 100644 --- a/Tools/projectGenerator/modules/core.inc +++ b/Tools/projectGenerator/modules/core.inc @@ -73,6 +73,7 @@ switch( Generator::$platform ) addEngineSrcDir('platform/threads'); addEngineSrcDir('platform/async'); addEngineSrcDir('platform/input'); +addEngineSrcDir('platform/output'); addEngineSrcDir('app'); addEngineSrcDir('app/net');