70 changes: 69 additions & 1 deletion Engine/source/T3D/shapeBase.cpp
Expand Up @@ -1969,6 +1969,75 @@ void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat)
mat->mul( gCamFXMgr.getTrans() );
}

void ShapeBase::getEyeCameraTransform(IDisplayDevice *displayDevice, U32 eyeId, MatrixF *outMat)
{
MatrixF temp(1);
Point3F eyePos;
Point3F rotEyePos;

DisplayPose inPose;
displayDevice->getFrameEyePose(&inPose, eyeId);
DisplayPose newPose = calcCameraDeltaPose(displayDevice->getCurrentConnection(), inPose);

// Ok, basically we just need to add on newPose to the camera transform
// NOTE: currently we dont support third-person camera in this mode
MatrixF cameraTransform(1);
F32 fakePos = 0;
getCameraTransform(&fakePos, &cameraTransform);

QuatF baserot = cameraTransform;
QuatF qrot = QuatF(newPose.orientation);
QuatF concatRot;
concatRot.mul(baserot, qrot);
concatRot.setMatrix(&temp);
temp.setPosition(cameraTransform.getPosition() + concatRot.mulP(newPose.position, &rotEyePos));

*outMat = temp;
}

DisplayPose ShapeBase::calcCameraDeltaPose(GameConnection *con, DisplayPose inPose)
{
// NOTE: this is intended to be similar to updateMove
// WARNING: does not take into account any move values

DisplayPose outPose;
outPose.orientation = getRenderTransform().toEuler();
outPose.position = inPose.position;

if (con && con->getControlSchemeAbsoluteRotation())
{
// Pitch
outPose.orientation.x = inPose.orientation.x;

// Constrain the range of mRot.x
while (outPose.orientation.x < -M_PI_F)
outPose.orientation.x += M_2PI_F;
while (outPose.orientation.x > M_PI_F)
outPose.orientation.x -= M_2PI_F;

// Yaw
outPose.orientation.z = inPose.orientation.z;

// Constrain the range of mRot.z
while (outPose.orientation.z < -M_PI_F)
outPose.orientation.z += M_2PI_F;
while (outPose.orientation.z > M_PI_F)
outPose.orientation.z -= M_2PI_F;

// Bank
if (mDataBlock->cameraCanBank)
{
outPose.orientation.y = inPose.orientation.y;
}

// Constrain the range of mRot.y
while (outPose.orientation.y > M_PI_F)
outPose.orientation.y -= M_2PI_F;
}

return outPose;
}

void ShapeBase::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
{
*min = mDataBlock->cameraMinDist;
Expand All @@ -1977,7 +2046,6 @@ void ShapeBase::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
rot->identity();
}


//----------------------------------------------------------------------------
F32 ShapeBase::getDamageFlash() const
{
Expand Down
8 changes: 7 additions & 1 deletion Engine/source/T3D/shapeBase.h
Expand Up @@ -63,7 +63,6 @@
#include "console/dynamicTypes.h"
#endif


class GFXCubemap;
class TSShapeInstance;
class SceneRenderState;
Expand Down Expand Up @@ -1583,6 +1582,13 @@ class ShapeBase : public GameBase, public ISceneLight
/// @param mat Camera transform (out)
virtual void getCameraTransform(F32* pos,MatrixF* mat);

/// Gets the view transform for a particular eye, taking into account the current absolute
/// orient and position values of the display device.
virtual void getEyeCameraTransform( IDisplayDevice *display, U32 eyeId, MatrixF *outMat );

/// Calculates a delta camera angle and view position based on inPose
virtual DisplayPose calcCameraDeltaPose(GameConnection *con, DisplayPose inPose);

/// Gets the index of a node inside a mounted image given the name
/// @param imageSlot Image slot
/// @param nodeName Node name
Expand Down
11 changes: 5 additions & 6 deletions Engine/source/gfx/D3D9/gfxD3D9Device.cpp
Expand Up @@ -695,9 +695,9 @@ GFXShader* GFXD3D9Device::createShader()
return shader;
}

void GFXD3D9Device::disableShaders()
void GFXD3D9Device::disableShaders(bool force)
{
setShader( NULL );
setShader( NULL, force );
setShaderConstBuffer( NULL );
}

Expand All @@ -706,25 +706,24 @@ void GFXD3D9Device::disableShaders()
// and to make sure redundant shader states are not being
// sent to the card.
//-----------------------------------------------------------------------------
void GFXD3D9Device::setShader( GFXShader *shader )
void GFXD3D9Device::setShader( GFXShader *shader, bool force )
{
GFXD3D9Shader *d3dShader = static_cast<GFXD3D9Shader*>( shader );

IDirect3DPixelShader9 *pixShader = ( d3dShader != NULL ? d3dShader->mPixShader : NULL );
IDirect3DVertexShader9 *vertShader = ( d3dShader ? d3dShader->mVertShader : NULL );

if( pixShader != mLastPixShader )
if( pixShader != mLastPixShader || force )
{
mD3DDevice->SetPixelShader( pixShader );
mLastPixShader = pixShader;
}

if( vertShader != mLastVertShader )
if( vertShader != mLastVertShader || force )
{
mD3DDevice->SetVertexShader( vertShader );
mLastVertShader = vertShader;
}

}

//-----------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions Engine/source/gfx/D3D9/gfxD3D9Device.h
Expand Up @@ -238,7 +238,7 @@ class GFXD3D9Device : public GFXDevice
// }

virtual GFXShader* createShader();
void disableShaders();
void disableShaders(bool force = false);

/// Device helper function
virtual D3DPRESENT_PARAMETERS setupPresentParams( const GFXVideoMode &mode, const HWND &hwnd ) const = 0;
Expand Down Expand Up @@ -272,7 +272,7 @@ class GFXD3D9Device : public GFXDevice

virtual F32 getPixelShaderVersion() const { return mPixVersion; }
virtual void setPixelShaderVersion( F32 version ){ mPixVersion = version; }
virtual void setShader( GFXShader *shader );
virtual void setShader( GFXShader *shader, bool force = false );
virtual U32 getNumSamplers() const { return mNumSamplers; }
virtual U32 getNumRenderTargets() const { return mNumRenderTargets; }
// }
Expand Down
4 changes: 3 additions & 1 deletion Engine/source/gfx/gfxDevice.cpp
Expand Up @@ -161,7 +161,6 @@ GFXDevice::GFXDevice()
mAllowRender = true;
mCurrentRenderStyle = RS_Standard;
mCurrentProjectionOffset = Point2F::Zero;
mStereoEyeOffset = Point3F::Zero;
mCanCurrentlyRender = false;
mInitialized = false;

Expand Down Expand Up @@ -197,6 +196,9 @@ GFXDevice::GFXDevice()
#elif defined TORQUE_OS_PS3
GFXShader::addGlobalMacro( "TORQUE_OS_PS3" );
#endif

mStereoTargets[0] = NULL;
mStereoTargets[1] = NULL;
}

GFXDrawUtil* GFXDevice::getDrawUtil()
Expand Down
72 changes: 66 additions & 6 deletions Engine/source/gfx/gfxDevice.h
Expand Up @@ -213,6 +213,9 @@ class GFXDevice
/// The device is about to finish rendering a frame
deEndOfFrame,

/// The device has rendered a frame and ended the scene
dePostFrame,

/// The device has started rendering a frame's field (such as for side-by-side rendering)
deStartOfField,

Expand Down Expand Up @@ -244,7 +247,12 @@ class GFXDevice
enum GFXDeviceRenderStyles
{
RS_Standard = 0,
RS_StereoSideBySide = (1<<0),
RS_StereoSideBySide = (1<<0), // Render into current Render Target side-by-side
};

enum GFXDeviceLimits
{
NumStereoPorts = 2
};

private:
Expand Down Expand Up @@ -277,7 +285,19 @@ class GFXDevice
Point2F mCurrentProjectionOffset;

/// Eye offset used when using a stereo rendering style
Point3F mStereoEyeOffset;
Point3F mStereoEyeOffset[NumStereoPorts];

MatrixF mStereoEyeTransforms[NumStereoPorts];
MatrixF mInverseStereoEyeTransforms[NumStereoPorts];

/// Fov port settings
FovPort mFovPorts[NumStereoPorts];

/// Destination viewports for stereo rendering
RectI mStereoViewports[NumStereoPorts];

/// Destination targets for stereo rendering
GFXTextureTarget* mStereoTargets[NumStereoPorts];

/// This will allow querying to see if a device is initialized and ready to
/// have operations performed on it.
Expand Down Expand Up @@ -323,10 +343,50 @@ class GFXDevice
void setCurrentProjectionOffset(const Point2F& offset) { mCurrentProjectionOffset = offset; }

/// Get the current eye offset used during stereo rendering
const Point3F& getStereoEyeOffset() { return mStereoEyeOffset; }
const Point3F* getStereoEyeOffsets() { return mStereoEyeOffset; }

const MatrixF* getStereoEyeTransforms() { return mStereoEyeTransforms; }
const MatrixF* getInverseStereoEyeTransforms() { return mInverseStereoEyeTransforms; }

/// Set the current eye offset used during stereo rendering
void setStereoEyeOffset(const Point3F& offset) { mStereoEyeOffset = offset; }
void setStereoEyeOffsets(Point3F *offsets) { dMemcpy(mStereoEyeOffset, offsets, sizeof(Point3F) * NumStereoPorts); }

void setStereoEyeTransforms(MatrixF *transforms) { dMemcpy(mStereoEyeTransforms, transforms, sizeof(mStereoEyeTransforms)); dMemcpy(mInverseStereoEyeTransforms, transforms, sizeof(mInverseStereoEyeTransforms)); mInverseStereoEyeTransforms[0].inverse(); mInverseStereoEyeTransforms[1].inverse(); }

/// Set the current eye offset used during stereo rendering. Assumes NumStereoPorts are available.
void setFovPort(const FovPort *ports) { dMemcpy(mFovPorts, ports, sizeof(mFovPorts)); }

/// Get the current eye offset used during stereo rendering
const FovPort* getSteroFovPort() { return mFovPorts; }

/// Sets stereo viewports
void setSteroViewports(const RectI *ports) { dMemcpy(mStereoViewports, ports, sizeof(RectI) * NumStereoPorts); }

/// Sets stereo render targets
void setStereoTargets(GFXTextureTarget **targets) { mStereoTargets[0] = targets[0]; mStereoTargets[1] = targets[1]; }

RectI* getStereoViewports() { return mStereoViewports; }

/// Activates a stereo render target, setting the correct viewport to render eye contents.
/// If eyeId is -1, set a viewport encompassing the entire size of the render targets.
void activateStereoTarget(S32 eyeId)
{
if (eyeId == -1)
{
if (mStereoTargets[0])
{
setActiveRenderTarget(mStereoTargets[0], true);
}
}
else
{
if (mStereoTargets[eyeId])
{
setActiveRenderTarget(mStereoTargets[eyeId], false);
}
setViewport(mStereoViewports[eyeId]);
}
}

GFXCardProfiler* getCardProfiler() const { return mCardProfiler; }

Expand Down Expand Up @@ -722,8 +782,8 @@ class GFXDevice
/// Returns the number of simultaneous render targets supported by the device.
virtual U32 getNumRenderTargets() const = 0;

virtual void setShader( GFXShader *shader ) {}
virtual void disableShaders() {} // TODO Remove when T3D 4.0
virtual void setShader( GFXShader *shader, bool force = false ) {}
virtual void disableShaders( bool force = false ) {} // TODO Remove when T3D 4.0

/// Set the buffer! (Actual set happens on the next draw call, just like textures, state blocks, etc)
void setShaderConstBuffer(GFXShaderConstBuffer* buffer);
Expand Down
207 changes: 187 additions & 20 deletions Engine/source/gui/3d/guiTSControl.cpp
Expand Up @@ -22,6 +22,7 @@

#include "platform/platform.h"
#include "gui/3d/guiTSControl.h"
#include "gui/core/guiOffscreenCanvas.h"

#include "console/engineAPI.h"
#include "scene/sceneManager.h"
Expand All @@ -34,7 +35,12 @@
#include "scene/reflectionManager.h"
#include "postFx/postEffectManager.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/gfxDrawUtil.h"
#include "gfx/gfxDebugEvent.h"

GFXTextureObject *gLastStereoTexture = NULL;

#define TS_OVERLAY_SCREEN_WIDTH 0.75

