guiTSControl.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "gui/3d/guiTSControl.h"
  24. #include "gui/core/guiOffscreenCanvas.h"
  25. #include "console/engineAPI.h"
  26. #include "scene/sceneManager.h"
  27. #include "lighting/lightManager.h"
  28. #include "gfx/sim/debugDraw.h"
  29. #include "gfx/gfxTransformSaver.h"
  30. #include "gfx/screenshot.h"
  31. #include "math/mathUtils.h"
  32. #include "gui/core/guiCanvas.h"
  33. #include "scene/reflectionManager.h"
  34. #include "postFx/postEffectManager.h"
  35. #include "gfx/gfxTransformSaver.h"
  36. #include "gfx/gfxDrawUtil.h"
  37. #include "gfx/gfxDebugEvent.h"
  38. #include "core/stream/fileStream.h"
  39. #include "platform/output/IDisplayDevice.h"
  40. #include "T3D/gameBase/extended/extendedMove.h"
  41. #define TS_OVERLAY_SCREEN_WIDTH 0.75
  42. IMPLEMENT_CONOBJECT( GuiTSCtrl );
  43. ConsoleDocClass( GuiTSCtrl,
  44. "@brief Abstract base class for controls that render 3D scenes.\n\n"
  45. "GuiTSCtrl is the base class for controls that render 3D camera views in Torque. The class itself "
  46. "does not implement a concrete scene rendering. Use GuiObjectView to display invidiual shapes in "
  47. "the Gui and GameTSCtrl to render full scenes.\n\n"
  48. "@see GameTSCtrl\n"
  49. "@see GuiObjectView\n"
  50. "@ingroup Gui3D\n"
  51. );
  52. U32 GuiTSCtrl::smFrameCount = 0;
  53. bool GuiTSCtrl::smUseLatestDisplayTransform = true;
  54. Vector<GuiTSCtrl*> GuiTSCtrl::smAwakeTSCtrls;
  55. ImplementEnumType( GuiTSRenderStyles,
  56. "Style of rendering for a GuiTSCtrl.\n\n"
  57. "@ingroup Gui3D" )
  58. { GuiTSCtrl::RenderStyleStandard, "standard" },
  59. { GuiTSCtrl::RenderStyleStereoSideBySide, "stereo side by side" },
  60. { GuiTSCtrl::RenderStyleStereoSeparate, "stereo separate" },
  61. EndImplementEnumType;
  62. //-----------------------------------------------------------------------------
  63. namespace
  64. {
  65. void _drawLine( const Point3F &p0, const Point3F &p1, const ColorI &color, F32 width )
  66. {
  67. F32 x1, x2, y1, y2, z1, z2;
  68. x1 = p0.x;
  69. y1 = p0.y;
  70. z1 = p0.z;
  71. x2 = p1.x;
  72. y2 = p1.y;
  73. z2 = p1.z;
  74. //
  75. // Convert Line a----------b
  76. //
  77. // Into Quad v0---------v1
  78. // a b
  79. // v2---------v3
  80. //
  81. Point2F start(x1, y1);
  82. Point2F end(x2, y2);
  83. Point2F perp, lineVec;
  84. // handle degenerate case where point a = b
  85. if(x1 == x2 && y1 == y2)
  86. {
  87. perp.set(0.0f, width * 0.5f);
  88. lineVec.set(0.1f, 0.0f);
  89. }
  90. else
  91. {
  92. perp.set(start.y - end.y, end.x - start.x);
  93. lineVec.set(end.x - start.x, end.y - start.y);
  94. perp.normalize(width * 0.5f);
  95. lineVec.normalize(0.1f);
  96. }
  97. start -= lineVec;
  98. end += lineVec;
  99. GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, 4, GFXBufferTypeVolatile);
  100. verts.lock();
  101. verts[0].point.set( start.x+perp.x, start.y+perp.y, z1 );
  102. verts[1].point.set( end.x+perp.x, end.y+perp.y, z2 );
  103. verts[2].point.set( start.x-perp.x, start.y-perp.y, z1 );
  104. verts[3].point.set( end.x-perp.x, end.y-perp.y, z2 );
  105. verts[0].color = color;
  106. verts[1].color = color;
  107. verts[2].color = color;
  108. verts[3].color = color;
  109. verts.unlock();
  110. GFX->setVertexBuffer( verts );
  111. GFXStateBlockDesc desc;
  112. desc.setCullMode(GFXCullNone);
  113. desc.setZReadWrite(false);
  114. desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
  115. GFX->setStateBlockByDesc( desc );
  116. GFX->setupGenericShaders();
  117. GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
  118. }
  119. }
  120. //-----------------------------------------------------------------------------
  121. GuiTSCtrl::GuiTSCtrl()
  122. {
  123. mCameraZRot = 0;
  124. mForceFOV = 0;
  125. mReflectPriority = 1.0f;
  126. mRenderStyle = RenderStyleStandard;
  127. mSaveModelview.identity();
  128. mSaveProjection.identity();
  129. mSaveViewport.set( 0, 0, 10, 10 );
  130. mSaveWorldToScreenScale.set( 0, 0 );
  131. mLastCameraQuery.cameraMatrix.identity();
  132. mLastCameraQuery.fov = 45.0f;
  133. mLastCameraQuery.object = NULL;
  134. mLastCameraQuery.farPlane = 10.0f;
  135. mLastCameraQuery.nearPlane = 0.01f;
  136. mLastCameraQuery.hasFovPort = false;
  137. mLastCameraQuery.hasStereoTargets = false;
  138. mLastCameraQuery.ortho = false;
  139. }
  140. //-----------------------------------------------------------------------------
  141. void GuiTSCtrl::initPersistFields()
  142. {
  143. addGroup( "Camera" );
  144. addField("cameraZRot", TypeF32, Offset(mCameraZRot, GuiTSCtrl),
  145. "Z rotation angle of camera." );
  146. addField("forceFOV", TypeF32, Offset(mForceFOV, GuiTSCtrl),
  147. "The vertical field of view in degrees or zero to use the normal camera FOV." );
  148. endGroup( "Camera" );
  149. addGroup( "Rendering" );
  150. addField( "reflectPriority", TypeF32, Offset( mReflectPriority, GuiTSCtrl ),
  151. "The share of the per-frame reflection update work this control's rendering should run.\n"
  152. "The reflect update priorities of all visible GuiTSCtrls are added together and each control is assigned "
  153. "a share of the per-frame reflection update time according to its percentage of the total priority value." );
  154. addField("renderStyle", TYPEID< RenderStyles >(), Offset(mRenderStyle, GuiTSCtrl),
  155. "Indicates how this control should render its contents." );
  156. endGroup( "Rendering" );
  157. Parent::initPersistFields();
  158. }
  159. //-----------------------------------------------------------------------------
  160. void GuiTSCtrl::consoleInit()
  161. {
  162. Con::addVariable("$TSControl::frameCount", TypeS32, &smFrameCount, "The number of frames that have been rendered since this control was created.\n"
  163. "@ingroup Rendering\n");
  164. Con::addVariable("$TSControl::useLatestDisplayTransform", TypeBool, &smUseLatestDisplayTransform, "Use the latest view transform when rendering stereo instead of the one calculated by the last move.\n"
  165. "@ingroup Rendering\n");
  166. }
  167. //-----------------------------------------------------------------------------
  168. bool GuiTSCtrl::onWake()
  169. {
  170. if ( !Parent::onWake() )
  171. return false;
  172. // Add ourselves to the active viewport list.
  173. AssertFatal( !smAwakeTSCtrls.contains( this ),
  174. "GuiTSCtrl::onWake - This control is already in the awake list!" );
  175. smAwakeTSCtrls.push_back( this );
  176. // For VR
  177. mLastCameraQuery.drawCanvas = getRoot();
  178. return true;
  179. }
  180. //-----------------------------------------------------------------------------
  181. void GuiTSCtrl::onSleep()
  182. {
  183. Parent::onSleep();
  184. AssertFatal( smAwakeTSCtrls.contains( this ),
  185. "GuiTSCtrl::onSleep - This control is not in the awake list!" );
  186. smAwakeTSCtrls.remove( this );
  187. }
  188. //-----------------------------------------------------------------------------
  189. void GuiTSCtrl::onPreRender()
  190. {
  191. setUpdate();
  192. }
  193. //-----------------------------------------------------------------------------
  194. bool GuiTSCtrl::processCameraQuery(CameraQuery *)
  195. {
  196. return false;
  197. }
  198. //-----------------------------------------------------------------------------
  199. void GuiTSCtrl::renderWorld(const RectI& /*updateRect*/)
  200. {
  201. }
  202. //-----------------------------------------------------------------------------
  203. F32 GuiTSCtrl::projectRadius( F32 dist, F32 radius ) const
  204. {
  205. // Fixup any negative or zero distance so we
  206. // don't get a divide by zero.
  207. dist = dist > 0.0f ? dist : 0.001f;
  208. return ( radius / dist ) * mSaveWorldToScreenScale.y;
  209. }
  210. //-----------------------------------------------------------------------------
  211. bool GuiTSCtrl::project( const Point3F &pt, Point3F *dest ) const
  212. {
  213. return MathUtils::mProjectWorldToScreen(pt,dest,mSaveViewport,mSaveModelview,mSaveProjection);
  214. }
  215. //-----------------------------------------------------------------------------
  216. bool GuiTSCtrl::unproject( const Point3F &pt, Point3F *dest ) const
  217. {
  218. MathUtils::mProjectScreenToWorld(pt,dest,mSaveViewport,mSaveModelview,mSaveProjection,mLastCameraQuery.farPlane,mLastCameraQuery.nearPlane);
  219. return true;
  220. }
  221. //-----------------------------------------------------------------------------
  222. F32 GuiTSCtrl::calculateViewDistance(F32 radius)
  223. {
  224. F32 fov = mLastCameraQuery.fov;
  225. F32 wwidth;
  226. F32 wheight;
  227. F32 renderWidth = (mRenderStyle == RenderStyleStereoSideBySide) ? F32(getWidth())*0.5f : F32(getWidth());
  228. F32 renderHeight = F32(getHeight());
  229. F32 aspectRatio = renderWidth / renderHeight;
  230. // Use the FOV to calculate the viewport height scale
  231. // then generate the width scale from the aspect ratio.
  232. if(!mLastCameraQuery.ortho)
  233. {
  234. wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2.0f);
  235. wwidth = aspectRatio * wheight;
  236. }
  237. else
  238. {
  239. wheight = mLastCameraQuery.fov;
  240. wwidth = aspectRatio * wheight;
  241. }
  242. // Now determine if we should use the width
  243. // fov or height fov.
  244. //
  245. // If the window is taller than it is wide, use the
  246. // width fov to keep the object completely in view.
  247. if (wheight > wwidth)
  248. fov = mAtan( wwidth / mLastCameraQuery.nearPlane ) * 2.0f;
  249. return radius / mTan(fov / 2.0f);
  250. }
  251. //-----------------------------------------------------------------------------
  252. static FovPort CalculateFovPortForCanvas(const RectI viewport, const CameraQuery &cameraQuery)
  253. {
  254. F32 wwidth;
  255. F32 wheight;
  256. F32 renderWidth = viewport.extent.x;
  257. F32 renderHeight = viewport.extent.y;
  258. F32 aspectRatio = renderWidth / renderHeight;
  259. // Use the FOV to calculate the viewport height scale
  260. // then generate the width scale from the aspect ratio.
  261. if(!cameraQuery.ortho)
  262. {
  263. wheight = /*cameraQuery.nearPlane * */ mTan(cameraQuery.fov / 2.0f);
  264. wwidth = aspectRatio * wheight;
  265. }
  266. else
  267. {
  268. wheight = cameraQuery.fov;
  269. wwidth = aspectRatio * wheight;
  270. }
  271. F32 hscale = wwidth * 2.0f / renderWidth;
  272. F32 vscale = wheight * 2.0f / renderHeight;
  273. F32 left = 0.0f * hscale - wwidth;
  274. F32 right = renderWidth * hscale - wwidth;
  275. F32 top = wheight - vscale * 0.0f;
  276. F32 bottom = wheight - vscale * renderHeight;
  277. FovPort fovPort;
  278. fovPort.upTan = top;
  279. fovPort.downTan = -bottom;
  280. fovPort.leftTan = -left;
  281. fovPort.rightTan = right;
  282. return fovPort;
  283. }
  284. void GuiTSCtrl::_internalRender(RectI viewport, Frustum &frustum)
  285. {
  286. GFXTransformSaver saver;
  287. Point2I renderSize = viewport.extent;
  288. if (mForceFOV != 0)
  289. mLastCameraQuery.fov = mDegToRad(mForceFOV);
  290. if (mCameraZRot)
  291. {
  292. MatrixF rotMat(EulerF(0, 0, mDegToRad(mCameraZRot)));
  293. mLastCameraQuery.cameraMatrix.mul(rotMat);
  294. }
  295. if (mReflectPriority > 0)
  296. {
  297. // Get the total reflection priority.
  298. F32 totalPriority = 0;
  299. for (U32 i = 0; i < smAwakeTSCtrls.size(); i++)
  300. if (smAwakeTSCtrls[i]->isVisible())
  301. totalPriority += smAwakeTSCtrls[i]->mReflectPriority;
  302. REFLECTMGR->update(mReflectPriority / totalPriority,
  303. renderSize,
  304. mLastCameraQuery);
  305. }
  306. GFX->setViewport(viewport);
  307. // Clear the zBuffer so GUI doesn't hose object rendering accidentally
  308. GFX->clear(GFXClearZBuffer, ColorI(20, 20, 20), 1.0f, 0);
  309. GFX->setFrustum(frustum);
  310. mSaveProjection = GFX->getProjectionMatrix();
  311. if (mLastCameraQuery.ortho)
  312. {
  313. mOrthoWidth = frustum.getWidth();
  314. mOrthoHeight = frustum.getHeight();
  315. }
  316. // We're going to be displaying this render at size of this control in
  317. // pixels - let the scene know so that it can calculate e.g. reflections
  318. // correctly for that final display result.
  319. gClientSceneGraph->setDisplayTargetResolution(renderSize);
  320. // Set the GFX world matrix to the world-to-camera transform, but don't
  321. // change the cameraMatrix in mLastCameraQuery. This is because
  322. // mLastCameraQuery.cameraMatrix is supposed to contain the camera-to-world
  323. // transform. In-place invert would save a copy but mess up any GUIs that
  324. // depend on that value.
  325. MatrixF worldToCamera = mLastCameraQuery.cameraMatrix;
  326. worldToCamera.inverse();
  327. GFX->setWorldMatrix(worldToCamera);
  328. mSaveProjection = GFX->getProjectionMatrix();
  329. mSaveModelview = GFX->getWorldMatrix();
  330. mSaveViewport = viewport;
  331. mSaveWorldToScreenScale = GFX->getWorldToScreenScale();
  332. mSaveFrustum = GFX->getFrustum();
  333. mSaveFrustum.setTransform(mLastCameraQuery.cameraMatrix);
  334. // Set the default non-clip projection as some
  335. // objects depend on this even in non-reflect cases.
  336. gClientSceneGraph->setNonClipProjection(mSaveProjection);
  337. // Give the post effect manager the worldToCamera, and cameraToScreen matrices
  338. PFXMGR->setFrameMatrices(mSaveModelview, mSaveProjection);
  339. renderWorld(viewport);
  340. DebugDrawer* debugDraw = DebugDrawer::get();
  341. if (mRenderStyle == RenderStyleStereoSideBySide && debugDraw->willDraw())
  342. {
  343. // For SBS we need to render over each viewport
  344. Frustum frustum;
  345. GFX->setViewport(mLastCameraQuery.stereoViewports[0]);
  346. MathUtils::makeFovPortFrustum(&frustum, mLastCameraQuery.ortho, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.fovPort[0]);
  347. GFX->setFrustum(frustum);
  348. debugDraw->render(false);
  349. GFX->setViewport(mLastCameraQuery.stereoViewports[1]);
  350. MathUtils::makeFovPortFrustum(&frustum, mLastCameraQuery.ortho, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.fovPort[1]);
  351. GFX->setFrustum(frustum);
  352. debugDraw->render();
  353. }
  354. else
  355. {
  356. debugDraw->render();
  357. }
  358. // Render the canvas overlay if its available
  359. if (mStereoCanvas.getPointer() && mStereoGuiTarget.getPointer() && mStereoCanvas->size() != 0)
  360. {
  361. GFXDEBUGEVENT_SCOPE(StereoGui_Render, ColorI(255, 0, 0));
  362. MatrixF proj(1);
  363. Frustum originalFrustum = frustum;
  364. GFXTextureObject *texObject = mStereoGuiTarget->getTexture(0);
  365. const FovPort *currentFovPort = GFX->getStereoFovPort();
  366. const MatrixF *eyeTransforms = GFX->getStereoEyeTransforms();
  367. const Point3F *eyeOffset = GFX->getStereoEyeOffsets();
  368. Frustum gfxFrustum = originalFrustum;
  369. GFX->setClipRect(viewport);
  370. GFX->setViewport(viewport);
  371. GFX->setFrustum(frustum);
  372. MatrixF eyeWorldTrans(1);
  373. if (mLastCameraQuery.currentEye != -1)
  374. {
  375. eyeWorldTrans.setPosition(Point3F(eyeOffset[mLastCameraQuery.currentEye].x, eyeOffset[mLastCameraQuery.currentEye].y, eyeOffset[mLastCameraQuery.currentEye].z));
  376. }
  377. MatrixF eyeWorld(1);
  378. eyeWorld.mul(eyeWorldTrans);
  379. eyeWorld.inverse();
  380. GFX->setWorldMatrix(eyeWorld);
  381. GFX->setViewMatrix(MatrixF::Identity);
  382. if (!mStereoOverlayVB.getPointer())
  383. {
  384. mStereoOverlayVB.set(GFX, 4, GFXBufferTypeStatic);
  385. GFXVertexPCT *verts = mStereoOverlayVB.lock(0, 4);
  386. F32 texLeft = 0.0f;
  387. F32 texRight = 1.0f;
  388. F32 texTop = 1.0f;
  389. F32 texBottom = 0.0f;
  390. F32 rectRatio = gfxFrustum.getWidth() / gfxFrustum.getHeight();
  391. F32 rectWidth = gfxFrustum.getWidth() * TS_OVERLAY_SCREEN_WIDTH;
  392. F32 rectHeight = rectWidth * rectRatio;
  393. F32 screenLeft = -rectWidth * 0.5;
  394. F32 screenRight = rectWidth * 0.5;
  395. F32 screenTop = -rectHeight * 0.5;
  396. F32 screenBottom = rectHeight * 0.5;
  397. const F32 fillConv = 0.0f;
  398. const F32 frustumDepthAdjusted = gfxFrustum.getNearDist() + 0.012;
  399. verts[0].point.set(screenLeft - fillConv, frustumDepthAdjusted, screenTop - fillConv);
  400. verts[1].point.set(screenRight - fillConv, frustumDepthAdjusted, screenTop - fillConv);
  401. verts[2].point.set(screenLeft - fillConv, frustumDepthAdjusted, screenBottom - fillConv);
  402. verts[3].point.set(screenRight - fillConv, frustumDepthAdjusted, screenBottom - fillConv);
  403. verts[0].color = verts[1].color = verts[2].color = verts[3].color = ColorI(255, 255, 255, 255);
  404. verts[0].texCoord.set(texLeft, texTop);
  405. verts[1].texCoord.set(texRight, texTop);
  406. verts[2].texCoord.set(texLeft, texBottom);
  407. verts[3].texCoord.set(texRight, texBottom);
  408. mStereoOverlayVB.unlock();
  409. }
  410. if (!mStereoGuiSB.getPointer())
  411. {
  412. // DrawBitmapStretchSR
  413. GFXStateBlockDesc bitmapStretchSR;
  414. bitmapStretchSR.setCullMode(GFXCullNone);
  415. bitmapStretchSR.setZReadWrite(false, false);
  416. bitmapStretchSR.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
  417. bitmapStretchSR.samplersDefined = true;
  418. bitmapStretchSR.samplers[0] = GFXSamplerStateDesc::getClampLinear();
  419. bitmapStretchSR.samplers[0].minFilter = GFXTextureFilterPoint;
  420. bitmapStretchSR.samplers[0].mipFilter = GFXTextureFilterPoint;
  421. bitmapStretchSR.samplers[0].magFilter = GFXTextureFilterPoint;
  422. mStereoGuiSB = GFX->createStateBlock(bitmapStretchSR);
  423. }
  424. GFX->setPrimitiveBuffer(NULL);
  425. GFX->setVertexBuffer(mStereoOverlayVB);
  426. GFX->setStateBlock(mStereoGuiSB);
  427. GFX->setTexture(0, texObject);
  428. GFX->setupGenericShaders(GFXDevice::GSModColorTexture);
  429. GFX->drawPrimitive(GFXTriangleStrip, 0, 2);
  430. }
  431. saver.restore();
  432. }
  433. //-----------------------------------------------------------------------------
  434. void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
  435. {
  436. // Save the current transforms so we can restore
  437. // it for child control rendering below.
  438. GFXTransformSaver saver;
  439. bool renderingToTarget = false;
  440. mLastCameraQuery.displayDevice = NULL;
  441. if (!processCameraQuery(&mLastCameraQuery))
  442. {
  443. // We have no camera, but render the GUI children
  444. // anyway. This makes editing GuiTSCtrl derived
  445. // controls easier in the GuiEditor.
  446. renderChildControls(offset, updateRect);
  447. return;
  448. }
  449. // jamesu - currently a little bit of a hack. Ideally we need to ditch the viewports in the query data and just rely on the display device
  450. if (mLastCameraQuery.displayDevice)
  451. {
  452. if (mRenderStyle == RenderStyleStereoSideBySide)
  453. {
  454. mLastCameraQuery.displayDevice->setDrawMode(GFXDevice::RS_StereoSideBySide);
  455. }
  456. else if (mRenderStyle == RenderStyleStereoSeparate)
  457. {
  458. mLastCameraQuery.displayDevice->setDrawMode(GFXDevice::RS_StereoSeparate);
  459. }
  460. else
  461. {
  462. mLastCameraQuery.displayDevice->setDrawMode(GFXDevice::RS_Standard);
  463. }
  464. // The connection's display device may want to set the eye offset
  465. if (mLastCameraQuery.displayDevice->providesEyeOffsets())
  466. {
  467. mLastCameraQuery.displayDevice->getEyeOffsets(mLastCameraQuery.eyeOffset);
  468. }
  469. // Grab field of view for both eyes
  470. if (mLastCameraQuery.displayDevice->providesFovPorts())
  471. {
  472. mLastCameraQuery.displayDevice->getFovPorts(mLastCameraQuery.fovPort);
  473. mLastCameraQuery.hasFovPort = true;
  474. }
  475. mLastCameraQuery.displayDevice->getStereoViewports(mLastCameraQuery.stereoViewports);
  476. mLastCameraQuery.displayDevice->getStereoTargets(mLastCameraQuery.stereoTargets);
  477. mLastCameraQuery.hasStereoTargets = mLastCameraQuery.stereoTargets[0];
  478. }
  479. GFXTargetRef origTarget = GFX->getActiveRenderTarget();
  480. U32 origStyle = GFX->getCurrentRenderStyle();
  481. // Set up the appropriate render style
  482. U32 prevRenderStyle = GFX->getCurrentRenderStyle();
  483. Point2I renderSize = getExtent();
  484. Frustum frustum;
  485. mLastCameraQuery.currentEye = -1;
  486. if (mRenderStyle == RenderStyleStereoSideBySide)
  487. {
  488. GFX->setCurrentRenderStyle(GFXDevice::RS_StereoSideBySide);
  489. GFX->setStereoEyeOffsets(mLastCameraQuery.eyeOffset);
  490. GFX->setStereoHeadTransform(mLastCameraQuery.headMatrix);
  491. if (!mLastCameraQuery.hasStereoTargets)
  492. {
  493. // Need to calculate our current viewport here
  494. mLastCameraQuery.stereoViewports[0] = updateRect;
  495. mLastCameraQuery.stereoViewports[0].extent.x /= 2;
  496. mLastCameraQuery.stereoViewports[1] = mLastCameraQuery.stereoViewports[0];
  497. mLastCameraQuery.stereoViewports[1].point.x += mLastCameraQuery.stereoViewports[1].extent.x;
  498. }
  499. if (!mLastCameraQuery.hasFovPort)
  500. {
  501. // Need to make our own fovPort
  502. mLastCameraQuery.fovPort[0] = CalculateFovPortForCanvas(mLastCameraQuery.stereoViewports[0], mLastCameraQuery);
  503. mLastCameraQuery.fovPort[1] = CalculateFovPortForCanvas(mLastCameraQuery.stereoViewports[1], mLastCameraQuery);
  504. }
  505. GFX->setStereoFovPort(mLastCameraQuery.fovPort); // NOTE: this specifies fov for BOTH eyes
  506. GFX->setSteroViewports(mLastCameraQuery.stereoViewports);
  507. GFX->setStereoTargets(mLastCameraQuery.stereoTargets);
  508. MatrixF myTransforms[2];
  509. Frustum frustum;
  510. if (smUseLatestDisplayTransform)
  511. {
  512. // Use the view matrix determined from the display device
  513. myTransforms[0] = mLastCameraQuery.eyeTransforms[0];
  514. myTransforms[1] = mLastCameraQuery.eyeTransforms[1];
  515. }
  516. else
  517. {
  518. // Use the view matrix determined from the control object
  519. myTransforms[0] = mLastCameraQuery.cameraMatrix;
  520. myTransforms[1] = mLastCameraQuery.cameraMatrix;
  521. mLastCameraQuery.headMatrix = mLastCameraQuery.cameraMatrix; // override head
  522. QuatF qrot = mLastCameraQuery.cameraMatrix;
  523. Point3F pos = mLastCameraQuery.cameraMatrix.getPosition();
  524. Point3F rotEyePos;
  525. myTransforms[0].setPosition(pos + qrot.mulP(mLastCameraQuery.eyeOffset[0], &rotEyePos));
  526. myTransforms[1].setPosition(pos + qrot.mulP(mLastCameraQuery.eyeOffset[1], &rotEyePos));
  527. }
  528. GFX->setStereoEyeTransforms(myTransforms);
  529. // Allow render size to originate from the render target
  530. if (mLastCameraQuery.stereoTargets[0])
  531. {
  532. renderSize = mLastCameraQuery.stereoViewports[0].extent;
  533. renderingToTarget = true;
  534. }
  535. // NOTE: these calculations are essentially overridden later by the fov port settings when rendering each eye.
  536. MathUtils::makeFovPortFrustum(&frustum, mLastCameraQuery.ortho, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.fovPort[0]);
  537. GFX->activateStereoTarget(-1);
  538. _internalRender(RectI(updateRect.point, updateRect.extent), frustum);
  539. // Notify device we've rendered the right, thus the last stereo frame.
  540. GFX->getDeviceEventSignal().trigger(GFXDevice::deRightStereoFrameRendered);
  541. // Render preview
  542. if (mLastCameraQuery.displayDevice)
  543. {
  544. GFXTexHandle previewTexture = mLastCameraQuery.displayDevice->getPreviewTexture();
  545. if (!previewTexture.isNull())
  546. {
  547. GFX->setActiveRenderTarget(origTarget);
  548. GFX->setCurrentRenderStyle(origStyle);
  549. GFX->setClipRect(updateRect);
  550. renderDisplayPreview(updateRect, previewTexture);
  551. }
  552. }
  553. }
  554. else if (mRenderStyle == RenderStyleStereoSeparate && mLastCameraQuery.displayDevice)
  555. {
  556. // In this case we render the scene twice to different render targets, then
  557. // render the final composite view
  558. GFX->setCurrentRenderStyle(GFXDevice::RS_StereoSeparate);
  559. GFX->setStereoEyeOffsets(mLastCameraQuery.eyeOffset);
  560. GFX->setStereoHeadTransform(mLastCameraQuery.headMatrix);
  561. GFX->setStereoFovPort(mLastCameraQuery.fovPort); // NOTE: this specifies fov for BOTH eyes
  562. GFX->setSteroViewports(mLastCameraQuery.stereoViewports);
  563. GFX->setStereoTargets(mLastCameraQuery.stereoTargets);
  564. MatrixF myTransforms[2];
  565. if (smUseLatestDisplayTransform)
  566. {
  567. // Use the view matrix determined from the display device
  568. myTransforms[0] = mLastCameraQuery.eyeTransforms[0];
  569. myTransforms[1] = mLastCameraQuery.eyeTransforms[1];
  570. }
  571. else
  572. {
  573. // Use the view matrix determined from the control object
  574. myTransforms[0] = mLastCameraQuery.cameraMatrix;
  575. myTransforms[1] = mLastCameraQuery.cameraMatrix;
  576. QuatF qrot = mLastCameraQuery.cameraMatrix;
  577. Point3F pos = mLastCameraQuery.cameraMatrix.getPosition();
  578. Point3F rotEyePos;
  579. myTransforms[0].setPosition(pos + qrot.mulP(mLastCameraQuery.eyeOffset[0], &rotEyePos));
  580. myTransforms[1].setPosition(pos + qrot.mulP(mLastCameraQuery.eyeOffset[1], &rotEyePos));
  581. }
  582. MatrixF origMatrix = mLastCameraQuery.cameraMatrix;
  583. // Left
  584. MathUtils::makeFovPortFrustum(&frustum, mLastCameraQuery.ortho, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.fovPort[0]);
  585. mLastCameraQuery.cameraMatrix = myTransforms[0];
  586. frustum.update();
  587. GFX->activateStereoTarget(0);
  588. mLastCameraQuery.currentEye = 0;
  589. _internalRender(RectI(Point2I(0, 0), mLastCameraQuery.stereoTargets[0]->getSize()), frustum);
  590. GFX->getDeviceEventSignal().trigger(GFXDevice::deLeftStereoFrameRendered);
  591. // Right
  592. GFX->activateStereoTarget(1);
  593. mLastCameraQuery.currentEye = 1;
  594. MathUtils::makeFovPortFrustum(&frustum, mLastCameraQuery.ortho, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.fovPort[1]);
  595. mLastCameraQuery.cameraMatrix = myTransforms[1];
  596. frustum.update();
  597. _internalRender(RectI(Point2I(0, 0), mLastCameraQuery.stereoTargets[1]->getSize()), frustum);
  598. GFX->getDeviceEventSignal().trigger(GFXDevice::deRightStereoFrameRendered);
  599. mLastCameraQuery.cameraMatrix = origMatrix;
  600. // Render preview
  601. if (mLastCameraQuery.displayDevice)
  602. {
  603. GFXTexHandle previewTexture = mLastCameraQuery.displayDevice->getPreviewTexture();
  604. if (!previewTexture.isNull())
  605. {
  606. GFX->setActiveRenderTarget(origTarget);
  607. GFX->setCurrentRenderStyle(origStyle);
  608. GFX->setClipRect(updateRect);
  609. renderDisplayPreview(updateRect, previewTexture);
  610. }
  611. }
  612. }
  613. else
  614. {
  615. #ifdef TORQUE_OS_MAC
  616. Point2I screensize = getRoot()->getWindowSize();
  617. tempRect.point.y = screensize.y - (tempRect.point.y + tempRect.extent.y);
  618. #endif
  619. // set up the camera and viewport stuff:
  620. F32 wwidth;
  621. F32 wheight;
  622. F32 renderWidth = F32(renderSize.x);
  623. F32 renderHeight = F32(renderSize.y);
  624. F32 aspectRatio = renderWidth / renderHeight;
  625. // Use the FOV to calculate the viewport height scale
  626. // then generate the width scale from the aspect ratio.
  627. if (!mLastCameraQuery.ortho)
  628. {
  629. wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2.0f);
  630. wwidth = aspectRatio * wheight;
  631. }
  632. else
  633. {
  634. wheight = mLastCameraQuery.fov;
  635. wwidth = aspectRatio * wheight;
  636. }
  637. F32 hscale = wwidth * 2.0f / renderWidth;
  638. F32 vscale = wheight * 2.0f / renderHeight;
  639. F32 left = (updateRect.point.x - offset.x) * hscale - wwidth;
  640. F32 right = (updateRect.point.x + updateRect.extent.x - offset.x) * hscale - wwidth;
  641. F32 top = wheight - vscale * (updateRect.point.y - offset.y);
  642. F32 bottom = wheight - vscale * (updateRect.point.y + updateRect.extent.y - offset.y);
  643. frustum.set(mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane);
  644. // Manipulate the frustum for tiled screenshots
  645. const bool screenShotMode = gScreenShot && gScreenShot->isPending();
  646. if (screenShotMode)
  647. {
  648. gScreenShot->tileFrustum(frustum);
  649. GFX->setViewMatrix(MatrixF::Identity);
  650. }
  651. RectI tempRect = updateRect;
  652. #ifdef TORQUE_OS_MAC
  653. Point2I screensize = getRoot()->getWindowSize();
  654. tempRect.point.y = screensize.y - (tempRect.point.y + tempRect.extent.y);
  655. #endif
  656. _internalRender(tempRect, frustum);
  657. }
  658. // TODO: Some render to sort of overlay system?
  659. // Allow subclasses to render 2D elements.
  660. GFX->setActiveRenderTarget(origTarget);
  661. GFX->setCurrentRenderStyle(origStyle);
  662. GFX->setClipRect(updateRect);
  663. renderGui(offset, updateRect);
  664. if (shouldRenderChildControls())
  665. {
  666. renderChildControls(offset, updateRect);
  667. }
  668. smFrameCount++;
  669. }
  670. //-----------------------------------------------------------------------------
  671. void GuiTSCtrl::drawLine( Point3F p0, Point3F p1, const ColorI &color, F32 width )
  672. {
  673. if ( !mSaveFrustum.clipSegment( p0, p1 ) )
  674. return;
  675. MathUtils::mProjectWorldToScreen( p0, &p0, mSaveViewport, mSaveModelview, mSaveProjection );
  676. MathUtils::mProjectWorldToScreen( p1, &p1, mSaveViewport, mSaveModelview, mSaveProjection );
  677. p0.x = mClampF( p0.x, 0.0f, mSaveViewport.extent.x );
  678. p0.y = mClampF( p0.y, 0.0f, mSaveViewport.extent.y );
  679. p1.x = mClampF( p1.x, 0.0f, mSaveViewport.extent.x );
  680. p1.y = mClampF( p1.y, 0.0f, mSaveViewport.extent.y );
  681. p0.z = p1.z = 0.0f;
  682. _drawLine( p0, p1, color, width );
  683. }
  684. //-----------------------------------------------------------------------------
  685. void GuiTSCtrl::drawLineList( const Vector<Point3F> &points, const ColorI color, F32 width )
  686. {
  687. for ( S32 i = 0; i < points.size() - 1; i++ )
  688. drawLine( points[i], points[i+1], color, width );
  689. }
  690. //-----------------------------------------------------------------------------
  691. void GuiTSCtrl::setStereoGui(GuiOffscreenCanvas *canvas)
  692. {
  693. mStereoGuiTarget = canvas ? canvas->getTarget() : NULL;
  694. mStereoCanvas = canvas;
  695. }
  696. //-----------------------------------------------------------------------------
  697. void GuiTSCtrl::renderDisplayPreview(const RectI &updateRect, GFXTexHandle &previewTexture)
  698. {
  699. GFX->setWorldMatrix(MatrixF(1));
  700. GFX->setViewMatrix(MatrixF::Identity);
  701. GFX->setClipRect(updateRect);
  702. GFX->getDrawUtil()->drawRectFill(RectI(Point2I(0, 0), Point2I(1024, 768)), ColorI::BLACK);
  703. GFX->getDrawUtil()->drawRect(RectI(Point2I(0, 0), Point2I(1024, 768)), ColorI::RED);
  704. if (!mStereoPreviewVB.getPointer())
  705. {
  706. mStereoPreviewVB.set(GFX, 4, GFXBufferTypeStatic);
  707. GFXVertexPCT *verts = mStereoPreviewVB.lock(0, 4);
  708. F32 texLeft = 0.0f;
  709. F32 texRight = 1.0f;
  710. F32 texTop = 0.0f;
  711. F32 texBottom = 1.0f;
  712. F32 rectWidth = updateRect.extent.x;
  713. F32 rectHeight = updateRect.extent.y;
  714. F32 screenLeft = 0;
  715. F32 screenRight = rectWidth;
  716. F32 screenTop = 0;
  717. F32 screenBottom = rectHeight;
  718. const F32 fillConv = 0.0f;
  719. const F32 frustumDepthAdjusted = 0.0f;
  720. verts[0].point.set(screenLeft - fillConv, screenTop - fillConv, 0.f);
  721. verts[1].point.set(screenRight - fillConv, screenTop - fillConv, 0.f);
  722. verts[2].point.set(screenLeft - fillConv, screenBottom - fillConv, 0.f);
  723. verts[3].point.set(screenRight - fillConv, screenBottom - fillConv, 0.f);
  724. verts[0].color = verts[1].color = verts[2].color = verts[3].color = ColorI(255, 255, 255, 255);
  725. verts[0].texCoord.set(texLeft, texTop);
  726. verts[1].texCoord.set(texRight, texTop);
  727. verts[2].texCoord.set(texLeft, texBottom);
  728. verts[3].texCoord.set(texRight, texBottom);
  729. mStereoPreviewVB.unlock();
  730. }
  731. if (!mStereoPreviewSB.getPointer())
  732. {
  733. // DrawBitmapStretchSR
  734. GFXStateBlockDesc bitmapStretchSR;
  735. bitmapStretchSR.setCullMode(GFXCullNone);
  736. bitmapStretchSR.setZReadWrite(false, false);
  737. bitmapStretchSR.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
  738. bitmapStretchSR.samplersDefined = true;
  739. bitmapStretchSR.samplers[0] = GFXSamplerStateDesc::getClampLinear();
  740. bitmapStretchSR.samplers[0].minFilter = GFXTextureFilterPoint;
  741. bitmapStretchSR.samplers[0].mipFilter = GFXTextureFilterPoint;
  742. bitmapStretchSR.samplers[0].magFilter = GFXTextureFilterPoint;
  743. mStereoPreviewSB = GFX->createStateBlock(bitmapStretchSR);
  744. }
  745. GFX->setVertexBuffer(mStereoPreviewVB);
  746. GFX->setStateBlock(mStereoPreviewSB);
  747. GFX->setTexture(0, previewTexture);
  748. GFX->setupGenericShaders(GFXDevice::GSModColorTexture);
  749. GFX->drawPrimitive(GFXTriangleStrip, 0, 2);
  750. }
  751. //=============================================================================
  752. // Console Methods.
  753. //=============================================================================
  754. // MARK: ---- Console Methods ----
  755. //-----------------------------------------------------------------------------
  756. DefineEngineMethod( GuiTSCtrl, unproject, Point3F, ( Point3F screenPosition ),,
  757. "Transform 3D screen-space coordinates (x, y, depth) to world space.\n"
  758. "This method can be, for example, used to find the world-space position relating to the current mouse cursor position.\n"
  759. "@param screenPosition The x/y position on the screen plus the depth from the screen-plane outwards.\n"
  760. "@return The world-space position corresponding to the given screen-space coordinates." )
  761. {
  762. Point3F worldPos;
  763. object->unproject( screenPosition, &worldPos );
  764. return worldPos;
  765. }
  766. //-----------------------------------------------------------------------------
  767. DefineEngineMethod( GuiTSCtrl, project, Point3F, ( Point3F worldPosition ),,
  768. "Transform world-space coordinates to screen-space (x, y, depth) coordinates.\n"
  769. "@param worldPosition The world-space position to transform to screen-space.\n"
  770. "@return The " )
  771. {
  772. Point3F screenPos;
  773. object->project( worldPosition, &screenPos );
  774. return screenPos;
  775. }
  776. //-----------------------------------------------------------------------------
  777. DefineEngineMethod( GuiTSCtrl, getWorldToScreenScale, Point2F, (),,
  778. "Get the ratio between world-space units and pixels.\n"
  779. "@return The amount of world-space units covered by the extent of a single pixel." )
  780. {
  781. return object->getWorldToScreenScale();
  782. }
  783. //-----------------------------------------------------------------------------
  784. DefineEngineMethod( GuiTSCtrl, calculateViewDistance, F32, ( F32 radius ),,
  785. "Given the camera's current FOV, get the distance from the camera's viewpoint at which the given radius will fit in the render area.\n"
  786. "@param radius Radius in world-space units which should fit in the view.\n"
  787. "@return The distance from the viewpoint at which the given radius would be fully visible." )
  788. {
  789. return object->calculateViewDistance( radius );
  790. }
  791. DefineEngineMethod( GuiTSCtrl, setStereoGui, void, ( GuiOffscreenCanvas* canvas ),,
  792. "Sets the current stereo texture to an offscreen canvas\n"
  793. "@param canvas The desired canvas." )
  794. {
  795. object->setStereoGui(canvas);
  796. }