12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "console/consoleTypes.h"
- #include "console/console.h"
- #include "console/engineAPI.h"
- #include "gui/core/guiCanvas.h"
- #include "gui/editor/guiShapeEdPreview.h"
- #include "renderInstance/renderPassManager.h"
- #include "lighting/lightManager.h"
- #include "lighting/lightInfo.h"
- #include "core/resourceManager.h"
- #include "scene/sceneManager.h"
- #include "scene/sceneRenderState.h"
- #include "gfx/primBuilder.h"
- #include "gfx/gfxDrawUtil.h"
- #include "collision/concretePolyList.h"
- #include "T3D/assets/ShapeAsset.h"
- #include "T3D/assets/ShapeAnimationAsset.h"
- #ifdef TORQUE_COLLADA
- #include "collision/optimizedPolyList.h"
- #include "ts/collada/colladaUtils.h"
- #endif
- static const F32 sMoveScaler = 50.0f;
- static const F32 sZoomScaler = 200.0f;
- static const S32 sNodeRectSize = 16;
- IMPLEMENT_CONOBJECT( GuiShapeEdPreview );
- ConsoleDocClass( GuiShapeEdPreview,
- "@brief This control provides the 3D view for the Shape Editor tool, and is "
- "not intended for general purpose use.\n"
- "@ingroup GuiControls\n"
- "@internal"
- );
- IMPLEMENT_CALLBACK( GuiShapeEdPreview, onThreadPosChanged, void, ( F32 pos, bool inTransition ), ( pos, inTransition),
- "Called when the position of the active thread has changed, such as during "
- "playback." );
- GuiShapeEdPreview::GuiShapeEdPreview()
- : mOrbitDist( 5.0f ),
- mMoveSpeed ( 1.0f ),
- mZoomSpeed ( 1.0f ),
- mGridDimension( 30, 30 ),
- mModel( NULL ),
- mModelName(StringTable->EmptyString()),
- mRenderGhost( false ),
- mRenderNodes( false ),
- mRenderBounds( false ),
- mRenderObjBox( false ),
- mRenderColMeshes( false ),
- mRenderMounts( true ),
- mSunDiffuseColor( 255, 255, 255, 255 ),
- mSelectedNode( -1 ),
- mSunAmbientColor( 140, 140, 140, 255 ),
- mHoverNode( -1 ),
- mSelectedObject( -1 ),
- mUsingAxisGizmo( false ),
- mSelectedObjDetail( 0 ),
- mEditingSun( false ),
- mGizmoDragID( 0 ),
- mTimeScale( 1.0f ),
- mActiveThread( -1 ),
- mFakeSun( NULL ),
- mLastRenderTime( 0 ),
- mCameraRot( 0, 0, 3.9f ),
- mSunRot( 45.0f, 0, 135.0f ),
- mRenderCameraAxes( false ),
- mOrbitPos( 0, 0, 0 ),
- mFixedDetail( true ),
- mCurrentDL( 0 ),
- mDetailSize( 0 ),
- mDetailPolys( 0 ),
- mPixelSize( 0 ),
- mNumMaterials( 0 ),
- mNumDrawCalls( 0 ),
- mNumBones( 0 ),
- mNumWeights( 0 ),
- mColMeshes( 0 ),
- mColPolys( 0 )
- {
- mActive = true;
- // By default don't do dynamic reflection
- // updates for this viewport.
- mReflectPriority = 0.0f;
- }
- GuiShapeEdPreview::~GuiShapeEdPreview()
- {
- SAFE_DELETE( mModel );
- SAFE_DELETE( mFakeSun );
- }
- void GuiShapeEdPreview::initPersistFields()
- {
- docsURL;
- addGroup( "Rendering" );
- addField( "editSun", TypeBool, Offset( mEditingSun, GuiShapeEdPreview ),
- "If true, dragging the gizmo will rotate the sun direction" );
- addField( "selectedNode", TypeS32, Offset( mSelectedNode, GuiShapeEdPreview ),
- "Index of the selected node, or -1 if none" );
- addField( "selectedObject", TypeS32, Offset( mSelectedObject, GuiShapeEdPreview ),
- "Index of the selected object, or -1 if none" );
- addField( "selectedObjDetail", TypeS32, Offset( mSelectedObjDetail, GuiShapeEdPreview ),
- "Index of the selected object detail mesh, or 0 if none" );
- addField( "gridDimension", TypePoint2I, Offset( mGridDimension, GuiShapeEdPreview ),
- "Grid dimensions (number of rows and columns) in the form \"rows cols\"" );
- addField( "renderGrid", TypeBool, Offset( mRenderGridPlane, EditTSCtrl ),
- "Flag indicating whether to draw the grid" );
- addField( "renderNodes", TypeBool, Offset( mRenderNodes, GuiShapeEdPreview ),
- "Flag indicating whether to render the shape nodes" );
- addField( "renderGhost", TypeBool, Offset( mRenderGhost, GuiShapeEdPreview ),
- "Flag indicating whether to render the shape in 'ghost' mode (transparent)" );
- addField( "renderBounds", TypeBool, Offset( mRenderBounds, GuiShapeEdPreview ),
- "Flag indicating whether to render the shape bounding box" );
- addField( "renderObjBox", TypeBool, Offset( mRenderObjBox, GuiShapeEdPreview ),
- "Flag indicating whether to render the selected object's bounding box" );
- addField( "renderColMeshes", TypeBool, Offset( mRenderColMeshes, GuiShapeEdPreview ),
- "Flag indicating whether to render the shape's collision geometry" );
- addField( "renderMounts", TypeBool, Offset( mRenderMounts, GuiShapeEdPreview ),
- "Flag indicating whether to render mounted objects" );
- endGroup( "Rendering" );
- addGroup( "Sun" );
- addProtectedField( "sunDiffuse", TypeColorI, Offset( mSunDiffuseColor, GuiShapeEdPreview ), &setFieldSunDiffuse, &defaultProtectedGetFn,
- "Ambient color for the sun" );
- addProtectedField( "sunAmbient", TypeColorI, Offset( mSunAmbientColor, GuiShapeEdPreview ), &setFieldSunAmbient, &defaultProtectedGetFn,
- "Diffuse color for the sun" );
- addProtectedField( "sunAngleX", TypeF32, Offset( mSunRot.x, GuiShapeEdPreview ), &setFieldSunAngleX, &defaultProtectedGetFn,
- "X-axis rotation angle for the sun" );
- addProtectedField( "sunAngleZ", TypeF32, Offset( mSunRot.z, GuiShapeEdPreview ), &setFieldSunAngleZ, &defaultProtectedGetFn,
- "Z-axis rotation angle for the sun" );
- endGroup( "Sun" );
- addGroup( "Animation" );
- addField( "activeThread", TypeS32, Offset( mActiveThread, GuiShapeEdPreview ),
- "Index of the active thread, or -1 if none" );
- addProtectedField( "threadPos", TypeF32, 0, &setFieldThreadPos, &getFieldThreadPos,
- "Current position of the active thread (0-1)" );
- addProtectedField( "threadDirection", TypeS32, 0, &setFieldThreadDir, &getFieldThreadDir,
- "Playback direction of the active thread" );
- addProtectedField( "threadPingPong", TypeBool, 0, &setFieldThreadPingPong, &getFieldThreadPingPong,
- "'PingPong' mode of the active thread" );
- endGroup( "Animation" );
- addGroup( "Detail Stats" );
- addField( "fixedDetail", TypeBool, Offset( mFixedDetail, GuiShapeEdPreview ),
- "If false, the current detail is selected based on camera distance" );
- addField( "orbitDist", TypeF32, Offset( mOrbitDist, GuiShapeEdPreview ),
- "The current distance from the camera to the model" );
- addProtectedField( "currentDL", TypeS32, Offset( mCurrentDL, GuiShapeEdPreview ), &setFieldCurrentDL, &defaultProtectedGetFn,
- "The current detail level" );
- addProtectedField( "detailSize", TypeS32, Offset( mDetailSize, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The size of the current detail" );
- addProtectedField( "detailPolys", TypeS32, Offset( mDetailPolys, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "Number of polygons in the current detail" );
- addProtectedField( "pixelSize", TypeF32, Offset( mPixelSize, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The current pixel size of the model" );
- addProtectedField( "numMaterials", TypeS32, Offset( mNumMaterials, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The number of materials in the current detail level" );
- addProtectedField( "numDrawCalls", TypeS32, Offset( mNumDrawCalls, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The number of draw calls in the current detail level" );
- addProtectedField( "numBones", TypeS32, Offset( mNumBones, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The number of bones in the current detail level (skins only)" );
- addProtectedField( "numWeights", TypeS32, Offset( mNumWeights, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The number of vertex weights in the current detail level (skins only)" );
- addProtectedField( "colMeshes", TypeS32, Offset( mColMeshes, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The number of collision meshes in the shape" );
- addProtectedField( "colPolys", TypeS32, Offset( mColPolys, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn,
- "The total number of collision polygons (all meshes) in the shape" );
- endGroup( "Detail Stats" );
- Parent::initPersistFields();
- }
- bool GuiShapeEdPreview::setFieldCurrentDL( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui )
- gui->setCurrentDetail( mFloor( dAtof( data ) + 0.5f ) );
- return false;
- }
- bool GuiShapeEdPreview::setFieldSunDiffuse( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui )
- {
- Con::setData( TypeColorI, &gui->mSunDiffuseColor, 0, 1, &data );
- gui->updateSun();
- }
- return false;
- }
- bool GuiShapeEdPreview::setFieldSunAmbient( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui )
- {
- Con::setData( TypeColorI, &gui->mSunAmbientColor, 0, 1, &data );
- gui->updateSun();
- }
- return false;
- }
- bool GuiShapeEdPreview::setFieldSunAngleX( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui )
- {
- Con::setData( TypeF32, &gui->mSunRot.x, 0, 1, &data );
- gui->updateSun();
- }
- return false;
- }
- bool GuiShapeEdPreview::setFieldSunAngleZ( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui )
- {
- Con::setData( TypeF32, &gui->mSunRot.z, 0, 1, &data );
- gui->updateSun();
- }
- return false;
- }
- bool GuiShapeEdPreview::setFieldThreadPos( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key )
- gui->mModel->setPos( gui->mThreads[gui->mActiveThread].key, dAtof( data ) );
- return false;
- }
- const char *GuiShapeEdPreview::getFieldThreadPos( void *object, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key )
- return Con::getFloatArg( gui->mModel->getPos( gui->mThreads[gui->mActiveThread].key ) );
- else
- return "0";
- }
- bool GuiShapeEdPreview::setFieldThreadDir( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui && ( gui->mActiveThread >= 0 ) )
- {
- Thread& thread = gui->mThreads[gui->mActiveThread];
- Con::setData( TypeS32, &(thread.direction), 0, 1, &data );
- if ( thread.key )
- gui->mModel->setTimeScale( thread.key, gui->mTimeScale * thread.direction );
- }
- return false;
- }
- const char *GuiShapeEdPreview::getFieldThreadDir( void *object, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui && ( gui->mActiveThread >= 0 ) )
- return Con::getIntArg( gui->mThreads[gui->mActiveThread].direction );
- else
- return "0";
- }
- bool GuiShapeEdPreview::setFieldThreadPingPong( void *object, const char *index, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui && ( gui->mActiveThread >= 0 ) )
- Con::setData( TypeBool, &(gui->mThreads[gui->mActiveThread].pingpong), 0, 1, &data );
- return false;
- }
- const char *GuiShapeEdPreview::getFieldThreadPingPong( void *object, const char *data )
- {
- GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
- if ( gui && ( gui->mActiveThread >= 0 ) )
- return Con::getIntArg( gui->mThreads[gui->mActiveThread].pingpong );
- else
- return "0";
- }
- bool GuiShapeEdPreview::onWake()
- {
- if (!Parent::onWake())
- return false;
- if (!mFakeSun )
- mFakeSun = LIGHTMGR->createLightInfo();
- mFakeSun->setRange( 2000000.0f );
- updateSun();
- mGizmoProfile->mode = MoveMode;
- return( true );
- }
- void GuiShapeEdPreview::setDisplayType( S32 type )
- {
- Parent::setDisplayType( type );
- mOrthoCamTrans.set( 0, 0, 0 );
- }
- //-----------------------------------------------------------------------------
- void GuiShapeEdPreview::setCurrentDetail(S32 dl)
- {
- if ( mModel )
- {
- TSShape* shape = mModel->getShape();
- S32 smallest = shape->mSmallestVisibleDL;
- shape->mSmallestVisibleDL = shape->details.size() - 1;
- mModel->setCurrentDetail( dl );
- shape->mSmallestVisibleDL = smallest;
- // Match the camera distance to this detail if necessary
- //@todo if ( !gui->mFixedDetail )
- }
- }
- bool GuiShapeEdPreview::setObjectModel(const char* modelName)
- {
- SAFE_DELETE( mModel );
- unmountAll();
- mThreads.clear();
- mActiveThread = -1;
- ResourceManager::get().getChangedSignal().remove(this, &GuiShapeEdPreview::_onResourceChanged);
- if (modelName && modelName[0])
- {
- Resource<TSShape> model = ResourceManager::get().load( modelName );
- if (! bool( model ))
- {
- Con::warnf( avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName ));
- return false;
- }
- mModel = new TSShapeInstance( model, true );
- AssertFatal( mModel, avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName ));
- TSShape* shape = mModel->getShape();
- // Initialize camera values:
- mOrbitPos = shape->center;
- // Set camera move and zoom speed according to model size
- mMoveSpeed = shape->mRadius / sMoveScaler;
- mZoomSpeed = shape->mRadius / sZoomScaler;
- // Reset node selection
- mHoverNode = -1;
- mSelectedNode = -1;
- mSelectedObject = -1;
- mSelectedObjDetail = 0;
- mProjectedNodes.setSize( shape->nodes.size() );
- // Reset detail stats
- mCurrentDL = 0;
- // the first time recording
- mLastRenderTime = Platform::getVirtualMilliseconds();
- mModelName = StringTable->insert(modelName);
- //Now to reflect changes when the model file is changed.
- ResourceManager::get().getChangedSignal().notify(this, &GuiShapeEdPreview::_onResourceChanged);
- }
- else
- {
- mModelName = StringTable->EmptyString();
- }
- return true;
- }
- bool GuiShapeEdPreview::setObjectShapeAsset(const char* assetId)
- {
- SAFE_DELETE(mModel);
- unmountAll();
- mThreads.clear();
- mActiveThread = -1;
- StringTableEntry modelName = StringTable->EmptyString();
- if (AssetDatabase.isDeclaredAsset(assetId))
- {
- StringTableEntry id = StringTable->insert(assetId);
- StringTableEntry assetType = AssetDatabase.getAssetType(id);
- if (assetType == StringTable->insert("ShapeAsset"))
- {
- ShapeAsset* asset = AssetDatabase.acquireAsset<ShapeAsset>(id);
- modelName = asset->getShapeFilePath();
- AssetDatabase.releaseAsset(id);
- }
- else if (assetType == StringTable->insert("ShapeAnimationAsset"))
- {
- ShapeAnimationAsset* asset = AssetDatabase.acquireAsset<ShapeAnimationAsset>(id);
- modelName = asset->getAnimationPath();
- AssetDatabase.releaseAsset(id);
- }
- }
- return setObjectModel(modelName);
- }
- void GuiShapeEdPreview::_onResourceChanged(const Torque::Path& path)
- {
- if (path != Torque::Path(mModelName))
- return;
- setObjectModel(path.getFullPath());
- }
- void GuiShapeEdPreview::addThread()
- {
- if ( mModel )
- {
- mThreads.increment();
- if ( mActiveThread == -1 )
- mActiveThread = 0;
- }
- }
- void GuiShapeEdPreview::removeThread(S32 slot)
- {
- if ( slot < mThreads.size() )
- {
- if ( mThreads[slot].key )
- mModel->destroyThread( mThreads[slot].key );
- mThreads.erase( slot );
- if ( mActiveThread >= mThreads.size() )
- mActiveThread = mThreads.size() - 1;
- }
- }
- void GuiShapeEdPreview::setTimeScale( F32 scale )
- {
- // Update time scale for all threads
- mTimeScale = scale;
- for ( S32 i = 0; i < mThreads.size(); i++ )
- {
- if ( mThreads[i].key )
- mModel->setTimeScale( mThreads[i].key, mTimeScale * mThreads[i].direction );
- }
- }
- void GuiShapeEdPreview::setActiveThreadSequence(const char* seqName, F32 duration, F32 pos, bool play)
- {
- if ( mActiveThread == -1 )
- return;
- setThreadSequence(mThreads[mActiveThread], mModel, seqName, duration, pos, play);
- }
- void GuiShapeEdPreview::setThreadSequence(GuiShapeEdPreview::Thread& thread, TSShapeInstance* shape, const char* seqName, F32 duration, F32 pos, bool play)
- {
- thread.seqName = seqName;
- S32 seq = shape->getShape()->findSequence( thread.seqName );
- if ( thread.key && ( shape->getSequence(thread.key) == seq ) )
- return;
- if ( seq == -1 )
- {
- // This thread is now set to an invalid sequence, so the key must be
- // removed, but we keep the thread info around in case the user changes
- // back to a valid sequence
- if ( thread.key )
- {
- shape->destroyThread( thread.key );
- thread.key = NULL;
- }
- }
- else
- {
- // Add a TSThread key if one does not already exist
- if ( !thread.key )
- {
- thread.key = shape->addThread();
- shape->setTimeScale( thread.key, mTimeScale * thread.direction );
- }
- // Transition to slider or synched position?
- if ( pos == -1.0f )
- pos = shape->getPos( thread.key );
- if ( duration == 0.0f )
- {
- // No transition => go straight to new sequence
- shape->setSequence( thread.key, seq, pos );
- }
- else
- {
- // Get the current position if transitioning to the sync position
- shape->setTimeScale( thread.key, thread.direction >= 0 ? 1 : -1 );
- shape->transitionToSequence( thread.key, seq, pos, duration, play );
- shape->setTimeScale( thread.key, mTimeScale * thread.direction );
- }
- }
- }
- const char* GuiShapeEdPreview::getThreadSequence() const
- {
- return ( mActiveThread >= 0 ) ? mThreads[mActiveThread].seqName.c_str() : "";
- }
- void GuiShapeEdPreview::refreshThreadSequences()
- {
- S32 oldActive = mActiveThread;
- for ( S32 i = 0; i < mThreads.size(); i++ )
- {
- Thread& thread = mThreads[i];
- if ( !thread.key )
- continue;
- // Detect changed (or removed) sequence indices
- if ( mModel->getSequence(thread.key) != mModel->getShape()->findSequence( thread.seqName ) )
- {
- mActiveThread = i;
- setThreadSequence( thread, mModel, thread.seqName, 0.0f, mModel->getPos( thread.key ), false );
- }
- }
- mActiveThread = oldActive;
- }
- //-----------------------------------------------------------------------------
- // MOUNTING
- bool GuiShapeEdPreview::mountShape(const char* shapeAssetId, const char* nodeName, const char* mountType, S32 slot)
- {
- if ( !shapeAssetId || !shapeAssetId[0] )
- return false;
- if (!AssetDatabase.isDeclaredAsset(shapeAssetId))
- return false;
- ShapeAsset* model = AssetDatabase.acquireAsset<ShapeAsset>(shapeAssetId);
- if (model == nullptr || !model->getShapeResource())
- return false;
- TSShapeInstance* tsi = new TSShapeInstance(model->getShapeResource(), true );
- if ( slot == -1 )
- {
- slot = mMounts.size();
- mMounts.push_back( new MountedShape );
- }
- else
- {
- // Check if we are switching shapes
- if ( mMounts[slot]->mShape->getShape() != tsi->getShape() )
- {
- delete mMounts[slot]->mShape;
- mMounts[slot]->mShape = NULL;
- mMounts[slot]->mThread.init();
- }
- else
- {
- // Keep using the existing shape
- delete tsi;
- tsi = mMounts[slot]->mShape;
- }
- }
- MountedShape* mount = mMounts[slot];
- mount->mShape = tsi;
- if ( dStrEqual( mountType, "Wheel" ) )
- mount->mType = MountedShape::Wheel;
- else if ( dStrEqual( mountType, "Image" ) )
- mount->mType = MountedShape::Image;
- else
- mount->mType = MountedShape::Object;
- setMountNode( slot, nodeName);
- return true;
- }
- void GuiShapeEdPreview::setMountNode(S32 mountSlot, const char* nodeName)
- {
- if ( mountSlot < mMounts.size() )
- {
- MountedShape* mount = mMounts[mountSlot];
- mount->mNode = mModel ? mModel->getShape()->findNode( nodeName ) : -1;
- mount->mTransform.identity();
- switch ( mount->mType )
- {
- case MountedShape::Image:
- {
- // Mount point is either the node called 'mountPoint' or the origin
- S32 node = mount->mShape->getShape()->findNode( "mountPoint" );
- if ( node != -1 )
- {
- mount->mShape->getShape()->getNodeWorldTransform( node, &mount->mTransform );
- mount->mTransform.inverse();
- }
- }
- break;
- case MountedShape::Wheel:
- // Rotate shape according to node's x position (left or right)
- {
- F32 rotAngle = M_PI_F/2;
- if ( mount->mNode != -1 )
- {
- MatrixF hubMat;
- mModel->getShape()->getNodeWorldTransform( mount->mNode, &hubMat );
- if ( hubMat.getPosition().x < 0 )
- rotAngle = -M_PI_F/2;
- }
- mount->mTransform.set( EulerF( 0, 0, rotAngle ) );
- }
- break;
- default:
- // No mount transform (use origin)
- break;
- }
- }
- }
- const char* GuiShapeEdPreview::getMountThreadSequence(S32 mountSlot) const
- {
- if ( mountSlot < mMounts.size() )
- {
- MountedShape* mount = mMounts[mountSlot];
- return mount->mThread.seqName;
- }
- else
- return "";
- }
- void GuiShapeEdPreview::setMountThreadSequence(S32 mountSlot, const char* seqName)
- {
- if ( mountSlot < mMounts.size() )
- {
- MountedShape* mount = mMounts[mountSlot];
- setThreadSequence( mount->mThread, mount->mShape, seqName );
- }
- }
- F32 GuiShapeEdPreview::getMountThreadPos(S32 mountSlot) const
- {
- if ( mountSlot < mMounts.size() )
- {
- MountedShape* mount = mMounts[mountSlot];
- if ( mount->mThread.key )
- return mount->mShape->getPos( mount->mThread.key );
- }
- return 0;
- }
- void GuiShapeEdPreview::setMountThreadPos(S32 mountSlot, F32 pos)
- {
- if ( mountSlot < mMounts.size() )
- {
- MountedShape* mount = mMounts[mountSlot];
- if ( mount->mThread.key )
- mount->mShape->setPos( mount->mThread.key, pos );
- }
- }
- F32 GuiShapeEdPreview::getMountThreadDir(S32 mountSlot) const
- {
- if ( mountSlot < mMounts.size() )
- {
- MountedShape* mount = mMounts[mountSlot];
- return mount->mThread.direction;
- }
- return 0;
- }
- void GuiShapeEdPreview::setMountThreadDir(S32 mountSlot, F32 dir)
- {
- if ( mountSlot < mMounts.size() )
- {
- MountedShape* mount = mMounts[mountSlot];
- mount->mThread.direction = dir;
- if ( mount->mThread.key )
- mount->mShape->setTimeScale( mount->mThread.key, mTimeScale * mount->mThread.direction );
- }
- }
- void GuiShapeEdPreview::unmountShape(S32 mountSlot)
- {
- if ( mountSlot < mMounts.size() )
- {
- delete mMounts[mountSlot];
- mMounts.erase( mountSlot );
- }
- }
- void GuiShapeEdPreview::unmountAll()
- {
- for ( S32 i = 0; i < mMounts.size(); i++)
- delete mMounts[i];
- mMounts.clear();
- }
- void GuiShapeEdPreview::refreshShape()
- {
- if ( mModel )
- {
- // Nodes or details may have changed => refresh the shape instance
- mModel->setMaterialList( mModel->mMaterialList );
- mModel->initNodeTransforms();
- mModel->initMeshObjects();
- TSShape* shape = mModel->getShape();
- mProjectedNodes.setSize( shape->nodes.size() );
- if ( mSelectedObject >= shape->objects.size() )
- {
- mSelectedObject = -1;
- mSelectedObjDetail = 0;
- }
- // Re-compute the collision mesh stats
- mColMeshes = 0;
- mColPolys = 0;
- for ( S32 i = 0; i < shape->details.size(); i++ )
- {
- const TSShape::Detail& det = shape->details[i];
- const String& detName = shape->getName( det.nameIndex );
- if ( ( det.subShapeNum < 0 ) || !detName.startsWith( "collision-" ) )
- continue;
- mColPolys += det.polyCount;
- S32 od = det.objectDetailNum;
- S32 start = shape->subShapeFirstObject[det.subShapeNum];
- S32 end = start + shape->subShapeNumObjects[det.subShapeNum];
- for ( S32 j = start; j < end; j++ )
- {
- const TSShape::Object &obj = shape->objects[j];
- const TSMesh* mesh = ( od < obj.numMeshes ) ? shape->meshes[obj.startMeshIndex + od] : NULL;
- if ( mesh )
- mColMeshes++;
- }
- }
- }
- }
- void GuiShapeEdPreview::updateSun()
- {
- if ( mFakeSun )
- {
- // Update sun colors
- mFakeSun->setColor( mSunDiffuseColor );
- mFakeSun->setAmbient( mSunAmbientColor );
- // Determine the new sun direction and position
- Point3F vec;
- MatrixF xRot, zRot;
- xRot.set( EulerF( mDegToRad(mSunRot.x), 0.0f, 0.0f ));
- zRot.set( EulerF( 0.0f, 0.0f, mDegToRad(mSunRot.z) ));
- zRot.mul( xRot );
- zRot.getColumn( 1, &vec );
- mFakeSun->setDirection( vec );
- //mFakeSun->setPosition( vec * -10000.0f );
- }
- }
- void GuiShapeEdPreview::updateNodeTransforms()
- {
- if ( mModel )
- mModel->mDirtyFlags[0] |= TSShapeInstance::TransformDirty;
- }
- bool GuiShapeEdPreview::getMeshHidden( const char* name ) const
- {
- if ( mModel )
- {
- S32 objIndex = mModel->getShape()->findObject( name );
- if ( objIndex != -1 )
- return mModel->mMeshObjects[objIndex].forceHidden;
- }
- return false;
- }
- void GuiShapeEdPreview::setMeshHidden( const char* name, bool hidden )
- {
- if ( mModel )
- {
- S32 objIndex = mModel->getShape()->findObject( name );
- if ( objIndex != -1 )
- mModel->setMeshForceHidden( objIndex, hidden );
- }
- }
- void GuiShapeEdPreview::setAllMeshesHidden( bool hidden )
- {
- if ( mModel )
- {
- for ( S32 i = 0; i < mModel->mMeshObjects.size(); i++ )
- mModel->setMeshForceHidden( i, hidden );
- }
- }
- void GuiShapeEdPreview::get3DCursor( GuiCursor *&cursor,
- bool &visible,
- const Gui3DMouseEvent &event_ )
- {
- cursor = NULL;
- visible = false;
- GuiCanvas *root = getRoot();
- if ( !root )
- return;
- S32 currCursor = PlatformCursorController::curArrow;
- if ( root->mCursorChanged == currCursor )
- return;
- PlatformWindow *window = root->getPlatformWindow();
- PlatformCursorController *controller = window->getCursorController();
- // We've already changed the cursor,
- // so set it back before we change it again.
- if ( root->mCursorChanged != -1 )
- controller->popCursor();
- // Now change the cursor shape
- controller->pushCursor( currCursor );
- root->mCursorChanged = currCursor;
- }
- void GuiShapeEdPreview::fitToShape()
- {
- if ( !mModel )
- return;
- // Determine the shape bounding box given the current camera rotation
- MatrixF camRotMatrix( smCamMatrix );
- camRotMatrix.setPosition( Point3F::Zero );
- camRotMatrix.inverse();
- Box3F bounds;
- computeSceneBounds( bounds );
- mOrbitPos = bounds.getCenter();
- camRotMatrix.mul( bounds );
- // Estimate the camera distance to fill the view by comparing the radii
- // of the box and the viewport
- F32 len_x = bounds.len_x();
- F32 len_z = bounds.len_z();
- F32 shapeRadius = mSqrt( len_x*len_x + len_z*len_z ) / 2;
- F32 viewRadius = 0.45f * getMin( getExtent().x, getExtent().y );
- // Set camera parameters
- if ( mDisplayType == DisplayTypePerspective )
- {
- mOrbitDist = ( shapeRadius / viewRadius ) * mSaveWorldToScreenScale.y;
- }
- else
- {
- mOrthoCamTrans.set( 0, 0, 0 );
- mOrthoFOV = shapeRadius * viewRadius / 320;
- }
- }
- void GuiShapeEdPreview::setOrbitPos( const Point3F& pos )
- {
- mOrbitPos = pos;
- }
- void GuiShapeEdPreview::exportToCollada( const String& path )
- {
- #ifdef TORQUE_COLLADA
- if ( mModel )
- {
- MatrixF orientation( true );
- orientation.setPosition( mModel->getShape()->mBounds.getCenter() );
- orientation.inverse();
- OptimizedPolyList polyList;
- polyList.setBaseTransform( orientation );
- mModel->buildPolyList( &polyList, mCurrentDL );
- for ( S32 i = 0; i < mMounts.size(); i++ )
- {
- MountedShape* mount = mMounts[i];
- MatrixF mat( true );
- if ( mount->mNode != -1 )
- {
- mat = mModel->mNodeTransforms[ mount->mNode ];
- mat *= mount->mTransform;
- }
- polyList.setTransform( &mat, Point3F::One );
- mount->mShape->buildPolyList( &polyList, 0 );
- }
- // Use a ColladaUtils function to do the actual export to a Collada file
- ColladaUtils::exportToCollada( path, polyList );
- }
- #endif
- }
- //-----------------------------------------------------------------------------
- // Camera control and Node editing
- // - moving the mouse over a node will highlight (but not select) it
- // - left clicking on a node will select it, the gizmo will appear
- // - left clicking on no node will unselect the current node
- // - left dragging the gizmo will translate/rotate the node
- // - middle drag translates the view
- // - right drag rotates the view
- // - mouse wheel zooms the view
- // - holding shift while changing the view speeds them up
- void GuiShapeEdPreview::handleMouseDown(const GuiEvent& event, GizmoMode mode)
- {
- if (!mActive || !mVisible || !mAwake )
- return;
- mouseLock();
- mLastMousePos = event.mousePoint;
- if ( mRenderNodes && ( mode == NoneMode ) )
- {
- mGizmoDragID++;
- make3DMouseEvent( mLastEvent, event );
- // Check gizmo first
- mUsingAxisGizmo = false;
- if ( mSelectedNode != -1 )
- {
- mGizmo->on3DMouseDown( mLastEvent );
- if ( mGizmo->getSelection() != Gizmo::None )
- {
- mUsingAxisGizmo = true;
- return;
- }
- }
- // Check if we have clicked on a node
- S32 selected = collideNode( mLastEvent );
- if ( selected != mSelectedNode )
- {
- mSelectedNode = selected;
- Con::executef( this, "onNodeSelected", Con::getIntArg( mSelectedNode ));
- }
- }
- //if ( mode == RotateMode )
- // mRenderCameraAxes = true;
- }
- void GuiShapeEdPreview::handleMouseUp(const GuiEvent& event, GizmoMode mode)
- {
- mouseUnlock();
- mUsingAxisGizmo = false;
- if ( mRenderNodes && ( mode == NoneMode ) )
- {
- make3DMouseEvent( mLastEvent, event );
- mGizmo->on3DMouseUp( mLastEvent );
- }
- //if ( mode == RotateMode )
- // mRenderCameraAxes = false;
- }
- void GuiShapeEdPreview::handleMouseMove(const GuiEvent& event, GizmoMode mode)
- {
- if ( mRenderNodes && ( mode == NoneMode ) )
- {
- make3DMouseEvent( mLastEvent, event );
- if ( mSelectedNode != -1 )
- {
- // Check if the mouse is hovering over an axis
- mGizmo->on3DMouseMove( mLastEvent );
- if ( mGizmo->getSelection() != Gizmo::None )
- return;
- }
- // Check if we are over another node
- mHoverNode = collideNode( mLastEvent );
- }
- }
- void GuiShapeEdPreview::handleMouseDragged(const GuiEvent& event, GizmoMode mode)
- {
- // For non-perspective views, ignore rotation, and let EditTSCtrl handle
- // translation
- if ( mDisplayType != DisplayTypePerspective )
- {
- if ( mode == MoveMode )
- {
- Parent::onRightMouseDragged( event );
- return;
- }
- else if ( mode == RotateMode )
- return;
- }
- Point2F delta( event.mousePoint.x - mLastMousePos.x, event.mousePoint.y - mLastMousePos.y );
- mLastMousePos = event.mousePoint;
- // Use shift to increase speed
- delta.x *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f;
- delta.y *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f;
- if ( mode == NoneMode )
- {
- if ( mEditingSun )
- {
- mSunRot.x += mRadToDeg( delta.y );
- mSunRot.z += mRadToDeg( delta.x );
- updateSun();
- }
- else if ( mRenderNodes )
- {
- make3DMouseEvent( mLastEvent, event );
- if ( mUsingAxisGizmo )
- {
- // Use gizmo to modify the transform of the selected node
- mGizmo->on3DMouseDragged( mLastEvent );
- switch ( mGizmoProfile->mode )
- {
- case MoveMode:
- // Update node transform
- if ( mSelectedNode != -1 )
- {
- Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition() + mGizmo->getOffset();
- mModel->mNodeTransforms[mSelectedNode].setPosition( pos );
- }
- break;
- case RotateMode:
- // Update node transform
- if ( mSelectedNode != -1 )
- {
- EulerF rot = mGizmo->getDeltaRot();
- mModel->mNodeTransforms[mSelectedNode].mul( MatrixF( rot ) );
- }
- break;
- default:
- break;
- }
- // Notify the change in node transform
- const char* name = mModel->getShape()->getNodeName(mSelectedNode).c_str();
- const Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition();
- AngAxisF aa(mModel->mNodeTransforms[mSelectedNode]);
- char buffer[256];
- dSprintf(buffer, sizeof(buffer), "%g %g %g %g %g %g %g",
- pos.x, pos.y, pos.z, aa.axis.x, aa.axis.y, aa.axis.z, aa.angle);
- Con::executef(this, "onEditNodeTransform", name, buffer, Con::getIntArg(mGizmoDragID));
- }
- }
- }
- else
- {
- switch ( mode )
- {
- case MoveMode:
- {
- VectorF offset(-delta.x, 0, delta.y );
- smCamMatrix.mulV( offset );
- mOrbitPos += offset * mMoveSpeed;
- }
- break;
- case RotateMode:
- mCameraRot.x += delta.y;
- mCameraRot.z += delta.x;
- break;
- default:
- break;
- }
- }
- }
- void GuiShapeEdPreview::on3DMouseWheelUp(const Gui3DMouseEvent& event)
- {
- if ( mDisplayType == DisplayTypePerspective )
- {
- // Use shift and ctrl to increase speed
- F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f;
- mOrbitDist -= mFabs(event.fval) * mZoomSpeed * mod;
- }
- }
- void GuiShapeEdPreview::on3DMouseWheelDown(const Gui3DMouseEvent& event)
- {
- if ( mDisplayType == DisplayTypePerspective )
- {
- // Use shift and ctrl to increase speed
- F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f;
- mOrbitDist += mFabs(event.fval) * mZoomSpeed * mod;
- }
- }
- //-----------------------------------------------------------------------------
- // NODE PICKING
- void GuiShapeEdPreview::updateProjectedNodePoints()
- {
- if ( mModel )
- {
- // Project the 3D node position to get the 2D screen coordinates
- for ( S32 i = 0; i < mModel->mNodeTransforms.size(); i++)
- project( mModel->mNodeTransforms[i].getPosition(), &mProjectedNodes[i] );
- }
- }
- S32 GuiShapeEdPreview::collideNode(const Gui3DMouseEvent& event) const
- {
- // Check if the given position is inside the screen rectangle of
- // any shape node
- S32 nodeIndex = -1;
- F32 minZ = 0;
- for ( S32 i = 0; i < mProjectedNodes.size(); i++)
- {
- const Point3F& pt = mProjectedNodes[i];
- if ( pt.z > 1.0f )
- continue;
- RectI rect( pt.x - sNodeRectSize/2, pt.y - sNodeRectSize/2, sNodeRectSize, sNodeRectSize );
- if ( rect.pointInRect( event.mousePoint ) )
- {
- if ( ( nodeIndex == -1 ) || ( pt.z < minZ ) )
- {
- nodeIndex = i;
- minZ = pt.z;
- }
- }
- }
- return nodeIndex;
- }
- //-----------------------------------------------------------------------------
- // RENDERING
- bool GuiShapeEdPreview::getCameraTransform(MatrixF* cameraMatrix)
- {
- // Adjust the camera so that we are still facing the model
- if ( mDisplayType == DisplayTypePerspective )
- {
- Point3F vec;
- MatrixF xRot, zRot;
- xRot.set( EulerF( mCameraRot.x, 0.0f, 0.0f ));
- zRot.set( EulerF( 0.0f, 0.0f, mCameraRot.z ));
- cameraMatrix->mul( zRot, xRot );
- cameraMatrix->getColumn( 1, &vec );
- cameraMatrix->setColumn( 3, mOrbitPos - vec*mOrbitDist );
- }
- else
- {
- cameraMatrix->identity();
- if ( mModel )
- {
- Point3F camPos = mModel->getShape()->mBounds.getCenter();
- F32 offset = mModel->getShape()->mBounds.len();
- switch (mDisplayType)
- {
- case DisplayTypeTop: camPos.z += offset; break;
- case DisplayTypeBottom: camPos.z -= offset; break;
- case DisplayTypeFront: camPos.y += offset; break;
- case DisplayTypeBack: camPos.y -= offset; break;
- case DisplayTypeRight: camPos.x += offset; break;
- case DisplayTypeLeft: camPos.x -= offset; break;
- default:
- break;
- }
- cameraMatrix->setColumn( 3, camPos );
- }
- }
- return true;
- }
- void GuiShapeEdPreview::computeSceneBounds(Box3F& bounds)
- {
- if ( mModel )
- mModel->computeBounds( mCurrentDL, bounds );
- if (bounds.getExtents().x < POINT_EPSILON || bounds.getExtents().y < POINT_EPSILON || bounds.getExtents().z < POINT_EPSILON)
- {
- bounds.set(Point3F::Zero);
- //We probably don't have any actual meshes in this model, so compute using the bones if we have them
- for (S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
- {
- Point3F nodePos = mModel->mNodeTransforms[i].getPosition();
- bounds.extend(nodePos);
- }
- }
- }
- void GuiShapeEdPreview::updateDetailLevel(const SceneRenderState* state)
- {
- // Make sure current detail is valid
- if ( !mModel->getShape()->details.size() )
- return;
- if ( mModel->getCurrentDetail() >= mModel->getShape()->details.size() )
- setCurrentDetail( mModel->getShape()->details.size() - 1 );
- // Convert between FOV and distance so zoom is consistent between Perspective
- // and Orthographic views (conversion factor found by trial and error)
- const F32 fov2dist = 1.3f;
- if ( mDisplayType == DisplayTypePerspective )
- mOrthoFOV = mOrbitDist / fov2dist;
- else
- mOrbitDist = mOrthoFOV * fov2dist;
- // Use fixed distance in orthographic view (value found by trial + error)
- F32 dist = ( mDisplayType == DisplayTypePerspective ) ? mOrbitDist : 0.1f;
- // Select the appropriate detail level, and update the detail stats
- S32 currentDetail = mModel->getCurrentDetail();
- mModel->setDetailFromDistance( state, dist ); // need to call this to update smLastPixelSize
- if ( mFixedDetail )
- setCurrentDetail( currentDetail );
- if ( mModel->getCurrentDetail() < 0 )
- setCurrentDetail( 0 );
- currentDetail = mModel->getCurrentDetail();
- const TSShape::Detail& det = mModel->getShape()->details[ currentDetail ];
- mDetailPolys = det.polyCount;
- mDetailSize = det.size;
- mPixelSize = TSShapeInstance::smLastPixelSize;
- mNumMaterials = 0;
- mNumDrawCalls = 0;
- mNumBones = 0;
- mNumWeights = 0;
- if ( det.subShapeNum < 0 )
- {
- mNumMaterials = 1;
- mNumDrawCalls = 1;
- }
- else
- {
- Vector<U32> usedMaterials;
- S32 start = mModel->getShape()->subShapeFirstObject[det.subShapeNum];
- S32 end = start + mModel->getShape()->subShapeNumObjects[det.subShapeNum];
- for ( S32 iObj = start; iObj < end; iObj++ )
- {
- const TSShape::Object& obj = mModel->getShape()->objects[iObj];
- if ( obj.numMeshes <= currentDetail )
- continue;
- const TSMesh* mesh = mModel->getShape()->meshes[ obj.startMeshIndex + currentDetail ];
- if ( !mesh )
- continue;
- // Count the number of draw calls and materials
- mNumDrawCalls += mesh->mPrimitives.size();
- for ( S32 iPrim = 0; iPrim < mesh->mPrimitives.size(); iPrim++ )
- usedMaterials.push_back_unique( mesh->mPrimitives[iPrim].matIndex & TSDrawPrimitive::MaterialMask );
- // For skinned meshes, count the number of bones and weights
- if ( mesh->getMeshType() == TSMesh::SkinMeshType )
- {
- const TSSkinMesh* skin = dynamic_cast<const TSSkinMesh*>(mesh);
- mNumBones += skin->batchData.initialTransforms.size();
- mNumWeights += skin->weight.size();
- }
- }
- mNumMaterials = usedMaterials.size();
- }
- // Detect changes in detail level
- if ( mCurrentDL != currentDetail )
- {
- mCurrentDL = currentDetail;
- Con::executef( this, "onDetailChanged");
- }
- }
- void GuiShapeEdPreview::updateThreads(F32 delta)
- {
- // Advance time on all threads
- for ( S32 i = 0; i < mThreads.size(); i++ )
- {
- Thread& thread = mThreads[i];
- if ( !thread.key || !thread.direction )
- continue;
- // Make sure thread priority matches sequence priority (which may have changed)
- mModel->setPriority( thread.key, mModel->getShape()->sequences[mModel->getSequence( thread.key )].priority );
- // Handle ping-pong
- if ( thread.pingpong && !mModel->isInTransition( thread.key ) )
- {
- // Determine next position, then adjust if needed
- F32 threadPos = mModel->getPos( thread.key );
- F32 nextPos = threadPos + ( mModel->getTimeScale( thread.key ) * delta / mModel->getDuration( thread.key ) );
- if ( nextPos < 0 )
- {
- // Reflect position and swap playback direction
- nextPos = -nextPos;
- mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) );
- mModel->setPos( thread.key, nextPos );
- }
- else if ( nextPos > 1.0f )
- {
- // Reflect position and swap playback direction
- nextPos = 2.0f - nextPos;
- mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) );
- mModel->setPos( thread.key, nextPos );
- }
- else
- {
- // Advance time normally
- mModel->advanceTime( delta, thread.key );
- }
- }
- else
- {
- // Advance time normally
- mModel->advanceTime( delta, thread.key );
- }
- // Invoke script callback if active thread position has changed
- if ( i == mActiveThread )
- {
- F32 threadPos = mModel->getPos( thread.key );
- bool inTransition = mModel->isInTransition( thread.key );
- onThreadPosChanged_callback( threadPos, inTransition );
- }
- }
- // Mark threads as dirty so they will be re-sorted, in case the user changed
- // sequence priority or blend flags
- mModel->setDirty( TSShapeInstance::ThreadDirty );
- // Advance time on all mounted shape threads
- for ( S32 i = 0; i < mMounts.size(); i++ )
- {
- MountedShape* mount = mMounts[i];
- if ( mount->mThread.key )
- mount->mShape->advanceTime( delta, mount->mThread.key );
- }
- }
- void GuiShapeEdPreview::renderWorld(const RectI &updateRect)
- {
- if ( !mModel )
- return;
- mSaveFrustum = GFX->getFrustum();
- mSaveFrustum.setFarDist( 100000.0f );
- GFX->setFrustum( mSaveFrustum );
- mSaveFrustum.setTransform( smCamMatrix );
- mSaveProjection = GFX->getProjectionMatrix();
- mSaveWorldToScreenScale = GFX->getWorldToScreenScale();
- FogData savedFogData = gClientSceneGraph->getFogData();
- gClientSceneGraph->setFogData( FogData() ); // no fog in preview window
- SceneRenderState state
- (
- gClientSceneGraph,
- SPT_Diffuse,
- SceneCameraState( GFX->getViewport(), mSaveFrustum,
- GFX->getWorldMatrix(), GFX->getProjectionMatrix() )
- );
- // Set up pass transforms
- RenderPassManager *renderPass = state.getRenderPass();
- renderPass->assignSharedXform( RenderPassManager::View, GFX->getWorldMatrix() );
- renderPass->assignSharedXform( RenderPassManager::Projection, GFX->getProjectionMatrix() );
- // Set up our TS render state here.
- TSRenderState rdata;
- rdata.setSceneState(&state);
- LIGHTMGR->unregisterAllLights();
- LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mFakeSun );
- // We might have some forward lit materials
- // so pass down a query to gather lights.
- LightQuery query;
- query.init( SphereF( Point3F::Zero, 1 ) );
- rdata.setLightQuery( &query );
- // Update projected node points (for mouse picking)
- updateProjectedNodePoints();
- // Determine time elapsed since last render (for animation playback)
- S32 time = Platform::getVirtualMilliseconds();
- S32 dt = time - mLastRenderTime;
- mLastRenderTime = time;
- if ( mModel )
- {
- updateDetailLevel( &state );
- // Render the grid
- renderGrid();
- // Animate the model
- updateThreads( (F32)dt / 1000.f );
- mModel->animate();
- // Render the shape
- GFX->setStateBlock( mDefaultGuiSB );
- if ( mRenderGhost )
- rdata.setFadeOverride( 0.5f );
- GFX->pushWorldMatrix();
- GFX->setWorldMatrix( MatrixF::Identity );
- mModel->render( rdata );
- // Render mounted objects
- if ( mRenderMounts )
- {
- for ( S32 i = 0; i < mMounts.size(); i++ )
- {
- MountedShape* mount = mMounts[i];
- GFX->pushWorldMatrix();
- if ( mount->mNode != -1 )
- {
- GFX->multWorld( mModel->mNodeTransforms[ mount->mNode ] );
- GFX->multWorld( mount->mTransform );
- }
- mount->mShape->animate();
- mount->mShape->render( rdata );
- GFX->popWorldMatrix();
- }
- }
- GFX->popWorldMatrix();
- renderPass->renderPass( &state );
- // @todo: Model and other elements (bounds, grid etc) use different
- // zBuffers, so at the moment, draw order determines what is on top
- // Render collision volumes
- renderCollisionMeshes();
- // Render the shape bounding box
- if ( mRenderBounds )
- {
- Point3F boxSize = mModel->getShape()->mBounds.maxExtents - mModel->getShape()->mBounds.minExtents;
- GFXStateBlockDesc desc;
- desc.fillMode = GFXFillWireframe;
- GFX->getDrawUtil()->drawCube( desc, boxSize, mModel->getShape()->center, ColorI::WHITE );
- }
- // Render the selected object bounding box
- if ( mRenderObjBox && ( mSelectedObject != -1 ) )
- {
- const TSShape::Object& obj = mModel->getShape()->objects[mSelectedObject];
- const TSMesh* mesh = ( mCurrentDL < obj.numMeshes ) ? mModel->getShape()->meshes[obj.startMeshIndex + mSelectedObjDetail] : NULL;
- if ( mesh )
- {
- GFX->pushWorldMatrix();
- if ( obj.nodeIndex != -1 )
- GFX->multWorld( mModel->mNodeTransforms[ obj.nodeIndex ] );
- const Box3F& bounds = mesh->getBounds();
- GFXStateBlockDesc desc;
- desc.fillMode = GFXFillWireframe;
- GFX->getDrawUtil()->drawCube( desc, bounds.getExtents(), bounds.getCenter(), ColorI::RED );
- GFX->popWorldMatrix();
- }
- }
- // Render the sun direction if currently editing it
- renderSunDirection();
- // render the nodes in the model
- renderNodes();
- // use the gizmo to render the camera axes
- if ( mRenderCameraAxes )
- {
- GizmoMode savedMode = mGizmoProfile->mode;
- mGizmoProfile->mode = MoveMode;
- Point3F pos;
- Point2I screenCenter( updateRect.point + updateRect.extent/2 );
- unproject( Point3F( screenCenter.x, screenCenter.y, 0.5 ), &pos );
- mGizmo->set( MatrixF::Identity, pos, Point3F::One);
- mGizmo->renderGizmo( smCamMatrix );
- mGizmoProfile->mode = savedMode;
- }
- }
- gClientSceneGraph->setFogData( savedFogData ); // restore fog setting
- }
- void GuiShapeEdPreview::renderGui(Point2I offset, const RectI& updateRect)
- {
- // Render the 2D stuff here
- // Render the names of the hovered and selected nodes
- if ( mModel )
- {
- if ( mRenderNodes && mHoverNode != -1 )
- renderNodeName( mHoverNode, LinearColorF::WHITE );
- if ( mSelectedNode != -1 )
- renderNodeName( mSelectedNode, LinearColorF::WHITE );
- }
- }
- void GuiShapeEdPreview::renderGrid()
- {
- if ( mRenderGridPlane )
- {
- // Use EditTSCtrl to render the grid in non-perspective views
- if ( mDisplayType != DisplayTypePerspective )
- {
- Parent::renderGrid();
- return;
- }
- // Round grid dimension up to a multiple of the minor ticks
- Point2I dim(mGridDimension.x + mGridPlaneMinorTicks, mGridDimension.y + mGridPlaneMinorTicks);
- dim /= ( mGridPlaneMinorTicks + 1 );
- dim *= ( mGridPlaneMinorTicks + 1 );
- Point2F minorStep( mGridPlaneSize, mGridPlaneSize );
- Point2F size( minorStep.x * dim.x, minorStep.y * dim.y );
- Point2F majorStep( minorStep * ( mGridPlaneMinorTicks + 1 ) );
- GFXStateBlockDesc desc;
- desc.setBlend( true );
- desc.setZReadWrite( true, false );
- GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, minorStep, mGridPlaneMinorTickColor );
- GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, majorStep, mGridPlaneColor );
- }
- }
- void GuiShapeEdPreview::renderSunDirection() const
- {
- if ( mEditingSun )
- {
- // Render four arrows aiming in the direction of the sun's light
- ColorI color = LinearColorF( mFakeSun->getColor()).toColorI();
- F32 length = mModel->getShape()->mBounds.len() * 0.8f;
- // Get the sun's vectors
- Point3F fwd = mFakeSun->getTransform().getForwardVector();
- Point3F up = mFakeSun->getTransform().getUpVector() * length / 8;
- Point3F right = mFakeSun->getTransform().getRightVector() * length / 8;
- // Calculate the start and end points of the first arrow (bottom left)
- Point3F start = mModel->getShape()->center - fwd * length - up/2 - right/2;
- Point3F end = mModel->getShape()->center - fwd * length / 3 - up/2 - right/2;
- GFXStateBlockDesc desc;
- desc.setZReadWrite( true, true );
- GFXDrawUtil* drawUtil = GFX->getDrawUtil();
- drawUtil->drawArrow( desc, start, end, color );
- drawUtil->drawArrow( desc, start + up, end + up, color );
- drawUtil->drawArrow( desc, start + right, end + right, color );
- drawUtil->drawArrow( desc, start + up + right, end + up + right, color );
- }
- }
- void GuiShapeEdPreview::renderNodes() const
- {
- if ( mRenderNodes )
- {
- // Render links between nodes
- GFXStateBlockDesc desc;
- desc.setZReadWrite( false, true );
- desc.setCullMode( GFXCullNone );
- GFX->setStateBlockByDesc( desc );
- PrimBuild::color( ColorI::WHITE );
- PrimBuild::begin( GFXLineList, mModel->getShape()->nodes.size() * 2 );
- for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
- {
- const TSShape::Node& node = mModel->getShape()->nodes[i];
- if (node.parentIndex >= 0)
- {
- Point3F start(mModel->mNodeTransforms[i].getPosition());
- Point3F end(mModel->mNodeTransforms[node.parentIndex].getPosition());
- PrimBuild::vertex3f( start.x, start.y, start.z );
- PrimBuild::vertex3f( end.x, end.y, end.z );
- }
- }
- PrimBuild::end();
- // Render the node axes
- for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
- {
- // Render the selected and hover nodes last (so they are on top)
- if ( ( i == mSelectedNode ) || ( i == mHoverNode ) )
- continue;
- renderNodeAxes( i, LinearColorF::WHITE );
- }
- // Render the hovered node
- if ( mHoverNode != -1 )
- renderNodeAxes( mHoverNode, LinearColorF::GREEN );
- }
- // Render the selected node (even if mRenderNodes is false)
- if ( mSelectedNode != -1 )
- {
- renderNodeAxes( mSelectedNode, LinearColorF::GREEN );
- const MatrixF& nodeMat = mModel->mNodeTransforms[mSelectedNode];
- mGizmo->set( nodeMat, nodeMat.getPosition(), Point3F::One);
- mGizmo->renderGizmo( smCamMatrix );
- }
- }
- void GuiShapeEdPreview::renderNodeAxes(S32 index, const LinearColorF& nodeColor) const
- {
- if(mModel->mNodeTransforms.size() <= index || index < 0)
- return;
- const Point3F xAxis( 1.0f, 0.15f, 0.15f );
- const Point3F yAxis( 0.15f, 1.0f, 0.15f );
- const Point3F zAxis( 0.15f, 0.15f, 1.0f );
- GFXStateBlockDesc desc;
- desc.setZReadWrite( false, true );
- desc.setCullMode( GFXCullNone );
- // Render nodes the same size regardless of zoom
- F32 scale = mOrbitDist / 60;
- GFX->pushWorldMatrix();
- GFX->multWorld( mModel->mNodeTransforms[index] );
- const ColorI color = LinearColorF(nodeColor).toColorI();
- GFX->getDrawUtil()->drawCube( desc, xAxis * scale, Point3F::Zero, color );
- GFX->getDrawUtil()->drawCube( desc, yAxis * scale, Point3F::Zero, color );
- GFX->getDrawUtil()->drawCube( desc, zAxis * scale, Point3F::Zero, color );
- GFX->popWorldMatrix();
- }
- void GuiShapeEdPreview::renderNodeName(S32 index, const LinearColorF& textColor) const
- {
- if(index < 0 || index >= mModel->getShape()->nodes.size() || index >= mProjectedNodes.size())
- return;
- const TSShape::Node& node = mModel->getShape()->nodes[index];
- const String& nodeName = mModel->getShape()->getName( node.nameIndex );
- Point2I pos( mProjectedNodes[index].x, mProjectedNodes[index].y + sNodeRectSize + 6 );
- GFX->getDrawUtil()->setBitmapModulation( LinearColorF(textColor).toColorI());
- GFX->getDrawUtil()->drawText( mProfile->mFont, pos, nodeName.c_str() );
- }
- void GuiShapeEdPreview::renderCollisionMeshes() const
- {
- if ( mRenderColMeshes )
- {
- ConcretePolyList polylist;
- polylist.setTransform( &MatrixF::Identity, Point3F::One );
- for ( S32 iDet = 0; iDet < mModel->getShape()->details.size(); iDet++ )
- {
- const TSShape::Detail& det = mModel->getShape()->details[iDet];
- const String& detName = mModel->getShape()->getName( det.nameIndex );
- // Ignore non-collision details
- if ( detName.startsWith( "Collision-" ) )
- mModel->buildPolyList( &polylist, iDet );
- }
- polylist.render();
- }
- }
- //-----------------------------------------------------------------------------
- // Console methods (GuiShapeEdPreview)
- //-----------------------------------------------------------------------------
- DefineEngineMethod( GuiShapeEdPreview, setOrbitPos, void, ( Point3F pos ),,
- "Set the camera orbit position\n\n"
- "@param pos Position in the form \"x y z\"\n" )
- {
- object->setOrbitPos( pos );
- }
- DefineEngineMethod( GuiShapeEdPreview, setModel, bool, ( const char* shapePath ),,
- "Sets the model to be displayed in this control\n\n"
- "@param shapeName Name of the model to display.\n"
- "@return True if the model was loaded successfully, false otherwise.\n" )
- {
- return object->setObjectModel( shapePath );
- }
- DefineEngineMethod(GuiShapeEdPreview, setShapeAsset, bool, (const char* shapeAsset), ,
- "Sets the model to be displayed in this control\n\n"
- "@param shapeName Name of the model to display.\n"
- "@return True if the model was loaded successfully, false otherwise.\n")
- {
- return object->setObjectShapeAsset(shapeAsset);
- }
- DefineEngineMethod( GuiShapeEdPreview, fitToShape, void, (),,
- "Adjust the camera position and zoom to fit the shape within the view.\n\n" )
- {
- object->fitToShape();
- }
- DefineEngineMethod( GuiShapeEdPreview, refreshShape, void, (),,
- "Refresh the shape (used when the shape meshes or nodes have been added or removed)\n\n" )
- {
- object->refreshShape();
- }
- DefineEngineMethod( GuiShapeEdPreview, updateNodeTransforms, void, (),,
- "Refresh the shape node transforms (used when a node transform has been modified externally)\n\n" )
- {
- object->updateNodeTransforms();
- }
- DefineEngineMethod( GuiShapeEdPreview, computeShapeBounds, Box3F, (),,
- "Compute the bounding box of the shape using the current detail and node transforms\n\n"
- "@return the bounding box \"min.x min.y min.z max.x max.y max.z\"" )
- {
- Box3F bounds;
- object->computeSceneBounds(bounds);
- return bounds;
- }
- DefineEngineMethod( GuiShapeEdPreview, getMeshHidden, bool, ( const char* name ),,
- "Return whether the named object is currently hidden\n\n" )
- {
- return object->getMeshHidden( name );
- }
- DefineEngineMethod( GuiShapeEdPreview, setMeshHidden, void, ( const char* name, bool hidden ),,
- "Show or hide the named object in the shape\n\n" )
- {
- object->setMeshHidden( name, hidden );
- }
- DefineEngineMethod( GuiShapeEdPreview, setAllMeshesHidden, void, ( bool hidden ),,
- "Show or hide all objects in the shape\n\n" )
- {
- object->setAllMeshesHidden( hidden );
- }
- DefineEngineMethod( GuiShapeEdPreview, exportToCollada, void, ( const char* path ),,
- "Export the current shape and all mounted objects to COLLADA (.dae).\n"
- "Note that animation is not exported, and all geometry is combined into a "
- "single mesh.\n\n"
- "@param path Destination filename\n" )
- {
- object->exportToCollada( path );
- }
- //-----------------------------------------------------------------------------
- // THREADS
- DefineEngineMethod( GuiShapeEdPreview, addThread, void, (),,
- "Add a new thread (initially without any sequence set)\n\n" )
- {
- object->addThread();
- }
- DefineEngineMethod( GuiShapeEdPreview, removeThread, void, ( S32 slot ),,
- "Removes the specifed thread\n\n"
- "@param slot index of the thread to remove\n" )
- {
- object->removeThread( slot );
- }
- DefineEngineMethod( GuiShapeEdPreview, getThreadCount, S32, (),,
- "Get the number of threads\n\n"
- "@return the number of threads\n" )
- {
- return object->getThreadCount();
- }
- DefineEngineMethod( GuiShapeEdPreview, setTimeScale, void, ( F32 scale ),,
- "Set the time scale of all threads\n\n"
- "@param scale new time scale value\n" )
- {
- object->setTimeScale( scale );
- }
- DefineEngineMethod( GuiShapeEdPreview, setThreadSequence, void, ( const char* name, F32 duration, F32 pos, bool play ), ( 0, 0, false ),
- "Sets the sequence to play for the active thread.\n\n"
- "@param name name of the sequence to play\n"
- "@param duration transition duration (0 for no transition)\n"
- "@param pos position in the new sequence to transition to\n"
- "@param play if true, the new sequence will play during the transition\n" )
- {
- object->setActiveThreadSequence( name, duration, pos, play );
- }
- DefineEngineMethod( GuiShapeEdPreview, getThreadSequence, const char*, (),,
- "Get the name of the sequence assigned to the active thread" )
- {
- return object->getThreadSequence();
- }
- DefineEngineMethod( GuiShapeEdPreview, refreshThreadSequences, void, (),,
- "Refreshes thread sequences (in case of removed/renamed sequences" )
- {
- object->refreshThreadSequences();
- }
- //-----------------------------------------------------------------------------
- // Mounting
- DefineEngineMethod( GuiShapeEdPreview, mountShape, bool, ( const char* shapeAssetId, const char* nodeName, const char* type, S32 slot ),,
- "Mount a shape onto the main shape at the specified node\n\n"
- "@param shapeAssetId AssetId of the shape to mount\n"
- "@param nodeName name of the node on the main shape to mount to\n"
- "@param type type of mounting to use (Object, Image or Wheel)\n"
- "@param slot mount slot\n" )
- {
- return object->mountShape(shapeAssetId, nodeName, type, slot );
- }
- DefineEngineMethod( GuiShapeEdPreview, setMountNode, void, ( S32 slot, const char* nodeName ),,
- "Set the node a shape is mounted to.\n\n"
- "@param slot mounted shape slot\n"
- "@param nodename name of the node to mount to\n" )
- {
- object->setMountNode( slot, nodeName );
- }
- DefineEngineMethod( GuiShapeEdPreview, getMountThreadSequence, const char*, ( S32 slot ),,
- "Get the name of the sequence playing on this mounted shape\n"
- "@param slot mounted shape slot\n"
- "@return name of the sequence (if any)\n" )
- {
- return object->getMountThreadSequence( slot );
- }
- DefineEngineMethod( GuiShapeEdPreview, setMountThreadSequence, void, ( S32 slot, const char* name ),,
- "Set the sequence to play for the shape mounted in the specified slot\n"
- "@param slot mounted shape slot\n"
- "@param name name of the sequence to play\n" )
- {
- object->setMountThreadSequence( slot, name );
- }
- DefineEngineMethod( GuiShapeEdPreview, getMountThreadPos, F32, ( S32 slot ),,
- "Get the playback position of the sequence playing on this mounted shape\n"
- "@param slot mounted shape slot\n"
- "@return playback position of the sequence (0-1)\n" )
- {
- return object->getMountThreadPos( slot );
- }
- DefineEngineMethod( GuiShapeEdPreview, setMountThreadPos, void, ( S32 slot, F32 pos ),,
- "Set the sequence position of the shape mounted in the specified slot\n"
- "@param slot mounted shape slot\n"
- "@param pos sequence position (0-1)\n" )
- {
- object->setMountThreadPos( slot, pos );
- }
- DefineEngineMethod( GuiShapeEdPreview, getMountThreadDir, F32, ( S32 slot ),,
- "Get the playback direction of the sequence playing on this mounted shape\n"
- "@param slot mounted shape slot\n"
- "@return direction of the sequence (-1=reverse, 0=paused, 1=forward)\n" )
- {
- return object->getMountThreadDir( slot );
- }
- DefineEngineMethod( GuiShapeEdPreview, setMountThreadDir, void, ( S32 slot, F32 dir ),,
- "Set the playback direction of the shape mounted in the specified slot\n"
- "@param slot mounted shape slot\n"
- "@param dir playback direction (-1=backwards, 0=paused, 1=forwards)\n" )
- {
- object->setMountThreadDir( slot, dir );
- }
- DefineEngineMethod( GuiShapeEdPreview, unmountShape, void, ( S32 slot ),,
- "Unmount the shape in the specified slot\n"
- "@param slot mounted shape slot\n" )
- {
- return object->unmountShape( slot );
- }
- DefineEngineMethod( GuiShapeEdPreview, unmountAll, void, (),,
- "Unmount all shapes\n" )
- {
- return object->unmountAll();
- }
|