瀏覽代碼

Merge pull request #324 from DavidWyand-GG/SideBySideRendering

Side by side rendering
David Wyand 12 年之前
父節點
當前提交
2805ec81c8
共有 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');