guiTSControl.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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 "console/engineAPI.h"
  25. #include "scene/sceneManager.h"
  26. #include "lighting/lightManager.h"
  27. #include "gfx/sim/debugDraw.h"
  28. #include "gfx/gfxTransformSaver.h"
  29. #include "gfx/screenshot.h"
  30. #include "math/mathUtils.h"
  31. #include "gui/core/guiCanvas.h"
  32. #include "scene/reflectionManager.h"
  33. #include "postFx/postEffectManager.h"
  34. #include "gfx/gfxTransformSaver.h"
  35. IMPLEMENT_CONOBJECT( GuiTSCtrl );
  36. ConsoleDocClass( GuiTSCtrl,
  37. "@brief Abstract base class for controls that render 3D scenes.\n\n"
  38. "GuiTSCtrl is the base class for controls that render 3D camera views in Torque. The class itself "
  39. "does not implement a concrete scene rendering. Use GuiObjectView to display invidiual shapes in "
  40. "the Gui and GameTSCtrl to render full scenes.\n\n"
  41. "@see GameTSCtrl\n"
  42. "@see GuiObjectView\n"
  43. "@ingroup Gui3D\n"
  44. );
  45. U32 GuiTSCtrl::smFrameCount = 0;
  46. Vector<GuiTSCtrl*> GuiTSCtrl::smAwakeTSCtrls;
  47. ImplementEnumType( GuiTSRenderStyles,
  48. "Style of rendering for a GuiTSCtrl.\n\n"
  49. "@ingroup Gui3D" )
  50. { GuiTSCtrl::RenderStyleStandard, "standard" },
  51. { GuiTSCtrl::RenderStyleStereoSideBySide, "stereo side by side" },
  52. EndImplementEnumType;
  53. //-----------------------------------------------------------------------------
  54. namespace
  55. {
  56. void _drawLine( const Point3F &p0, const Point3F &p1, const ColorI &color, F32 width )
  57. {
  58. F32 x1, x2, y1, y2, z1, z2;
  59. x1 = p0.x;
  60. y1 = p0.y;
  61. z1 = p0.z;
  62. x2 = p1.x;
  63. y2 = p1.y;
  64. z2 = p1.z;
  65. //
  66. // Convert Line a----------b
  67. //
  68. // Into Quad v0---------v1
  69. // a b
  70. // v2---------v3
  71. //
  72. Point2F start(x1, y1);
  73. Point2F end(x2, y2);
  74. Point2F perp, lineVec;
  75. // handle degenerate case where point a = b
  76. if(x1 == x2 && y1 == y2)
  77. {
  78. perp.set(0.0f, width * 0.5f);
  79. lineVec.set(0.1f, 0.0f);
  80. }
  81. else
  82. {
  83. perp.set(start.y - end.y, end.x - start.x);
  84. lineVec.set(end.x - start.x, end.y - start.y);
  85. perp.normalize(width * 0.5f);
  86. lineVec.normalize(0.1f);
  87. }
  88. start -= lineVec;
  89. end += lineVec;
  90. GFXVertexBufferHandle<GFXVertexPC> verts(GFX, 4, GFXBufferTypeVolatile);
  91. verts.lock();
  92. verts[0].point.set( start.x+perp.x, start.y+perp.y, z1 );
  93. verts[1].point.set( end.x+perp.x, end.y+perp.y, z2 );
  94. verts[2].point.set( start.x-perp.x, start.y-perp.y, z1 );
  95. verts[3].point.set( end.x-perp.x, end.y-perp.y, z2 );
  96. verts[0].color = color;
  97. verts[1].color = color;
  98. verts[2].color = color;
  99. verts[3].color = color;
  100. verts.unlock();
  101. GFX->setVertexBuffer( verts );
  102. GFXStateBlockDesc desc;
  103. desc.setCullMode(GFXCullNone);
  104. desc.setZReadWrite(false);
  105. desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
  106. GFX->setStateBlockByDesc( desc );
  107. GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
  108. }
  109. }
  110. //-----------------------------------------------------------------------------
  111. GuiTSCtrl::GuiTSCtrl()
  112. {
  113. mCameraZRot = 0;
  114. mForceFOV = 0;
  115. mReflectPriority = 1.0f;
  116. mRenderStyle = RenderStyleStandard;
  117. mSaveModelview.identity();
  118. mSaveProjection.identity();
  119. mSaveViewport.set( 0, 0, 10, 10 );
  120. mSaveWorldToScreenScale.set( 0, 0 );
  121. mLastCameraQuery.cameraMatrix.identity();
  122. mLastCameraQuery.fov = 45.0f;
  123. mLastCameraQuery.object = NULL;
  124. mLastCameraQuery.farPlane = 10.0f;
  125. mLastCameraQuery.nearPlane = 0.01f;
  126. mLastCameraQuery.projectionOffset = Point2F::Zero;
  127. mLastCameraQuery.eyeOffset = Point3F::Zero;
  128. mLastCameraQuery.ortho = false;
  129. }
  130. //-----------------------------------------------------------------------------
  131. void GuiTSCtrl::initPersistFields()
  132. {
  133. addGroup( "Camera" );
  134. addField("cameraZRot", TypeF32, Offset(mCameraZRot, GuiTSCtrl),
  135. "Z rotation angle of camera." );
  136. addField("forceFOV", TypeF32, Offset(mForceFOV, GuiTSCtrl),
  137. "The vertical field of view in degrees or zero to use the normal camera FOV." );
  138. endGroup( "Camera" );
  139. addGroup( "Rendering" );
  140. addField( "reflectPriority", TypeF32, Offset( mReflectPriority, GuiTSCtrl ),
  141. "The share of the per-frame reflection update work this control's rendering should run.\n"
  142. "The reflect update priorities of all visible GuiTSCtrls are added together and each control is assigned "
  143. "a share of the per-frame reflection update time according to its percentage of the total priority value." );
  144. addField("renderStyle", TYPEID< RenderStyles >(), Offset(mRenderStyle, GuiTSCtrl),
  145. "Indicates how this control should render its contents." );
  146. endGroup( "Rendering" );
  147. Parent::initPersistFields();
  148. }
  149. //-----------------------------------------------------------------------------
  150. void GuiTSCtrl::consoleInit()
  151. {
  152. Con::addVariable("$TSControl::frameCount", TypeS32, &smFrameCount, "The number of frames that have been rendered since this control was created.\n"
  153. "@ingroup Rendering\n");
  154. }
  155. //-----------------------------------------------------------------------------
  156. bool GuiTSCtrl::onWake()
  157. {
  158. if ( !Parent::onWake() )
  159. return false;
  160. // Add ourselves to the active viewport list.
  161. AssertFatal( !smAwakeTSCtrls.contains( this ),
  162. "GuiTSCtrl::onWake - This control is already in the awake list!" );
  163. smAwakeTSCtrls.push_back( this );
  164. return true;
  165. }
  166. //-----------------------------------------------------------------------------
  167. void GuiTSCtrl::onSleep()
  168. {
  169. Parent::onSleep();
  170. AssertFatal( smAwakeTSCtrls.contains( this ),
  171. "GuiTSCtrl::onSleep - This control is not in the awake list!" );
  172. smAwakeTSCtrls.remove( this );
  173. }
  174. //-----------------------------------------------------------------------------
  175. void GuiTSCtrl::onPreRender()
  176. {
  177. setUpdate();
  178. }
  179. //-----------------------------------------------------------------------------
  180. bool GuiTSCtrl::processCameraQuery(CameraQuery *)
  181. {
  182. return false;
  183. }
  184. //-----------------------------------------------------------------------------
  185. void GuiTSCtrl::renderWorld(const RectI& /*updateRect*/)
  186. {
  187. }
  188. //-----------------------------------------------------------------------------
  189. F32 GuiTSCtrl::projectRadius( F32 dist, F32 radius ) const
  190. {
  191. // Fixup any negative or zero distance so we
  192. // don't get a divide by zero.
  193. dist = dist > 0.0f ? dist : 0.001f;
  194. return ( radius / dist ) * mSaveWorldToScreenScale.y;
  195. }
  196. //-----------------------------------------------------------------------------
  197. bool GuiTSCtrl::project( const Point3F &pt, Point3F *dest ) const
  198. {
  199. return MathUtils::mProjectWorldToScreen(pt,dest,mSaveViewport,mSaveModelview,mSaveProjection);
  200. }
  201. //-----------------------------------------------------------------------------
  202. bool GuiTSCtrl::unproject( const Point3F &pt, Point3F *dest ) const
  203. {
  204. MathUtils::mProjectScreenToWorld(pt,dest,mSaveViewport,mSaveModelview,mSaveProjection,mLastCameraQuery.farPlane,mLastCameraQuery.nearPlane);
  205. return true;
  206. }
  207. //-----------------------------------------------------------------------------
  208. F32 GuiTSCtrl::calculateViewDistance(F32 radius)
  209. {
  210. F32 fov = mLastCameraQuery.fov;
  211. F32 wwidth;
  212. F32 wheight;
  213. F32 renderWidth = (mRenderStyle == RenderStyleStereoSideBySide) ? F32(getWidth())*0.5f : F32(getWidth());
  214. F32 renderHeight = F32(getHeight());
  215. F32 aspectRatio = renderWidth / renderHeight;
  216. // Use the FOV to calculate the viewport height scale
  217. // then generate the width scale from the aspect ratio.
  218. if(!mLastCameraQuery.ortho)
  219. {
  220. wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2.0f);
  221. wwidth = aspectRatio * wheight;
  222. }
  223. else
  224. {
  225. wheight = mLastCameraQuery.fov;
  226. wwidth = aspectRatio * wheight;
  227. }
  228. // Now determine if we should use the width
  229. // fov or height fov.
  230. //
  231. // If the window is taller than it is wide, use the
  232. // width fov to keep the object completely in view.
  233. if (wheight > wwidth)
  234. fov = mAtan( wwidth / mLastCameraQuery.nearPlane ) * 2.0f;
  235. return radius / mTan(fov / 2.0f);
  236. }
  237. //-----------------------------------------------------------------------------
  238. void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
  239. {
  240. // Save the current transforms so we can restore
  241. // it for child control rendering below.
  242. GFXTransformSaver saver;
  243. if(!processCameraQuery(&mLastCameraQuery))
  244. {
  245. // We have no camera, but render the GUI children
  246. // anyway. This makes editing GuiTSCtrl derived
  247. // controls easier in the GuiEditor.
  248. renderChildControls( offset, updateRect );
  249. return;
  250. }
  251. // Set up the appropriate render style
  252. U32 prevRenderStyle = GFX->getCurrentRenderStyle();
  253. Point2F prevProjectionOffset = GFX->getCurrentProjectionOffset();
  254. Point3F prevEyeOffset = GFX->getStereoEyeOffset();
  255. if(mRenderStyle == RenderStyleStereoSideBySide)
  256. {
  257. GFX->setCurrentRenderStyle(GFXDevice::RS_StereoSideBySide);
  258. GFX->setCurrentProjectionOffset(mLastCameraQuery.projectionOffset);
  259. GFX->setStereoEyeOffset(mLastCameraQuery.eyeOffset);
  260. }
  261. else
  262. {
  263. GFX->setCurrentRenderStyle(GFXDevice::RS_Standard);
  264. }
  265. if ( mReflectPriority > 0 )
  266. {
  267. // Get the total reflection priority.
  268. F32 totalPriority = 0;
  269. for ( U32 i=0; i < smAwakeTSCtrls.size(); i++ )
  270. if ( smAwakeTSCtrls[i]->isVisible() )
  271. totalPriority += smAwakeTSCtrls[i]->mReflectPriority;
  272. REFLECTMGR->update( mReflectPriority / totalPriority,
  273. getExtent(),
  274. mLastCameraQuery );
  275. }
  276. if(mForceFOV != 0)
  277. mLastCameraQuery.fov = mDegToRad(mForceFOV);
  278. if(mCameraZRot)
  279. {
  280. MatrixF rotMat(EulerF(0, 0, mDegToRad(mCameraZRot)));
  281. mLastCameraQuery.cameraMatrix.mul(rotMat);
  282. }
  283. // set up the camera and viewport stuff:
  284. F32 wwidth;
  285. F32 wheight;
  286. F32 renderWidth = (mRenderStyle == RenderStyleStereoSideBySide) ? F32(getWidth())*0.5f : F32(getWidth());
  287. F32 renderHeight = F32(getHeight());
  288. F32 aspectRatio = renderWidth / renderHeight;
  289. // Use the FOV to calculate the viewport height scale
  290. // then generate the width scale from the aspect ratio.
  291. if(!mLastCameraQuery.ortho)
  292. {
  293. wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2.0f);
  294. wwidth = aspectRatio * wheight;
  295. }
  296. else
  297. {
  298. wheight = mLastCameraQuery.fov;
  299. wwidth = aspectRatio * wheight;
  300. }
  301. F32 hscale = wwidth * 2.0f / renderWidth;
  302. F32 vscale = wheight * 2.0f / renderHeight;
  303. Frustum frustum;
  304. if(mRenderStyle == RenderStyleStereoSideBySide)
  305. {
  306. F32 left = 0.0f * hscale - wwidth;
  307. F32 right = renderWidth * hscale - wwidth;
  308. F32 top = wheight - vscale * 0.0f;
  309. F32 bottom = wheight - vscale * renderHeight;
  310. frustum.set( mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane );
  311. }
  312. else
  313. {
  314. F32 left = (updateRect.point.x - offset.x) * hscale - wwidth;
  315. F32 right = (updateRect.point.x + updateRect.extent.x - offset.x) * hscale - wwidth;
  316. F32 top = wheight - vscale * (updateRect.point.y - offset.y);
  317. F32 bottom = wheight - vscale * (updateRect.point.y + updateRect.extent.y - offset.y);
  318. frustum.set( mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane );
  319. }
  320. // Manipulate the frustum for tiled screenshots
  321. const bool screenShotMode = gScreenShot && gScreenShot->isPending();
  322. if ( screenShotMode )
  323. {
  324. gScreenShot->tileFrustum( frustum );
  325. GFX->setViewMatrix(MatrixF::Identity);
  326. }
  327. RectI tempRect = updateRect;
  328. #ifdef TORQUE_OS_MAC
  329. Point2I screensize = getRoot()->getWindowSize();
  330. tempRect.point.y = screensize.y - (tempRect.point.y + tempRect.extent.y);
  331. #endif
  332. GFX->setViewport( tempRect );
  333. // Clear the zBuffer so GUI doesn't hose object rendering accidentally
  334. GFX->clear( GFXClearZBuffer , ColorI(20,20,20), 1.0f, 0 );
  335. GFX->setFrustum( frustum );
  336. if(mLastCameraQuery.ortho)
  337. {
  338. mOrthoWidth = frustum.getWidth();
  339. mOrthoHeight = frustum.getHeight();
  340. }
  341. // We're going to be displaying this render at size of this control in
  342. // pixels - let the scene know so that it can calculate e.g. reflections
  343. // correctly for that final display result.
  344. gClientSceneGraph->setDisplayTargetResolution(getExtent());
  345. // Set the GFX world matrix to the world-to-camera transform, but don't
  346. // change the cameraMatrix in mLastCameraQuery. This is because
  347. // mLastCameraQuery.cameraMatrix is supposed to contain the camera-to-world
  348. // transform. In-place invert would save a copy but mess up any GUIs that
  349. // depend on that value.
  350. MatrixF worldToCamera = mLastCameraQuery.cameraMatrix;
  351. worldToCamera.inverse();
  352. GFX->setWorldMatrix( worldToCamera );
  353. mSaveProjection = GFX->getProjectionMatrix();
  354. mSaveModelview = GFX->getWorldMatrix();
  355. mSaveViewport = updateRect;
  356. mSaveWorldToScreenScale = GFX->getWorldToScreenScale();
  357. mSaveFrustum = GFX->getFrustum();
  358. mSaveFrustum.setTransform( mLastCameraQuery.cameraMatrix );
  359. // Set the default non-clip projection as some
  360. // objects depend on this even in non-reflect cases.
  361. gClientSceneGraph->setNonClipProjection( mSaveProjection );
  362. // Give the post effect manager the worldToCamera, and cameraToScreen matrices
  363. PFXMGR->setFrameMatrices( mSaveModelview, mSaveProjection );
  364. renderWorld(updateRect);
  365. DebugDrawer::get()->render();
  366. // Restore the previous matrix state before
  367. // we begin rendering the child controls.
  368. saver.restore();
  369. // Restore the render style and any stereo parameters
  370. GFX->setCurrentRenderStyle(prevRenderStyle);
  371. GFX->setCurrentProjectionOffset(prevProjectionOffset);
  372. GFX->setStereoEyeOffset(prevEyeOffset);
  373. // Allow subclasses to render 2D elements.
  374. GFX->setClipRect(updateRect);
  375. renderGui( offset, updateRect );
  376. renderChildControls(offset, updateRect);
  377. smFrameCount++;
  378. }
  379. //-----------------------------------------------------------------------------
  380. void GuiTSCtrl::drawLine( Point3F p0, Point3F p1, const ColorI &color, F32 width )
  381. {
  382. if ( !mSaveFrustum.clipSegment( p0, p1 ) )
  383. return;
  384. MathUtils::mProjectWorldToScreen( p0, &p0, mSaveViewport, mSaveModelview, mSaveProjection );
  385. MathUtils::mProjectWorldToScreen( p1, &p1, mSaveViewport, mSaveModelview, mSaveProjection );
  386. p0.x = mClampF( p0.x, 0.0f, mSaveViewport.extent.x );
  387. p0.y = mClampF( p0.y, 0.0f, mSaveViewport.extent.y );
  388. p1.x = mClampF( p1.x, 0.0f, mSaveViewport.extent.x );
  389. p1.y = mClampF( p1.y, 0.0f, mSaveViewport.extent.y );
  390. p0.z = p1.z = 0.0f;
  391. _drawLine( p0, p1, color, width );
  392. }
  393. //-----------------------------------------------------------------------------
  394. void GuiTSCtrl::drawLineList( const Vector<Point3F> &points, const ColorI color, F32 width )
  395. {
  396. for ( S32 i = 0; i < points.size() - 1; i++ )
  397. drawLine( points[i], points[i+1], color, width );
  398. }
  399. //=============================================================================
  400. // Console Methods.
  401. //=============================================================================
  402. // MARK: ---- Console Methods ----
  403. //-----------------------------------------------------------------------------
  404. DefineEngineMethod( GuiTSCtrl, unproject, Point3F, ( Point3F screenPosition ),,
  405. "Transform 3D screen-space coordinates (x, y, depth) to world space.\n"
  406. "This method can be, for example, used to find the world-space position relating to the current mouse cursor position.\n"
  407. "@param screenPosition The x/y position on the screen plus the depth from the screen-plane outwards.\n"
  408. "@return The world-space position corresponding to the given screen-space coordinates." )
  409. {
  410. Point3F worldPos;
  411. object->unproject( screenPosition, &worldPos );
  412. return worldPos;
  413. }
  414. //-----------------------------------------------------------------------------
  415. DefineEngineMethod( GuiTSCtrl, project, Point3F, ( Point3F worldPosition ),,
  416. "Transform world-space coordinates to screen-space (x, y, depth) coordinates.\n"
  417. "@param worldPosition The world-space position to transform to screen-space.\n"
  418. "@return The " )
  419. {
  420. Point3F screenPos;
  421. object->project( worldPosition, &screenPos );
  422. return screenPos;
  423. }
  424. //-----------------------------------------------------------------------------
  425. DefineEngineMethod( GuiTSCtrl, getWorldToScreenScale, Point2F, (),,
  426. "Get the ratio between world-space units and pixels.\n"
  427. "@return The amount of world-space units covered by the extent of a single pixel." )
  428. {
  429. return object->getWorldToScreenScale();
  430. }
  431. //-----------------------------------------------------------------------------
  432. DefineEngineMethod( GuiTSCtrl, calculateViewDistance, F32, ( F32 radius ),,
  433. "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"
  434. "@param radius Radius in world-space units which should fit in the view.\n"
  435. "@return The distance from the viewpoint at which the given radius would be fully visible." )
  436. {
  437. return object->calculateViewDistance( radius );
  438. }