Преглед на файлове

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.
DavidWyand-GG преди 12 години
родител
ревизия
b32e7688c2
променени са 28 файла, в които са добавени 465 реда и са изтрити 41 реда
  1. 8 0
      Engine/source/T3D/gameBase/gameConnection.cpp
  2. 7 0
      Engine/source/T3D/gameBase/gameConnection.h
  3. 40 3
      Engine/source/T3D/gameFunctions.cpp
  4. 10 2
      Engine/source/T3D/lightFlareData.cpp
  5. 3 0
      Engine/source/gfx/gfxDevice.cpp
  6. 34 0
      Engine/source/gfx/gfxDevice.h
  7. 61 10
      Engine/source/gui/3d/guiTSControl.cpp
  8. 18 4
      Engine/source/gui/3d/guiTSControl.h
  9. 32 3
      Engine/source/lighting/advanced/advancedLightBinManager.cpp
  10. 5 2
      Engine/source/lighting/advanced/advancedLightBinManager.h
  11. 1 1
      Engine/source/lighting/advanced/advancedLightManager.cpp
  12. 18 0
      Engine/source/math/util/frustum.cpp
  13. 20 0
      Engine/source/math/util/frustum.h
  14. 44 0
      Engine/source/platform/output/IDisplayDevice.h
  15. 33 4
      Engine/source/postFx/postEffect.cpp
  16. 64 1
      Engine/source/scene/sceneManager.cpp
  17. 12 0
      Engine/source/scene/sceneManager.h
  18. 3 1
      Engine/source/scene/sceneRenderState.cpp
  19. 23 0
      Engine/source/scene/sceneRenderState.h
  20. 3 2
      Templates/Empty/game/core/scripts/client/postFx/lightRay.cs
  21. 8 0
      Templates/Empty/game/shaders/common/hlslStructs.h
  22. 2 2
      Templates/Empty/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl
  23. 1 1
      Templates/Empty/game/shaders/common/lighting/advanced/vectorLightP.hlsl
  24. 3 2
      Templates/Full/game/core/scripts/client/postFx/lightRay.cs
  25. 8 0
      Templates/Full/game/shaders/common/hlslStructs.h
  26. 2 2
      Templates/Full/game/shaders/common/lighting/advanced/farFrustumQuadV.hlsl
  27. 1 1
      Templates/Full/game/shaders/common/lighting/advanced/vectorLightP.hlsl
  28. 1 0
      Tools/projectGenerator/modules/core.inc

+ 8 - 0
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 "

+ 7 - 0
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();

+ 40 - 3
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;

+ 10 - 2
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;

+ 3 - 0
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;
    

+ 34 - 0
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.

+ 61 - 10
Engine/source/gui/3d/guiTSControl.cpp

@@ -53,6 +53,13 @@ ConsoleDocClass( GuiTSCtrl,
 U32 GuiTSCtrl::smFrameCount = 0;
 Vector<GuiTSCtrl*> 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 );

+ 18 - 4
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<GuiTSCtrl*> 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_

+ 32 - 3
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 ) );

+ 5 - 2
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<FarFrustumQuadVert> mFarFrustumQuadVerts;
 
 

+ 1 - 1
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,

+ 18 - 0
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);
+   }
 }
 
 //-----------------------------------------------------------------------------

+ 20 - 0
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

+ 44 - 0
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_

+ 33 - 4
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;
 

+ 64 - 1
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.
 

+ 12 - 0
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
 {

+ 3 - 1
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();

+ 23 - 0
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.
       /// @{
 

+ 3 - 2
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;

+ 8 - 0
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;

+ 2 - 2
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;

+ 1 - 1
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);
 

+ 3 - 2
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;

+ 8 - 0
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;

+ 2 - 2
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;

+ 1 - 1
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);
 

+ 1 - 0
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');