Browse Source

Working on animation import

Josh Engebretson 10 years ago
parent
commit
c20a18c17d

+ 2 - 2
Data/AtomicEditor/Resources/EditorData/AtomicEditor/editor/ui/inspectorframe.tb.txt

@@ -1,4 +1,4 @@
 TBContainer: skin: AEContainer, gravity: all, id: mainframecontainer
-	lp: min-width: 320
+	lp: min-width: 340
 	TBScrollContainer: scroll-mode: auto, id: inspectorcontainerscroll, gravity: all
-		TBLayout: distribution: gravity, id: inspectorcontainer, gravity: all
+		TBLayout: distribution: gravity, id: inspectorcontainer, gravity: all

+ 1 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/tsconfig.json

@@ -27,6 +27,7 @@
         "./ui/inspector/DataBinding.ts",
         "./ui/inspector/InspectorFrame.ts",
         "./ui/inspector/MaterialInspector.ts",
+        "./ui/inspector/ModelInspector.ts",
         "./ui/inspector/NodeInspector.ts",
         "./ui/inspector/TextureSelector.ts",
         "./ui/modal/MessageModal.ts",

+ 15 - 1
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/inspector/InspectorFrame.ts

@@ -6,6 +6,7 @@ import DataBinding = require("./DataBinding");
 // inspectors
 
 import MaterialInspector = require("./MaterialInspector");
+import ModelInspector = require("./ModelInspector");
 import NodeInspector = require("./NodeInspector");
 
 var UI = Atomic.UI;
@@ -27,7 +28,7 @@ class InspectorFrame extends ScriptWidget {
 
 
     }