IMPLEMENT_CONOBJECT( GuiTSCtrl );

Expand All @@ -51,6 +57,7 @@ ConsoleDocClass( GuiTSCtrl,
);

U32 GuiTSCtrl::smFrameCount = 0;
bool GuiTSCtrl::smUseLatestDisplayTransform = true;
Vector<GuiTSCtrl*> GuiTSCtrl::smAwakeTSCtrls;

ImplementEnumType( GuiTSRenderStyles,
Expand All @@ -60,7 +67,6 @@ ImplementEnumType( GuiTSRenderStyles,
{ GuiTSCtrl::RenderStyleStereoSideBySide, "stereo side by side" },
EndImplementEnumType;


//-----------------------------------------------------------------------------

namespace
Expand Down Expand Up @@ -153,7 +159,6 @@ GuiTSCtrl::GuiTSCtrl()
mLastCameraQuery.nearPlane = 0.01f;

mLastCameraQuery.projectionOffset = Point2F::Zero;
mLastCameraQuery.eyeOffset = Point3F::Zero;

mLastCameraQuery.ortho = false;
}
Expand Down Expand Up @@ -192,6 +197,8 @@ void GuiTSCtrl::consoleInit()
{
Con::addVariable("$TSControl::frameCount", TypeS32, &smFrameCount, "The number of frames that have been rendered since this control was created.\n"
"@ingroup Rendering\n");
Con::addVariable("$TSControl::useLatestDisplayTransform", TypeBool, &smUseLatestDisplayTransform, "Use the latest view transform when rendering stereo instead of the one calculated by the last move.\n"
"@ingroup Rendering\n");
}

//-----------------------------------------------------------------------------
Expand All @@ -206,6 +213,9 @@ bool GuiTSCtrl::onWake()
"GuiTSCtrl::onWake - This control is already in the awake list!" );
smAwakeTSCtrls.push_back( this );

// For VR
mLastCameraQuery.drawCanvas = getRoot();

return true;
}

Expand Down Expand Up @@ -307,6 +317,7 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
// Save the current transforms so we can restore
// it for child control rendering below.
GFXTransformSaver saver;
bool renderingToTarget = false;

if(!processCameraQuery(&mLastCameraQuery))
{
Expand All @@ -317,15 +328,52 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
return;
}

GFXTargetRef origTarget = GFX->getActiveRenderTarget();

// Set up the appropriate render style
U32 prevRenderStyle = GFX->getCurrentRenderStyle();
Point2F prevProjectionOffset = GFX->getCurrentProjectionOffset();
Point3F prevEyeOffset = GFX->getStereoEyeOffset();
Point2I renderSize = getExtent();

if(mRenderStyle == RenderStyleStereoSideBySide)
{
GFX->setCurrentRenderStyle(GFXDevice::RS_StereoSideBySide);
GFX->setCurrentProjectionOffset(mLastCameraQuery.projectionOffset);
GFX->setStereoEyeOffset(mLastCameraQuery.eyeOffset);
GFX->setStereoEyeOffsets(mLastCameraQuery.eyeOffset);
GFX->setFovPort(mLastCameraQuery.fovPort); // NOTE: this specifies fov for BOTH eyes
GFX->setSteroViewports(mLastCameraQuery.stereoViewports);
GFX->setStereoTargets(mLastCameraQuery.stereoTargets);

MatrixF myTransforms[2];

if (smUseLatestDisplayTransform)
{
// Use the view matrix determined from the display device
myTransforms[0] = mLastCameraQuery.eyeTransforms[0];
myTransforms[1] = mLastCameraQuery.eyeTransforms[1];
}
else
{
// Use the view matrix determined from the control object
myTransforms[0] = mLastCameraQuery.cameraMatrix;
myTransforms[1] = mLastCameraQuery.cameraMatrix;

QuatF qrot = mLastCameraQuery.cameraMatrix;
Point3F pos = mLastCameraQuery.cameraMatrix.getPosition();
Point3F rotEyePos;

myTransforms[0].setPosition(pos + qrot.mulP(mLastCameraQuery.eyeOffset[0], &rotEyePos));
myTransforms[1].setPosition(pos + qrot.mulP(mLastCameraQuery.eyeOffset[1], &rotEyePos));
}

GFX->setStereoEyeTransforms(myTransforms);

// Allow render size to originate from the render target
if (mLastCameraQuery.stereoTargets[0])
{
renderSize = mLastCameraQuery.stereoViewports[0].extent;
renderingToTarget = true;
}
}
else
{
Expand Down Expand Up @@ -357,8 +405,8 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
// set up the camera and viewport stuff:
F32 wwidth;
F32 wheight;
F32 renderWidth = (mRenderStyle == RenderStyleStereoSideBySide) ? F32(getWidth())*0.5f : F32(getWidth());
F32 renderHeight = F32(getHeight());
F32 renderWidth = F32(renderSize.x);
F32 renderHeight = F32(renderSize.y);
F32 aspectRatio = renderWidth / renderHeight;

// Use the FOV to calculate the viewport height scale
Expand All @@ -380,12 +428,8 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
Frustum frustum;
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 );
// NOTE: these calculations are essentially overridden later by the fov port settings when rendering each eye.
MathUtils::makeFovPortFrustum(&frustum, mLastCameraQuery.ortho, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.fovPort[0]);
}
else
{
Expand All @@ -407,15 +451,24 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)

RectI tempRect = updateRect;

#ifdef TORQUE_OS_MAC
Point2I screensize = getRoot()->getWindowSize();
tempRect.point.y = screensize.y - (tempRect.point.y + tempRect.extent.y);
#endif
if (!renderingToTarget)
{
#ifdef TORQUE_OS_MAC
Point2I screensize = getRoot()->getWindowSize();
tempRect.point.y = screensize.y - (tempRect.point.y + tempRect.extent.y);
#endif

GFX->setViewport( tempRect );
GFX->setViewport( tempRect );
}
else
{
// Activate stereo RT
GFX->activateStereoTarget(-1);
}

// Clear the zBuffer so GUI doesn't hose object rendering accidentally
GFX->clear( GFXClearZBuffer , ColorI(20,20,20), 1.0f, 0 );
//GFX->clear( GFXClearTarget, ColorI(255,0,0), 1.0f, 0);

GFX->setFrustum( frustum );
if(mLastCameraQuery.ortho)
Expand All @@ -427,7 +480,7 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
// We're going to be displaying this render at size of this control in
// pixels - let the scene know so that it can calculate e.g. reflections
// correctly for that final display result.
gClientSceneGraph->setDisplayTargetResolution(getExtent());
gClientSceneGraph->setDisplayTargetResolution(renderSize);

// Set the GFX world matrix to the world-to-camera transform, but don't
// change the cameraMatrix in mLastCameraQuery. This is because
Expand Down Expand Up @@ -455,20 +508,121 @@ void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
renderWorld(updateRect);
DebugDrawer::get()->render();

// Render the canvas overlay if its available
if (mRenderStyle == RenderStyleStereoSideBySide && mStereoGuiTarget.getPointer())
{
GFXDEBUGEVENT_SCOPE( StereoGui_Render, ColorI( 255, 0, 0 ) );
MatrixF proj(1);

Frustum originalFrustum = GFX->getFrustum();
GFXTextureObject *texObject = mStereoGuiTarget->getTexture(0);
const FovPort *currentFovPort = GFX->getSteroFovPort();
const MatrixF *eyeTransforms = GFX->getStereoEyeTransforms();
const MatrixF *worldEyeTransforms = GFX->getInverseStereoEyeTransforms();
const Point3F *eyeOffset = GFX->getStereoEyeOffsets();

for (U32 i=0; i<2; i++)
{
GFX->activateStereoTarget(i);
Frustum gfxFrustum = originalFrustum;
const F32 frustumDepth = gfxFrustum.getNearDist();
MathUtils::makeFovPortFrustum(&gfxFrustum, true, gfxFrustum.getNearDist(), gfxFrustum.getFarDist(), currentFovPort[i], eyeTransforms[i]);
GFX->setFrustum(gfxFrustum);

MatrixF eyeWorldTrans(1);
eyeWorldTrans.setPosition(Point3F(eyeOffset[i].x,eyeOffset[i].y,eyeOffset[i].z));
MatrixF eyeWorld(1);
eyeWorld.mul(eyeWorldTrans);
eyeWorld.inverse();

GFX->setWorldMatrix(eyeWorld);
GFX->setViewMatrix(MatrixF::Identity);

if (!mStereoOverlayVB.getPointer())
{
mStereoOverlayVB.set(GFX, 4, GFXBufferTypeStatic);
GFXVertexPCT *verts = mStereoOverlayVB.lock(0, 4);

F32 texLeft = 0.0f;
F32 texRight = 1.0f;
F32 texTop = 1.0f;
F32 texBottom = 0.0f;

F32 rectRatio = gfxFrustum.getWidth() / gfxFrustum.getHeight();
F32 rectWidth = gfxFrustum.getWidth() * TS_OVERLAY_SCREEN_WIDTH;
F32 rectHeight = rectWidth * rectRatio;

F32 screenLeft = -rectWidth * 0.5;
F32 screenRight = rectWidth * 0.5;
F32 screenTop = -rectHeight * 0.5;
F32 screenBottom = rectHeight * 0.5;

const F32 fillConv = 0.0f;
const F32 frustumDepth = gfxFrustum.getNearDist() + 0.012;
verts[0].point.set( screenLeft - fillConv, frustumDepth, screenTop - fillConv );
verts[1].point.set( screenRight - fillConv, frustumDepth, screenTop - fillConv );
verts[2].point.set( screenLeft - fillConv, frustumDepth, screenBottom - fillConv );
verts[3].point.set( screenRight - fillConv, frustumDepth, screenBottom - fillConv );

verts[0].color = verts[1].color = verts[2].color = verts[3].color = ColorI(255,255,255,255);

verts[0].texCoord.set( texLeft, texTop );
verts[1].texCoord.set( texRight, texTop );
verts[2].texCoord.set( texLeft, texBottom );
verts[3].texCoord.set( texRight, texBottom );

mStereoOverlayVB.unlock();
}

if (!mStereoGuiSB.getPointer())
{
// DrawBitmapStretchSR
GFXStateBlockDesc bitmapStretchSR;
bitmapStretchSR.setCullMode(GFXCullNone);
bitmapStretchSR.setZReadWrite(false, false);
bitmapStretchSR.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
bitmapStretchSR.samplersDefined = true;

bitmapStretchSR.samplers[0] = GFXSamplerStateDesc::getClampLinear();
bitmapStretchSR.samplers[0].minFilter = GFXTextureFilterPoint;
bitmapStretchSR.samplers[0].mipFilter = GFXTextureFilterPoint;
bitmapStretchSR.samplers[0].magFilter = GFXTextureFilterPoint;

mStereoGuiSB = GFX->createStateBlock(bitmapStretchSR);
}

GFX->setVertexBuffer(mStereoOverlayVB);
GFX->setStateBlock(mStereoGuiSB);
GFX->setTexture( 0, texObject );
GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
}
}

// Restore the previous matrix state before
// we begin rendering the child controls.
saver.restore();

// Restore the render style and any stereo parameters
GFX->setActiveRenderTarget(origTarget);
GFX->setCurrentRenderStyle(prevRenderStyle);
GFX->setCurrentProjectionOffset(prevProjectionOffset);
GFX->setStereoEyeOffset(prevEyeOffset);


if(mRenderStyle == RenderStyleStereoSideBySide && gLastStereoTexture)
{
GFX->setClipRect(updateRect);
GFX->getDrawUtil()->drawBitmapStretch(gLastStereoTexture, updateRect);
}

// Allow subclasses to render 2D elements.
GFX->setClipRect(updateRect);
renderGui( offset, updateRect );

renderChildControls(offset, updateRect);
if (shouldRenderChildControls())
{
renderChildControls(offset, updateRect);
}
smFrameCount++;
}

Expand Down Expand Up @@ -499,6 +653,12 @@ void GuiTSCtrl::drawLineList( const Vector<Point3F> &points, const ColorI color,
drawLine( points[i], points[i+1], color, width );
}


void GuiTSCtrl::setStereoGui(GuiOffscreenCanvas *canvas)
{
mStereoGuiTarget = canvas ? canvas->getTarget() : NULL;
}

