/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OgreXSIMeshExporter.h" #include "OgreXSISkeletonExporter.h" #include "OgreXSIMaterialExporter.h" #include "OgreLogManager.h" #include "OgreException.h" #include "OgreXSIHelper.h" #include "OgreProgressiveMesh.h" #include "OgreString.h" #include "OgreLogManager.h" #include "OgreMeshManager.h" #include "OgreSkeletonManager.h" #include "OgreDefaultHardwareBufferManager.h" #include "OgreMaterialManager.h" using namespace XSI; #define OGRE_XSI_EXPORTER_VERSION L"1.2.0" // define columns of animations list #define ANIMATION_LIST_EXPORT_COL 0 #define ANIMATION_LIST_NAME_COL 1 #define ANIMATION_LIST_START_COL 2 #define ANIMATION_LIST_END_COL 3 #define ANIMATION_LIST_IKFREQ_COL 4 /** This is the main file for the OGRE XSI plugin. The purpose of the methods in this file are as follows: XSILoadPlugin registers the export command, the menu item, and the option dialog XSIUnloadPlugin cleans up OgreMeshExportCommand_Init Defines the arguments to the export command OgreMeshExportCommand_Execute Runs the exporter using arguments obtained from a context object (I assume this is to allow general access to this export rather than using the property dialog) OgreMeshExportMenu_Init Defines the menu text and the event callback to execute (OnOgreMeshExportMenu) OnOgreMeshExportMenu Callback event when clicking the export menu option. Adds an instance of the options dialog as a property, then uses the InspectObj XSI command to pop it up in a modal dialog. If it wasn't cancelled, performs an export. OgreMeshExportOptions_Define Defines the persistable parameters on the options dialog OgreMeshExportOptions_DefineLayout Defines the visual layout of the options dialog OgreMeshExportOptions_PPGEvent Event handler for when the options dialog is interacted with */ CString GetUserSelectedObject(); CStatus Popup( const CString& in_inputobjs, const CString& in_keywords, const CString& in_title, const CValue& in_mode, bool in_throw ); void DeleteObj( const CValue& in_inputobj ); Ogre::AnimationList animList; #ifdef unix extern "C" #endif /** Registers the export command, the menu item, and the option dialog */ CStatus XSILoadPlugin( XSI::PluginRegistrar& registrar ) { registrar.PutAuthor( L"Steve Streeting" ); registrar.PutName( L"OGRE Exporter Plugin" ); registrar.PutVersion( 1, 0 ); registrar.PutURL(L"http://www.ogre3d.org"); // register the mesh export command registrar.RegisterCommand( L"OgreMeshExportCommand", L"OgreMeshExportCommand" ); // register the menu under File > Export registrar.RegisterMenu(siMenuMainFileExportID, L"OgreMeshExportMenu", false, false); // register the export dialog properties factory registrar.RegisterProperty( L"OgreMeshExportOptions" ); #ifdef _DEBUG Application app; app.LogMessage( registrar.GetName() + L" has been loaded."); #endif return XSI::CStatus::OK; } #ifdef unix extern "C" #endif /** Cleans up */ XSI::CStatus XSIUnloadPlugin( const XSI::PluginRegistrar& registrar ) { #ifdef _DEBUG Application app; app.LogMessage(registrar.GetName() + L" has been unloaded."); #endif return XSI::CStatus::OK; } #ifdef unix extern "C" #endif /** Defines the arguments to the export command */ XSI::CStatus OgreMeshExportCommand_Init( const XSI::CRef& context ) { Context ctx(context); Command cmd(ctx.GetSource()); Application app; app.LogMessage( L"Defining: " + cmd.GetName() ); ArgumentArray args = cmd.GetArguments(); args.Add( L"objectName", L"" ); args.Add( L"targetMeshFileName", L"c:/default.mesh" ); args.Add( L"calculateEdgeLists", L"true" ); args.Add( L"calculateTangents", L"false" ); args.Add( L"exportSkeleton", L"true" ); args.Add( L"exportVertexAnimation", L"true" ); args.Add( L"targetSkeletonFileName", L"c:/default.skeleton" ); args.Add( L"fps", L"24" ); args.Add( L"animationList", L"" ); return XSI::CStatus::OK; } #ifdef unix extern "C" #endif /** Runs the exporter using arguments obtained from a context object (I assume this is to allow general access to this export rather than using the property dialog) */ XSI::CStatus OgreMeshExportCommand_Execute( XSI::CRef& in_context ) { Application app; Context ctxt(in_context); CValueArray args = ctxt.GetAttribute( L"Arguments" ); #ifdef _DEBUG for (long i=0; i 0) { param = prop.GetParameters().GetItem( L"lodDistanceIncrement" ); float distanceInc = param.GetValue(); param = prop.GetParameters().GetItem(L"lodQuota"); CString quota = param.GetValue(); param = prop.GetParameters().GetItem(L"lodReduction"); float reduction = param.GetValue(); lodData = new Ogre::XsiMeshExporter::LodData; float currentInc = distanceInc; for (int l = 0; l < numlods; ++l) { lodData->distances.push_back(currentInc); currentInc += distanceInc; } lodData->quota = (quota == L"p") ? Ogre::ProgressiveMesh::VRQ_PROPORTIONAL : Ogre::ProgressiveMesh::VRQ_CONSTANT; if (lodData->quota == Ogre::ProgressiveMesh::VRQ_PROPORTIONAL) lodData->reductionValue = reduction * 0.01; else lodData->reductionValue = reduction; } param = prop.GetParameters().GetItem( L"exportSkeleton" ); bool exportSkeleton = param.GetValue(); param = prop.GetParameters().GetItem( L"exportVertexAnimation" ); bool exportVertexAnimation = param.GetValue(); param = prop.GetParameters().GetItem( L"exportMaterials" ); bool exportMaterials = param.GetValue(); param = prop.GetParameters().GetItem( L"copyTextures" ); bool copyTextures = param.GetValue(); // create singletons Ogre::ResourceGroupManager rgm; Ogre::MeshManager meshMgr; Ogre::SkeletonManager skelMgr; Ogre::MaterialManager matMgr; Ogre::DefaultHardwareBufferManager hardwareBufMgr; // determine number of exportsteps size_t numSteps = 3 + OGRE_XSI_NUM_MESH_STEPS; if (numlods > 0) numSteps++; if (edgeLists) numSteps++; if (tangents) numSteps++; if (exportSkeleton) numSteps += 3; Ogre::ProgressManager progressMgr(numSteps); // Any material prefix? We need that for mesh linking too param = prop.GetParameters().GetItem( L"materialPrefix" ); Ogre::String materialPrefix = XSItoOgre(param.GetValue()); param = prop.GetParameters().GetItem( L"fps" ); float fps = param.GetValue(); if (fps == 0.0f) { OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "You must supply a valid value for 'FPS'", "OGRE Export"); } Ogre::AnimationList selAnimList; if (exportSkeleton || exportVertexAnimation) { param = prop.GetParameters().GetItem( L"animationList" ); GridData gd(param.GetValue()); for (int a = 0; a < gd.GetRowCount(); ++a) { if (gd.GetCell(ANIMATION_LIST_EXPORT_COL, a) == true) { Ogre::AnimationEntry ae; ae.animationName = XSItoOgre(gd.GetCell(ANIMATION_LIST_NAME_COL, a)); ae.ikSampleInterval = gd.GetCell(ANIMATION_LIST_IKFREQ_COL, a); ae.startFrame = gd.GetCell(ANIMATION_LIST_START_COL, a); ae.endFrame = gd.GetCell(ANIMATION_LIST_END_COL, a); selAnimList.push_back(ae); } } } if (exportSkeleton) { param = prop.GetParameters().GetItem( L"targetSkeletonFileName" ); Ogre::String skeletonFileName = XSItoOgre(param.GetValue()); if (skeletonFileName.empty()) { OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "You must supply a skeleton file name", "OGRE Exporter"); } // Truncate the skeleton filename to just the name (no path) Ogre::String skelName = skeletonFileName; int pos = skeletonFileName.find_last_of("\\"); if (pos == Ogre::String::npos) { pos = skeletonFileName.find_last_of("/"); } if (pos != Ogre::String::npos) { skelName = skelName.substr(pos+1, skelName.size() - pos - 1); } // Do the mesh Ogre::DeformerMap& deformers = meshExporter.exportMesh(meshFileName, mergeSubmeshes, exportChildren, edgeLists, tangents, exportVertexAnimation, selAnimList, fps, materialPrefix, lodData, skelName); // do the skeleton skelExporter.exportSkeleton(skeletonFileName, deformers, fps, selAnimList); } else { // No skeleton meshExporter.exportMesh(meshFileName, mergeSubmeshes, exportChildren, edgeLists, tangents, exportVertexAnimation, selAnimList, fps, materialPrefix, lodData); } delete lodData; // Do we want to export materials too? if (exportMaterials) { param = prop.GetParameters().GetItem( L"targetMaterialFileName" ); Ogre::String materialFileName = XSItoOgre(param.GetValue()); Ogre::XsiMaterialExporter matExporter; try { matExporter.exportMaterials(meshExporter.getMaterials(), meshExporter.getTextureProjectionMap(), materialFileName, copyTextures); } catch (Ogre::Exception& e) { // ignore, non-fatal and will be in log } } } } catch (Ogre::Exception& e) { // Will already have been logged to the Ogre log manager // Tell XSI app.LogMessage(OgretoXSI(e.getDescription()), XSI::siFatalMsg); app.LogMessage(OgretoXSI(e.getFullDescription()), XSI::siInfoMsg); } //DeleteObj( L"OgreMeshExportOptions" ); return st; } #ifdef unix extern "C" #endif /** Defines the persistable parameters on the options dialog */ CStatus OgreMeshExportOptions_Define( const CRef & in_Ctx ) { // Here is where we add all the parameters to the // Custom Property. This will be called each time // an new instance of the Custom Property is called. // It is not called when an persisted Custom Property is loaded. Application app ; CustomProperty prop = Context(in_Ctx).GetSource(); Parameter param ; // Default capabilities for most of these parameters int caps = siPersistable ; CValue nullValue; // Used for arguments we don't want to set prop.AddParameter( L"version",CValue::siString, caps, L"Version", L"", nullValue, param) ; prop.AddParameter( L"objectName",CValue::siString, caps, L"Object Name", L"", nullValue, param) ; prop.AddParameter( L"objects",CValue::siRefArray, caps, L"Collection of selected objects", L"", nullValue, param) ; prop.AddParameter( L"targetMeshFileName",CValue::siString, caps, L"Mesh Filename", L"", nullValue, param) ; prop.AddParameter( L"mergeSubmeshes",CValue::siBool, caps, L"Merge objects with same material?", L"If false, a separate named SubMesh will be created for every PolygonMesh " L"preserving your model divisions. If true, the exporter will merge all " L"PolygonMesh objects with the same material, which is more efficient, but " L"does not preserve your modelling divisions.", CValue(true), param) ; prop.AddParameter( L"exportChildren",CValue::siBool, caps, L"Export Children", L"If true, children of all selected objects will be exported.", CValue(true), param) ; prop.AddParameter( L"calculateEdgeLists",CValue::siBool, caps, L"Calculate Edge Lists (stencil shadows)", L"", CValue(true), param) ; prop.AddParameter( L"calculateTangents",CValue::siBool, caps, L"Calculate Tangents (normal mapping)", L"", CValue(false), param) ; prop.AddParameter( L"numLodLevels",CValue::siInt2, caps, L"Levels of Detail", L"", CValue(0L), param) ; prop.AddParameter( L"lodDistanceIncrement",CValue::siFloat, caps, L"Distance Increment", L"", CValue(2000L), //default CValue(1L), // hard min CValue(1000000L), // hard max CValue(50L), // suggested min CValue(10000L), // suggested max param) ; prop.AddParameter( L"lodQuota",CValue::siString, caps, L"Reduction Style", L"", L"p", param) ; prop.AddParameter( L"lodReduction",CValue::siFloat, caps, L"Reduction Value", L"", CValue(50.0f), param) ; prop.AddParameter( L"exportSkeleton",CValue::siBool, caps, L"Export Skeleton", L"", CValue(true), param) ; prop.AddParameter( L"exportVertexAnimation",CValue::siBool, caps, L"Export Vertex Animation", L"", CValue(true), param) ; prop.AddParameter( L"targetSkeletonFileName",CValue::siString, caps, L"Skeleton Filename", L"", nullValue, param) ; prop.AddParameter( L"fps",CValue::siInt2, caps, L"Frames per second", L"", CValue(24l), param) ; prop.AddGridParameter(L"animationList"); prop.AddParameter( L"exportMaterials", CValue::siBool, caps, L"Export Materials", L"", CValue(true), param); prop.AddParameter( L"copyTextures", CValue::siBool, caps, L"Copy Textures To Folder", L"", CValue(true), param); prop.AddParameter( L"targetMaterialFileName",CValue::siString, caps, L"Material Filename", L"", nullValue, param) ; prop.AddParameter( L"materialPrefix",CValue::siString, caps, L"Material Prefix", L"", nullValue, param) ; return CStatus::OK; } #ifdef unix extern "C" #endif /** Defines the visual layout of the options dialog */ CStatus OgreMeshExportOptions_DefineLayout( const CRef & in_Ctx ) { // XSI will call this to define the visual appearance of the CustomProperty // The layout is shared between all instances of the CustomProperty // and is CACHED!!!. You can force the code to re-execute by using the // XSIUtils.Reload feature, or right-clicking the property page and selecting 'Refresh' PPGLayout oLayout = Context( in_Ctx ).GetSource() ; PPGItem item ; oLayout.Clear() ; // Mesh tab oLayout.AddTab(L"Basic"); // Object oLayout.AddGroup(L"Object(s) to export"); item = oLayout.AddItem(L"objectName"); item.PutAttribute( siUINoLabel, true ); oLayout.EndGroup(); oLayout.AddGroup(L"Mesh"); item = oLayout.AddItem(L"targetMeshFileName", L"Target", siControlFilePath); item.PutAttribute( siUINoLabel, true ); item.PutAttribute( siUIFileFilter, L"OGRE Mesh format (*.mesh)|*.mesh|All Files (*.*)|*.*||" ); item = oLayout.AddItem(L"mergeSubmeshes") ; item = oLayout.AddItem(L"exportChildren") ; item = oLayout.AddItem(L"calculateEdgeLists"); item = oLayout.AddItem(L"calculateTangents"); oLayout.AddGroup(L"Level of Detail Reduction"); item = oLayout.AddItem(L"numLodLevels"); item = oLayout.AddItem(L"lodDistanceIncrement"); CValueArray vals; vals.Add(L"Percentage"); vals.Add(L"p"); vals.Add(L"Constant"); vals.Add(L"c"); item = oLayout.AddEnumControl(L"lodQuota", vals, L"Quota", XSI::siControlCombo); item = oLayout.AddItem(L"lodReduction"); oLayout.EndGroup(); oLayout.EndGroup(); oLayout.AddTab(L"Materials"); // Material Tab item = oLayout.AddItem(L"exportMaterials") ; item = oLayout.AddItem(L"targetMaterialFileName", L"Target", siControlFilePath) ; item.PutAttribute( siUINoLabel, true ); item.PutAttribute( siUIFileFilter, L"OGRE Material script (*.material)|*.material|All Files (*.*)|*.*||" ); item = oLayout.AddItem(L"materialPrefix"); item = oLayout.AddItem(L"copyTextures"); // Skeleton Tab oLayout.AddTab(L"Animation"); item = oLayout.AddItem(L"exportVertexAnimation"); item = oLayout.AddItem(L"exportSkeleton"); item = oLayout.AddItem(L"targetSkeletonFileName", L"Target", siControlFilePath); item.PutAttribute( siUINoLabel, true ); item.PutAttribute( siUIFileFilter, L"OGRE Skeleton format (*.skeleton)|*.skeleton|All Files (*.*)|*.*||" ); item = oLayout.AddItem(L"fps"); oLayout.AddGroup(L"Animations"); item = oLayout.AddItem(L"animationList", L"Animations", siControlGrid); item.PutAttribute( siUINoLabel, true ); item.PutAttribute(siUIGridColumnWidths, L"0:15:250:30:30:75"); item.PutAttribute(siUIGridHideRowHeader, true); oLayout.AddRow(); item = oLayout.AddButton(L"refreshAnimation", L"Refresh"); item = oLayout.AddButton(L"addAnimation", L"Add"); item = oLayout.AddButton(L"removeAnimation", L"Remove"); oLayout.EndRow(); oLayout.EndGroup(); // Make animatino name read-only (not any more) //item.PutAttribute(siUIGridReadOnlyColumns, L"1:0:0:0"); return CStatus::OK; } bool hasSkeleton(X3DObject& si, bool recurse) { if (si.GetEnvelopes().GetCount() > 0) { return true; } if (recurse) { CRefArray children = si.GetChildren(); for(long i = 0; i < children.GetCount(); i++) { X3DObject child(children[i]); bool ret = hasSkeleton(child, recurse); if (ret) return ret; } } return false; } bool hasSkeleton(Selection& sel, bool recurse) { // iterate over selection for (int i = 0; i < sel.GetCount(); ++i) { X3DObject obj(sel[i]); bool ret = hasSkeleton(obj, recurse); if (ret) return ret; } return false; } void findAnimations(XSI::Model& model, Ogre::AnimationList& animList) { if (model.HasMixer()) { // Scan the mixer for all clips // At this point we're only interested in the top-level and do not // cascade into all clip containers, since we're interested in the // top-level timeline splits XSI::Mixer mixer = model.GetMixer(); CRefArray clips = mixer.GetClips(); for (int c = 0; c < clips.GetCount(); ++c) { XSI::Clip clip(clips[c]); XSI::CString clipType = clip.GetType(); if (clipType == siClipAnimationType || clipType == siClipShapeType || clipType == siClipAnimCompoundType || // nested fcurves clipType == siClipShapeCompoundType) // nested shape { XSI::TimeControl timeControl = clip.GetTimeControl(); Ogre::AnimationEntry anim; anim.animationName = XSItoOgre(clip.GetName()); anim.startFrame = timeControl.GetStartOffset(); long length = (1.0 / timeControl.GetScale()) * (timeControl.GetClipOut() - timeControl.GetClipIn() + 1); anim.endFrame = anim.startFrame + length - 1; anim.ikSampleInterval = 5.0f; animList.push_back(anim); } } } } void getAnimations(XSI::Model& root, Ogre::AnimationList& animList) { animList.clear(); findAnimations(root, animList); // Find all children (recursively) XSI::CRefArray children = root.FindChildren(L"", siModelType, XSI::CStringArray()); for (int c = 0; c < children.GetCount(); ++c) { XSI::Model child(children[c]); findAnimations(child, animList); } // Now iterate over the list and eliminate overlapping elements for (Ogre::AnimationList::iterator i = animList.begin(); i != animList.end(); ++i) { Ogre::AnimationList::iterator j = i; ++j; for (; j != animList.end();) { bool remove = false; if (j->endFrame >= i->startFrame) { // Merge this one into i, extend i's start to j remove = true; i->startFrame = j->startFrame; } if (j->startFrame <= i->endFrame) { remove = true; i->endFrame = j->endFrame; } if (remove) { j = animList.erase(j); } else { ++j; } } } } void populateAnimationsList(XSI::GridData gd) { // 5 columns gd.PutColumnCount(5); // Export column is a check box gd.PutColumnType(ANIMATION_LIST_EXPORT_COL, siColumnBool); // Labels gd.PutColumnLabel(ANIMATION_LIST_EXPORT_COL, L""); gd.PutColumnLabel(ANIMATION_LIST_NAME_COL, L"Name"); gd.PutColumnLabel(ANIMATION_LIST_START_COL, L"Start"); gd.PutColumnLabel(ANIMATION_LIST_END_COL, L"End"); gd.PutColumnLabel(ANIMATION_LIST_IKFREQ_COL, L"Sample Freq"); Application app; Model appRoot(app.GetActiveSceneRoot()); getAnimations(appRoot, animList); gd.PutRowCount(animList.size()); long row = 0; for (Ogre::AnimationList::iterator a = animList.begin(); a != animList.end(); ++a, ++row) { gd.PutCell(ANIMATION_LIST_NAME_COL, row, OgretoXSI(a->animationName)); // default to export gd.PutCell(ANIMATION_LIST_EXPORT_COL, row, true); gd.PutCell(ANIMATION_LIST_START_COL, row, a->startFrame); gd.PutCell(ANIMATION_LIST_END_COL, row, a->endFrame); gd.PutCell(ANIMATION_LIST_IKFREQ_COL, row, a->ikSampleInterval); } } #ifdef unix extern "C" #endif /** Event handler for when the options dialog is interacted with */ CStatus OgreMeshExportOptions_PPGEvent( const CRef& io_Ctx ) { // This callback is called when events happen in the user interface // This is where you implement the "logic" code. Application app ; static bool hasSkel = false; PPGEventContext ctx( io_Ctx ) ; PPGEventContext::PPGEvent eventID = ctx.GetEventID() ; CustomProperty prop = ctx.GetSource() ; Parameter objectNameParam = prop.GetParameters().GetItem( L"objectName" ) ; // On open dialog if ( eventID == PPGEventContext::siOnInit ) { // Pre-populate object with currently selected item(s) Selection sel(app.GetSelection()); if (sel.GetCount() > 0) { CString val; for (int i = 0; i < sel.GetCount(); ++i) { val += SIObject(sel[i]).GetName(); if (i < sel.GetCount() - 1) val += L", "; } prop.PutParameterValue(L"objectName", val); } else { // no selection, assume entire scene prop.PutParameterValue(L"objectName", CString(L"[Entire Scene]")); } // Make the selection read-only objectNameParam.PutCapabilityFlag( siReadOnly, true ); // default the frame rate to that selected in animation panel prop.PutParameterValue(L"fps", CTime().GetFrameRate()); // enable / disable the skeleton export based on envelopes if (!hasSkeleton(sel, true)) { prop.PutParameterValue(L"exportSkeleton", false); Parameter param = prop.GetParameters().GetItem(L"exportSkeleton"); param.PutCapabilityFlag(siReadOnly, true); param = prop.GetParameters().GetItem(L"targetSkeletonFileName"); param.PutCapabilityFlag(siReadOnly, true); hasSkel = false; } else { prop.PutParameterValue(L"exportSkeleton", true); Parameter param = prop.GetParameters().GetItem(L"exportSkeleton"); param.PutCapabilityFlag(siReadOnly, false); param = prop.GetParameters().GetItem(L"targetSkeletonFileName"); param.PutCapabilityFlag(siReadOnly, false); hasSkel = true; } // value of param is a griddata object // initialise it with all detected animations if it's empty Parameter param = prop.GetParameters().GetItem(L"animationList"); GridData gd(param.GetValue()); if (gd.GetRowCount() == 0 || gd.GetCell(0,0) == L"No data has been set") { populateAnimationsList(gd); } } // On clicking a button else if ( eventID == PPGEventContext::siButtonClicked ) { CValue buttonPressed = ctx.GetAttribute( L"Button" ); // Clicked the refresh animation button if ( buttonPressed.GetAsText() == L"refreshAnimation" ) { long btn; CStatus ret = app.GetUIToolkit().MsgBox( L"Are you sure you want to lose the current contents " L"of the animations list and to refresh it from mixers?", siMsgYesNo, L"Confirm", btn); if (btn == 6) { Parameter param = prop.GetParameters().GetItem(L"animationList"); GridData gd(param.GetValue()); populateAnimationsList(gd); } } else if( buttonPressed.GetAsText() == L"addAnimation" ) { Parameter param = prop.GetParameters().GetItem(L"animationList"); GridData gd(param.GetValue()); gd.PutRowCount(gd.GetRowCount() + 1); // default export to true and sample rate gd.PutCell(ANIMATION_LIST_EXPORT_COL, gd.GetRowCount()-1, true); gd.PutCell(ANIMATION_LIST_IKFREQ_COL, gd.GetRowCount()-1, 5L); } else if( buttonPressed.GetAsText() == L"removeAnimation" ) { Parameter param = prop.GetParameters().GetItem(L"animationList"); GridData gd(param.GetValue()); GridWidget gw = gd.GetGridWidget(); // cell-level selection, so have to search for selection in every cell long selRow = -1; for (long row = 0; row < gd.GetRowCount() && selRow == -1; ++row) { for (long col = 0; col < gd.GetColumnCount() && selRow == -1; ++col) { if (gw.IsCellSelected(col, row)) { selRow = row; } } } if (selRow != -1) { long btn; CStatus ret = app.GetUIToolkit().MsgBox( L"Are you sure you want to remove this animation entry?", siMsgYesNo, L"Confirm", btn); if (btn == 6) { // Move all the contents up one for (long row = selRow; row < gd.GetRowCount(); ++row) { for (long col = 0; col < gd.GetColumnCount(); ++col) { gd.PutCell(col, row, gd.GetCell(col, row+1)); } } // remove last row gd.PutRowCount(gd.GetRowCount() - 1); } } } } // Changed a parameter else if ( eventID == PPGEventContext::siParameterChange ) { Parameter changed = ctx.GetSource() ; CustomProperty prop = changed.GetParent() ; CString paramName = changed.GetScriptName() ; // Check paramName against parameter names, perform custom onChanged event if (paramName == L"targetMeshFileName") { // Default skeleton name if blank Ogre::String meshName = XSItoOgre(changed.GetValue()); if (hasSkel && Ogre::StringUtil::endsWith(meshName, "mesh") && prop.GetParameterValue(L"targetSkeletonFileName") == L"") { Ogre::String skelName = meshName.substr(0, meshName.size() - 4) + "skeleton"; CString xsiSkelName = OgretoXSI(skelName); prop.PutParameterValue(L"targetSkeletonFileName", xsiSkelName); } if (Ogre::StringUtil::endsWith(meshName, "mesh") && prop.GetParameterValue(L"targetMaterialFileName") == L"") { // default material script name if blank Ogre::String matName = meshName.substr(0, meshName.size() - 4) + "material"; CString xsiMatName = OgretoXSI(matName); prop.PutParameterValue(L"targetMaterialFileName", xsiMatName); } } } return CStatus::OK; } CString GetUserSelectedObject() { Application app; Model root(app.GetActiveSceneRoot()); CStringArray emptyArray; CRefArray cRefArray = root.FindChildren( L"", L"", emptyArray, true ); CStringArray nameArray(cRefArray.GetCount()); for ( long i=0; i < cRefArray.GetCount(); i++ ) { nameArray[i] = SIObject(cRefArray[i]).GetName(); } //todo qsort the nameArray // Using the COMAPIHandler for creating a "XSIDial.XSIDialog" CComAPIHandler xsidialog; xsidialog.CreateInstance( L"XSIDial.XSIDialog"); CValue index; CValueArray args(cRefArray.GetCount()); for (long y=0; y < cRefArray.GetCount(); y++) args[y]=nameArray[y]; xsidialog.Call(L"Combo",index,L"Select Item",args ); long ind = (long)index; return args[ind]; } CStatus Popup( const CString& in_inputobjs, const CString& in_keywords, const CString& in_title, const CValue& /*number*/ in_mode, bool in_throw ) { Application app; CValueArray args(5); CValue retval; long i(0); args[i++]= in_inputobjs; args[i++]= in_keywords; args[i++]= in_title; args[i++]= in_mode; args[i++]= in_throw; return app.ExecuteCommand( L"InspectObj", args, retval ); } void DeleteObj( const CValue& in_inputobj ) { Application app; CValueArray args(1); CValue retval; long i(0); args[i++]= in_inputobj; CStatus st = app.ExecuteCommand( L"DeleteObj", args, retval ); return; }