-    
+
     handleEditResource(ev: UIEvents.EditorResourceEvent) {
 
         var path = ev.path;
@@ -58,6 +59,19 @@ class InspectorFrame extends ScriptWidget {
 
     inspectAsset(asset: ToolCore.Asset) {
 
+       if (asset.importerTypeName == "ModelImporter") {
+
+         var container = this.getWidget("inspectorcontainer");
+         container.deleteAllChildren();
+
+         var inspector = new ModelInspector();
+         container.addChild(inspector);
+
+         inspector.inspect(asset);
+
+
+       }
+
         if (asset.importerTypeName == "MaterialImporter") {
 
           var cache = Atomic.getResourceCache();

+ 9 - 7
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/inspector/MaterialInspector.ts

@@ -10,6 +10,7 @@ var solidSource = new Atomic.UIMenuItemSource();
 solidSource.addItem(new Atomic.UIMenuItem("Diffuse", "Diffuse"));
 solidSource.addItem(new Atomic.UIMenuItem("Diffuse Emissive", "Diffuse Emissive"));
 solidSource.addItem(new Atomic.UIMenuItem("Diffuse Normal", "Diffuse Normal"));
+solidSource.addItem(new Atomic.UIMenuItem("Diffuse Normal Specular", "Diffuse Normal Specular"));
 
 var tranSource = new Atomic.UIMenuItemSource();
 tranSource.addItem(new Atomic.UIMenuItem("Alpha", "Alpha"));
@@ -40,6 +41,7 @@ var techniqueLookup = {
     "Techniques/Diff.xml": "Diffuse",
     "Techniques/DiffEmissive.xml": "Diffuse Emissive",
     "Techniques/DiffNormal.xml": "Diffuse Normal",
+    "Techniques/DiffNormalSpec.xml": "Diffuse Normal Specular"
 }
 
 var techniqueReverseLookup = {};
@@ -152,7 +154,6 @@ class MaterialInspector extends ScriptWidget {
     createTechniquePopup(): Atomic.UIWidget {
 
         var button = this.techniqueButton = new Atomic.UIButton();
-
         var technique = this.material.getTechnique(0);
 
         button.text = techniqueLookup[technique.name];
@@ -160,7 +161,7 @@ class MaterialInspector extends ScriptWidget {
         button.fontDescription = this.fd;
 
         var lp = new Atomic.UILayoutParams();
-        lp.width = 140;
+        lp.width = 180;
         button.layoutParams = lp;
 
         button.onClick = function() {
@@ -204,7 +205,7 @@ class MaterialInspector extends ScriptWidget {
         section.contentRoot.addChild(attrsVerticalLayout);
 
         // TODO: Filter on technique
-        var textureUnits = [Atomic.TU_DIFFUSE, Atomic.TU_NORMAL]// ,Atomic.TU_EMISSIVE, Atomic.TU_SPECULAR, Atomic.TU_ENVIRONMENT,
+        var textureUnits = [Atomic.TU_DIFFUSE, Atomic.TU_NORMAL, Atomic.TU_SPECULAR];// ,Atomic.TU_EMISSIVE, Atomic.TU_ENVIRONMENT,
             //Atomic.TU_CUSTOM1, Atomic.TU_CUSTOM2];
 
         for (var i in textureUnits) {
@@ -260,7 +261,7 @@ class MaterialInspector extends ScriptWidget {
         this.material = material;
 
         var mlp = new Atomic.UILayoutParams();
-        mlp.width = 304;
+        mlp.width = 340;
 
         var materialLayout = new Atomic.UILayout();
         materialLayout.spacing = 4;
@@ -281,7 +282,7 @@ class MaterialInspector extends ScriptWidget {
         var attrsVerticalLayout = new Atomic.UILayout(Atomic.UI_AXIS_Y);
         attrsVerticalLayout.spacing = 3;
         attrsVerticalLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
-        attrsVerticalLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
+        attrsVerticalLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_PREFERRED;
 
         // NAME
         var nameLayout = new Atomic.UILayout();
@@ -301,7 +302,7 @@ class MaterialInspector extends ScriptWidget {
         field.skinBg = "TBAttrEditorField";;
         field.fontDescription = this.fd;
         var lp = new Atomic.UILayoutParams();
-        lp.width = 140;
+        lp.width = 180;
         field.layoutParams = lp;
 
         field.text = material.getTexture(Atomic.TU_DIFFUSE).name;
@@ -313,7 +314,8 @@ class MaterialInspector extends ScriptWidget {
         // TECHNIQUE LAYOUT
 
         var techniqueLayout = new Atomic.UILayout();
-        techniqueLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+        techniqueLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_GRAVITY;
+        techniqueLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_PREFERRED;
 
         name = new Atomic.UITextField();
         name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;

+ 88 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/inspector/ModelInspector.ts

@@ -0,0 +1,88 @@
+
+
+import ScriptWidget = require("../ScriptWidget");
+
+class ModelInspector extends ScriptWidget {
+
+    constructor() {
+
+        super();
+
+        this.subscribeToEvent(this, "WidgetEvent", (data) => this.handleWidgetEvent(data));
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+
+    }
+
+    inspect(asset: ToolCore.Asset) {
+
+        var fd = new Atomic.UIFontDescription();
+        fd.id = "Vera";
+        fd.size = 11;
+
+        var nlp = new Atomic.UILayoutParams();
+        nlp.width = 320;
+
+        var assetLayout = new Atomic.UILayout();
+        assetLayout.spacing = 4;
+
+        assetLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+        assetLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+        assetLayout.layoutParams = nlp;
+        assetLayout.axis = Atomic.UI_AXIS_Y;
+
+        // node attr layout
+
+        var modelSection = new Atomic.UISection();
+        modelSection.text = "Model Import";
+        modelSection.value = 1;
+        //modelSection.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
+        //modelSection.skinBg = "InspectorTextLabel";
+        assetLayout.addChild(modelSection);
+
+        var attrsVerticalLayout = new Atomic.UILayout(Atomic.UI_AXIS_Y);
+        attrsVerticalLayout.spacing = 3;
+        attrsVerticalLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+        attrsVerticalLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
+
+        modelSection.contentRoot.addChild(attrsVerticalLayout);
+
+        var attrLayout = new Atomic.UILayout();
+
+        attrLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+
+        var name = new Atomic.UITextField();
+        name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
+        name.skinBg = "InspectorTextAttrName";
+        name.text = "Scale";
+        name.fontDescription = fd;
+        attrLayout.addChild(name);
+
+        var scaleEdit = new Atomic.UIEditField();
+        scaleEdit.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
+        scaleEdit.skinBg = "TBAttrEditorField";;
+        scaleEdit.fontDescription = fd;
+        var lp = new Atomic.UILayoutParams();
+        lp.width = 180;
+        scaleEdit.layoutParams = lp;
+
+        var importer = <ToolCore.ModelImporter> asset.importer;
+
+        scaleEdit.text = importer.scale.toString();
+
+        attrLayout.addChild(scaleEdit);
+
+        attrsVerticalLayout.addChild(attrLayout);
+
+        this.addChild(assetLayout);
+
+    }
+
+    bindings:Array<DataBinding> = new Array();
+
+
+}
+
+export = ModelInspector;

+ 5 - 0
Source/Atomic/Resource/JSONValue.cpp

@@ -106,6 +106,11 @@ JSONValue JSONValue::CreateChild(const String& name, JSONValueType valueType)
     return GetChild(name, valueType);
 }
 
+bool JSONValue::HasMember(const String& name) const
+{
+    return value_->HasMember(name.CString());
+}
+
 JSONValue JSONValue::GetChild(const String& name, JSONValueType valueType) const
 {
     assert(IsObject());

+ 4 - 0
Source/Atomic/Resource/JSONValue.h

@@ -129,6 +129,10 @@ public:
 
     /// Is object type.
     bool IsObject() const;
+
+    /// HasMember
+    bool HasMember(const String& name) const;
+
     /// Return child names (only object and array child name).
     Vector<String> GetChildNames() const;
     /// Return member value names.

+ 1 - 1
Source/AtomicJS/Packages/ToolCore/ToolCore.json

@@ -4,7 +4,7 @@
 							 "Source/ToolCore/Import", "Source/ToolCore/Assets"],
 	"classes" : ["ToolEnvironment", "ToolSystem", "Project", "ProjectFile", "Platform", "PlatformMac", "PlatformWeb",
 							 "PlatformWindows", "Command", "PlayCmd", "OpenAssetImporter",
-							 "Asset", "AssetDatabase"],
+							 "Asset", "AssetDatabase", "AssetImporter", "ModelImporter"],
 	"typescript_decl" : {
 
 		"AssetDatabase" : [

+ 2 - 0
Source/ToolCore/Assets/Asset.h

@@ -37,6 +37,8 @@ public:
     const StringHash GetImporterType() { return importer_.Null() ? String::EMPTY : importer_->GetType(); }
     const String& GetImporterTypeName() { return importer_.Null() ? String::EMPTY : importer_->GetTypeName(); }
 
+    AssetImporter* GetImporter() { return importer_; }
+
     void SetDirty(bool dirty) { dirty_ = dirty; }
     bool IsDirty() const { return dirty_; }
 

+ 49 - 2
Source/ToolCore/Assets/ModelImporter.cpp

@@ -1,4 +1,6 @@
 
+#include <Atomic/IO/Log.h>
+
 #include "../Import/OpenAssetImporter.h"
 
 #include "Asset.h"
@@ -11,7 +13,7 @@ namespace ToolCore
 /// Node + Model (static or animated)
 ModelImporter::ModelImporter(Context* context) : AssetImporter(context)
 {
-
+    SetDefaults();
 }
 
 ModelImporter::~ModelImporter()
@@ -22,6 +24,12 @@ ModelImporter::~ModelImporter()
 void ModelImporter::SetDefaults()
 {
     AssetImporter::SetDefaults();
+
+    scale_ = 1.0f;
+    startTime_ = -1.0f;
+    endTime_ = -1.0f;
+    importAnimations_ = false;
+
 }
 
 bool ModelImporter::Import(const String& guid)
@@ -36,13 +44,36 @@ bool ModelImporter::Import(const String& guid)
 
     //importer->SetVerboseLog(true);
 
+    bool animationsOnly = false;
+
+    String modelAssetFilename = asset->GetPath();
+
+    if (modelAssetFilename.Contains("@"))
+    {
+        animationsOnly = true;
+    }
+
+    importer->SetScale(scale_);
+    importer->SetStartTime(startTime_);
+    importer->SetEndTime(endTime_);
+    importer->SetExportAnimations(importAnimations_);
+
     if (importer->Load(asset->GetPath()))
     {
         String cachePath = db->GetCachePath();
 
         cachePath += guid;
 
-        importer->ExportModel(cachePath);
+        importer->ExportModel(cachePath, animationsOnly);
+
+        const Vector<OpenAssetImporter::AnimationInfo>& infos = importer->GetAnimationInfos();
+
+        for (unsigned i = 0; i < infos.Size(); i++)
+        {
+            const OpenAssetImporter::AnimationInfo& info = infos.At(i);
+
+            LOGINFOF("Import Info: %s : %s", info.name_.CString(), info.cacheFilename_.CString());
+        }
     }
     else
     {
@@ -59,6 +90,17 @@ bool ModelImporter::LoadSettingsInternal()
 
     JSONValue import = jsonRoot_.GetChild("ModelImporter", JSON_OBJECT);
 
+    SetDefaults();
+
+    if (import.HasMember("scale"))
+        scale_ = import.GetFloat("scale");
+    if (import.HasMember("startTime"))
+        startTime_ = import.GetFloat("startTime");
+    if (import.HasMember("endTime"))
+        endTime_ = import.GetFloat("endTime");
+    if (import.HasMember("importAnimations"))
+        importAnimations_ = import.GetBool("importAnimations");
+
     return true;
 }
 
@@ -69,6 +111,11 @@ bool ModelImporter::SaveSettingsInternal()
 
     JSONValue import = jsonRoot_.CreateChild("ModelImporter");
 
+    import.SetFloat("scale", scale_);
+    import.SetFloat("startTime", startTime_);
+    import.SetFloat("endTime", endTime_);
+    import.SetBool("importAnimations", importAnimations_);
+
     return true;
 }
 

+ 15 - 0
Source/ToolCore/Assets/ModelImporter.h

@@ -11,12 +11,22 @@ class ModelImporter : public AssetImporter
     OBJECT(ModelImporter);
 
 public:
+
     /// Construct.
     ModelImporter(Context* context);
     virtual ~ModelImporter();
 
     virtual void SetDefaults();
 
+    float GetScale() { return scale_; }
+    void SetScale(float scale) {scale_ = scale; }
+
+    float GetStartTime() { return startTime_; }
+    void SetStartTime(float startTime) { startTime_ = startTime; }
+
+    float GetEndTime() { return endTime_; }
+    void SetEndTime(float endTime) { endTime_ = endTime; }
+
     bool Import(const String& guid);
 
 protected:
@@ -24,6 +34,11 @@ protected:
     virtual bool LoadSettingsInternal();
     virtual bool SaveSettingsInternal();
 
+    float scale_;
+    float startTime_;
+    float endTime_;
+    bool importAnimations_;
+
 };
 
 }

+ 180 - 38
Source/ToolCore/Import/OpenAssetImporter.cpp

@@ -35,6 +35,7 @@
 
 #include <Atomic/Atomic3D/AnimatedModel.h>
 #include <Atomic/Atomic3D/Animation.h>
+#include <Atomic/Atomic3D/AnimationController.h>
 
 #include <Atomic/Graphics/Geometry.h>
 #include <Atomic/Graphics/IndexBuffer.h>
@@ -67,8 +68,11 @@ OpenAssetImporter::OpenAssetImporter(Context* context) : Object(context) ,
     noOverwriteTexture_(true),
     noOverwriteNewerTexture_(true),
     checkUniqueModel_(true),
+    scale_(1.0f),
     maxBones_(64),
-    defaultTicksPerSecond_(4800.0f)
+    defaultTicksPerSecond_(4800.0f),
+    startTime_(-1),
+    endTime_(-1)
 {
 
     aiFlagsDefault_ =
@@ -118,12 +122,84 @@ bool OpenAssetImporter::Load(const String &assetPath)
 
     rootNode_ = scene_->mRootNode;
 
+    ApplyScale();
+
     // DumpNodes(rootNode_, 0);
 
     return true;
 
 }
 
+void OpenAssetImporter::ApplyScale(aiNode* node)
+{
+    if (!node)
+        return;
+
+    aiVector3D pos, scale;
+    aiQuaternion rot;
+    node->mTransformation.Decompose(scale, rot, pos);
+
+    pos *= scale_;
+
+    node->mTransformation = aiMatrix4x4(scale, rot, pos);
+
+    for (unsigned i = 0; i < node->mNumChildren; i++)
+    {
+        ApplyScale(node->mChildren[i]);
+    }
+
+}
+
+void OpenAssetImporter::ApplyScale()
+{
+    if (scale_ == 1.0f)
+        return;
+
+    ApplyScale(scene_->mRootNode);
+
+    for (unsigned i = 0; i < scene_->mNumMeshes; i++)
+    {
+        aiMesh* mesh = scene_->mMeshes[i];
+
+        for (unsigned j = 0; j < mesh->mNumVertices; j++)
+        {
+            mesh->mVertices[j] *= scale_;
+        }
+
+        for (unsigned j = 0; j < mesh->mNumBones; ++j)
+        {
+            aiBone* bone = mesh->mBones[j];
+
+            aiVector3D pos, scale;
+            aiQuaternion rot;
+            bone->mOffsetMatrix.Decompose(scale, rot, pos);
+
+            pos *= scale_;
+
+            bone->mOffsetMatrix = aiMatrix4x4(scale, rot, pos);
+
+        }
+    }
+
+    for (unsigned i = 0; i < scene_->mNumAnimations; i++)
+    {
+        aiAnimation* animation = scene_->mAnimations[i];
+
+        for (unsigned j = 0; j < animation->mNumChannels; j++)
+        {
+            aiNodeAnim* channel = animation->mChannels[j];
+
+            for (unsigned k = 0; k < channel->mNumPositionKeys; k++)
+            {
+                channel->mPositionKeys[k].mValue *= scale_;
+            }
+
+        }
+
+    }
+
+}
+
 void OpenAssetImporter::ExportModel(const String& outName, bool animationOnly)
 {
     if (outName.Empty())
@@ -136,18 +212,21 @@ void OpenAssetImporter::ExportModel(const String& outName, bool animationOnly)
     CollectMeshes(scene_, model, model.rootNode_);
     CollectBones(model, animationOnly);
     BuildBoneCollisionInfo(model);
-    BuildAndSaveModel(model);
+
+    if (!animationOnly)
+        BuildAndSaveModel(model);
+
     if (!noAnimations_)
     {
         CollectAnimations(&model);
         BuildAndSaveAnimations(&model);
 
         // Save scene-global animations
-        CollectAnimations();
-        BuildAndSaveAnimations();
+        // CollectAnimations();
+        // BuildAndSaveAnimations();
     }
 
-    if (!noMaterials_)
+    if (!noMaterials_ && !animationOnly)
     {
         HashSet<String> usedTextures;
         ExportMaterials(usedTextures);
@@ -175,8 +254,19 @@ void OpenAssetImporter::ExportModel(const String& outName, bool animationOnly)
 
     SharedPtr<Node> node(new Node(context_));
     node->SetName("Model");
+
+    StaticModel* modelComponent = 0;
+    /*
     StaticModel* staticModel = node->CreateComponent<StaticModel>();
     staticModel->SetModel(mdl);
+    */
+
+    AnimatedModel* animatedModel = node->CreateComponent<AnimatedModel>();
+    modelComponent = animatedModel;
+    animatedModel->SetModel(mdl, false);
+
+    // create animation controller
+    AnimationController* controller = node->CreateComponent<AnimationController>();
 
     if (!noMaterials_)
     {
@@ -187,7 +277,7 @@ void OpenAssetImporter::ExportModel(const String& outName, bool animationOnly)
 
             String materialName = sourceAssetPath_ + matName;
 
-            staticModel->SetMaterial(j, cache->GetResource<Material>(materialName));
+            modelComponent->SetMaterial(j, cache->GetResource<Material>(materialName));
         }
 
     }
@@ -562,34 +652,70 @@ void OpenAssetImporter::CollectBones(OutModel& model, bool animationOnly)
     HashSet<aiNode*> necessary;
     HashSet<aiNode*> rootNodes;
 
-    for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+    if (animationOnly && !model.meshes_.Size())
     {
-        aiMesh* mesh = model.meshes_[i];
-        aiNode* meshNode = model.meshNodes_[i];
-        aiNode* meshParentNode = meshNode->mParent;
         aiNode* rootNode = 0;
 
-        for (unsigned j = 0; j < mesh->mNumBones; ++j)
+        for (unsigned i = 0; i < scene_->mNumAnimations; ++i)
         {
-            aiBone* bone = mesh->mBones[j];
-            String boneName(FromAIString(bone->mName));
-            aiNode* boneNode = GetNode(boneName, scene_->mRootNode, true);
-            if (!boneNode)
-                ErrorExit("Could not find scene node for bone " + boneName);
-            necessary.Insert(boneNode);
-            rootNode = boneNode;
-
-            for (;;)
+            aiAnimation* anim = scene_->mAnimations[i];
+
+            for (unsigned j = 0; j < anim->mNumChannels; ++j)
             {
-                boneNode = boneNode->mParent;
-                if (!boneNode || ((boneNode == meshNode || boneNode == meshParentNode) && !animationOnly))
-                    break;
-                rootNode = boneNode;
+                aiNodeAnim* channel = anim->mChannels[j];
+                String channelName = FromAIString(channel->mNodeName);
+
+                aiNode* boneNode = GetNode(channelName, scene_->mRootNode, true);
+
                 necessary.Insert(boneNode);
+                rootNode = boneNode;
+
+                for (;;)
+                {
+                    boneNode = boneNode->mParent;
+                    if (!boneNode)// || ((boneNode == meshNode || boneNode == meshParentNode) && !animationOnly))
+                        break;
+                    rootNode = boneNode;
+                    necessary.Insert(boneNode);
+                }
+
+                if (rootNodes.Find(rootNode) == rootNodes.End())
+                    rootNodes.Insert(rootNode);
+
             }
+        }
+    }
+    else
+    {
+        for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+        {
+            aiMesh* mesh = model.meshes_[i];
+            aiNode* meshNode = model.meshNodes_[i];
+            aiNode* meshParentNode = meshNode->mParent;
+            aiNode* rootNode = 0;
+
+            for (unsigned j = 0; j < mesh->mNumBones; ++j)
+            {
+                aiBone* bone = mesh->mBones[j];
+                String boneName(FromAIString(bone->mName));
+                aiNode* boneNode = GetNode(boneName, scene_->mRootNode, true);
+                if (!boneNode)
+                    ErrorExit("Could not find scene node for bone " + boneName);
+                necessary.Insert(boneNode);
+                rootNode = boneNode;
+
+                for (;;)
+                {
+                    boneNode = boneNode->mParent;
+                    if (!boneNode || ((boneNode == meshNode || boneNode == meshParentNode) && !animationOnly))
+                        break;
+                    rootNode = boneNode;
+                    necessary.Insert(boneNode);
+                }
 
-            if (rootNodes.Find(rootNode) == rootNodes.End())
-                rootNodes.Insert(rootNode);
+                if (rootNodes.Find(rootNode) == rootNodes.End())
+                    rootNodes.Insert(rootNode);
+            }
         }
     }
 
@@ -761,20 +887,31 @@ void OpenAssetImporter::BuildAndSaveAnimations(OutModel* model)
             ticksPerSecond = defaultTicksPerSecond_;
         float tickConversion = 1.0f / ticksPerSecond;
 
-        // Find out the start time of animation from each channel's first keyframe for adjusting the keyframe times
-        // to start from zero
-        float startTime = duration;
-        for (unsigned j = 0; j < anim->mNumChannels; ++j)
+        float startTime;
+
+        if (startTime_ >= 0.0 && endTime_ >= 0.0)
         {
-            aiNodeAnim* channel = anim->mChannels[j];
-            if (channel->mNumPositionKeys > 0)
-                startTime = Min(startTime, (float)channel->mPositionKeys[0].mTime);
-            if (channel->mNumRotationKeys > 0)
-                startTime = Min(startTime, (float)channel->mRotationKeys[0].mTime);
-            if (channel->mScalingKeys > 0)
-                startTime = Min(startTime, (float)channel->mScalingKeys[0].mTime);
+            startTime = startTime_;
+            duration = endTime_ - startTime_;
+        }
+        else
+        {
+            // Find out the start time of animation from each channel's first keyframe for adjusting the keyframe times
+            // to start from zero
+            startTime = duration;
+            for (unsigned j = 0; j < anim->mNumChannels; ++j)
+            {
+                aiNodeAnim* channel = anim->mChannels[j];
+                if (channel->mNumPositionKeys > 0)
+                    startTime = Min(startTime, (float)channel->mPositionKeys[0].mTime);
+                if (channel->mNumRotationKeys > 0)
+                    startTime = Min(startTime, (float)channel->mRotationKeys[0].mTime);
+                if (channel->mScalingKeys > 0)
+                    startTime = Min(startTime, (float)channel->mScalingKeys[0].mTime);
+            }
+
+            duration -= startTime;
         }
-        duration -= startTime;
 
         SharedPtr<Animation> outAnim(new Animation(context_));
         outAnim->SetAnimationName(animName);
@@ -939,6 +1076,11 @@ void OpenAssetImporter::BuildAndSaveAnimations(OutModel* model)
         if (!outFile.Open(animOutName, FILE_WRITE))
             ErrorExit("Could not open output file " + animOutName);
         outAnim->Save(outFile);
+
+        AnimationInfo info;
+        info.name_ = SanitateAssetName(animName);
+        info.cacheFilename_ = animOutName;
+        animationInfos_.Push(info);
     }
 }
 

+ 23 - 1
Source/ToolCore/Import/OpenAssetImporter.h

@@ -41,6 +41,12 @@ class OpenAssetImporter : public Object
 
 public:
 
+    struct AnimationInfo
+    {
+        String name_;
+        String cacheFilename_;
+    };
+
     OpenAssetImporter(Context* context);
     virtual ~OpenAssetImporter();
 
@@ -48,10 +54,20 @@ public:
 
     void ExportModel(const String& outName, bool animationOnly = false);
 
+    void SetStartTime(float startTime) { startTime_ = startTime; }
+    void SetEndTime(float endTime) { endTime_ = endTime; }
+    void SetScale(float scale) { scale_ = scale; }
+    void SetExportAnimations(bool exportAnimations) { noAnimations_ = !exportAnimations; }
+
     void SetVerboseLog(bool verboseLog) { verboseLog_ = verboseLog; }
 
+    const Vector<AnimationInfo>& GetAnimationInfos() { return animationInfos_; }
+
 private:
 
+    void ApplyScale();
+    void ApplyScale(aiNode* node);
+
     void BuildAndSaveModel(OutModel& model);
     void BuildAndSaveAnimations(OutModel* model = 0);
 
@@ -67,7 +83,7 @@ private:
     String GetMeshMaterialName(aiMesh* mesh);
     String GenerateMaterialName(aiMaterial* material);
     String GetMaterialTextureName(const String& nameIn);
-    String GenerateTextureName(unsigned texIndex);
+    String GenerateTextureName(unsigned texIndex);    
 
     // TODO: See AssetImporter
     // void CombineLods(const PODVector<float>& lodDistances, const Vector<String>& modelNames, const String& outName)
@@ -99,6 +115,7 @@ private:
     bool noOverwriteTexture_;
     bool noOverwriteNewerTexture_;
     bool checkUniqueModel_;
+    float scale_;
     unsigned maxBones_;
 
     unsigned aiFlagsDefault_;
@@ -110,8 +127,13 @@ private:
     HashSet<aiAnimation*> allAnimations_;
     PODVector<aiAnimation*> sceneAnimations_;
 
+    Vector<AnimationInfo> animationInfos_;
+
     float defaultTicksPerSecond_;
 
+    float startTime_;
+    float endTime_;
+
 };
 
 }

+ 8 - 0
Source/ToolCore/Import/OpenAssetUtils.cpp

@@ -30,6 +30,14 @@ namespace ToolCore
 
 void CollectMeshes(const aiScene* scene, OutModel& model, aiNode* node)
 {
+
+    // skip LOD for now
+    String name = node->mName.C_Str();
+    if (name.Find("LOD1") != String::NPOS || name.Find("LOD2") != String::NPOS)
+    {
+        return;
+    }
+
     for (unsigned i = 0; i < node->mNumMeshes; ++i)
     {
         aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];