//=============================================================================
// Console Methods.
//=============================================================================
Expand Down Expand Up @@ -547,3 +707,10 @@ DefineEngineMethod( GuiTSCtrl, calculateViewDistance, F32, ( F32 radius ),,
{
return object->calculateViewDistance( radius );
}

DefineEngineMethod( GuiTSCtrl, setStereoGui, void, ( GuiOffscreenCanvas* canvas ),,
"Sets the current stereo texture to an offscreen canvas\n"
"@param canvas The desired canvas." )
{
object->setStereoGui(canvas);
}
28 changes: 25 additions & 3 deletions Engine/source/gui/3d/guiTSControl.h
Expand Up @@ -30,16 +30,29 @@
#include "math/mMath.h"
#endif


#ifndef _MATTEXTURETARGET_H_
#include "materials/matTextureTarget.h"
#endif

class IDisplayDevice;
class GuiOffscreenCanvas;

struct CameraQuery
{
SimObject* object;
F32 nearPlane;
F32 farPlane;
F32 fov;
FovPort fovPort[2]; // fov for each eye
Point2F projectionOffset;
Point3F eyeOffset;
Point3F eyeOffset[2];
MatrixF eyeTransforms[2];
bool ortho;
MatrixF cameraMatrix;
RectI stereoViewports[2]; // destination viewports
GFXTextureTarget* stereoTargets[2];
GuiCanvas* drawCanvas; // Canvas we are drawing to. Needed for VR
};

/// Abstract base class for 3D viewport GUIs.
Expand All @@ -50,11 +63,12 @@ class GuiTSCtrl : public GuiContainer
public:
enum RenderStyles {
RenderStyleStandard = 0,
RenderStyleStereoSideBySide = (1<<0),
RenderStyleStereoSideBySide = (1<<0)
};

protected:
static U32 smFrameCount;
static bool smUseLatestDisplayTransform;
F32 mCameraZRot;
F32 mForceFOV;

Expand Down Expand Up @@ -83,7 +97,11 @@ class GuiTSCtrl : public GuiContainer

/// The last camera query set in onRender.
/// @see getLastCameraQuery
CameraQuery mLastCameraQuery;
CameraQuery mLastCameraQuery;

NamedTexTargetRef mStereoGuiTarget;
GFXVertexBufferHandle<GFXVertexPCT> mStereoOverlayVB;
GFXStateBlockRef mStereoGuiSB;

public:

Expand Down Expand Up @@ -155,6 +173,10 @@ class GuiTSCtrl : public GuiContainer

static const U32& getFrameCount() { return smFrameCount; }

bool shouldRenderChildControls() { return mRenderStyle == RenderStyleStandard; }

void setStereoGui(GuiOffscreenCanvas *canvas);

DECLARE_CONOBJECT(GuiTSCtrl);
DECLARE_CATEGORY( "Gui 3D" );
DECLARE_DESCRIPTION( "Abstract base class for controls that render a 3D viewport." );
Expand Down
114 changes: 101 additions & 13 deletions Engine/source/gui/core/guiCanvas.cpp
Expand Up @@ -36,6 +36,7 @@
#include "gfx/video/videoCapture.h"
#include "lighting/lightManager.h"
#include "core/strings/stringUnit.h"
#include "gui/core/guiOffscreenCanvas.h"

#ifndef TORQUE_TGB_ONLY
#include "scene/sceneObject.h"
Expand Down Expand Up @@ -126,7 +127,8 @@ GuiCanvas::GuiCanvas(): GuiControl(),
mMouseDownPoint(0.0f,0.0f),
mPlatformWindow(NULL),
mLastRenderMs(0),
mDisplayWindow(true)
mDisplayWindow(true),
mMenuBarCtrl(NULL)
{
setBounds(0, 0, 640, 480);
mAwake = true;
Expand Down Expand Up @@ -508,6 +510,55 @@ bool GuiCanvas::isCursorShown()
return mPlatformWindow->isCursorVisible();
}

void GuiCanvas::cursorClick(S32 buttonId, bool isDown)
{
InputEventInfo inputEvent;
inputEvent.deviceType = MouseDeviceType;
inputEvent.deviceInst = 0;
inputEvent.objType = SI_BUTTON;
inputEvent.objInst = (InputObjectInstances)(KEY_BUTTON0 + buttonId);
inputEvent.modifier = (InputModifiers)0;
inputEvent.ascii = 0;
inputEvent.action = isDown ? SI_MAKE : SI_BREAK;
inputEvent.fValue = isDown ? 1.0 : 0.0;

processMouseEvent(inputEvent);
}

void GuiCanvas::cursorNudge(F32 x, F32 y)
{
// Generate a base Movement along and Axis event
InputEventInfo inputEvent;
inputEvent.deviceType = MouseDeviceType;
inputEvent.deviceInst = 0;
inputEvent.objType = SI_AXIS;
inputEvent.modifier = (InputModifiers)0;
inputEvent.ascii = 0;

// Generate delta movement along each axis
Point2F cursDelta(x, y);

// If X axis changed, generate a relative event
if(mFabs(cursDelta.x) > 0.1)
{
inputEvent.objInst = SI_XAXIS;
inputEvent.action = SI_MOVE;
inputEvent.fValue = cursDelta.x;
processMouseEvent(inputEvent);
}

// If Y axis changed, generate a relative event
if(mFabs(cursDelta.y) > 0.1)
{
inputEvent.objInst = SI_YAXIS;
inputEvent.action = SI_MOVE;
inputEvent.fValue = cursDelta.y;
processMouseEvent(inputEvent);
}

processMouseEvent(inputEvent);
}

void GuiCanvas::addAcceleratorKey(GuiControl *ctrl, U32 index, U32 keyCode, U32 modifier)
{
if (keyCode > 0 && ctrl)
Expand Down Expand Up @@ -708,14 +759,22 @@ bool GuiCanvas::processMouseEvent(InputEventInfo &inputEvent)
//
// 'mCursorPt' basically is an accumulation of errors and the number of bugs that have cropped up with
// the GUI clicking stuff where it is not supposed to are probably all to blame on this.

// Need to query platform for specific things
AssertISV(mPlatformWindow, "GuiCanvas::processMouseEvent - no window present!");
PlatformCursorController *pController = mPlatformWindow->getCursorController();
AssertFatal(pController != NULL, "GuiCanvas::processInputEvent - No Platform Controller Found")

//copy the modifier into the new event
mLastEvent.modifier = inputEvent.modifier;
S32 mouseDoubleClickWidth = 12;
S32 mouseDoubleClickHeight = 12;
U32 mouseDoubleClickTime = 500;

// Query platform for mouse info if its available
PlatformCursorController *pController = mPlatformWindow ? mPlatformWindow->getCursorController() : NULL;
if (pController)
{
mouseDoubleClickWidth = pController->getDoubleClickWidth();
mouseDoubleClickHeight = pController->getDoubleClickHeight();
mouseDoubleClickTime = pController->getDoubleClickTime();
}

//copy the modifier into the new event
mLastEvent.modifier = inputEvent.modifier;

if(inputEvent.objType == SI_AXIS &&
(inputEvent.objInst == SI_XAXIS || inputEvent.objInst == SI_YAXIS))
Expand Down Expand Up @@ -747,7 +806,7 @@ bool GuiCanvas::processMouseEvent(InputEventInfo &inputEvent)
// moving too much.
Point2F movement = mMouseDownPoint - mCursorPt;

if ((mAbs((S32)movement.x) > pController->getDoubleClickWidth()) || (mAbs((S32)movement.y) > pController->getDoubleClickHeight() ) )
if ((mAbs((S32)movement.x) > mouseDoubleClickWidth) || (mAbs((S32)movement.y) > mouseDoubleClickHeight ) )
{
mLeftMouseLast = false;
mMiddleMouseLast = false;
Expand Down Expand Up @@ -799,7 +858,7 @@ bool GuiCanvas::processMouseEvent(InputEventInfo &inputEvent)
if (mLeftMouseLast)
{
//if it was within the double click time count the clicks
if (curTime - mLastMouseDownTime <= pController->getDoubleClickTime())
if (curTime - mLastMouseDownTime <= mouseDoubleClickTime)
mLastMouseClickCount++;
else
mLastMouseClickCount = 1;
Expand Down Expand Up @@ -833,7 +892,7 @@ bool GuiCanvas::processMouseEvent(InputEventInfo &inputEvent)
if (mRightMouseLast)
{
//if it was within the double click time count the clicks
if (curTime - mLastMouseDownTime <= pController->getDoubleClickTime())
if (curTime - mLastMouseDownTime <= mouseDoubleClickTime)
mLastMouseClickCount++;
else
mLastMouseClickCount = 1;
Expand Down Expand Up @@ -864,7 +923,7 @@ bool GuiCanvas::processMouseEvent(InputEventInfo &inputEvent)
if (mMiddleMouseLast)
{
//if it was within the double click time count the clicks
if (curTime - mLastMouseDownTime <= pController->getDoubleClickTime())
if (curTime - mLastMouseDownTime <= mouseDoubleClickTime)
mLastMouseClickCount++;
else
mLastMouseClickCount = 1;
Expand Down Expand Up @@ -1768,6 +1827,21 @@ void GuiCanvas::renderFrame(bool preRenderOnly, bool bufferSwap /* = true */)

PROFILE_END();

// Render all offscreen canvas objects here since we may need them in the render loop
if (GuiOffscreenCanvas::sList.size() != 0)
{
// Reset the entire state since oculus shit will have barfed it.
GFX->disableShaders(true);
GFX->updateStates(true);

for (Vector<GuiOffscreenCanvas*>::iterator itr = GuiOffscreenCanvas::sList.begin(); itr != GuiOffscreenCanvas::sList.end(); itr++)
{
(*itr)->renderFrame(false, false);
}

GFX->setActiveRenderTarget(renderTarget);
}

// Can't render if waiting for device to reset.
if ( !beginSceneRes )
{
Expand Down Expand Up @@ -1907,7 +1981,8 @@ void GuiCanvas::renderFrame(bool preRenderOnly, bool bufferSwap /* = true */)
PROFILE_START(GFXEndScene);
GFX->endScene();
PROFILE_END();


GFX->getDeviceEventSignal().trigger( GFXDevice::dePostFrame );
swapBuffers();

GuiCanvas::getGuiCanvasFrameSignal().trigger(false);
Expand Down Expand Up @@ -2761,3 +2836,16 @@ ConsoleMethod( GuiCanvas, hideWindow, void, 2, 2, "" )
WindowManager->setDisplayWindow(false);
object->getPlatformWindow()->setDisplayWindow(false);
}

ConsoleMethod( GuiCanvas, cursorClick, void, 4, 4, "button, isDown" )
{
const S32 buttonId = dAtoi(argv[2]);
const bool isDown = dAtob(argv[3]);

object->cursorClick(buttonId, isDown);
}

ConsoleMethod( GuiCanvas, cursorNudge, void, 4, 4, "x, y" )
{
object->cursorNudge(dAtof(argv[2]), dAtof(argv[3]));
}
4 changes: 4 additions & 0 deletions Engine/source/gui/core/guiCanvas.h
Expand Up @@ -331,6 +331,10 @@ class GuiCanvas : public GuiControl, public IProcessInput

/// Returns true if the cursor is being rendered.
virtual bool isCursorShown();

void cursorClick(S32 buttonId, bool isDown);

void cursorNudge(F32 x, F32 y);
/// @}

///used by the tooltip resource
Expand Down
3 changes: 2 additions & 1 deletion Engine/source/gui/core/guiControl.cpp
Expand Up @@ -2380,7 +2380,8 @@ void GuiControl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent
// so set it back before we change it again.

PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow();
AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible.");
if (!pWindow)
return;
PlatformCursorController *pController = pWindow->getCursorController();
AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");

Expand Down
273 changes: 273 additions & 0 deletions Engine/source/gui/core/guiOffscreenCanvas.cpp
@@ -0,0 +1,273 @@
#include "gui/core/guiOffscreenCanvas.h"
#include "gfx/gfxDrawUtil.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/gfxAPI.h"
#include "gfx/gfxDebugEvent.h"

#include "console/consoleTypes.h"
#include "console/console.h"

IMPLEMENT_CONOBJECT(GuiOffscreenCanvas);

Vector<GuiOffscreenCanvas*> GuiOffscreenCanvas::sList;

GuiOffscreenCanvas::GuiOffscreenCanvas()
{
mTargetFormat = GFXFormatR8G8B8A8;
mTargetSize = Point2I(256,256);
mTargetName = "offscreenCanvas";
mTargetDirty = true;
mDynamicTarget = false;
}

GuiOffscreenCanvas::~GuiOffscreenCanvas()
{
}

void GuiOffscreenCanvas::initPersistFields()
{
addField( "targetSize", TypePoint2I, Offset( mTargetSize, GuiOffscreenCanvas ),"" );
addField( "targetFormat", TypeGFXFormat, Offset( mTargetFormat, GuiOffscreenCanvas ), "");
addField( "targetName", TypeRealString, Offset( mTargetName, GuiOffscreenCanvas ), "");
addField( "dynamicTarget", TypeBool, Offset( mDynamicTarget, GuiOffscreenCanvas ), "");

Parent::initPersistFields();
}

bool GuiOffscreenCanvas::onAdd()
{
if (GuiControl::onAdd()) // jamesu - skip GuiCanvas onAdd since it sets up GFX which is bad
{
// ensure that we have a cursor
setCursor(dynamic_cast<GuiCursor*>(Sim::findObject("DefaultCursor")));

mRenderFront = true;
sList.push_back(this);

//Con::printf("Registering target %s...", mTargetName.c_str());
mNamedTarget.registerWithName( mTargetName );

_setupTargets();

GFXTextureManager::addEventDelegate( this, &GuiOffscreenCanvas::_onTextureEvent );

return true;
}
return false;
}

void GuiOffscreenCanvas::onRemove()
{
GFXTextureManager::removeEventDelegate( this, &GuiOffscreenCanvas::_onTextureEvent );

_teardownTargets();

U32 idx = sList.find_next(this);
if (idx != (U32)-1)
{
sList.erase(idx);
}

mTarget = NULL;
mTargetTexture = NULL;

Parent::onRemove();
}

void GuiOffscreenCanvas::_setupTargets()
{
_teardownTargets();

if (!mTarget.isValid())
{
mTarget = GFX->allocRenderToTextureTarget();
}

// Update color
if (!mTargetTexture.isValid() || mTargetSize != mTargetTexture.getWidthHeight())
{
mTargetTexture.set( mTargetSize.x, mTargetSize.y, mTargetFormat, &GFXDefaultRenderTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ), 1, 0 );
}

mTarget->attachTexture( GFXTextureTarget::RenderSlot(GFXTextureTarget::Color0), mTargetTexture );
mNamedTarget.setTexture(0, mTargetTexture);
}

void GuiOffscreenCanvas::_teardownTargets()
{
mNamedTarget.release();
mTargetTexture = NULL;
mTargetDirty = true;
}

void GuiOffscreenCanvas::renderFrame(bool preRenderOnly, bool bufferSwap /* = true */)
{
if (!mTargetDirty)
return;

#ifdef TORQUE_ENABLE_GFXDEBUGEVENTS
char buf[256];
dSprintf(buf, sizeof(buf), "OffsceenCanvas %s", getName() ? getName() : getIdString());
GFXDEBUGEVENT_SCOPE_EX(GuiOffscreenCanvas_renderFrame, ColorI::GREEN, buf);
#endif

PROFILE_START(OffscreenCanvasPreRender);

#ifdef TORQUE_GFX_STATE_DEBUG
GFX->getDebugStateManager()->startFrame();
#endif

if (mTarget->getSize() != mTargetSize)
{
_setupTargets();
mNamedTarget.setViewport( RectI( Point2I::Zero, mTargetSize ) );
}

// Make sure the root control is the size of the canvas.
Point2I size = mTarget->getSize();

if(size.x == 0 || size.y == 0)
{
PROFILE_END();
return;
}

RectI screenRect(0, 0, size.x, size.y);

maintainSizing();

//preRender (recursive) all controls
preRender();

PROFILE_END();

// Are we just doing pre-render?
if(preRenderOnly)
{
return;
}

resetUpdateRegions();

PROFILE_START(OffscreenCanvasRenderControls);

GuiCursor *mouseCursor = NULL;
bool cursorVisible = true;

Point2I cursorPos((S32)mCursorPt.x, (S32)mCursorPt.y);
mouseCursor = mDefaultCursor;

mLastCursorEnabled = cursorVisible;
mLastCursor = mouseCursor;
mLastCursorPt = cursorPos;

// Set active target
GFX->pushActiveRenderTarget();
GFX->setActiveRenderTarget(mTarget);

// Clear the current viewport area
GFX->setViewport( screenRect );
GFX->clear( GFXClearTarget, ColorF(0,0,0,0), 1.0f, 0 );

resetUpdateRegions();

// Make sure we have a clean matrix state
// before we start rendering anything!
GFX->setWorldMatrix( MatrixF::Identity );
GFX->setViewMatrix( MatrixF::Identity );
GFX->setProjectionMatrix( MatrixF::Identity );

RectI contentRect(Point2I(0,0), mTargetSize);
{
// Render active GUI Dialogs
for(iterator i = begin(); i != end(); i++)
{
// Get the control
GuiControl *contentCtrl = static_cast<GuiControl*>(*i);

GFX->setClipRect( contentRect );
GFX->setStateBlock(mDefaultGuiSB);

contentCtrl->onRender(contentCtrl->getPosition(), contentRect);
}

// Fill Blue if no Dialogs
if(this->size() == 0)
GFX->clear( GFXClearTarget, ColorF(0,0,1,1), 1.0f, 0 );

GFX->setClipRect( contentRect );

// Draw cursor
//
if (mCursorEnabled && mouseCursor && mShowCursor)
{
Point2I pos((S32)mCursorPt.x, (S32)mCursorPt.y);
Point2I spot = mouseCursor->getHotSpot();

pos -= spot;
mouseCursor->render(pos);
}

GFX->getDrawUtil()->clearBitmapModulation();
}

mTarget->resolve();
GFX->popActiveRenderTarget();

PROFILE_END();

// Keep track of the last time we rendered.
mLastRenderMs = Platform::getRealMilliseconds();
mTargetDirty = mDynamicTarget;
}

Point2I GuiOffscreenCanvas::getWindowSize()
{
return mTargetSize;
}

Point2I GuiOffscreenCanvas::getCursorPos()
{
return Point2I(mCursorPt.x, mCursorPt.y);
}

void GuiOffscreenCanvas::setCursorPos(const Point2I &pt)
{
mCursorPt.x = F32( pt.x );
mCursorPt.y = F32( pt.y );
}

void GuiOffscreenCanvas::showCursor(bool state)
{
mShowCursor = state;
}

bool GuiOffscreenCanvas::isCursorShown()
{
return mShowCursor;
}

void GuiOffscreenCanvas::_onTextureEvent( GFXTexCallbackCode code )
{
switch(code)
{
case GFXZombify:
_teardownTargets();
break;

case GFXResurrect:
_setupTargets();
break;
}
}

DefineEngineMethod(GuiOffscreenCanvas, resetTarget, void, (), , "")
{
object->_setupTargets();
}

DefineEngineMethod(GuiOffscreenCanvas, markDirty, void, (), , "")
{
object->markDirty();
}

63 changes: 63 additions & 0 deletions Engine/source/gui/core/guiOffscreenCanvas.h
@@ -0,0 +1,63 @@
#ifndef _GUIOFFSCREENCANVAS_H_
#define _GUIOFFSCREENCANVAS_H_

#include "math/mMath.h"
#include "gui/core/guiCanvas.h"
#include "core/util/tVector.h"

#ifndef _MATTEXTURETARGET_H_
#include "materials/matTextureTarget.h"
#endif

class GuiTextureDebug;

class GuiOffscreenCanvas : public GuiCanvas
{
public:
typedef GuiCanvas Parent;

GuiOffscreenCanvas();
~GuiOffscreenCanvas();

bool onAdd();
void onRemove();

void renderFrame(bool preRenderOnly, bool bufferSwap);

Point2I getWindowSize();

Point2I getCursorPos();
void setCursorPos(const Point2I &pt);
void showCursor(bool state);
bool isCursorShown();

void _onTextureEvent( GFXTexCallbackCode code );

void _setupTargets();
void _teardownTargets();

NamedTexTargetRef getTarget() { return &mNamedTarget; }

void markDirty() { mTargetDirty = true; }

static void initPersistFields();

DECLARE_CONOBJECT(GuiOffscreenCanvas);

protected:
GFXTextureTargetRef mTarget;
NamedTexTarget mNamedTarget;
GFXTexHandle mTargetTexture;

GFXFormat mTargetFormat;
Point2I mTargetSize;
String mTargetName;

bool mTargetDirty;
bool mDynamicTarget;

public:
static Vector<GuiOffscreenCanvas*> sList;
};

#endif
24 changes: 24 additions & 0 deletions Engine/source/math/mathUtils.cpp
Expand Up @@ -21,6 +21,7 @@
//-----------------------------------------------------------------------------

#include "platform/platform.h"
#include "math/util/frustum.h"
#include "math/mathUtils.h"

#include "math/mMath.h"
Expand Down Expand Up @@ -1409,6 +1410,29 @@ void makeProjection( MatrixF *outMatrix,

//-----------------------------------------------------------------------------

void makeFovPortFrustum(
Frustum *outFrustum,
bool isOrtho,
F32 nearDist,
F32 farDist,
const FovPort &inPort,
const MatrixF &transform)
{
F32 leftSize = nearDist * inPort.leftTan;
F32 rightSize = nearDist * inPort.rightTan;
F32 upSize = nearDist * inPort.upTan;
F32 downSize = nearDist * inPort.downTan;

F32 left = -leftSize;
F32 right = rightSize;
F32 top = upSize;
F32 bottom = -downSize;

outFrustum->set(isOrtho, left, right, top, bottom, nearDist, farDist, transform);
}

//-----------------------------------------------------------------------------

/// This is the special rotation matrix applied to
/// projection matricies for GFX.
///
Expand Down
11 changes: 11 additions & 0 deletions Engine/source/math/mathUtils.h
Expand Up @@ -39,6 +39,10 @@
#include "core/util/tVector.h"
#endif

#ifndef _MATHUTIL_FRUSTUM_H_
#include "math/util/frustum.h"
#endif


class Box3F;
class RectI;
Expand Down Expand Up @@ -326,6 +330,13 @@ namespace MathUtils
F32 aspectRatio,
F32 nearPlane );

void makeFovPortFrustum( Frustum *outFrustum,
bool isOrtho,
F32 nearDist,
F32 farDist,
const FovPort &inPort,
const MatrixF &transform = MatrixF(1) );

/// Build a GFX projection matrix from the frustum parameters
/// including the optional rotation required by GFX.
void makeProjection( MatrixF *outMatrix,
Expand Down
4 changes: 3 additions & 1 deletion Engine/source/math/util/frustum.cpp
Expand Up @@ -28,6 +28,8 @@
#include "math/mSphere.h"
#include "platform/profiler.h"

static const MatrixF sGFXProjRotMatrix( EulerF( (M_PI_F / 2.0f), 0.0f, 0.0f ) );


//TODO: For OBB/frustum intersections and ortho frustums, we can resort to a much quicker AABB/OBB test

Expand Down Expand Up @@ -174,7 +176,7 @@ void Frustum::set( const MatrixF &projMat, bool normalize )
mPlanes[ i ].normalize();
}

/* // Create the corner points via plane intersections.
/*// Create the corner points via plane intersections.
mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneLeft ], &mPoints[ NearTopLeft ] );
mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneRight ], &mPoints[ NearTopRight ] );
mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneLeft ], &mPoints[ NearBottomLeft ] );
Expand Down
8 changes: 8 additions & 0 deletions Engine/source/math/util/frustum.h
Expand Up @@ -53,6 +53,14 @@

