guiShapeEdPreview.cpp 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948
  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 "console/consoleTypes.h"
  23. #include "console/console.h"
  24. #include "console/engineAPI.h"
  25. #include "gui/core/guiCanvas.h"
  26. #include "gui/editor/guiShapeEdPreview.h"
  27. #include "renderInstance/renderPassManager.h"
  28. #include "lighting/lightManager.h"
  29. #include "lighting/lightInfo.h"
  30. #include "core/resourceManager.h"
  31. #include "scene/sceneManager.h"
  32. #include "scene/sceneRenderState.h"
  33. #include "gfx/primBuilder.h"
  34. #include "gfx/gfxDrawUtil.h"
  35. #include "collision/concretePolyList.h"
  36. #include "T3D/assets/ShapeAsset.h"
  37. #include "T3D/assets/ShapeAnimationAsset.h"
  38. #ifdef TORQUE_COLLADA
  39. #include "collision/optimizedPolyList.h"
  40. #include "ts/collada/colladaUtils.h"
  41. #endif
  42. static const F32 sMoveScaler = 50.0f;
  43. static const F32 sZoomScaler = 200.0f;
  44. static const S32 sNodeRectSize = 16;
  45. IMPLEMENT_CONOBJECT( GuiShapeEdPreview );
  46. ConsoleDocClass( GuiShapeEdPreview,
  47. "@brief This control provides the 3D view for the Shape Editor tool, and is "
  48. "not intended for general purpose use.\n"
  49. "@ingroup GuiControls\n"
  50. "@internal"
  51. );
  52. IMPLEMENT_CALLBACK( GuiShapeEdPreview, onThreadPosChanged, void, ( F32 pos, bool inTransition ), ( pos, inTransition),
  53. "Called when the position of the active thread has changed, such as during "
  54. "playback." );
  55. GuiShapeEdPreview::GuiShapeEdPreview()
  56. : mOrbitDist( 5.0f ),
  57. mMoveSpeed ( 1.0f ),
  58. mZoomSpeed ( 1.0f ),
  59. mGridDimension( 30, 30 ),
  60. mModel( NULL ),
  61. mModelName(StringTable->EmptyString()),
  62. mRenderGhost( false ),
  63. mRenderNodes( false ),
  64. mRenderBounds( false ),
  65. mRenderObjBox( false ),
  66. mRenderColMeshes( false ),
  67. mRenderMounts( true ),
  68. mSunDiffuseColor( 255, 255, 255, 255 ),
  69. mSelectedNode( -1 ),
  70. mSunAmbientColor( 140, 140, 140, 255 ),
  71. mHoverNode( -1 ),
  72. mSelectedObject( -1 ),
  73. mUsingAxisGizmo( false ),
  74. mSelectedObjDetail( 0 ),
  75. mEditingSun( false ),
  76. mGizmoDragID( 0 ),
  77. mTimeScale( 1.0f ),
  78. mActiveThread( -1 ),
  79. mFakeSun( NULL ),
  80. mLastRenderTime( 0 ),
  81. mCameraRot( 0, 0, 3.9f ),
  82. mSunRot( 45.0f, 0, 135.0f ),
  83. mRenderCameraAxes( false ),
  84. mOrbitPos( 0, 0, 0 ),
  85. mFixedDetail( true ),
  86. mCurrentDL( 0 ),
  87. mDetailSize( 0 ),
  88. mDetailPolys( 0 ),
  89. mPixelSize( 0 ),
  90. mNumMaterials( 0 ),
  91. mNumDrawCalls( 0 ),
  92. mNumBones( 0 ),
  93. mNumWeights( 0 ),
  94. mColMeshes( 0 ),
  95. mColPolys( 0 )
  96. {
  97. mActive = true;
  98. // By default don't do dynamic reflection
  99. // updates for this viewport.
  100. mReflectPriority = 0.0f;
  101. }
  102. GuiShapeEdPreview::~GuiShapeEdPreview()
  103. {
  104. SAFE_DELETE( mModel );
  105. SAFE_DELETE( mFakeSun );
  106. }
  107. void GuiShapeEdPreview::initPersistFields()
  108. {
  109. addGroup( "Rendering" );
  110. addField( "editSun", TypeBool, Offset( mEditingSun, GuiShapeEdPreview ),
  111. "If true, dragging the gizmo will rotate the sun direction" );
  112. addField( "selectedNode", TypeS32, Offset( mSelectedNode, GuiShapeEdPreview ),
  113. "Index of the selected node, or -1 if none" );
  114. addField( "selectedObject", TypeS32, Offset( mSelectedObject, GuiShapeEdPreview ),
  115. "Index of the selected object, or -1 if none" );
  116. addField( "selectedObjDetail", TypeS32, Offset( mSelectedObjDetail, GuiShapeEdPreview ),
  117. "Index of the selected object detail mesh, or 0 if none" );
  118. addField( "gridDimension", TypePoint2I, Offset( mGridDimension, GuiShapeEdPreview ),
  119. "Grid dimensions (number of rows and columns) in the form \"rows cols\"" );
  120. addField( "renderGrid", TypeBool, Offset( mRenderGridPlane, EditTSCtrl ),
  121. "Flag indicating whether to draw the grid" );
  122. addField( "renderNodes", TypeBool, Offset( mRenderNodes, GuiShapeEdPreview ),
  123. "Flag indicating whether to render the shape nodes" );
  124. addField( "renderGhost", TypeBool, Offset( mRenderGhost, GuiShapeEdPreview ),
  125. "Flag indicating whether to render the shape in 'ghost' mode (transparent)" );
  126. addField( "renderBounds", TypeBool, Offset( mRenderBounds, GuiShapeEdPreview ),
  127. "Flag indicating whether to render the shape bounding box" );
  128. addField( "renderObjBox", TypeBool, Offset( mRenderObjBox, GuiShapeEdPreview ),
  129. "Flag indicating whether to render the selected object's bounding box" );
  130. addField( "renderColMeshes", TypeBool, Offset( mRenderColMeshes, GuiShapeEdPreview ),
  131. "Flag indicating whether to render the shape's collision geometry" );
  132. addField( "renderMounts", TypeBool, Offset( mRenderMounts, GuiShapeEdPreview ),
  133. "Flag indicating whether to render mounted objects" );
  134. endGroup( "Rendering" );
  135. addGroup( "Sun" );
  136. addProtectedField( "sunDiffuse", TypeColorI, Offset( mSunDiffuseColor, GuiShapeEdPreview ), &setFieldSunDiffuse, &defaultProtectedGetFn,
  137. "Ambient color for the sun" );
  138. addProtectedField( "sunAmbient", TypeColorI, Offset( mSunAmbientColor, GuiShapeEdPreview ), &setFieldSunAmbient, &defaultProtectedGetFn,
  139. "Diffuse color for the sun" );
  140. addProtectedField( "sunAngleX", TypeF32, Offset( mSunRot.x, GuiShapeEdPreview ), &setFieldSunAngleX, &defaultProtectedGetFn,
  141. "X-axis rotation angle for the sun" );
  142. addProtectedField( "sunAngleZ", TypeF32, Offset( mSunRot.z, GuiShapeEdPreview ), &setFieldSunAngleZ, &defaultProtectedGetFn,
  143. "Z-axis rotation angle for the sun" );
  144. endGroup( "Sun" );
  145. addGroup( "Animation" );
  146. addField( "activeThread", TypeS32, Offset( mActiveThread, GuiShapeEdPreview ),
  147. "Index of the active thread, or -1 if none" );
  148. addProtectedField( "threadPos", TypeF32, 0, &setFieldThreadPos, &getFieldThreadPos,
  149. "Current position of the active thread (0-1)" );
  150. addProtectedField( "threadDirection", TypeS32, 0, &setFieldThreadDir, &getFieldThreadDir,
  151. "Playback direction of the active thread" );
  152. addProtectedField( "threadPingPong", TypeBool, 0, &setFieldThreadPingPong, &getFieldThreadPingPong,
  153. "'PingPong' mode of the active thread" );
  154. endGroup( "Animation" );
  155. addGroup( "Detail Stats" );
  156. addField( "fixedDetail", TypeBool, Offset( mFixedDetail, GuiShapeEdPreview ),
  157. "If false, the current detail is selected based on camera distance" );
  158. addField( "orbitDist", TypeF32, Offset( mOrbitDist, GuiShapeEdPreview ),
  159. "The current distance from the camera to the model" );
  160. addProtectedField( "currentDL", TypeS32, Offset( mCurrentDL, GuiShapeEdPreview ), &setFieldCurrentDL, &defaultProtectedGetFn,
  161. "The current detail level" );
  162. addProtectedField( "detailSize", TypeS32, Offset( mDetailSize, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  163. "The size of the current detail" );
  164. addProtectedField( "detailPolys", TypeS32, Offset( mDetailPolys, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  165. "Number of polygons in the current detail" );
  166. addProtectedField( "pixelSize", TypeF32, Offset( mPixelSize, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  167. "The current pixel size of the model" );
  168. addProtectedField( "numMaterials", TypeS32, Offset( mNumMaterials, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  169. "The number of materials in the current detail level" );
  170. addProtectedField( "numDrawCalls", TypeS32, Offset( mNumDrawCalls, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  171. "The number of draw calls in the current detail level" );
  172. addProtectedField( "numBones", TypeS32, Offset( mNumBones, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  173. "The number of bones in the current detail level (skins only)" );
  174. addProtectedField( "numWeights", TypeS32, Offset( mNumWeights, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  175. "The number of vertex weights in the current detail level (skins only)" );
  176. addProtectedField( "colMeshes", TypeS32, Offset( mColMeshes, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  177. "The number of collision meshes in the shape" );
  178. addProtectedField( "colPolys", TypeS32, Offset( mColPolys, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
  179. "The total number of collision polygons (all meshes) in the shape" );
  180. endGroup( "Detail Stats" );
  181. Parent::initPersistFields();
  182. }
  183. bool GuiShapeEdPreview::setFieldCurrentDL( void *object, const char *index, const char *data )
  184. {
  185. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  186. if ( gui )
  187. gui->setCurrentDetail( mFloor( dAtof( data ) + 0.5f ) );
  188. return false;
  189. }
  190. bool GuiShapeEdPreview::setFieldSunDiffuse( void *object, const char *index, const char *data )
  191. {
  192. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  193. if ( gui )
  194. {
  195. Con::setData( TypeColorI, &gui->mSunDiffuseColor, 0, 1, &data );
  196. gui->updateSun();
  197. }
  198. return false;
  199. }
  200. bool GuiShapeEdPreview::setFieldSunAmbient( void *object, const char *index, const char *data )
  201. {
  202. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  203. if ( gui )
  204. {
  205. Con::setData( TypeColorI, &gui->mSunAmbientColor, 0, 1, &data );
  206. gui->updateSun();
  207. }
  208. return false;
  209. }
  210. bool GuiShapeEdPreview::setFieldSunAngleX( void *object, const char *index, const char *data )
  211. {
  212. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  213. if ( gui )
  214. {
  215. Con::setData( TypeF32, &gui->mSunRot.x, 0, 1, &data );
  216. gui->updateSun();
  217. }
  218. return false;
  219. }
  220. bool GuiShapeEdPreview::setFieldSunAngleZ( void *object, const char *index, const char *data )
  221. {
  222. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  223. if ( gui )
  224. {
  225. Con::setData( TypeF32, &gui->mSunRot.z, 0, 1, &data );
  226. gui->updateSun();
  227. }
  228. return false;
  229. }
  230. bool GuiShapeEdPreview::setFieldThreadPos( void *object, const char *index, const char *data )
  231. {
  232. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  233. if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key )
  234. gui->mModel->setPos( gui->mThreads[gui->mActiveThread].key, dAtof( data ) );
  235. return false;
  236. }
  237. const char *GuiShapeEdPreview::getFieldThreadPos( void *object, const char *data )
  238. {
  239. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  240. if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key )
  241. return Con::getFloatArg( gui->mModel->getPos( gui->mThreads[gui->mActiveThread].key ) );
  242. else
  243. return "0";
  244. }
  245. bool GuiShapeEdPreview::setFieldThreadDir( void *object, const char *index, const char *data )
  246. {
  247. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  248. if ( gui && ( gui->mActiveThread >= 0 ) )
  249. {
  250. Thread& thread = gui->mThreads[gui->mActiveThread];
  251. Con::setData( TypeS32, &(thread.direction), 0, 1, &data );
  252. if ( thread.key )
  253. gui->mModel->setTimeScale( thread.key, gui->mTimeScale * thread.direction );
  254. }
  255. return false;
  256. }
  257. const char *GuiShapeEdPreview::getFieldThreadDir( void *object, const char *data )
  258. {
  259. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  260. if ( gui && ( gui->mActiveThread >= 0 ) )
  261. return Con::getIntArg( gui->mThreads[gui->mActiveThread].direction );
  262. else
  263. return "0";
  264. }
  265. bool GuiShapeEdPreview::setFieldThreadPingPong( void *object, const char *index, const char *data )
  266. {
  267. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  268. if ( gui && ( gui->mActiveThread >= 0 ) )
  269. Con::setData( TypeBool, &(gui->mThreads[gui->mActiveThread].pingpong), 0, 1, &data );
  270. return false;
  271. }
  272. const char *GuiShapeEdPreview::getFieldThreadPingPong( void *object, const char *data )
  273. {
  274. GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
  275. if ( gui && ( gui->mActiveThread >= 0 ) )
  276. return Con::getIntArg( gui->mThreads[gui->mActiveThread].pingpong );
  277. else
  278. return "0";
  279. }
  280. bool GuiShapeEdPreview::onWake()
  281. {
  282. if (!Parent::onWake())
  283. return false;
  284. if (!mFakeSun )
  285. mFakeSun = LIGHTMGR->createLightInfo();
  286. mFakeSun->setRange( 2000000.0f );
  287. updateSun();
  288. mGizmoProfile->mode = MoveMode;
  289. return( true );
  290. }
  291. void GuiShapeEdPreview::setDisplayType( S32 type )
  292. {
  293. Parent::setDisplayType( type );
  294. mOrthoCamTrans.set( 0, 0, 0 );
  295. }
  296. //-----------------------------------------------------------------------------
  297. void GuiShapeEdPreview::setCurrentDetail(S32 dl)
  298. {
  299. if ( mModel )
  300. {
  301. TSShape* shape = mModel->getShape();
  302. S32 smallest = shape->mSmallestVisibleDL;
  303. shape->mSmallestVisibleDL = shape->details.size() - 1;
  304. mModel->setCurrentDetail( dl );
  305. shape->mSmallestVisibleDL = smallest;
  306. // Match the camera distance to this detail if necessary
  307. //@todo if ( !gui->mFixedDetail )
  308. }
  309. }
  310. bool GuiShapeEdPreview::setObjectModel(const char* modelName)
  311. {
  312. SAFE_DELETE( mModel );
  313. unmountAll();
  314. mThreads.clear();
  315. mActiveThread = -1;
  316. ResourceManager::get().getChangedSignal().remove(this, &GuiShapeEdPreview::_onResourceChanged);
  317. if (modelName && modelName[0])
  318. {
  319. Resource<TSShape> model = ResourceManager::get().load( modelName );
  320. if (! bool( model ))
  321. {
  322. Con::warnf( avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName ));
  323. return false;
  324. }
  325. mModel = new TSShapeInstance( model, true );
  326. AssertFatal( mModel, avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName ));
  327. TSShape* shape = mModel->getShape();
  328. // Initialize camera values:
  329. mOrbitPos = shape->center;
  330. // Set camera move and zoom speed according to model size
  331. mMoveSpeed = shape->mRadius / sMoveScaler;
  332. mZoomSpeed = shape->mRadius / sZoomScaler;
  333. // Reset node selection
  334. mHoverNode = -1;
  335. mSelectedNode = -1;
  336. mSelectedObject = -1;
  337. mSelectedObjDetail = 0;
  338. mProjectedNodes.setSize( shape->nodes.size() );
  339. // Reset detail stats
  340. mCurrentDL = 0;
  341. // the first time recording
  342. mLastRenderTime = Platform::getVirtualMilliseconds();
  343. mModelName = StringTable->insert(modelName);
  344. //Now to reflect changes when the model file is changed.
  345. ResourceManager::get().getChangedSignal().notify(this, &GuiShapeEdPreview::_onResourceChanged);
  346. }
  347. else
  348. {
  349. mModelName = StringTable->EmptyString();
  350. }
  351. return true;
  352. }
  353. bool GuiShapeEdPreview::setObjectShapeAsset(const char* assetId)
  354. {
  355. SAFE_DELETE(mModel);
  356. unmountAll();
  357. mThreads.clear();
  358. mActiveThread = -1;
  359. StringTableEntry modelName = StringTable->EmptyString();
  360. if (AssetDatabase.isDeclaredAsset(assetId))
  361. {
  362. StringTableEntry id = StringTable->insert(assetId);
  363. StringTableEntry assetType = AssetDatabase.getAssetType(id);
  364. if (assetType == StringTable->insert("ShapeAsset"))
  365. {
  366. ShapeAsset* asset = AssetDatabase.acquireAsset<ShapeAsset>(id);
  367. modelName = asset->getShapeFilePath();
  368. AssetDatabase.releaseAsset(id);
  369. }
  370. else if (assetType == StringTable->insert("ShapeAnimationAsset"))
  371. {
  372. ShapeAnimationAsset* asset = AssetDatabase.acquireAsset<ShapeAnimationAsset>(id);
  373. modelName = asset->getAnimationPath();
  374. AssetDatabase.releaseAsset(id);
  375. }
  376. }
  377. return setObjectModel(modelName);
  378. }
  379. void GuiShapeEdPreview::_onResourceChanged(const Torque::Path& path)
  380. {
  381. if (path != Torque::Path(mModelName))
  382. return;
  383. setObjectModel(path.getFullPath());
  384. }
  385. void GuiShapeEdPreview::addThread()
  386. {
  387. if ( mModel )
  388. {
  389. mThreads.increment();
  390. if ( mActiveThread == -1 )
  391. mActiveThread = 0;
  392. }
  393. }
  394. void GuiShapeEdPreview::removeThread(S32 slot)
  395. {
  396. if ( slot < mThreads.size() )
  397. {
  398. if ( mThreads[slot].key )
  399. mModel->destroyThread( mThreads[slot].key );
  400. mThreads.erase( slot );
  401. if ( mActiveThread >= mThreads.size() )
  402. mActiveThread = mThreads.size() - 1;
  403. }
  404. }
  405. void GuiShapeEdPreview::setTimeScale( F32 scale )
  406. {
  407. // Update time scale for all threads
  408. mTimeScale = scale;
  409. for ( S32 i = 0; i < mThreads.size(); i++ )
  410. {
  411. if ( mThreads[i].key )
  412. mModel->setTimeScale( mThreads[i].key, mTimeScale * mThreads[i].direction );
  413. }
  414. }
  415. void GuiShapeEdPreview::setActiveThreadSequence(const char* seqName, F32 duration, F32 pos, bool play)
  416. {
  417. if ( mActiveThread == -1 )
  418. return;
  419. setThreadSequence(mThreads[mActiveThread], mModel, seqName, duration, pos, play);
  420. }
  421. void GuiShapeEdPreview::setThreadSequence(GuiShapeEdPreview::Thread& thread, TSShapeInstance* shape, const char* seqName, F32 duration, F32 pos, bool play)
  422. {
  423. thread.seqName = seqName;
  424. S32 seq = shape->getShape()->findSequence( thread.seqName );
  425. if ( thread.key && ( shape->getSequence(thread.key) == seq ) )
  426. return;
  427. if ( seq == -1 )
  428. {
  429. // This thread is now set to an invalid sequence, so the key must be
  430. // removed, but we keep the thread info around in case the user changes
  431. // back to a valid sequence
  432. if ( thread.key )
  433. {
  434. shape->destroyThread( thread.key );
  435. thread.key = NULL;
  436. }
  437. }
  438. else
  439. {
  440. // Add a TSThread key if one does not already exist
  441. if ( !thread.key )
  442. {
  443. thread.key = shape->addThread();
  444. shape->setTimeScale( thread.key, mTimeScale * thread.direction );
  445. }
  446. // Transition to slider or synched position?
  447. if ( pos == -1.0f )
  448. pos = shape->getPos( thread.key );
  449. if ( duration == 0.0f )
  450. {
  451. // No transition => go straight to new sequence
  452. shape->setSequence( thread.key, seq, pos );
  453. }
  454. else
  455. {
  456. // Get the current position if transitioning to the sync position
  457. shape->setTimeScale( thread.key, thread.direction >= 0 ? 1 : -1 );
  458. shape->transitionToSequence( thread.key, seq, pos, duration, play );
  459. shape->setTimeScale( thread.key, mTimeScale * thread.direction );
  460. }
  461. }
  462. }
  463. const char* GuiShapeEdPreview::getThreadSequence() const
  464. {
  465. return ( mActiveThread >= 0 ) ? mThreads[mActiveThread].seqName.c_str() : "";
  466. }
  467. void GuiShapeEdPreview::refreshThreadSequences()
  468. {
  469. S32 oldActive = mActiveThread;
  470. for ( S32 i = 0; i < mThreads.size(); i++ )
  471. {
  472. Thread& thread = mThreads[i];
  473. if ( !thread.key )
  474. continue;
  475. // Detect changed (or removed) sequence indices
  476. if ( mModel->getSequence(thread.key) != mModel->getShape()->findSequence( thread.seqName ) )
  477. {
  478. mActiveThread = i;
  479. setThreadSequence( thread, mModel, thread.seqName, 0.0f, mModel->getPos( thread.key ), false );
  480. }
  481. }
  482. mActiveThread = oldActive;
  483. }
  484. //-----------------------------------------------------------------------------
  485. // MOUNTING
  486. bool GuiShapeEdPreview::mountShape(const char* shapeAssetId, const char* nodeName, const char* mountType, S32 slot)
  487. {
  488. if ( !shapeAssetId || !shapeAssetId[0] )
  489. return false;
  490. if (!AssetDatabase.isDeclaredAsset(shapeAssetId))
  491. return false;
  492. ShapeAsset* model = AssetDatabase.acquireAsset<ShapeAsset>(shapeAssetId);
  493. if (model == nullptr || !model->getShapeResource())
  494. return false;
  495. TSShapeInstance* tsi = new TSShapeInstance(model->getShapeResource(), true );
  496. if ( slot == -1 )
  497. {
  498. slot = mMounts.size();
  499. mMounts.push_back( new MountedShape );
  500. }
  501. else
  502. {
  503. // Check if we are switching shapes
  504. if ( mMounts[slot]->mShape->getShape() != tsi->getShape() )
  505. {
  506. delete mMounts[slot]->mShape;
  507. mMounts[slot]->mShape = NULL;
  508. mMounts[slot]->mThread.init();
  509. }
  510. else
  511. {
  512. // Keep using the existing shape
  513. delete tsi;
  514. tsi = mMounts[slot]->mShape;
  515. }
  516. }
  517. MountedShape* mount = mMounts[slot];
  518. mount->mShape = tsi;
  519. if ( dStrEqual( mountType, "Wheel" ) )
  520. mount->mType = MountedShape::Wheel;
  521. else if ( dStrEqual( mountType, "Image" ) )
  522. mount->mType = MountedShape::Image;
  523. else
  524. mount->mType = MountedShape::Object;
  525. setMountNode( slot, nodeName);
  526. return true;
  527. }
  528. void GuiShapeEdPreview::setMountNode(S32 mountSlot, const char* nodeName)
  529. {
  530. if ( mountSlot < mMounts.size() )
  531. {
  532. MountedShape* mount = mMounts[mountSlot];
  533. mount->mNode = mModel ? mModel->getShape()->findNode( nodeName ) : -1;
  534. mount->mTransform.identity();
  535. switch ( mount->mType )
  536. {
  537. case MountedShape::Image:
  538. {
  539. // Mount point is either the node called 'mountPoint' or the origin
  540. S32 node = mount->mShape->getShape()->findNode( "mountPoint" );
  541. if ( node != -1 )
  542. {
  543. mount->mShape->getShape()->getNodeWorldTransform( node, &mount->mTransform );
  544. mount->mTransform.inverse();
  545. }
  546. }
  547. break;
  548. case MountedShape::Wheel:
  549. // Rotate shape according to node's x position (left or right)
  550. {
  551. F32 rotAngle = M_PI_F/2;
  552. if ( mount->mNode != -1 )
  553. {
  554. MatrixF hubMat;
  555. mModel->getShape()->getNodeWorldTransform( mount->mNode, &hubMat );
  556. if ( hubMat.getPosition().x < 0 )
  557. rotAngle = -M_PI_F/2;
  558. }
  559. mount->mTransform.set( EulerF( 0, 0, rotAngle ) );
  560. }
  561. break;
  562. default:
  563. // No mount transform (use origin)
  564. break;
  565. }
  566. }
  567. }
  568. const char* GuiShapeEdPreview::getMountThreadSequence(S32 mountSlot) const
  569. {
  570. if ( mountSlot < mMounts.size() )
  571. {
  572. MountedShape* mount = mMounts[mountSlot];
  573. return mount->mThread.seqName;
  574. }
  575. else
  576. return "";
  577. }
  578. void GuiShapeEdPreview::setMountThreadSequence(S32 mountSlot, const char* seqName)
  579. {
  580. if ( mountSlot < mMounts.size() )
  581. {
  582. MountedShape* mount = mMounts[mountSlot];
  583. setThreadSequence( mount->mThread, mount->mShape, seqName );
  584. }
  585. }
  586. F32 GuiShapeEdPreview::getMountThreadPos(S32 mountSlot) const
  587. {
  588. if ( mountSlot < mMounts.size() )
  589. {
  590. MountedShape* mount = mMounts[mountSlot];
  591. if ( mount->mThread.key )
  592. return mount->mShape->getPos( mount->mThread.key );
  593. }
  594. return 0;
  595. }
  596. void GuiShapeEdPreview::setMountThreadPos(S32 mountSlot, F32 pos)
  597. {
  598. if ( mountSlot < mMounts.size() )
  599. {
  600. MountedShape* mount = mMounts[mountSlot];
  601. if ( mount->mThread.key )
  602. mount->mShape->setPos( mount->mThread.key, pos );
  603. }
  604. }
  605. F32 GuiShapeEdPreview::getMountThreadDir(S32 mountSlot) const
  606. {
  607. if ( mountSlot < mMounts.size() )
  608. {
  609. MountedShape* mount = mMounts[mountSlot];
  610. return mount->mThread.direction;
  611. }
  612. return 0;
  613. }
  614. void GuiShapeEdPreview::setMountThreadDir(S32 mountSlot, F32 dir)
  615. {
  616. if ( mountSlot < mMounts.size() )
  617. {
  618. MountedShape* mount = mMounts[mountSlot];
  619. mount->mThread.direction = dir;
  620. if ( mount->mThread.key )
  621. mount->mShape->setTimeScale( mount->mThread.key, mTimeScale * mount->mThread.direction );
  622. }
  623. }
  624. void GuiShapeEdPreview::unmountShape(S32 mountSlot)
  625. {
  626. if ( mountSlot < mMounts.size() )
  627. {
  628. delete mMounts[mountSlot];
  629. mMounts.erase( mountSlot );
  630. }
  631. }
  632. void GuiShapeEdPreview::unmountAll()
  633. {
  634. for ( S32 i = 0; i < mMounts.size(); i++)
  635. delete mMounts[i];
  636. mMounts.clear();
  637. }
  638. void GuiShapeEdPreview::refreshShape()
  639. {
  640. if ( mModel )
  641. {
  642. // Nodes or details may have changed => refresh the shape instance
  643. mModel->setMaterialList( mModel->mMaterialList );
  644. mModel->initNodeTransforms();
  645. mModel->initMeshObjects();
  646. TSShape* shape = mModel->getShape();
  647. mProjectedNodes.setSize( shape->nodes.size() );
  648. if ( mSelectedObject >= shape->objects.size() )
  649. {
  650. mSelectedObject = -1;
  651. mSelectedObjDetail = 0;
  652. }
  653. // Re-compute the collision mesh stats
  654. mColMeshes = 0;
  655. mColPolys = 0;
  656. for ( S32 i = 0; i < shape->details.size(); i++ )
  657. {
  658. const TSShape::Detail& det = shape->details[i];
  659. const String& detName = shape->getName( det.nameIndex );
  660. if ( ( det.subShapeNum < 0 ) || !detName.startsWith( "collision-" ) )
  661. continue;
  662. mColPolys += det.polyCount;
  663. S32 od = det.objectDetailNum;
  664. S32 start = shape->subShapeFirstObject[det.subShapeNum];
  665. S32 end = start + shape->subShapeNumObjects[det.subShapeNum];
  666. for ( S32 j = start; j < end; j++ )
  667. {
  668. const TSShape::Object &obj = shape->objects[j];
  669. const TSMesh* mesh = ( od < obj.numMeshes ) ? shape->meshes[obj.startMeshIndex + od] : NULL;
  670. if ( mesh )
  671. mColMeshes++;
  672. }
  673. }
  674. }
  675. }
  676. void GuiShapeEdPreview::updateSun()
  677. {
  678. if ( mFakeSun )
  679. {
  680. // Update sun colors
  681. mFakeSun->setColor( mSunDiffuseColor );
  682. mFakeSun->setAmbient( mSunAmbientColor );
  683. // Determine the new sun direction and position
  684. Point3F vec;
  685. MatrixF xRot, zRot;
  686. xRot.set( EulerF( mDegToRad(mSunRot.x), 0.0f, 0.0f ));
  687. zRot.set( EulerF( 0.0f, 0.0f, mDegToRad(mSunRot.z) ));
  688. zRot.mul( xRot );
  689. zRot.getColumn( 1, &vec );
  690. mFakeSun->setDirection( vec );
  691. //mFakeSun->setPosition( vec * -10000.0f );
  692. }
  693. }
  694. void GuiShapeEdPreview::updateNodeTransforms()
  695. {
  696. if ( mModel )
  697. mModel->mDirtyFlags[0] |= TSShapeInstance::TransformDirty;
  698. }
  699. bool GuiShapeEdPreview::getMeshHidden( const char* name ) const
  700. {
  701. if ( mModel )
  702. {
  703. S32 objIndex = mModel->getShape()->findObject( name );
  704. if ( objIndex != -1 )
  705. return mModel->mMeshObjects[objIndex].forceHidden;
  706. }
  707. return false;
  708. }
  709. void GuiShapeEdPreview::setMeshHidden( const char* name, bool hidden )
  710. {
  711. if ( mModel )
  712. {
  713. S32 objIndex = mModel->getShape()->findObject( name );
  714. if ( objIndex != -1 )
  715. mModel->setMeshForceHidden( objIndex, hidden );
  716. }
  717. }
  718. void GuiShapeEdPreview::setAllMeshesHidden( bool hidden )
  719. {
  720. if ( mModel )
  721. {
  722. for ( S32 i = 0; i < mModel->mMeshObjects.size(); i++ )
  723. mModel->setMeshForceHidden( i, hidden );
  724. }
  725. }
  726. void GuiShapeEdPreview::get3DCursor( GuiCursor *&cursor,
  727. bool &visible,
  728. const Gui3DMouseEvent &event_ )
  729. {
  730. cursor = NULL;
  731. visible = false;
  732. GuiCanvas *root = getRoot();
  733. if ( !root )
  734. return;
  735. S32 currCursor = PlatformCursorController::curArrow;
  736. if ( root->mCursorChanged == currCursor )
  737. return;
  738. PlatformWindow *window = root->getPlatformWindow();
  739. PlatformCursorController *controller = window->getCursorController();
  740. // We've already changed the cursor,
  741. // so set it back before we change it again.
  742. if ( root->mCursorChanged != -1 )
  743. controller->popCursor();
  744. // Now change the cursor shape
  745. controller->pushCursor( currCursor );
  746. root->mCursorChanged = currCursor;
  747. }
  748. void GuiShapeEdPreview::fitToShape()
  749. {
  750. if ( !mModel )
  751. return;
  752. // Determine the shape bounding box given the current camera rotation
  753. MatrixF camRotMatrix( smCamMatrix );
  754. camRotMatrix.setPosition( Point3F::Zero );
  755. camRotMatrix.inverse();
  756. Box3F bounds;
  757. computeSceneBounds( bounds );
  758. mOrbitPos = bounds.getCenter();
  759. camRotMatrix.mul( bounds );
  760. // Estimate the camera distance to fill the view by comparing the radii
  761. // of the box and the viewport
  762. F32 len_x = bounds.len_x();
  763. F32 len_z = bounds.len_z();
  764. F32 shapeRadius = mSqrt( len_x*len_x + len_z*len_z ) / 2;
  765. F32 viewRadius = 0.45f * getMin( getExtent().x, getExtent().y );
  766. // Set camera parameters
  767. if ( mDisplayType == DisplayTypePerspective )
  768. {
  769. mOrbitDist = ( shapeRadius / viewRadius ) * mSaveWorldToScreenScale.y;
  770. }
  771. else
  772. {
  773. mOrthoCamTrans.set( 0, 0, 0 );
  774. mOrthoFOV = shapeRadius * viewRadius / 320;
  775. }
  776. }
  777. void GuiShapeEdPreview::setOrbitPos( const Point3F& pos )
  778. {
  779. mOrbitPos = pos;
  780. }
  781. void GuiShapeEdPreview::exportToCollada( const String& path )
  782. {
  783. #ifdef TORQUE_COLLADA
  784. if ( mModel )
  785. {
  786. MatrixF orientation( true );
  787. orientation.setPosition( mModel->getShape()->mBounds.getCenter() );
  788. orientation.inverse();
  789. OptimizedPolyList polyList;
  790. polyList.setBaseTransform( orientation );
  791. mModel->buildPolyList( &polyList, mCurrentDL );
  792. for ( S32 i = 0; i < mMounts.size(); i++ )
  793. {
  794. MountedShape* mount = mMounts[i];
  795. MatrixF mat( true );
  796. if ( mount->mNode != -1 )
  797. {
  798. mat = mModel->mNodeTransforms[ mount->mNode ];
  799. mat *= mount->mTransform;
  800. }
  801. polyList.setTransform( &mat, Point3F::One );
  802. mount->mShape->buildPolyList( &polyList, 0 );
  803. }
  804. // Use a ColladaUtils function to do the actual export to a Collada file
  805. ColladaUtils::exportToCollada( path, polyList );
  806. }
  807. #endif
  808. }
  809. //-----------------------------------------------------------------------------
  810. // Camera control and Node editing
  811. // - moving the mouse over a node will highlight (but not select) it
  812. // - left clicking on a node will select it, the gizmo will appear
  813. // - left clicking on no node will unselect the current node
  814. // - left dragging the gizmo will translate/rotate the node
  815. // - middle drag translates the view
  816. // - right drag rotates the view
  817. // - mouse wheel zooms the view
  818. // - holding shift while changing the view speeds them up
  819. void GuiShapeEdPreview::handleMouseDown(const GuiEvent& event, GizmoMode mode)
  820. {
  821. if (!mActive || !mVisible || !mAwake )
  822. return;
  823. mouseLock();
  824. mLastMousePos = event.mousePoint;
  825. if ( mRenderNodes && ( mode == NoneMode ) )
  826. {
  827. mGizmoDragID++;
  828. make3DMouseEvent( mLastEvent, event );
  829. // Check gizmo first
  830. mUsingAxisGizmo = false;
  831. if ( mSelectedNode != -1 )
  832. {
  833. mGizmo->on3DMouseDown( mLastEvent );
  834. if ( mGizmo->getSelection() != Gizmo::None )
  835. {
  836. mUsingAxisGizmo = true;
  837. return;
  838. }
  839. }
  840. // Check if we have clicked on a node
  841. S32 selected = collideNode( mLastEvent );
  842. if ( selected != mSelectedNode )
  843. {
  844. mSelectedNode = selected;
  845. Con::executef( this, "onNodeSelected", Con::getIntArg( mSelectedNode ));
  846. }
  847. }
  848. //if ( mode == RotateMode )
  849. // mRenderCameraAxes = true;
  850. }
  851. void GuiShapeEdPreview::handleMouseUp(const GuiEvent& event, GizmoMode mode)
  852. {
  853. mouseUnlock();
  854. mUsingAxisGizmo = false;
  855. if ( mRenderNodes && ( mode == NoneMode ) )
  856. {
  857. make3DMouseEvent( mLastEvent, event );
  858. mGizmo->on3DMouseUp( mLastEvent );
  859. }
  860. //if ( mode == RotateMode )
  861. // mRenderCameraAxes = false;
  862. }
  863. void GuiShapeEdPreview::handleMouseMove(const GuiEvent& event, GizmoMode mode)
  864. {
  865. if ( mRenderNodes && ( mode == NoneMode ) )
  866. {
  867. make3DMouseEvent( mLastEvent, event );
  868. if ( mSelectedNode != -1 )
  869. {
  870. // Check if the mouse is hovering over an axis
  871. mGizmo->on3DMouseMove( mLastEvent );
  872. if ( mGizmo->getSelection() != Gizmo::None )
  873. return;
  874. }
  875. // Check if we are over another node
  876. mHoverNode = collideNode( mLastEvent );
  877. }
  878. }
  879. void GuiShapeEdPreview::handleMouseDragged(const GuiEvent& event, GizmoMode mode)
  880. {
  881. // For non-perspective views, ignore rotation, and let EditTSCtrl handle
  882. // translation
  883. if ( mDisplayType != DisplayTypePerspective )
  884. {
  885. if ( mode == MoveMode )
  886. {
  887. Parent::onRightMouseDragged( event );
  888. return;
  889. }
  890. else if ( mode == RotateMode )
  891. return;
  892. }
  893. Point2F delta( event.mousePoint.x - mLastMousePos.x, event.mousePoint.y - mLastMousePos.y );
  894. mLastMousePos = event.mousePoint;
  895. // Use shift to increase speed
  896. delta.x *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f;
  897. delta.y *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f;
  898. if ( mode == NoneMode )
  899. {
  900. if ( mEditingSun )
  901. {
  902. mSunRot.x += mRadToDeg( delta.y );
  903. mSunRot.z += mRadToDeg( delta.x );
  904. updateSun();
  905. }
  906. else if ( mRenderNodes )
  907. {
  908. make3DMouseEvent( mLastEvent, event );
  909. if ( mUsingAxisGizmo )
  910. {
  911. // Use gizmo to modify the transform of the selected node
  912. mGizmo->on3DMouseDragged( mLastEvent );
  913. switch ( mGizmoProfile->mode )
  914. {
  915. case MoveMode:
  916. // Update node transform
  917. if ( mSelectedNode != -1 )
  918. {
  919. Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition() + mGizmo->getOffset();
  920. mModel->mNodeTransforms[mSelectedNode].setPosition( pos );
  921. }
  922. break;
  923. case RotateMode:
  924. // Update node transform
  925. if ( mSelectedNode != -1 )
  926. {
  927. EulerF rot = mGizmo->getDeltaRot();
  928. mModel->mNodeTransforms[mSelectedNode].mul( MatrixF( rot ) );
  929. }
  930. break;
  931. default:
  932. break;
  933. }
  934. // Notify the change in node transform
  935. const char* name = mModel->getShape()->getNodeName(mSelectedNode).c_str();
  936. const Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition();
  937. AngAxisF aa(mModel->mNodeTransforms[mSelectedNode]);
  938. char buffer[256];
  939. dSprintf(buffer, sizeof(buffer), "%g %g %g %g %g %g %g",
  940. pos.x, pos.y, pos.z, aa.axis.x, aa.axis.y, aa.axis.z, aa.angle);
  941. Con::executef(this, "onEditNodeTransform", name, buffer, Con::getIntArg(mGizmoDragID));
  942. }
  943. }
  944. }
  945. else
  946. {
  947. switch ( mode )
  948. {
  949. case MoveMode:
  950. {
  951. VectorF offset(-delta.x, 0, delta.y );
  952. smCamMatrix.mulV( offset );
  953. mOrbitPos += offset * mMoveSpeed;
  954. }
  955. break;
  956. case RotateMode:
  957. mCameraRot.x += delta.y;
  958. mCameraRot.z += delta.x;
  959. break;
  960. default:
  961. break;
  962. }
  963. }
  964. }
  965. void GuiShapeEdPreview::on3DMouseWheelUp(const Gui3DMouseEvent& event)
  966. {
  967. if ( mDisplayType == DisplayTypePerspective )
  968. {
  969. // Use shift and ctrl to increase speed
  970. F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f;
  971. mOrbitDist -= mFabs(event.fval) * mZoomSpeed * mod;
  972. }
  973. }
  974. void GuiShapeEdPreview::on3DMouseWheelDown(const Gui3DMouseEvent& event)
  975. {
  976. if ( mDisplayType == DisplayTypePerspective )
  977. {
  978. // Use shift and ctrl to increase speed
  979. F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f;
  980. mOrbitDist += mFabs(event.fval) * mZoomSpeed * mod;
  981. }
  982. }
  983. //-----------------------------------------------------------------------------
  984. // NODE PICKING
  985. void GuiShapeEdPreview::updateProjectedNodePoints()
  986. {
  987. if ( mModel )
  988. {
  989. // Project the 3D node position to get the 2D screen coordinates
  990. for ( S32 i = 0; i < mModel->mNodeTransforms.size(); i++)
  991. project( mModel->mNodeTransforms[i].getPosition(), &mProjectedNodes[i] );
  992. }
  993. }
  994. S32 GuiShapeEdPreview::collideNode(const Gui3DMouseEvent& event) const
  995. {
  996. // Check if the given position is inside the screen rectangle of
  997. // any shape node
  998. S32 nodeIndex = -1;
  999. F32 minZ = 0;
  1000. for ( S32 i = 0; i < mProjectedNodes.size(); i++)
  1001. {
  1002. const Point3F& pt = mProjectedNodes[i];
  1003. if ( pt.z > 1.0f )
  1004. continue;
  1005. RectI rect( pt.x - sNodeRectSize/2, pt.y - sNodeRectSize/2, sNodeRectSize, sNodeRectSize );
  1006. if ( rect.pointInRect( event.mousePoint ) )
  1007. {
  1008. if ( ( nodeIndex == -1 ) || ( pt.z < minZ ) )
  1009. {
  1010. nodeIndex = i;
  1011. minZ = pt.z;
  1012. }
  1013. }
  1014. }
  1015. return nodeIndex;
  1016. }
  1017. //-----------------------------------------------------------------------------
  1018. // RENDERING
  1019. bool GuiShapeEdPreview::getCameraTransform(MatrixF* cameraMatrix)
  1020. {
  1021. // Adjust the camera so that we are still facing the model
  1022. if ( mDisplayType == DisplayTypePerspective )
  1023. {
  1024. Point3F vec;
  1025. MatrixF xRot, zRot;
  1026. xRot.set( EulerF( mCameraRot.x, 0.0f, 0.0f ));
  1027. zRot.set( EulerF( 0.0f, 0.0f, mCameraRot.z ));
  1028. cameraMatrix->mul( zRot, xRot );
  1029. cameraMatrix->getColumn( 1, &vec );
  1030. cameraMatrix->setColumn( 3, mOrbitPos - vec*mOrbitDist );
  1031. }
  1032. else
  1033. {
  1034. cameraMatrix->identity();
  1035. if ( mModel )
  1036. {
  1037. Point3F camPos = mModel->getShape()->mBounds.getCenter();
  1038. F32 offset = mModel->getShape()->mBounds.len();
  1039. switch (mDisplayType)
  1040. {
  1041. case DisplayTypeTop: camPos.z += offset; break;
  1042. case DisplayTypeBottom: camPos.z -= offset; break;
  1043. case DisplayTypeFront: camPos.y += offset; break;
  1044. case DisplayTypeBack: camPos.y -= offset; break;
  1045. case DisplayTypeRight: camPos.x += offset; break;
  1046. case DisplayTypeLeft: camPos.x -= offset; break;
  1047. default:
  1048. break;
  1049. }
  1050. cameraMatrix->setColumn( 3, camPos );
  1051. }
  1052. }
  1053. return true;
  1054. }
  1055. void GuiShapeEdPreview::computeSceneBounds(Box3F& bounds)
  1056. {
  1057. if ( mModel )
  1058. mModel->computeBounds( mCurrentDL, bounds );
  1059. if (bounds.getExtents().x < POINT_EPSILON || bounds.getExtents().y < POINT_EPSILON || bounds.getExtents().z < POINT_EPSILON)
  1060. {
  1061. bounds.set(Point3F::Zero);
  1062. //We probably don't have any actual meshes in this model, so compute using the bones if we have them
  1063. for (S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
  1064. {
  1065. Point3F nodePos = mModel->mNodeTransforms[i].getPosition();
  1066. bounds.extend(nodePos);
  1067. }
  1068. }
  1069. }
  1070. void GuiShapeEdPreview::updateDetailLevel(const SceneRenderState* state)
  1071. {
  1072. // Make sure current detail is valid
  1073. if ( !mModel->getShape()->details.size() )
  1074. return;
  1075. if ( mModel->getCurrentDetail() >= mModel->getShape()->details.size() )
  1076. setCurrentDetail( mModel->getShape()->details.size() - 1 );
  1077. // Convert between FOV and distance so zoom is consistent between Perspective
  1078. // and Orthographic views (conversion factor found by trial and error)
  1079. const F32 fov2dist = 1.3f;
  1080. if ( mDisplayType == DisplayTypePerspective )
  1081. mOrthoFOV = mOrbitDist / fov2dist;
  1082. else
  1083. mOrbitDist = mOrthoFOV * fov2dist;
  1084. // Use fixed distance in orthographic view (value found by trial + error)
  1085. F32 dist = ( mDisplayType == DisplayTypePerspective ) ? mOrbitDist : 0.1f;
  1086. // Select the appropriate detail level, and update the detail stats
  1087. S32 currentDetail = mModel->getCurrentDetail();
  1088. mModel->setDetailFromDistance( state, dist ); // need to call this to update smLastPixelSize
  1089. if ( mFixedDetail )
  1090. setCurrentDetail( currentDetail );
  1091. if ( mModel->getCurrentDetail() < 0 )
  1092. setCurrentDetail( 0 );
  1093. currentDetail = mModel->getCurrentDetail();
  1094. const TSShape::Detail& det = mModel->getShape()->details[ currentDetail ];
  1095. mDetailPolys = det.polyCount;
  1096. mDetailSize = det.size;
  1097. mPixelSize = TSShapeInstance::smLastPixelSize;
  1098. mNumMaterials = 0;
  1099. mNumDrawCalls = 0;
  1100. mNumBones = 0;
  1101. mNumWeights = 0;
  1102. if ( det.subShapeNum < 0 )
  1103. {
  1104. mNumMaterials = 1;
  1105. mNumDrawCalls = 1;
  1106. }
  1107. else
  1108. {
  1109. Vector<U32> usedMaterials;
  1110. S32 start = mModel->getShape()->subShapeFirstObject[det.subShapeNum];
  1111. S32 end = start + mModel->getShape()->subShapeNumObjects[det.subShapeNum];
  1112. for ( S32 iObj = start; iObj < end; iObj++ )
  1113. {
  1114. const TSShape::Object& obj = mModel->getShape()->objects[iObj];
  1115. if ( obj.numMeshes <= currentDetail )
  1116. continue;
  1117. const TSMesh* mesh = mModel->getShape()->meshes[ obj.startMeshIndex + currentDetail ];
  1118. if ( !mesh )
  1119. continue;
  1120. // Count the number of draw calls and materials
  1121. mNumDrawCalls += mesh->mPrimitives.size();
  1122. for ( S32 iPrim = 0; iPrim < mesh->mPrimitives.size(); iPrim++ )
  1123. usedMaterials.push_back_unique( mesh->mPrimitives[iPrim].matIndex & TSDrawPrimitive::MaterialMask );
  1124. // For skinned meshes, count the number of bones and weights
  1125. if ( mesh->getMeshType() == TSMesh::SkinMeshType )
  1126. {
  1127. const TSSkinMesh* skin = dynamic_cast<const TSSkinMesh*>(mesh);
  1128. mNumBones += skin->batchData.initialTransforms.size();
  1129. mNumWeights += skin->weight.size();
  1130. }
  1131. }
  1132. mNumMaterials = usedMaterials.size();
  1133. }
  1134. // Detect changes in detail level
  1135. if ( mCurrentDL != currentDetail )
  1136. {
  1137. mCurrentDL = currentDetail;
  1138. Con::executef( this, "onDetailChanged");
  1139. }
  1140. }
  1141. void GuiShapeEdPreview::updateThreads(F32 delta)
  1142. {
  1143. // Advance time on all threads
  1144. for ( S32 i = 0; i < mThreads.size(); i++ )
  1145. {
  1146. Thread& thread = mThreads[i];
  1147. if ( !thread.key || !thread.direction )
  1148. continue;
  1149. // Make sure thread priority matches sequence priority (which may have changed)
  1150. mModel->setPriority( thread.key, mModel->getShape()->sequences[mModel->getSequence( thread.key )].priority );
  1151. // Handle ping-pong
  1152. if ( thread.pingpong && !mModel->isInTransition( thread.key ) )
  1153. {
  1154. // Determine next position, then adjust if needed
  1155. F32 threadPos = mModel->getPos( thread.key );
  1156. F32 nextPos = threadPos + ( mModel->getTimeScale( thread.key ) * delta / mModel->getDuration( thread.key ) );
  1157. if ( nextPos < 0 )
  1158. {
  1159. // Reflect position and swap playback direction
  1160. nextPos = -nextPos;
  1161. mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) );
  1162. mModel->setPos( thread.key, nextPos );
  1163. }
  1164. else if ( nextPos > 1.0f )
  1165. {
  1166. // Reflect position and swap playback direction
  1167. nextPos = 2.0f - nextPos;
  1168. mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) );
  1169. mModel->setPos( thread.key, nextPos );
  1170. }
  1171. else
  1172. {
  1173. // Advance time normally
  1174. mModel->advanceTime( delta, thread.key );
  1175. }
  1176. }
  1177. else
  1178. {
  1179. // Advance time normally
  1180. mModel->advanceTime( delta, thread.key );
  1181. }
  1182. // Invoke script callback if active thread position has changed
  1183. if ( i == mActiveThread )
  1184. {
  1185. F32 threadPos = mModel->getPos( thread.key );
  1186. bool inTransition = mModel->isInTransition( thread.key );
  1187. onThreadPosChanged_callback( threadPos, inTransition );
  1188. }
  1189. }
  1190. // Mark threads as dirty so they will be re-sorted, in case the user changed
  1191. // sequence priority or blend flags
  1192. mModel->setDirty( TSShapeInstance::ThreadDirty );
  1193. // Advance time on all mounted shape threads
  1194. for ( S32 i = 0; i < mMounts.size(); i++ )
  1195. {
  1196. MountedShape* mount = mMounts[i];
  1197. if ( mount->mThread.key )
  1198. mount->mShape->advanceTime( delta, mount->mThread.key );
  1199. }
  1200. }
  1201. void GuiShapeEdPreview::renderWorld(const RectI &updateRect)
  1202. {
  1203. if ( !mModel )
  1204. return;
  1205. mSaveFrustum = GFX->getFrustum();
  1206. mSaveFrustum.setFarDist( 100000.0f );
  1207. GFX->setFrustum( mSaveFrustum );
  1208. mSaveFrustum.setTransform( smCamMatrix );
  1209. mSaveProjection = GFX->getProjectionMatrix();
  1210. mSaveWorldToScreenScale = GFX->getWorldToScreenScale();
  1211. FogData savedFogData = gClientSceneGraph->getFogData();
  1212. gClientSceneGraph->setFogData( FogData() ); // no fog in preview window
  1213. SceneRenderState state
  1214. (
  1215. gClientSceneGraph,
  1216. SPT_Diffuse,
  1217. SceneCameraState( GFX->getViewport(), mSaveFrustum,
  1218. GFX->getWorldMatrix(), GFX->getProjectionMatrix() )
  1219. );
  1220. // Set up pass transforms
  1221. RenderPassManager *renderPass = state.getRenderPass();
  1222. renderPass->assignSharedXform( RenderPassManager::View, GFX->getWorldMatrix() );
  1223. renderPass->assignSharedXform( RenderPassManager::Projection, GFX->getProjectionMatrix() );
  1224. // Set up our TS render state here.
  1225. TSRenderState rdata;
  1226. rdata.setSceneState(&state);
  1227. LIGHTMGR->unregisterAllLights();
  1228. LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mFakeSun );
  1229. // We might have some forward lit materials
  1230. // so pass down a query to gather lights.
  1231. LightQuery query;
  1232. query.init( SphereF( Point3F::Zero, 1 ) );
  1233. rdata.setLightQuery( &query );
  1234. // Update projected node points (for mouse picking)
  1235. updateProjectedNodePoints();
  1236. // Determine time elapsed since last render (for animation playback)
  1237. S32 time = Platform::getVirtualMilliseconds();
  1238. S32 dt = time - mLastRenderTime;
  1239. mLastRenderTime = time;
  1240. if ( mModel )
  1241. {
  1242. updateDetailLevel( &state );
  1243. // Render the grid
  1244. renderGrid();
  1245. // Animate the model
  1246. updateThreads( (F32)dt / 1000.f );
  1247. mModel->animate();
  1248. // Render the shape
  1249. GFX->setStateBlock( mDefaultGuiSB );
  1250. if ( mRenderGhost )
  1251. rdata.setFadeOverride( 0.5f );
  1252. GFX->pushWorldMatrix();
  1253. GFX->setWorldMatrix( MatrixF::Identity );
  1254. mModel->render( rdata );
  1255. // Render mounted objects
  1256. if ( mRenderMounts )
  1257. {
  1258. for ( S32 i = 0; i < mMounts.size(); i++ )
  1259. {
  1260. MountedShape* mount = mMounts[i];
  1261. GFX->pushWorldMatrix();
  1262. if ( mount->mNode != -1 )
  1263. {
  1264. GFX->multWorld( mModel->mNodeTransforms[ mount->mNode ] );
  1265. GFX->multWorld( mount->mTransform );
  1266. }
  1267. mount->mShape->animate();
  1268. mount->mShape->render( rdata );
  1269. GFX->popWorldMatrix();
  1270. }
  1271. }
  1272. GFX->popWorldMatrix();
  1273. renderPass->renderPass( &state );
  1274. // @todo: Model and other elements (bounds, grid etc) use different
  1275. // zBuffers, so at the moment, draw order determines what is on top
  1276. // Render collision volumes
  1277. renderCollisionMeshes();
  1278. // Render the shape bounding box
  1279. if ( mRenderBounds )
  1280. {
  1281. Point3F boxSize = mModel->getShape()->mBounds.maxExtents - mModel->getShape()->mBounds.minExtents;
  1282. GFXStateBlockDesc desc;
  1283. desc.fillMode = GFXFillWireframe;
  1284. GFX->getDrawUtil()->drawCube( desc, boxSize, mModel->getShape()->center, ColorI::WHITE );
  1285. }
  1286. // Render the selected object bounding box
  1287. if ( mRenderObjBox && ( mSelectedObject != -1 ) )
  1288. {
  1289. const TSShape::Object& obj = mModel->getShape()->objects[mSelectedObject];
  1290. const TSMesh* mesh = ( mCurrentDL < obj.numMeshes ) ? mModel->getShape()->meshes[obj.startMeshIndex + mSelectedObjDetail] : NULL;
  1291. if ( mesh )
  1292. {
  1293. GFX->pushWorldMatrix();
  1294. if ( obj.nodeIndex != -1 )
  1295. GFX->multWorld( mModel->mNodeTransforms[ obj.nodeIndex ] );
  1296. const Box3F& bounds = mesh->getBounds();
  1297. GFXStateBlockDesc desc;
  1298. desc.fillMode = GFXFillWireframe;
  1299. GFX->getDrawUtil()->drawCube( desc, bounds.getExtents(), bounds.getCenter(), ColorI::RED );
  1300. GFX->popWorldMatrix();
  1301. }
  1302. }
  1303. // Render the sun direction if currently editing it
  1304. renderSunDirection();
  1305. // render the nodes in the model
  1306. renderNodes();
  1307. // use the gizmo to render the camera axes
  1308. if ( mRenderCameraAxes )
  1309. {
  1310. GizmoMode savedMode = mGizmoProfile->mode;
  1311. mGizmoProfile->mode = MoveMode;
  1312. Point3F pos;
  1313. Point2I screenCenter( updateRect.point + updateRect.extent/2 );
  1314. unproject( Point3F( screenCenter.x, screenCenter.y, 0.5 ), &pos );
  1315. mGizmo->set( MatrixF::Identity, pos, Point3F::One);
  1316. mGizmo->renderGizmo( smCamMatrix );
  1317. mGizmoProfile->mode = savedMode;
  1318. }
  1319. }
  1320. gClientSceneGraph->setFogData( savedFogData ); // restore fog setting
  1321. }
  1322. void GuiShapeEdPreview::renderGui(Point2I offset, const RectI& updateRect)
  1323. {
  1324. // Render the 2D stuff here
  1325. // Render the names of the hovered and selected nodes
  1326. if ( mModel )
  1327. {
  1328. if ( mRenderNodes && mHoverNode != -1 )
  1329. renderNodeName( mHoverNode, LinearColorF::WHITE );
  1330. if ( mSelectedNode != -1 )
  1331. renderNodeName( mSelectedNode, LinearColorF::WHITE );
  1332. }
  1333. }
  1334. void GuiShapeEdPreview::renderGrid()
  1335. {
  1336. if ( mRenderGridPlane )
  1337. {
  1338. // Use EditTSCtrl to render the grid in non-perspective views
  1339. if ( mDisplayType != DisplayTypePerspective )
  1340. {
  1341. Parent::renderGrid();
  1342. return;
  1343. }
  1344. // Round grid dimension up to a multiple of the minor ticks
  1345. Point2I dim(mGridDimension.x + mGridPlaneMinorTicks, mGridDimension.y + mGridPlaneMinorTicks);
  1346. dim /= ( mGridPlaneMinorTicks + 1 );
  1347. dim *= ( mGridPlaneMinorTicks + 1 );
  1348. Point2F minorStep( mGridPlaneSize, mGridPlaneSize );
  1349. Point2F size( minorStep.x * dim.x, minorStep.y * dim.y );
  1350. Point2F majorStep( minorStep * ( mGridPlaneMinorTicks + 1 ) );
  1351. GFXStateBlockDesc desc;
  1352. desc.setBlend( true );
  1353. desc.setZReadWrite( true, false );
  1354. GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, minorStep, mGridPlaneMinorTickColor );
  1355. GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, majorStep, mGridPlaneColor );
  1356. }
  1357. }
  1358. void GuiShapeEdPreview::renderSunDirection() const
  1359. {
  1360. if ( mEditingSun )
  1361. {
  1362. // Render four arrows aiming in the direction of the sun's light
  1363. ColorI color = LinearColorF( mFakeSun->getColor()).toColorI();
  1364. F32 length = mModel->getShape()->mBounds.len() * 0.8f;
  1365. // Get the sun's vectors
  1366. Point3F fwd = mFakeSun->getTransform().getForwardVector();
  1367. Point3F up = mFakeSun->getTransform().getUpVector() * length / 8;
  1368. Point3F right = mFakeSun->getTransform().getRightVector() * length / 8;
  1369. // Calculate the start and end points of the first arrow (bottom left)
  1370. Point3F start = mModel->getShape()->center - fwd * length - up/2 - right/2;
  1371. Point3F end = mModel->getShape()->center - fwd * length / 3 - up/2 - right/2;
  1372. GFXStateBlockDesc desc;
  1373. desc.setZReadWrite( true, true );
  1374. GFXDrawUtil* drawUtil = GFX->getDrawUtil();
  1375. drawUtil->drawArrow( desc, start, end, color );
  1376. drawUtil->drawArrow( desc, start + up, end + up, color );
  1377. drawUtil->drawArrow( desc, start + right, end + right, color );
  1378. drawUtil->drawArrow( desc, start + up + right, end + up + right, color );
  1379. }
  1380. }
  1381. void GuiShapeEdPreview::renderNodes() const
  1382. {
  1383. if ( mRenderNodes )
  1384. {
  1385. // Render links between nodes
  1386. GFXStateBlockDesc desc;
  1387. desc.setZReadWrite( false, true );
  1388. desc.setCullMode( GFXCullNone );
  1389. GFX->setStateBlockByDesc( desc );
  1390. PrimBuild::color( ColorI::WHITE );
  1391. PrimBuild::begin( GFXLineList, mModel->getShape()->nodes.size() * 2 );
  1392. for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
  1393. {
  1394. const TSShape::Node& node = mModel->getShape()->nodes[i];
  1395. if (node.parentIndex >= 0)
  1396. {
  1397. Point3F start(mModel->mNodeTransforms[i].getPosition());
  1398. Point3F end(mModel->mNodeTransforms[node.parentIndex].getPosition());
  1399. PrimBuild::vertex3f( start.x, start.y, start.z );
  1400. PrimBuild::vertex3f( end.x, end.y, end.z );
  1401. }
  1402. }
  1403. PrimBuild::end();
  1404. // Render the node axes
  1405. for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
  1406. {
  1407. // Render the selected and hover nodes last (so they are on top)
  1408. if ( ( i == mSelectedNode ) || ( i == mHoverNode ) )
  1409. continue;
  1410. renderNodeAxes( i, LinearColorF::WHITE );
  1411. }
  1412. // Render the hovered node
  1413. if ( mHoverNode != -1 )
  1414. renderNodeAxes( mHoverNode, LinearColorF::GREEN );
  1415. }
  1416. // Render the selected node (even if mRenderNodes is false)
  1417. if ( mSelectedNode != -1 )
  1418. {
  1419. renderNodeAxes( mSelectedNode, LinearColorF::GREEN );
  1420. const MatrixF& nodeMat = mModel->mNodeTransforms[mSelectedNode];
  1421. mGizmo->set( nodeMat, nodeMat.getPosition(), Point3F::One);
  1422. mGizmo->renderGizmo( smCamMatrix );
  1423. }
  1424. }
  1425. void GuiShapeEdPreview::renderNodeAxes(S32 index, const LinearColorF& nodeColor) const
  1426. {
  1427. if(mModel->mNodeTransforms.size() <= index || index < 0)
  1428. return;
  1429. const Point3F xAxis( 1.0f, 0.15f, 0.15f );
  1430. const Point3F yAxis( 0.15f, 1.0f, 0.15f );
  1431. const Point3F zAxis( 0.15f, 0.15f, 1.0f );
  1432. GFXStateBlockDesc desc;
  1433. desc.setZReadWrite( false, true );
  1434. desc.setCullMode( GFXCullNone );
  1435. // Render nodes the same size regardless of zoom
  1436. F32 scale = mOrbitDist / 60;
  1437. GFX->pushWorldMatrix();
  1438. GFX->multWorld( mModel->mNodeTransforms[index] );
  1439. const ColorI color = LinearColorF(nodeColor).toColorI();
  1440. GFX->getDrawUtil()->drawCube( desc, xAxis * scale, Point3F::Zero, color );
  1441. GFX->getDrawUtil()->drawCube( desc, yAxis * scale, Point3F::Zero, color );
  1442. GFX->getDrawUtil()->drawCube( desc, zAxis * scale, Point3F::Zero, color );
  1443. GFX->popWorldMatrix();
  1444. }
  1445. void GuiShapeEdPreview::renderNodeName(S32 index, const LinearColorF& textColor) const
  1446. {
  1447. if(index < 0 || index >= mModel->getShape()->nodes.size() || index >= mProjectedNodes.size())
  1448. return;
  1449. const TSShape::Node& node = mModel->getShape()->nodes[index];
  1450. const String& nodeName = mModel->getShape()->getName( node.nameIndex );
  1451. Point2I pos( mProjectedNodes[index].x, mProjectedNodes[index].y + sNodeRectSize + 6 );
  1452. GFX->getDrawUtil()->setBitmapModulation( LinearColorF(textColor).toColorI());
  1453. GFX->getDrawUtil()->drawText( mProfile->mFont, pos, nodeName.c_str() );
  1454. }
  1455. void GuiShapeEdPreview::renderCollisionMeshes() const
  1456. {
  1457. if ( mRenderColMeshes )
  1458. {
  1459. ConcretePolyList polylist;
  1460. polylist.setTransform( &MatrixF::Identity, Point3F::One );
  1461. for ( S32 iDet = 0; iDet < mModel->getShape()->details.size(); iDet++ )
  1462. {
  1463. const TSShape::Detail& det = mModel->getShape()->details[iDet];
  1464. const String& detName = mModel->getShape()->getName( det.nameIndex );
  1465. // Ignore non-collision details
  1466. if ( detName.startsWith( "Collision-" ) )
  1467. mModel->buildPolyList( &polylist, iDet );
  1468. }
  1469. polylist.render();
  1470. }
  1471. }
  1472. //-----------------------------------------------------------------------------
  1473. // Console methods (GuiShapeEdPreview)
  1474. //-----------------------------------------------------------------------------
  1475. DefineEngineMethod( GuiShapeEdPreview, setOrbitPos, void, ( Point3F pos ),,
  1476. "Set the camera orbit position\n\n"
  1477. "@param pos Position in the form \"x y z\"\n" )
  1478. {
  1479. object->setOrbitPos( pos );
  1480. }
  1481. DefineEngineMethod( GuiShapeEdPreview, setModel, bool, ( const char* shapePath ),,
  1482. "Sets the model to be displayed in this control\n\n"
  1483. "@param shapeName Name of the model to display.\n"
  1484. "@return True if the model was loaded successfully, false otherwise.\n" )
  1485. {
  1486. return object->setObjectModel( shapePath );
  1487. }
  1488. DefineEngineMethod(GuiShapeEdPreview, setShapeAsset, bool, (const char* shapeAsset), ,
  1489. "Sets the model to be displayed in this control\n\n"
  1490. "@param shapeName Name of the model to display.\n"
  1491. "@return True if the model was loaded successfully, false otherwise.\n")
  1492. {
  1493. return object->setObjectShapeAsset(shapeAsset);
  1494. }
  1495. DefineEngineMethod( GuiShapeEdPreview, fitToShape, void, (),,
  1496. "Adjust the camera position and zoom to fit the shape within the view.\n\n" )
  1497. {
  1498. object->fitToShape();
  1499. }
  1500. DefineEngineMethod( GuiShapeEdPreview, refreshShape, void, (),,
  1501. "Refresh the shape (used when the shape meshes or nodes have been added or removed)\n\n" )
  1502. {
  1503. object->refreshShape();
  1504. }
  1505. DefineEngineMethod( GuiShapeEdPreview, updateNodeTransforms, void, (),,
  1506. "Refresh the shape node transforms (used when a node transform has been modified externally)\n\n" )
  1507. {
  1508. object->updateNodeTransforms();
  1509. }
  1510. DefineEngineMethod( GuiShapeEdPreview, computeShapeBounds, Box3F, (),,
  1511. "Compute the bounding box of the shape using the current detail and node transforms\n\n"
  1512. "@return the bounding box \"min.x min.y min.z max.x max.y max.z\"" )
  1513. {
  1514. Box3F bounds;
  1515. object->computeSceneBounds(bounds);
  1516. return bounds;
  1517. }
  1518. DefineEngineMethod( GuiShapeEdPreview, getMeshHidden, bool, ( const char* name ),,
  1519. "Return whether the named object is currently hidden\n\n" )
  1520. {
  1521. return object->getMeshHidden( name );
  1522. }
  1523. DefineEngineMethod( GuiShapeEdPreview, setMeshHidden, void, ( const char* name, bool hidden ),,
  1524. "Show or hide the named object in the shape\n\n" )
  1525. {
  1526. object->setMeshHidden( name, hidden );
  1527. }
  1528. DefineEngineMethod( GuiShapeEdPreview, setAllMeshesHidden, void, ( bool hidden ),,
  1529. "Show or hide all objects in the shape\n\n" )
  1530. {
  1531. object->setAllMeshesHidden( hidden );
  1532. }
  1533. DefineEngineMethod( GuiShapeEdPreview, exportToCollada, void, ( const char* path ),,
  1534. "Export the current shape and all mounted objects to COLLADA (.dae).\n"
  1535. "Note that animation is not exported, and all geometry is combined into a "
  1536. "single mesh.\n\n"
  1537. "@param path Destination filename\n" )
  1538. {
  1539. object->exportToCollada( path );
  1540. }
  1541. //-----------------------------------------------------------------------------
  1542. // THREADS
  1543. DefineEngineMethod( GuiShapeEdPreview, addThread, void, (),,
  1544. "Add a new thread (initially without any sequence set)\n\n" )
  1545. {
  1546. object->addThread();
  1547. }
  1548. DefineEngineMethod( GuiShapeEdPreview, removeThread, void, ( S32 slot ),,
  1549. "Removes the specifed thread\n\n"
  1550. "@param slot index of the thread to remove\n" )
  1551. {
  1552. object->removeThread( slot );
  1553. }
  1554. DefineEngineMethod( GuiShapeEdPreview, getThreadCount, S32, (),,
  1555. "Get the number of threads\n\n"
  1556. "@return the number of threads\n" )
  1557. {
  1558. return object->getThreadCount();
  1559. }
  1560. DefineEngineMethod( GuiShapeEdPreview, setTimeScale, void, ( F32 scale ),,
  1561. "Set the time scale of all threads\n\n"
  1562. "@param scale new time scale value\n" )
  1563. {
  1564. object->setTimeScale( scale );
  1565. }
  1566. DefineEngineMethod( GuiShapeEdPreview, setThreadSequence, void, ( const char* name, F32 duration, F32 pos, bool play ), ( 0, 0, false ),
  1567. "Sets the sequence to play for the active thread.\n\n"
  1568. "@param name name of the sequence to play\n"
  1569. "@param duration transition duration (0 for no transition)\n"
  1570. "@param pos position in the new sequence to transition to\n"
  1571. "@param play if true, the new sequence will play during the transition\n" )
  1572. {
  1573. object->setActiveThreadSequence( name, duration, pos, play );
  1574. }
  1575. DefineEngineMethod( GuiShapeEdPreview, getThreadSequence, const char*, (),,
  1576. "Get the name of the sequence assigned to the active thread" )
  1577. {
  1578. return object->getThreadSequence();
  1579. }
  1580. DefineEngineMethod( GuiShapeEdPreview, refreshThreadSequences, void, (),,
  1581. "Refreshes thread sequences (in case of removed/renamed sequences" )
  1582. {
  1583. object->refreshThreadSequences();
  1584. }
  1585. //-----------------------------------------------------------------------------
  1586. // Mounting
  1587. DefineEngineMethod( GuiShapeEdPreview, mountShape, bool, ( const char* shapeAssetId, const char* nodeName, const char* type, S32 slot ),,
  1588. "Mount a shape onto the main shape at the specified node\n\n"
  1589. "@param shapeAssetId AssetId of the shape to mount\n"
  1590. "@param nodeName name of the node on the main shape to mount to\n"
  1591. "@param type type of mounting to use (Object, Image or Wheel)\n"
  1592. "@param slot mount slot\n" )
  1593. {
  1594. return object->mountShape(shapeAssetId, nodeName, type, slot );
  1595. }
  1596. DefineEngineMethod( GuiShapeEdPreview, setMountNode, void, ( S32 slot, const char* nodeName ),,
  1597. "Set the node a shape is mounted to.\n\n"
  1598. "@param slot mounted shape slot\n"
  1599. "@param nodename name of the node to mount to\n" )
  1600. {
  1601. object->setMountNode( slot, nodeName );
  1602. }
  1603. DefineEngineMethod( GuiShapeEdPreview, getMountThreadSequence, const char*, ( S32 slot ),,
  1604. "Get the name of the sequence playing on this mounted shape\n"
  1605. "@param slot mounted shape slot\n"
  1606. "@return name of the sequence (if any)\n" )
  1607. {
  1608. return object->getMountThreadSequence( slot );
  1609. }
  1610. DefineEngineMethod( GuiShapeEdPreview, setMountThreadSequence, void, ( S32 slot, const char* name ),,
  1611. "Set the sequence to play for the shape mounted in the specified slot\n"
  1612. "@param slot mounted shape slot\n"
  1613. "@param name name of the sequence to play\n" )
  1614. {
  1615. object->setMountThreadSequence( slot, name );
  1616. }
  1617. DefineEngineMethod( GuiShapeEdPreview, getMountThreadPos, F32, ( S32 slot ),,
  1618. "Get the playback position of the sequence playing on this mounted shape\n"
  1619. "@param slot mounted shape slot\n"
  1620. "@return playback position of the sequence (0-1)\n" )
  1621. {
  1622. return object->getMountThreadPos( slot );
  1623. }
  1624. DefineEngineMethod( GuiShapeEdPreview, setMountThreadPos, void, ( S32 slot, F32 pos ),,
  1625. "Set the sequence position of the shape mounted in the specified slot\n"
  1626. "@param slot mounted shape slot\n"
  1627. "@param pos sequence position (0-1)\n" )
  1628. {
  1629. object->setMountThreadPos( slot, pos );
  1630. }
  1631. DefineEngineMethod( GuiShapeEdPreview, getMountThreadDir, F32, ( S32 slot ),,
  1632. "Get the playback direction of the sequence playing on this mounted shape\n"
  1633. "@param slot mounted shape slot\n"
  1634. "@return direction of the sequence (-1=reverse, 0=paused, 1=forward)\n" )
  1635. {
  1636. return object->getMountThreadDir( slot );
  1637. }
  1638. DefineEngineMethod( GuiShapeEdPreview, setMountThreadDir, void, ( S32 slot, F32 dir ),,
  1639. "Set the playback direction of the shape mounted in the specified slot\n"
  1640. "@param slot mounted shape slot\n"
  1641. "@param dir playback direction (-1=backwards, 0=paused, 1=forwards)\n" )
  1642. {
  1643. object->setMountThreadDir( slot, dir );
  1644. }
  1645. DefineEngineMethod( GuiShapeEdPreview, unmountShape, void, ( S32 slot ),,
  1646. "Unmount the shape in the specified slot\n"
  1647. "@param slot mounted shape slot\n" )
  1648. {
  1649. return object->unmountShape( slot );
  1650. }
  1651. DefineEngineMethod( GuiShapeEdPreview, unmountAll, void, (),,
  1652. "Unmount all shapes\n" )
  1653. {
  1654. return object->unmountAll();
  1655. }