guiTSControl.cpp 16 KB

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