class OrientedBox3F;

/// Advanced fov specification for oculus
struct FovPort
{
float upTan;
float downTan;
float leftTan;
float rightTan;
};

/// Polyhedron data for use by frustums. Uses fixed-size vectors
/// and a static vector for the edge list as that never changes
Expand Down
199 changes: 0 additions & 199 deletions Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.cpp

This file was deleted.

69 changes: 0 additions & 69 deletions Engine/source/platform/input/oculusVR/barrelDistortionPostEffect.h

This file was deleted.

705 changes: 278 additions & 427 deletions Engine/source/platform/input/oculusVR/oculusVRDevice.cpp

Large diffs are not rendered by default.

81 changes: 38 additions & 43 deletions Engine/source/platform/input/oculusVR/oculusVRDevice.h
Expand Up @@ -32,7 +32,8 @@
#include "core/util/tSingleton.h"
#include "math/mQuat.h"
#include "math/mPoint4.h"
#include "OVR.h"
#include "gfx/gfxDevice.h"
#include "OVR_CAPI_0_5_0.h"

#define DEFAULT_RIFT_UNIT 0

Expand All @@ -44,13 +45,10 @@ class OculusVRDevice : public IInputDevice, public IDisplayDevice
// If no HMD is present simulate it being available
static bool smSimulateHMD;

