2
0

guiTSControl.cpp 18 KB

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