Oculus VR DK2 Support

- Updated to work with 0.5.x SDK
- Uses Oculus Rendering rather than PostFX
- Stereo rendering refactored so more rendering info is grabbed from the DisplayDevice
- Implements an Offscreen Canvas for in-game gui with oculus
- Message dialogs and metrics display can now go to the OffScreen Canvas (if oculus demo is setup correctly)
This commit is contained in:
James Urquhart 2015-05-06 23:07:48 +01:00
parent b3170bcddf
commit 3a457749ec
56 changed files with 2654 additions and 1426 deletions

View file

@ -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"
@ -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 );
@ -51,6 +57,7 @@ ConsoleDocClass( GuiTSCtrl,
);
U32 GuiTSCtrl::smFrameCount = 0;
bool GuiTSCtrl::smUseLatestDisplayTransform = true;
Vector<GuiTSCtrl*> GuiTSCtrl::smAwakeTSCtrls;
ImplementEnumType( GuiTSRenderStyles,
@ -60,7 +67,6 @@ ImplementEnumType( GuiTSRenderStyles,
{ GuiTSCtrl::RenderStyleStereoSideBySide, "stereo side by side" },
EndImplementEnumType;
//-----------------------------------------------------------------------------
namespace
@ -153,7 +159,6 @@ GuiTSCtrl::GuiTSCtrl()
mLastCameraQuery.nearPlane = 0.01f;
mLastCameraQuery.projectionOffset = Point2F::Zero;
mLastCameraQuery.eyeOffset = Point3F::Zero;
mLastCameraQuery.ortho = false;
}
@ -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");
}
//-----------------------------------------------------------------------------
@ -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;
}
@ -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))
{
@ -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
{
@ -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
@ -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
{
@ -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)
@ -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
@ -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++;
}
@ -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.
//=============================================================================
@ -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);
}

View file

@ -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.
@ -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;
@ -83,7 +97,11 @@ protected:
/// The last camera query set in onRender.
/// @see getLastCameraQuery
CameraQuery mLastCameraQuery;
CameraQuery mLastCameraQuery;
NamedTexTargetRef mStereoGuiTarget;
GFXVertexBufferHandle<GFXVertexPCT> mStereoOverlayVB;
GFXStateBlockRef mStereoGuiSB;
public:
@ -155,6 +173,10 @@ public:
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." );

View file

@ -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"
@ -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;
@ -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)
@ -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))
@ -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;
@ -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;
@ -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;
@ -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;
@ -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 )
{
@ -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);
@ -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]));
}

View file

@ -331,6 +331,10 @@ public:
/// 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

View file

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

View file

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

View file

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