// Use the chromatic aberration correction version of the barrel
// distortion shader.
static bool smUseChromaticAberrationCorrection;

// Type of rotation events to broadcast
static bool smGenerateAngleAxisRotationEvents;
static bool smGenerateEulerRotationEvents;
static bool smGeneratePositionEvents;

// Broadcast sensor rotation as axis
static bool smGenerateRotationAsAxisEvents;
Expand All @@ -66,37 +64,24 @@ class OculusVRDevice : public IInputDevice, public IDisplayDevice
// should be buffered.
static bool smGenerateWholeFrameEvents;

protected:
class DeviceListener : public OVR::MessageHandler
{
protected:
OculusVRDevice* mOwner;

public:
DeviceListener(OculusVRDevice* owner) { mOwner = owner; }
virtual ~DeviceListener() { mOwner = NULL; }
/// Determines desired pixel density for render target
static F32 smDesiredPixelDensity;

virtual void OnMessage(const OVR::Message&);
};
/// Determined if the window is moved to the oculus display
static bool smWindowDebug;

// Our OVR SDK device listener class
DeviceListener* mListener;
static F32 smPositionTrackingScale;

// The OVR SDK device manager
OVR::DeviceManager* mDeviceManager;
protected:

// Discovered HMD devices
Vector<OculusVRHMDDevice*> mHMDDevices;

// Discovered sensor devices
Vector<OculusVRSensorDevice*> mSensorDevices;

/// Is the device active
bool mActive;

// Should the input texture into the HMD (the render target that the scene has been
// rendered to) be scaled according to the HMD's distortion calculation?
bool mScaleInputTexture;
/// Which HMD is the active one
U32 mActiveDeviceId;

protected:
void cleanUp();
Expand All @@ -105,14 +90,10 @@ class OculusVRDevice : public IInputDevice, public IDisplayDevice
/// Input Event Manager
void buildCodeTable();

void addHMDDevice(OVR::HMDDevice* hmd);
void addHMDDevice(ovrHmd hmd);

void createSimulatedHMD();

void addSensorDevice(OVR::SensorDevice* sensor);

void createSimulatedSensor();

public:
OculusVRDevice();
~OculusVRDevice();
Expand All @@ -128,36 +109,50 @@ class OculusVRDevice : public IInputDevice, public IDisplayDevice
bool process();

// IDisplayDevice
virtual bool providesYFOV() const;
virtual F32 getYFOV() const;
virtual bool providesEyeOffset() const;
virtual const Point3F& getEyeOffset() const;
virtual bool providesFrameEyePose() const;
virtual void getFrameEyePose(DisplayPose *outPose, U32 eyeId) const;
virtual bool providesEyeOffsets() const;
virtual void getEyeOffsets(Point3F *dest) const;
virtual bool providesFovPorts() const;
virtual void getFovPorts(FovPort *out) const;
virtual bool providesProjectionOffset() const;
virtual const Point2F& getProjectionOffset() const;
virtual void getStereoViewports(RectI *out) const;
virtual void getStereoTargets(GFXTextureTarget **out) const;
virtual void onStartFrame();

// HMDs
U32 getHMDCount() const { return mHMDDevices.size(); }
const OculusVRHMDDevice* getHMDDevice(U32 index) const;
OculusVRHMDDevice* getHMDDevice(U32 index) const;
F32 getHMDCurrentIPD(U32 index);
void setHMDCurrentIPD(U32 index, F32 ipd);

// Sensors
U32 getSensorCount() const { return mSensorDevices.size(); }
U32 getSensorCount() const { return mHMDDevices.size(); }
const OculusVRSensorDevice* getSensorDevice(U32 index) const;
EulerF getSensorEulerRotation(U32 index);
VectorF getSensorAcceleration(U32 index);
EulerF getSensorAngularVelocity(U32 index);
VectorF getSensorMagnetometer(U32 index);
F32 getSensorPredictionTime(U32 index);
void setSensorPredictionTime(U32 index, F32 dt);
void setAllSensorPredictionTime(F32 dt);
bool getSensorGravityCorrection(U32 index);
void setSensorGravityCorrection(U32 index, bool state);
bool getSensorYawCorrection(U32 index);
void setSensorYawCorrection(U32 index, bool state);
bool getSensorMagnetometerCalibrated(U32 index);

void setOptimalDisplaySize(U32 idx, GuiCanvas *canvas);
void resetAllSensors();

bool isDiplayingWarning();
void dismissWarning();

String dumpMetrics(U32 idx);


void setDrawCanvas(GuiCanvas *canvas);

virtual void setCurrentConnection(GameConnection *connection);
virtual GameConnection* getCurrentConnection();

bool _handleDeviceEvent( GFXDevice::GFXDeviceEventType evt );

public:
// For ManagedSingleton.
static const char* getSingletonName() { return "OculusVRDevice"; }
Expand Down
660 changes: 536 additions & 124 deletions Engine/source/platform/input/oculusVR/oculusVRHMDDevice.cpp

Large diffs are not rendered by default.

180 changes: 100 additions & 80 deletions Engine/source/platform/input/oculusVR/oculusVRHMDDevice.h
Expand Up @@ -30,7 +30,16 @@
#include "math/mPoint4.h"
#include "platform/input/oculusVR/oculusVRConstants.h"
#include "platform/types.h"
#include "OVR.h"
#include "gfx/gfxTextureHandle.h"
#include "math/mRect.h"
#include "gfx/gfxDevice.h"

#include "OVR_CAPI_0_5_0.h"

class GuiCanvas;
class GameConnection;
struct DisplayPose;
class OculusVRSensorDevice;

class OculusVRHMDDevice
{
Expand All @@ -42,9 +51,19 @@ class OculusVRHMDDevice
protected:
bool mIsValid;

bool mIsSimulation;
bool mVsync;
bool mTimewarp;

bool mRenderConfigurationDirty;
bool mFrameReady;

ovrHmd mDevice;

U32 mSupportedDistortionCaps;
U32 mCurrentDistortionCaps;

OVR::HMDDevice* mDevice;
U32 mSupportedCaps;
U32 mCurrentCaps;

// From OVR::DeviceInfo
String mProductName;
Expand All @@ -66,13 +85,6 @@ class OculusVRHMDDevice
// Physical screen size in meters
Point2F mScreenSize;

// Physical offset from the top of the screen to the center of the
// eye, in meters. Usually half of the vertical physical screen size
F32 mVerticalEyeCenter;

// Physical distance from the eye to the screen
F32 mEyeToScreen;

// Physical distance between lens centers, in meters
F32 mLensSeparation;

Expand All @@ -82,50 +94,25 @@ class OculusVRHMDDevice
// Physical distance between the user's eye centers
F32 mInterpupillaryDistance;

// The eye IPD as a Point3F
Point3F mEyeWorldOffset;

// Radial distortion correction coefficients used by the barrel distortion shader
Point4F mKDistortion;

// Chromatic aberration correction coefficients
Point4F mChromaticAbCorrection;

// Calculated values of eye x offset from center in normalized (uv) coordinates
// where each eye is 0..1. Used for the mono to stereo postFX to simulate an
// eye offset of the camera. The x component is the left eye, the y component
// is the right eye.
Point2F mEyeUVOffset;

// Used to adjust where an eye's view is rendered to account for the lenses not
// being in the center of the physical screen half.
F32 mXCenterOffset;
// The amount to offset the projection matrix to account for the eye not being in the
// center of the screen.
Point2F mProjectionCenterOffset;

// When calculating the distortion scale to use to increase the size of the input texture
// this determines how we should attempt to fit the distorted view into the backbuffer.
Point2F mDistortionFit;
// Current pose of eyes
ovrPosef mCurrentEyePoses[2];
ovrEyeRenderDesc mEyeRenderDesc[2];

// Is the factor by which the input texture size is increased to make post-distortion
// result distortion fit the viewport. If the input texture is the same size as the
// backbuffer, then this should be 1.0.
F32 mDistortionScale;
ovrFovPort mCurrentFovPorts[2];

// Aspect ratio for a single eye
F32 mAspectRatio;
Point2I mWindowSize;

// Vertical field of view
F32 mYFOV;
GameConnection *mConnection;

// The amount to offset the projection matrix to account for the eye not being in the
// center of the screen.
Point2F mProjectionCenterOffset;
OculusVRSensorDevice *mSensor;
U32 mActionCodeIndex;

protected:
F32 calcScale(F32 fitRadius);

void calculateValues(bool calculateDistortionScale);

void createSimulatedPreviewRift(bool calculateDistortionScale);
void updateRenderInfo();

public:
OculusVRHMDDevice();
Expand All @@ -134,13 +121,12 @@ class OculusVRHMDDevice
void cleanUp();

// Set the HMD properties based on information from the OVR device
void set(OVR::HMDDevice* hmd, OVR::HMDInfo& info, bool calculateDistortionScale);
void set(ovrHmd hmd, U32 actionCodeIndex);

// Set the HMD properties based on a simulation of the given type
void createSimulation(SimulationTypes simulationType, bool calculateDistortionScale);
// Sets optimal display size for canvas
void setOptimalDisplaySize(GuiCanvas *canvas);

bool isValid() const {return mIsValid;}
bool isSimulated() const {return mIsSimulation;}

const char* getProductName() const { return mProductName.c_str(); }
const char* getManufacturer() const { return mManufacturer.c_str(); }
Expand All @@ -161,13 +147,6 @@ class OculusVRHMDDevice
// Physical screen size in meters
const Point2F& getScreenSize() const { return mScreenSize; }

// Physical offset from the top of the screen to the center of the
// eye, in meters. Usually half of the vertical physical screen size
F32 getVerticalEyeCenter() const { return mVerticalEyeCenter; }

// Physical distance from the eye to the screen
F32 getEyeToScreen() const { return mEyeToScreen; }

// Physical distance between lens centers, in meters
F32 getLensSeparation() const { return mLensSeparation; }

Expand All @@ -178,37 +157,78 @@ class OculusVRHMDDevice
F32 getIPD() const { return mInterpupillaryDistance; }

// Set a new physical distance between the user's eye centers
void setIPD(F32 ipd, bool calculateDistortionScale);
void setIPD(F32 ipd);

// Provides the IPD of one eye as a Point3F
const Point3F& getEyeWorldOffset() const { return mEyeWorldOffset; }
// The amount to offset the projection matrix to account for the eye not being in the
// center of the screen.
const Point2F& getProjectionCenterOffset() const { return mProjectionCenterOffset; }

void getStereoViewports(RectI *dest) const { dMemcpy(dest, mEyeViewport, sizeof(mEyeViewport)); }
void getStereoTargets(GFXTextureTarget **dest) const { dest[0] = mEyeRT[0]; dest[1] = mEyeRT[1]; }

// Radial distortion correction coefficients used by the barrel distortion shader
const Point4F& getKDistortion() const { return mKDistortion; }
void getFovPorts(FovPort *dest) const { dMemcpy(dest, mCurrentFovPorts, sizeof(mCurrentFovPorts)); }

/// Returns eye offsets in torque coordinate space, i.e. z being up, x being left-right, and y being depth (forward).
void getEyeOffsets(Point3F *offsets) const {
offsets[0] = Point3F(-mEyeRenderDesc[0].HmdToEyeViewOffset.x, mEyeRenderDesc[0].HmdToEyeViewOffset.z, -mEyeRenderDesc[0].HmdToEyeViewOffset.y);
offsets[1] = Point3F(-mEyeRenderDesc[1].HmdToEyeViewOffset.x, mEyeRenderDesc[1].HmdToEyeViewOffset.z, -mEyeRenderDesc[1].HmdToEyeViewOffset.y); }

// Chromatic aberration correction coefficients used by the barrel distortion shader
const Point4F& getChromaticAbCorrection() const { return mChromaticAbCorrection; }
void getFrameEyePose(DisplayPose *outPose, U32 eyeId) const;

// Calculated values of eye x offset from center in normalized (uv) coordinates.
const Point2F& getEyeUVOffset() const { return mEyeUVOffset; }
void updateCaps();

// Used to adjust where an eye's view is rendered to account for the lenses not
// being in the center of the physical screen half.
F32 getCenterOffset() const { return mXCenterOffset; }
void onStartFrame();
void onEndFrame();
void onDeviceDestroy();

// Is the factor by which the input texture size is increased to make post-distortion
// result distortion fit the viewport.
F32 getDistortionScale() const { return mDistortionScale; }
Point2I generateRenderTarget(GFXTextureTargetRef &target, GFXTexHandle &texture, GFXTexHandle &depth, Point2I desiredSize);
void clearRenderTargets();

// Aspect ration for a single eye
F32 getAspectRation() const { return mAspectRatio; }
bool isDisplayingWarning();
void dismissWarning();

// Vertical field of view
F32 getYFOV() const { return mYFOV; }
bool setupTargets();

// The amount to offset the projection matrix to account for the eye not being in the
// center of the screen.
const Point2F& getProjectionCenterOffset() const { return mProjectionCenterOffset; }
/// Designates canvas we are drawing to. Also updates render targets
void setDrawCanvas(GuiCanvas *canvas) { if (mDrawCanvas != canvas) { mDrawCanvas = canvas; } updateRenderInfo(); }

virtual void setCurrentConnection(GameConnection *connection) { mConnection = connection; }
virtual GameConnection* getCurrentConnection() { return mConnection; }

String dumpMetrics();

// Stereo RT
GFXTexHandle mStereoTexture;
GFXTexHandle mStereoDepthTexture;
GFXTextureTargetRef mStereoRT;

// Eye RTs (if we are using separate targets)
GFXTextureTargetRef mEyeRT[2];
GFXTexHandle mEyeTexture[2];
GFXTexHandle mEyeDepthTexture[2];

// Current render target size for each eye
Point2I mEyeRenderSize[2];

// Recommended eye target size for each eye
ovrSizei mRecomendedEyeTargetSize[2];

// Desired viewport for each eye
RectI mEyeViewport[2];

F32 mCurrentPixelDensity;
F32 smDesiredPixelDensity;

ovrTrackingState mLastTrackingState;

GFXDevice::GFXDeviceRenderStyles mDesiredRenderingMode;

GFXFormat mRTFormat;

// Canvas we should be drawing
GuiCanvas *mDrawCanvas;

OculusVRSensorDevice *getSensorDevice() { return mSensor; }
};

