Merge pull request #324 from DavidWyand-GG/SideBySideRendering

Side by side rendering
This commit is contained in:
David Wyand 2013-04-09 12:43:58 -07:00
commit 2805ec81c8
28 changed files with 465 additions and 41 deletions

View file

@ -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 "

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -159,6 +159,9 @@ GFXDevice::GFXDevice()
// misc
mAllowRender = true;
mCurrentRenderStyle = RS_Standard;
mCurrentProjectionOffset = Point2F::Zero;
mStereoEyeOffset = Point3F::Zero;
mCanCurrentlyRender = false;
mInitialized = false;

View file

@ -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.

View file

@ -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 );

View file

@ -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_

View file

@ -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 ) );

View file

@ -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;

View file

@ -438,7 +438,7 @@ void AdvancedLightManager::unregisterAllLights()
Parent::unregisterAllLights();
if ( mLightBinManager )
mLightBinManager->clear();
mLightBinManager->clearAllLights();
}
bool AdvancedLightManager::setTextureStage( const SceneData &sgData,

View file

@ -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);
}
}
//-----------------------------------------------------------------------------

View file

@ -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

View file

@ -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_

View file

@ -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;

View file

@ -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.

View file

@ -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
{

View file

@ -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();

View file

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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -73,6 +73,7 @@ switch( Generator::$platform )
addEngineSrcDir('platform/threads');
addEngineSrcDir('platform/async');
addEngineSrcDir('platform/input');
addEngineSrcDir('platform/output');
addEngineSrcDir('app');
addEngineSrcDir('app/net');