#endif // _OCULUSVRHMDDEVICE_H_
66 changes: 25 additions & 41 deletions Engine/source/platform/input/oculusVR/oculusVRSensorData.cpp
Expand Up @@ -20,6 +20,7 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "platform/input/oculusVR/oculusVRDevice.h"
#include "platform/input/oculusVR/oculusVRSensorData.h"
#include "platform/input/oculusVR/oculusVRUtil.h"
#include "console/console.h"
Expand All @@ -32,66 +33,44 @@ OculusVRSensorData::OculusVRSensorData()
void OculusVRSensorData::reset()
{
mDataSet = false;
mStatusFlags = 0;
}

void OculusVRSensorData::setData(OVR::SensorFusion& data, const F32& maxAxisRadius)
void OculusVRSensorData::setData(ovrTrackingState& data, const F32& maxAxisRadius)
{
// Sensor rotation
OVR::Quatf orientation;
if(data.GetPredictionDelta() > 0)
{
orientation = data.GetPredictedOrientation();
}
else
{
orientation = data.GetOrientation();
}
// Sensor rotation & position
OVR::Posef pose = data.HeadPose.ThePose;
OVR::Quatf orientation = pose.Rotation;
OVR::Vector3f position = data.HeadPose.ThePose.Position;

mPosition = Point3F(-position.z, position.x, position.y);
mPosition *= OculusVRDevice::smPositionTrackingScale;

OVR::Matrix4f orientMat(orientation);
OculusVRUtil::convertRotation(orientMat.M, mRot);
mRotQuat.set(mRot);

// Sensor rotation in Euler format
OculusVRUtil::convertRotation(orientation, mRotEuler);
OculusVRUtil::convertRotation(orientation, mRotEuler); // mRotEuler == pitch, roll, yaw FROM yaw, pitch, roll

//mRotEuler = EulerF(0,0,0);
float hmdYaw, hmdPitch, hmdRoll;
orientation.GetEulerAngles<OVR::Axis_Y, OVR::Axis_X, OVR::Axis_Z>(&hmdYaw, &hmdPitch, &hmdRoll);

// Sensor rotation as axis
OculusVRUtil::calculateAxisRotation(mRot, maxAxisRadius, mRotAxis);

// Sensor raw values
OVR::Vector3f accel = data.GetAcceleration();
OVR::Vector3f accel = data.HeadPose.LinearAcceleration;
OculusVRUtil::convertAcceleration(accel, mAcceleration);

OVR::Vector3f angVel = data.GetAngularVelocity();
OVR::Vector3f angVel = data.HeadPose.AngularVelocity;
OculusVRUtil::convertAngularVelocity(angVel, mAngVelocity);

OVR::Vector3f mag;
if(data.HasMagCalibration() && data.IsYawCorrectionEnabled())
{
mag = data.GetCalibratedMagnetometer();
}
else
{
mag = data.GetMagnetometer();
}
OVR::Vector3f mag = data.RawSensorData.Magnetometer;
OculusVRUtil::convertMagnetometer(mag, mMagnetometer);

mDataSet = true;
}

void OculusVRSensorData::simulateData(const F32& maxAxisRadius)
{
// Sensor rotation
mRot.identity();
mRotQuat.identity();
mRotEuler.zero();

// Sensor rotation as axis
OculusVRUtil::calculateAxisRotation(mRot, maxAxisRadius, mRotAxis);

// Sensor raw values
mAcceleration.zero();
mAngVelocity.zero();
mMagnetometer.zero();

mStatusFlags = data.StatusFlags;
mDataSet = true;
}

Expand Down Expand Up @@ -132,5 +111,10 @@ U32 OculusVRSensorData::compare(OculusVRSensorData* other, bool doRawCompare)
}
}

if (other->mStatusFlags != mStatusFlags)
{
result |= DIFF_STATUS;
}

return result;
}
14 changes: 9 additions & 5 deletions Engine/source/platform/input/oculusVR/oculusVRSensorData.h
Expand Up @@ -27,7 +27,7 @@
#include "math/mMatrix.h"
#include "math/mQuat.h"
#include "math/mPoint2.h"
#include "OVR.h"
#include "OVR_CAPI_0_5_0.h"

struct OculusVRSensorData
{
Expand All @@ -39,13 +39,18 @@ struct OculusVRSensorData
DIFF_ACCEL = (1<<3),
DIFF_ANGVEL = (1<<4),
DIFF_MAG = (1<<5),
DIFF_POS = (1<<6),
DIFF_STATUS = (1<<7),

DIFF_ROTAXIS = (DIFF_ROTAXISX | DIFF_ROTAXISY),
DIFF_RAW = (DIFF_ACCEL | DIFF_ANGVEL | DIFF_MAG),
};

bool mDataSet;

// Position
Point3F mPosition;

// Rotation
MatrixF mRot;
QuatF mRotQuat;
Expand All @@ -59,16 +64,15 @@ struct OculusVRSensorData
EulerF mAngVelocity;
VectorF mMagnetometer;

U32 mStatusFlags;

OculusVRSensorData();

/// Reset the data
void reset();

/// Set data based on given sensor fusion
void setData(OVR::SensorFusion& data, const F32& maxAxisRadius);

/// Simulate valid data
void simulateData(const F32& maxAxisRadius);
void setData(ovrTrackingState& data, const F32& maxAxisRadius);

/// Compare this data and given and return differences
U32 compare(OculusVRSensorData* other, bool doRawCompare);
Expand Down
231 changes: 101 additions & 130 deletions Engine/source/platform/input/oculusVR/oculusVRSensorDevice.cpp

Large diffs are not rendered by default.

70 changes: 32 additions & 38 deletions Engine/source/platform/input/oculusVR/oculusVRSensorDevice.h
Expand Up @@ -30,17 +30,12 @@
#include "math/mPoint4.h"
#include "platform/input/oculusVR/oculusVRConstants.h"
#include "platform/types.h"
#include "OVR.h"
#include "OVR_CAPI_0_5_0.h"

struct OculusVRSensorData;

class OculusVRSensorDevice
{
public:
enum SimulationTypes {
ST_RIFT_PREVIEW,
};

public:
// Action codes
static U32 OVR_SENSORROT[OculusVRConstants::MaxSensors]; // SI_ROT
Expand All @@ -54,15 +49,15 @@ class OculusVRSensorDevice
static U32 OVR_SENSORANGVEL[OculusVRConstants::MaxSensors]; // SI_POS but is EulerF
static U32 OVR_SENSORMAGNETOMETER[OculusVRConstants::MaxSensors]; // SI_POS

static U32 OVR_SENSORPOSITION[OculusVRConstants::MaxSensors];

protected:
bool mIsValid;

bool mIsSimulation;

OVR::SensorDevice* mDevice;

OVR::SensorFusion mSensorFusion;

ovrHmd mDevice;
U32 mCurrentTrackingCaps;
U32 mSupportedTrackingCaps;

// From OVR::DeviceInfo
String mProductName;
String mManufacturer;
Expand All @@ -76,6 +71,12 @@ class OculusVRSensorDevice
// Has yaw correction been disabled by the control panel
bool mYawCorrectionDisabled;

// Has position tracking been disabled
bool mPositionTrackingDisabled;

// Last tracking status
U32 mLastStatus;

// Assigned by the OculusVRDevice
S32 mActionCodeIndex;

Expand All @@ -86,9 +87,6 @@ class OculusVRSensorDevice
// for the sensor
OculusVRSensorData* mPrevData;

protected:
void createSimulatedPreviewRift(S32 actionCodeIndex);

public:
OculusVRSensorDevice();
virtual ~OculusVRSensorDevice();
Expand All @@ -98,44 +96,42 @@ class OculusVRSensorDevice
void cleanUp();

// Set the sensor properties based on information from the OVR device
void set(OVR::SensorDevice* sensor, OVR::SensorInfo& info, S32 actionCodeIndex);

// Set the sensor properties based on a simulation of the given type
void createSimulation(SimulationTypes simulationType, S32 actionCodeIndex);
void set(ovrHmd sensor, S32 actionCodeIndex);

bool isValid() const {return mIsValid;}
bool isSimulated() {return mIsSimulation;}

bool process(U32 deviceType, bool generateRotAsAngAxis, bool generateRotAsEuler, bool generateRotationAsAxisEvents, F32 maxAxisRadius, bool generateRawSensor);
bool process(U32 deviceType, bool generateRotAsAngAxis, bool generateRotAsEuler, bool generateRotationAsAxisEvents, bool generatePositionEvents, F32 maxAxisRadius, bool generateRawSensor);

void reset();

// Get the prediction time for the sensor fusion. The time is in seconds.
F32 getPredictionTime() const;

// Set the prediction time for the sensor fusion. The time is in seconds.
void setPredictionTime(F32 dt);

// Is gravity correction enabled for pitch and roll
bool getGravityCorrection() const;

// Set the pitch and roll gravity correction
void setGravityCorrection(bool state);

// Has yaw correction been disabled using the control panel
bool getYawCorrectionUserDisabled() const { return mYawCorrectionDisabled; }

// Is yaw correction enabled
bool getYawCorrection() const;

// Position is valid
bool getHasValidPosition() const { return mLastStatus & ovrStatus_PositionTracked; }

// Set the yaw correction. Note: if magnetometer calibration data is not present,
// or user has disabled yaw correction in the control panel, this method will
// not enable it.
void setYawCorrection(bool state);

// Sets position tracking state
void setPositionTracking(bool state);

// Is magnetometer calibration data available for this sensor
bool getMagnetometerCalibrationAvailable() const;

// Is position tracking data available for this sensor
bool getOrientationTrackingAvailable() const;

// Is position tracking data available for this sensor
bool getPositionTrackingAvailable() const;

U32 getLastTrackingStatus() const { return mLastStatus; }

const char* getProductName() { return mProductName.c_str(); }
const char* getManufacturer() { return mManufacturer.c_str(); }
U32 getVersion() { return mVersion; }
Expand All @@ -155,12 +151,10 @@ class OculusVRSensorDevice
// Get the current angular velocity reading, in rad/s
EulerF getAngularVelocity();

// Get the current magnetometer reading (direction and field strength), in Gauss.
// Uses magnetometer calibration if set.
VectorF getMagnetometer();
// Get the current position
Point3F getPosition();

// Get the current raw magnetometer reading (direction and field strength), in Gauss
VectorF getRawMagnetometer();
void updateTrackingCaps();
};

#endif // _OCULUSVRSENSORDEVICE_H_
9 changes: 8 additions & 1 deletion Engine/source/platform/input/oculusVR/oculusVRUtil.h
Expand Up @@ -25,7 +25,12 @@

#include "math/mPoint2.h"
#include "math/mMatrix.h"
#include "OVR.h"
#include "OVR_CAPI_0_5_0.h"

// NOTE: math code in oculus uses "Offset" which is a preprocessor macro
#define TorqueOffset Offset
#undef Offset
#include "Extras/OVR_Math.h"

namespace OculusVRUtil
{
Expand All @@ -48,4 +53,6 @@ namespace OculusVRUtil
void convertMagnetometer(OVR::Vector3f& inMagnetometer, VectorF& outMagnetometer);
}

#define Offset TorqueOffset

#endif // _OCULUSVRUTIL_H_
32 changes: 28 additions & 4 deletions Engine/source/platform/output/IDisplayDevice.h
Expand Up @@ -25,20 +25,44 @@

#include "console/consoleTypes.h"

class GameConnection;
class GuiCanvas;

// Defines a custom display device that requires particular rendering settings
// in order for a scene to display correctly.

/// Defines the basic display pose common to most display devices
typedef struct DisplayPose
{
EulerF orientation; /// Direction device is facing
Point3F position; /// Relative position of device in view space
} IDevicePose;

class IDisplayDevice
{
public:
virtual bool providesYFOV() const = 0;
virtual F32 getYFOV() const = 0;
virtual bool providesFrameEyePose() const = 0;
virtual void getFrameEyePose(IDevicePose *pose, U32 eye) const = 0;

virtual bool providesEyeOffsets() const = 0;
/// Returns eye offset not taking into account any position tracking info
virtual void getEyeOffsets(Point3F *dest) const = 0;

virtual bool providesEyeOffset() const = 0;
virtual const Point3F& getEyeOffset() const = 0;
virtual bool providesFovPorts() const = 0;
virtual void getFovPorts(FovPort *out) const = 0;

virtual bool providesProjectionOffset() const = 0;
virtual const Point2F& getProjectionOffset() const = 0;

virtual void getStereoViewports(RectI *out) const = 0;
virtual void getStereoTargets(GFXTextureTarget **out) const = 0;

virtual void setDrawCanvas(GuiCanvas *canvas) = 0;

virtual void setCurrentConnection(GameConnection *connection) = 0;
virtual GameConnection* getCurrentConnection() = 0;

virtual void onStartFrame() = 0;
};

#endif // _IDISPLAYDEVICE_H_
14 changes: 14 additions & 0 deletions Engine/source/postFx/postEffect.cpp
Expand Up @@ -144,6 +144,20 @@ GFX_ImplementTextureProfile( PostFxTextureProfile,
GFXTextureProfile::Static | GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap,
GFXTextureProfile::NONE );

GFX_ImplementTextureProfile( VRTextureProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::RenderTarget |
GFXTextureProfile::NoMipmap,
GFXTextureProfile::NONE );

GFX_ImplementTextureProfile( VRDepthProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::RenderTarget |
GFXTextureProfile::NoMipmap |
GFXTextureProfile::ZTarget,
GFXTextureProfile::NONE );

void PostEffect::EffectConst::set( const String &newVal )
{
Expand Down
4 changes: 4 additions & 0 deletions Engine/source/postFx/postEffectCommon.h
Expand Up @@ -101,6 +101,10 @@ struct PFXFrameState
///
GFX_DeclareTextureProfile( PostFxTextureProfile );

GFX_DeclareTextureProfile( VRTextureProfile );

GFX_DeclareTextureProfile( VRDepthProfile );

///
GFXDeclareVertexFormat( PFXVertex )
{
Expand Down
51 changes: 23 additions & 28 deletions Engine/source/scene/reflector.cpp
Expand Up @@ -606,63 +606,58 @@ void PlaneReflector::updateReflection( const ReflectParams &params )
RectI originalVP = GFX->getViewport();

Point2F projOffset = GFX->getCurrentProjectionOffset();
Point3F eyeOffset = GFX->getStereoEyeOffset();
const FovPort *currentFovPort = GFX->getSteroFovPort();
MatrixF inverseEyeTransforms[2];

// Render left half of display
RectI leftVP = originalVP;
leftVP.extent.x *= 0.5;
GFX->setViewport(leftVP);
// Calculate world transforms for eyes
inverseEyeTransforms[0] = params.query->eyeTransforms[0];
inverseEyeTransforms[1] = params.query->eyeTransforms[1];
inverseEyeTransforms[0].inverse();
inverseEyeTransforms[1].inverse();

Frustum originalFrustum = GFX->getFrustum();

MatrixF leftWorldTrans(true);
leftWorldTrans.setPosition(Point3F(eyeOffset.x, eyeOffset.y, eyeOffset.z));
MatrixF leftWorld(params.query->cameraMatrix);
leftWorld.mulL(leftWorldTrans);
// Render left half of display
GFX->activateStereoTarget(0);
GFX->setWorldMatrix(params.query->eyeTransforms[0]);

Frustum gfxFrustum = GFX->getFrustum();
gfxFrustum.setProjectionOffset(Point2F(projOffset.x, projOffset.y));
Frustum gfxFrustum = originalFrustum;
MathUtils::makeFovPortFrustum(&gfxFrustum, gfxFrustum.isOrtho(), gfxFrustum.getNearDist(), gfxFrustum.getFarDist(), currentFovPort[0], inverseEyeTransforms[0]);
GFX->setFrustum(gfxFrustum);

setGFXMatrices( leftWorld );
setGFXMatrices( params.query->eyeTransforms[0] );

SceneCameraState cameraStateLeft = SceneCameraState::fromGFX();
SceneRenderState renderStateLeft( gClientSceneGraph, SPT_Reflect, cameraStateLeft );
renderStateLeft.setSceneRenderStyle(SRS_SideBySide);
renderStateLeft.setSceneRenderField(0);
renderStateLeft.getMaterialDelegate().bind( REFLECTMGR, &ReflectionManager::getReflectionMaterial );
renderStateLeft.setDiffuseCameraTransform( params.query->cameraMatrix );
renderStateLeft.setDiffuseCameraTransform( params.query->eyeTransforms[0] );
renderStateLeft.disableAdvancedLightingBins(true);

gClientSceneGraph->renderSceneNoLights( &renderStateLeft, objTypeFlag );

// 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(params.query->cameraMatrix);
rightWorld.mulL(rightWorldTrans);

gfxFrustum = GFX->getFrustum();
gfxFrustum.setProjectionOffset(Point2F(-projOffset.x, projOffset.y));
GFX->activateStereoTarget(1);
GFX->setWorldMatrix(params.query->eyeTransforms[1]);

gfxFrustum = originalFrustum;
MathUtils::makeFovPortFrustum(&gfxFrustum, gfxFrustum.isOrtho(), gfxFrustum.getNearDist(), gfxFrustum.getFarDist(), currentFovPort[1], inverseEyeTransforms[1]);
GFX->setFrustum(gfxFrustum);

setGFXMatrices( rightWorld );
setGFXMatrices( params.query->eyeTransforms[1] );

SceneCameraState cameraStateRight = SceneCameraState::fromGFX();
SceneRenderState renderStateRight( gClientSceneGraph, SPT_Reflect, cameraStateRight );
renderStateRight.setSceneRenderStyle(SRS_SideBySide);
renderStateRight.setSceneRenderField(1);
renderStateRight.getMaterialDelegate().bind( REFLECTMGR, &ReflectionManager::getReflectionMaterial );
renderStateRight.setDiffuseCameraTransform( params.query->cameraMatrix );
renderStateRight.setDiffuseCameraTransform( params.query->eyeTransforms[1] );
renderStateRight.disableAdvancedLightingBins(true);

gClientSceneGraph->renderSceneNoLights( &renderStateRight, objTypeFlag );

// Restore previous values
gfxFrustum.clearProjectionOffset();
GFX->setFrustum(gfxFrustum);
GFX->setViewport(originalVP);
}
Expand Down
53 changes: 20 additions & 33 deletions Engine/source/scene/sceneManager.cpp
Expand Up @@ -36,6 +36,7 @@
#include "console/engineAPI.h"
#include "sim/netConnection.h"
#include "T3D/gameBase/gameConnection.h"
#include "math/mathUtils.h"

// For player object bounds workaround.
#include "T3D/player.h"
Expand Down Expand Up @@ -236,26 +237,21 @@ void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, S
// Store previous values
RectI originalVP = GFX->getViewport();
MatrixF originalWorld = GFX->getWorldMatrix();
Frustum originalFrustum = GFX->getFrustum();

Point2F projOffset = GFX->getCurrentProjectionOffset();
Point3F eyeOffset = GFX->getStereoEyeOffset();
const FovPort *currentFovPort = GFX->getSteroFovPort();
const MatrixF *eyeTransforms = GFX->getStereoEyeTransforms();
const MatrixF *worldEyeTransforms = GFX->getInverseStereoEyeTransforms();

// Indicate that we're about to start a field
// Render left half of display
GFX->activateStereoTarget(0);
GFX->beginField();

// 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->setWorldMatrix(worldEyeTransforms[0]);

Frustum gfxFrustum = originalFrustum;
MathUtils::makeFovPortFrustum(&gfxFrustum, gfxFrustum.isOrtho(), gfxFrustum.getNearDist(), gfxFrustum.getFarDist(), currentFovPort[0], eyeTransforms[0]);
GFX->setFrustum(gfxFrustum);

SceneCameraState cameraStateLeft = SceneCameraState::fromGFX();
Expand All @@ -266,25 +262,16 @@ void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, S
renderSceneNoLights( &renderStateLeft, objectMask, baseObject, baseZone );

// Indicate that we've just finished a field
//GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, ColorI(255,0,0), 1.0f, 0);
GFX->endField();

// Indicate that we're about to start a field

// Render right half of display
GFX->activateStereoTarget(1);
GFX->beginField();
GFX->setWorldMatrix(worldEyeTransforms[1]);

// 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));
gfxFrustum = originalFrustum;
MathUtils::makeFovPortFrustum(&gfxFrustum, gfxFrustum.isOrtho(), gfxFrustum.getNearDist(), gfxFrustum.getFarDist(), currentFovPort[1], eyeTransforms[1]);
GFX->setFrustum(gfxFrustum);

SceneCameraState cameraStateRight = SceneCameraState::fromGFX();
Expand All @@ -295,12 +282,12 @@ void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, S
renderSceneNoLights( &renderStateRight, objectMask, baseObject, baseZone );

// Indicate that we've just finished a field
//GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, ColorI(0,255,0), 1.0f, 0);
GFX->endField();

// Restore previous values
GFX->setWorldMatrix(originalWorld);
gfxFrustum.clearProjectionOffset();
GFX->setFrustum(gfxFrustum);
GFX->setFrustum(originalFrustum);
GFX->setViewport(originalVP);
}
else
Expand Down
3 changes: 2 additions & 1 deletion Templates/Empty/game/core/scripts/client/cursor.cs
Expand Up @@ -62,11 +62,12 @@ package CanvasCursorPackage
if ((%control.noCursor $= "") || !%control.noCursor)
{
showCursor();
return;
return true;
}
}
// If we get here, every control requested a hidden cursor, so we oblige.
hideCursor();
return false;
}

//---------------------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions Templates/Empty/game/core/scripts/client/metrics.cs
Expand Up @@ -243,9 +243,9 @@ function metrics( %expr )

if( %metricsExpr !$= "" )
{
Canvas.pushDialog( FrameOverlayGui, 1000 );
$GameCanvas.pushDialog( FrameOverlayGui, 1000 );
TextOverlayControl.setValue( %metricsExpr );
}
else
Canvas.popDialog(FrameOverlayGui);
$GameCanvas.popDialog(FrameOverlayGui);
}
153 changes: 134 additions & 19 deletions Templates/Empty/game/core/scripts/client/oculusVR.cs
Expand Up @@ -24,12 +24,103 @@
if(!isFunction(isOculusVRDeviceActive))
return;

function setupOculusActionMaps()
{
if (isObject(OculusWarningMap))
return;

new ActionMap(OculusWarningMap);
new ActionMap(OculusCanvasMap);

OculusWarningMap.bind(keyboard, space, dismissOculusVRWarnings);

OculusCanvasMap.bind( mouse, xaxis, oculusYaw );
OculusCanvasMap.bind( mouse, yaxis, oculusPitch );
OculusCanvasMap.bind( mouse, button0, oculusClick );
}

function oculusYaw(%val)
{
OculusCanvas.cursorNudge(%val * 0.10, 0);
}

function oculusPitch(%val)
{
OculusCanvas.cursorNudge(0, %val * 0.10);
}

function oculusClick(%active)
{
OculusCanvas.cursorClick(0, %active);
}

function GuiOffscreenCanvas::checkCursor(%this)
{
%count = %this.getCount();
for(%i = 0; %i < %count; %i++)
{
%control = %this.getObject(%i);
if ((%control.noCursor $= "") || !%control.noCursor)
{
%this.cursorOn();
return true;
}
}
// If we get here, every control requested a hidden cursor, so we oblige.

%this.cursorOff();
return false;
}

function GuiOffscreenCanvas::pushDialog(%this, %ctrl, %layer, %center)
{
Parent::pushDialog(%this, %ctrl, %layer, %center);
%cursorVisible = %this.checkCursor();

if (%cursorVisible)
{
echo("OffscreenCanvas visible");
OculusCanvasMap.pop();
OculusCanvasMap.push();
}
else
{
echo("OffscreenCanvas not visible");
OculusCanvasMap.pop();
}
}

function GuiOffscreenCanvas::popDialog(%this, %ctrl)
{
Parent::popDialog(%this, %ctrl);
%cursorVisible = %this.checkCursor();

if (%cursorVisible)
{
echo("OffscreenCanvas visible");
OculusCanvasMap.pop();
OculusCanvasMap.push();
}
else
{
echo("OffscreenCanvas not visible");
OculusCanvasMap.pop();
}
}


//-----------------------------------------------------------------------------

function oculusSensorMetricsCallback()
{
return " | OVR Sensor 0 |" @
" rot: " @ getOVRSensorEulerRotation(0);
return ovrDumpMetrics(0);
}


//-----------------------------------------------------------------------------
function onOculusStatusUpdate(%status)
{
$LastOculusTrackingState = %status;
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -60,23 +151,34 @@ function enableOculusVRDisplay(%gameConnection, %trueStereoRendering)
{
setOVRHMDAsGameConnectionDisplayDevice(%gameConnection);
PlayGui.renderStyle = "stereo side by side";

if(%trueStereoRendering)
setOptimalOVRCanvasSize(Canvas);

if (!isObject(OculusCanvas))
{
if($pref::OculusVR::UseChromaticAberrationCorrection)
{
OVRBarrelDistortionChromaPostFX.isEnabled = true;
}
else
{
OVRBarrelDistortionPostFX.isEnabled = true;
}
new GuiOffscreenCanvas(OculusCanvas) {
targetSize = "512 512";
targetName = "oculusCanvas";
dynamicTarget = true;
};
}
else

if (!isObject(OculusVROverlay))
{
OVRBarrelDistortionMonoPostFX.isEnabled = true;
exec("./oculusVROverlay.gui");
}

OculusCanvas.setContent(OculusVROverlay);
OculusCanvas.setCursor(DefaultCursor);
PlayGui.setStereoGui(OculusCanvas);
OculusCanvas.setCursorPos("128 128");
OculusCanvas.cursorOff();
$GameCanvas = OculusCanvas;

%ext = Canvas.getExtent();
$OculusMouseScaleX = 512.0 / 1920.0;
$OculusMouseScaleY = 512.0 / 1060.0;

//$gfx::wireframe = true;
// Reset all sensors
ovrResetAllSensors();
}
Expand All @@ -85,19 +187,23 @@ function enableOculusVRDisplay(%gameConnection, %trueStereoRendering)
// and barrel distortion for the Rift.
function disableOculusVRDisplay(%gameConnection)
{
%gameConnection.clearDisplayDevice();
OculusCanvas.popDialog();
OculusWarningMap.pop();
$GameCanvas = Canvas;

if (isObject(gameConnection))
{
%gameConnection.clearDisplayDevice();
}
PlayGui.renderStyle = "standard";
OVRBarrelDistortionPostFX.isEnabled = false;
OVRBarrelDistortionChromaPostFX.isEnabled = false;
OVRBarrelDistortionMonoPostFX.isEnabled = false;
}

// Helper function to set the standard Rift control scheme. You could place
// this function in GameConnection::initialControlSet() at the same time
// you call enableOculusVRDisplay().
function setStandardOculusVRControlScheme(%gameConnection)
{
if(isOVRHMDSimulated(0))
if($OculusVR::SimulateInput)
{
// We are simulating a HMD so allow the mouse and gamepad to control
// both yaw and pitch.
Expand Down Expand Up @@ -131,3 +237,12 @@ function resetOculusVRSensors()
{
ovrResetAllSensors();
}

function dismissOculusVRWarnings(%value)
{
//if (%value)
//{
ovrDismissWarnings();
OculusWarningMap.pop();
//}
}
19 changes: 19 additions & 0 deletions Templates/Empty/game/core/scripts/client/oculusVROverlay.gui
@@ -0,0 +1,19 @@
//--- OBJECT WRITE BEGIN ---
%guiContent = singleton GuiControl(OculusVROverlay) {
canSaveDynamicFields = "0";
Enabled = "1";
isContainer = "1";
Profile = "GuiContentProfile";
HorizSizing = "width";
VertSizing = "height";
Position = "0 0";
Extent = "512 512";
MinExtent = "8 8";
canSave = "1";
Visible = "1";
tooltipprofile = "GuiToolTipProfile";
hovertime = "1000";
useVariable = "0";
tile = "0";
};
//--- OBJECT WRITE END ---
21 changes: 11 additions & 10 deletions Templates/Empty/game/core/scripts/gui/messageBoxes/messageBox.ed.cs
Expand Up @@ -20,6 +20,7 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

$GameCanvas = 0;

// Cleanup Dialog created by 'core'
if( isObject( MessagePopupDlg ) )
Expand Down Expand Up @@ -76,7 +77,7 @@
//---------------------------------------------------------------------------------------------
function messageCallback(%dlg, %callback)
{
Canvas.popDialog(%dlg);
$GameCanvas.popDialog(%dlg);
eval(%callback);
}

Expand All @@ -89,7 +90,7 @@ function IOCallback(%dlg, %callback)
%callback = strreplace(%callback, "#", %text);
eval(%callback);

Canvas.popDialog(%dlg);
$GameCanvas.popDialog(%dlg);
}

//---------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -134,7 +135,7 @@ function MBSetText(%text, %frame, %msg)
function MessageBoxOK(%title, %message, %callback)
{
MBOKFrame.text = %title;
Canvas.pushDialog(MessageBoxOKDlg);
$GameCanvas.pushDialog(MessageBoxOKDlg);
MBSetText(MBOKText, MBOKFrame, %message);
MessageBoxOKDlg.callback = %callback;
}
Expand All @@ -147,7 +148,7 @@ function MessageBoxOK(%title, %message, %callback)
function MessageBoxOKCancel(%title, %message, %callback, %cancelCallback)
{
MBOKCancelFrame.text = %title;
Canvas.pushDialog(MessageBoxOKCancelDlg);
$GameCanvas.pushDialog(MessageBoxOKCancelDlg);
MBSetText(MBOKCancelText, MBOKCancelFrame, %message);
MessageBoxOKCancelDlg.callback = %callback;
MessageBoxOKCancelDlg.cancelCallback = %cancelCallback;
Expand All @@ -169,7 +170,7 @@ function MessageBoxOKCancelDetails(%title, %message, %details, %callback, %cance

MBOKCancelDetailsFrame.setText( %title );

Canvas.pushDialog(MessageBoxOKCancelDetailsDlg);
$GameCanvas.pushDialog(MessageBoxOKCancelDetailsDlg);
MBSetText(MBOKCancelDetailsText, MBOKCancelDetailsFrame, %message);
MBOKCancelDetailsInfoText.setText(%details);

Expand Down Expand Up @@ -233,7 +234,7 @@ function MessageBoxYesNo(%title, %message, %yesCallback, %noCallback)
{
MBYesNoFrame.text = %title;
MessageBoxYesNoDlg.profile = "GuiOverlayProfile";
Canvas.pushDialog(MessageBoxYesNoDlg);
$GameCanvas.pushDialog(MessageBoxYesNoDlg);
MBSetText(MBYesNoText, MBYesNoFrame, %message);
MessageBoxYesNoDlg.yesCallBack = %yesCallback;
MessageBoxYesNoDlg.noCallback = %noCallBack;
Expand All @@ -243,7 +244,7 @@ function MessageBoxYesNoCancel(%title, %message, %yesCallback, %noCallback, %can
{
MBYesNoCancelFrame.text = %title;
MessageBoxYesNoDlg.profile = "GuiOverlayProfile";
Canvas.pushDialog(MessageBoxYesNoCancelDlg);
$GameCanvas.pushDialog(MessageBoxYesNoCancelDlg);
MBSetText(MBYesNoCancelText, MBYesNoCancelFrame, %message);
MessageBoxYesNoCancelDlg.yesCallBack = %yesCallback;
MessageBoxYesNoCancelDlg.noCallback = %noCallBack;
Expand All @@ -264,7 +265,7 @@ function MessagePopup(%title, %message, %delay)
{
// Currently two lines max.
MessagePopFrame.setText(%title);
Canvas.pushDialog(MessagePopupDlg);
$GameCanvas.pushDialog(MessagePopupDlg);
MBSetText(MessagePopText, MessagePopFrame, %message);
if (%delay !$= "")
schedule(%delay, 0, CloseMessagePopup);
Expand All @@ -279,7 +280,7 @@ function MessagePopup(%title, %message, %delay)
function IODropdown(%title, %message, %simgroup, %callback, %cancelCallback)
{
IODropdownFrame.text = %title;
Canvas.pushDialog(IODropdownDlg);
$GameCanvas.pushDialog(IODropdownDlg);
MBSetText(IODropdownText, IODropdownFrame, %message);

if(isObject(%simgroup))
Expand All @@ -305,7 +306,7 @@ function IODropdown(%title, %message, %simgroup, %callback, %cancelCallback)

function CloseMessagePopup()
{
Canvas.popDialog(MessagePopupDlg);
$GameCanvas.popDialog(MessagePopupDlg);
}

//---------------------------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions Templates/Empty/game/main.cs
Expand Up @@ -43,6 +43,8 @@ function createCanvas(%windowTitle)
displayWindow = $platform !$= "windows";
};

$GameCanvas = %foo;

// Set the window title
if (isObject(Canvas))
Canvas.setWindowTitle(getEngineName() @ " - " @ $appName);
Expand Down
2 changes: 2 additions & 0 deletions Templates/Empty/game/main.cs.in
Expand Up @@ -42,6 +42,8 @@ function createCanvas(%windowTitle)
{
displayWindow = $platform !$= "windows";
};

$GameCanvas = %foo;

// Set the window title
if (isObject(Canvas))
Expand Down
3 changes: 2 additions & 1 deletion Templates/Full/game/core/scripts/client/cursor.cs
Expand Up @@ -62,11 +62,12 @@ package CanvasCursorPackage
if ((%control.noCursor $= "") || !%control.noCursor)
{
showCursor();
return;
return true;
}
}
// If we get here, every control requested a hidden cursor, so we oblige.
hideCursor();
return false;
}

//---------------------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions Templates/Full/game/core/scripts/client/metrics.cs
Expand Up @@ -243,9 +243,9 @@ function metrics( %expr )

if( %metricsExpr !$= "" )
{
Canvas.pushDialog( FrameOverlayGui, 1000 );
$GameCanvas.pushDialog( FrameOverlayGui, 1000 );
TextOverlayControl.setValue( %metricsExpr );
}
else
Canvas.popDialog(FrameOverlayGui);
$GameCanvas.popDialog(FrameOverlayGui);
}