Browse Source

Added animation preview tool bar

Johnny 9 years ago
parent
commit
73c8441c96

BIN
Resources/EditorData/AtomicEditor/editor/skin/blendleft.png


+ 3 - 0
Resources/EditorData/AtomicEditor/editor/skin/skin.tb.txt

@@ -109,6 +109,9 @@ elements
 	StepButton
 		bitmap step.png
 
+	BlendleftButton
+		bitmap blendleft.png
+
 	PowerOffButton
 		bitmap power_off.png
 

+ 38 - 0
Resources/EditorData/AtomicEditor/editor/ui/animationtoolbar.tb.txt

@@ -0,0 +1,38 @@
+TBLayout: axis: y, distribution: gravity, spacing: 10
+	lb: min_width: 750
+	TBLayout:
+		TBLayout:
+			TBButton
+				@include definitions>menubutton
+				TBSkinImage: skin: PlayButton, id: skin_image
+				id play_left
+				tooltip Play Animation
+				lp: width: 30, height: 30
+			TBContainer: skin: AEContainer, gravity: all, id: leftanimcontainer
+			TBButton
+				@include definitions>menubutton
+				TBSkinImage: skin: StepButton, id: skin_image
+				id blend_right
+				tooltip Blend Animation to the Right
+				lp: width: 30, height: 30
+			TBButton
+				@include definitions>menubutton
+				TBSkinImage: skin: StopButton, id: skin_image
+				id stop
+				tooltip Stop Playing all animations
+				lp: width: 34, height: 30
+			TBButton
+				@include definitions>menubutton
+				TBSkinImage: skin: BlendleftButton, id: skin_image
+				id blend_left
+				tooltip Blend Animation to the Left
+				lp: width: 30, height: 30
+			TBContainer: skin: AEContainer, gravity: all, id: rightanimcontainer
+			TBButton
+				@include definitions>menubutton
+				TBSkinImage: skin: PlayButton, id: skin_image
+				id play_right
+				tooltip Play Animation
+				lp: width: 30, height: 30
+	TBLayout: distribution: gravity, axis: x, spacing: 4
+			TBContainer: skin: AEContainer, gravity: all, id: blendcontainer

+ 2 - 0
Resources/EditorData/AtomicEditor/editor/ui/mainframe.tb.txt

@@ -47,6 +47,8 @@ TBLayout: distribution: gravity, axis: y
                         TBWidget: gravity: top bottom
                 TBLayout: distribution: gravity, axis: y
                     TBLayout: gravity: left right, id: maintoolbarcontainer
+                    TBLayout: gravity: left right, id: animationtoolbarcontainer
+                    TBLayout: gravity: left right, id: animationpropertiescontainer
                     TBSeparator: gravity: left right, skin: AESeparator
                     TBLayout: distribution: gravity, id: resourceviewcontainer
                     TBContainer: skin: AEContainer, gravity: left right bottom, id: consolecontainer

+ 58 - 0
Resources/EditorData/AtomicEditor/templates/animation_viewer.scene

@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<scene id="1">
+	<attribute name="Name" value="" />
+	<attribute name="Time Scale" value="1" />
+	<attribute name="Smoothing Constant" value="50" />
+	<attribute name="Snap Threshold" value="5" />
+	<attribute name="Elapsed Time" value="0" />
+	<attribute name="Next Replicated Node ID" value="362" />
+	<attribute name="Next Replicated Component ID" value="1974" />
+	<attribute name="Next Local Node ID" value="16778496" />
+	<attribute name="Next Local Component ID" value="16777216" />
+	<attribute name="Variables" />
+	<attribute name="Variable Names" value="" />
+	<component type="PhysicsWorld" id="1" />
+	<component type="Octree" id="2" />
+	<component type="DebugRenderer" id="3" />
+	<node id="2">
+		<attribute name="Is Enabled" value="true" />
+		<attribute name="Name" value="Zone" />
+		<attribute name="Position" value="0 0 0" />
+		<attribute name="Rotation" value="1 0 0 0" />
+		<attribute name="Scale" value="1 1 1" />
+		<attribute name="Variables" />
+		<component type="Zone" id="4">
+			<attribute name="Bounding Box Min" value="-10000 -10000 -10000" />
+			<attribute name="Bounding Box Max" value="10000 10000 10000" />
+			<attribute name="Ambient Color" value="0.4 0.4 0.4 1" />
+		</component>
+	</node>
+	<node id="3">
+		<attribute name="Is Enabled" value="true" />
+		<attribute name="Name" value="GlobalLight" />
+		<attribute name="Position" value="0 0 0" />
+		<attribute name="Rotation" value="0.888074 0.325058 -0.325058 0" />
+		<attribute name="Scale" value="1 1 1" />
+		<attribute name="Variables" />
+		<component type="Light" id="5">
+			<attribute name="Light Type" value="Directional" />
+			<attribute name="Cast Shadows" value="true" />
+			<attribute name="CSM Splits" value="10 20 50 0" />
+			<attribute name="View Size Quantize" value="1" />
+			<attribute name="View Size Minimum" value="5" />
+			<attribute name="Depth Constant Bias" value="0.00025" />
+			<attribute name="Depth Slope Bias" value="0.001" />
+		</component>
+	</node>
+	<node id="361">
+		<attribute name="Is Enabled" value="true" />
+		<attribute name="Name" value="Camera" />
+		<attribute name="Position" value="0 0 -5" />
+		<attribute name="Rotation" value="1 0 0 0" />
+		<attribute name="Scale" value="1 1 1" />
+		<attribute name="Variables" />
+		<component type="Camera" id="1973">
+			<attribute name="FOV" value="45" />
+		</component>
+	</node>
+</scene>

+ 37 - 0
Script/AtomicEditor/resources/ResourceOps.ts

@@ -295,3 +295,40 @@ export function CreateNewMaterial(resourcePath: string, materialName: string, re
     return true;
 
 }
+
+//TODO - Replace this by creating a temporary scene that cannot be saved
+export function CreateNewAnimationPreviewScene(reportError: boolean = true): boolean {
+
+    var title = "Animation Viewer Error";
+
+    var templateFilename = "AtomicEditor/templates/template_scene.scene";
+    var templateFile = Atomic.cache.getFile(templateFilename);
+
+
+    if (!templateFile) {
+
+        if (reportError)
+            resourceOps.sendEvent(EditorEvents.ModalError, { title: title, message: "Failed to open template scene: " + templateFile });
+        return false;
+
+    }
+
+    var animFilename = "AtomicEditor/templates/animation_viewer.scene";
+    var animFile = Atomic.cache.getFile(animFilename);
+
+    if (!animFile) {
+
+        if (reportError)
+            resourceOps.sendEvent(EditorEvents.ModalError, { title: title, message: "Failed to open animation viewer: " + animFilename });
+        return false;
+
+    }
+
+    //Reset the animation viewer scene to a blank scene
+    animFile = templateFile;
+
+    resourceOps.sendEvent(EditorEvents.EditResource, { path: animFilename });
+
+    return true;
+
+}

+ 226 - 0
Script/AtomicEditor/ui/AnimationToolbar.ts

@@ -0,0 +1,226 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES 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.
+//
+
+import EditorEvents = require("editor/EditorEvents");
+import EditorUI = require("ui/EditorUI");
+import HierarchyFrame = require("ui/frames/HierarchyFrame");
+import InspectorUtils = require("ui/frames/inspector/InspectorUtils");
+import ResourceOps = require("resources/ResourceOps");
+import ModalOps = require("ui/modal/ModalOps");
+
+class AnimationToolbar extends Atomic.UIWidget {
+
+    constructor(parent: Atomic.UIWidget, properties: Atomic.UIWidget, asset: ToolCore.Asset) {
+
+        super();
+
+        this.load("AtomicEditor/editor/ui/animationtoolbar.tb.txt");
+        this.asset = asset;
+
+        this.leftAnimContainer = <Atomic.UILayout>this.getWidget("leftanimcontainer");
+        this.rightAnimContainer = <Atomic.UILayout>this.getWidget("rightanimcontainer");
+
+        this.subscribeToEvent(this, "WidgetEvent", (ev) => this.handleWidgetEvent(ev));
+        this.subscribeToEvent(EditorEvents.ActiveSceneEditorChange, (data) => this.handleActiveSceneEditorChanged(data));
+        this.subscribeToEvent(EditorEvents.SceneClosed, (data) => this.handleSceneClosed(data));
+
+        var leftAnimationField = InspectorUtils.createAttrEditFieldWithSelectButton("Animation A", this.leftAnimContainer);
+        leftAnimationField.selectButton.onClick = function () { this.openAnimationSelectionBox(leftAnimationField.editField, this.leftAnim); }.bind(this);
+
+        var rightAnimationField = InspectorUtils.createAttrEditFieldWithSelectButton("Animation B", this.rightAnimContainer);
+        rightAnimationField.selectButton.onClick = function () { this.openAnimationSelectionBox(rightAnimationField.editField, this.rightAnim); }.bind(this);
+
+        this.leftAnimEditfield = leftAnimationField.editField;
+        this.rightAnimEditfield = rightAnimationField.editField;
+
+        var leftStateContainer = <Atomic.UILayout>this.getWidget("leftstatedropdown");
+        var rightStateContainer = <Atomic.UILayout>this.getWidget("rightstatedropdown");
+
+        ResourceOps.CreateNewAnimationPreviewScene();
+        this.populateScene();
+
+        parent.addChild(this);
+
+        //Animation properties bar
+        this.animationPropertiesContainer = new Atomic.UILayout();
+
+        this.animationSpeed = InspectorUtils.createAttrEditField("Playback Speed:", this.animationPropertiesContainer);
+        this.animationSpeed.setAdaptToContentSize(true);
+
+        this.blendSpeed = InspectorUtils.createAttrEditField("Blend Speed:", this.animationPropertiesContainer);
+        this.blendSpeed.setAdaptToContentSize(true);
+
+        //Set default values
+        this.animationSpeed.setText("1");
+        this.blendSpeed.setText("0");
+
+        properties.addChild(this.animationPropertiesContainer);
+
+        this.modalOps = new ModalOps();
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE_CLICK) {
+            if (this.animationController != null) {
+
+                if (ev.target.id == "play_left") {
+                    if (this.animationController.playExclusive(this.leftAnimEditfield.text, 0, true))
+                        this.animationController.setSpeed(this.leftAnimEditfield.text, Number(this.animationSpeed.text));
+                    else
+                        this.showAnimationWarning();
+
+                    return true;
+                }
+                if (ev.target.id == "play_right") {
+                    if (this.animationController.playExclusive(this.rightAnimEditfield.text, 0, true))
+                        this.animationController.setSpeed(this.rightAnimEditfield.text, Number(this.animationSpeed.text));
+                    else
+                        this.showAnimationWarning();
+
+                    return true;
+                }
+                if (ev.target.id == "blend_left") {
+                    if (this.animationController.playExclusive(this.leftAnimEditfield.text, 0, true, Number(this.blendSpeed.text)))
+                        this.animationController.setSpeed(this.leftAnimEditfield.text, Number(this.animationSpeed.text));
+                    else
+                        this.showAnimationWarning();
+
+                    return true;
+                }
+                if (ev.target.id == "blend_right") {
+                    if (this.animationController.playExclusive(this.rightAnimEditfield.text, 0, true, Number(this.blendSpeed.text)))
+                        this.animationController.setSpeed(this.rightAnimEditfield.text, Number(this.animationSpeed.text));
+                    else
+                        this.modalOps.showError("Animation Toolbar Warning", "The animation cannot be played. Please make sure the animation you are trying to play exists in the AnimationController Component.");
+
+                    return true;
+                }
+                if (ev.target.id == "stop") {
+                    this.animationController.stopAll();
+                    return true;
+                }
+            }
+        }
+        return true;
+    }
+
+    handleSceneClosed(ev: EditorEvents.SceneClosedEvent) {
+        if (ev.scene == this.scene) {
+            Atomic.fileSystem.delete(this.sceneAssetPath);
+
+            if (this.animationPropertiesContainer)
+                this.animationPropertiesContainer.remove();
+
+            this.remove();
+        }
+    }
+
+    closeViewer() {
+        Atomic.fileSystem.delete(this.sceneAssetPath);
+            this.sceneEditor.close();
+            if (this.animationPropertiesContainer)
+                this.animationPropertiesContainer.remove();
+            this.remove();
+    }
+
+    handleActiveSceneEditorChanged(event: EditorEvents.ActiveSceneEditorChangeEvent) {
+
+        if (!event.sceneEditor)
+            return;
+
+        this.sceneEditor = event.sceneEditor;
+        this.scene = event.sceneEditor.scene;
+
+        if (this.scene) {
+            this.unsubscribeFromEvents(this.scene);
+            return;
+        }
+
+        if (!event.sceneEditor)
+            return;
+    }
+
+    populateScene() {
+
+        this.scene.setUpdateEnabled(true);
+
+        var modelNode = this.asset.instantiateNode(this.scene, this.asset.name);
+        this.modelNode = modelNode;
+
+        this.sceneEditor.selection.addNode(modelNode, true);
+        this.sceneEditor.sceneView3D.frameSelection();
+
+        this.animatedModel = <Atomic.AnimatedModel>modelNode.getComponent("AnimatedModel");
+        this.animationController = <Atomic.AnimationController>modelNode.getComponent("AnimationController");
+        var model = this.animatedModel.model;
+        this.animatedModel.setBoneCreationOverride(true);
+        this.animatedModel.setModel(model, true);
+
+        var animComp = new Atomic.AnimatedModel();
+        var animContComp = new Atomic.AnimationController();
+    }
+
+    openAnimationSelectionBox(animationWidget: Atomic.UIEditField, animationSlot: Atomic.Animation) {
+
+        EditorUI.getModelOps().showResourceSelection("Select Animation", "ModelImporter", "Animation", function (resource: Atomic.Animation, args: any) {
+            var animation = resource;
+            if (animation) {
+                animationSlot = animation;
+                animationWidget.text = animation.getAnimationName();
+            }
+        });
+    }
+    showAnimationWarning() {
+        this.modalOps.showError("Animation Preview Warning", "The animation cannot be played. Please make sure the animation you are trying to play exists in the AnimationController Component.");
+    }
+
+    modalOps: ModalOps;
+    //Animation Toolbar Widgets
+    animationController: Atomic.AnimationController;
+    animatedModel: Atomic.AnimatedModel;
+    scene: Atomic.Scene = null;
+    sceneEditor: Editor.SceneEditor3D;
+    modelNode: Atomic.Node;
+
+    leftAnimContainer: Atomic.UILayout;
+    rightAnimContainer: Atomic.UILayout;
+    blendFileContainer: Atomic.UILayout;
+    leftAnimEditfield: Atomic.UIEditField;
+    rightAnimEditfield: Atomic.UIEditField;
+
+    leftAnim: Atomic.Animation;
+    rightAnim: Atomic.Animation;
+
+    asset: ToolCore.Asset;
+    sceneAssetPath: string;
+    stateDropDownList: string[];
+    //Animation Properties Widgets
+    animationPropertiesContainer: Atomic.UILayout;
+    animationSpeed: Atomic.UIEditField;
+    blendSpeed: Atomic.UIEditField;
+}
+
+export = AnimationToolbar;
+
+

+ 12 - 0
Script/AtomicEditor/ui/frames/MainFrame.ts

@@ -26,6 +26,7 @@ import WelcomeFrame = require("./WelcomeFrame");
 import InspectorFrame = require("./inspector/InspectorFrame");
 import HierarchyFrame = require("./HierarchyFrame");
 import MainToolbar = require("ui//MainToolbar");
+import AnimationToolbar = require("ui//AnimationToolbar");
 
 import UIEvents = require("ui/UIEvents");
 
@@ -174,6 +175,16 @@ class MainFrame extends ScriptWidget {
 
     }
 
+    showAnimationToolbar(asset: ToolCore.Asset) {
+        if (this.animationToolbar != null) {
+            this.animationToolbar.closeViewer();
+            this.animationToolbar = new AnimationToolbar(this.getWidget("animationtoolbarcontainer"), this.getWidget("animationpropertiescontainer"), asset);
+        }
+        else {
+            this.animationToolbar = new AnimationToolbar(this.getWidget("animationtoolbarcontainer"), this.getWidget("animationpropertiescontainer"), asset)
+        }
+    }
+
     projectframe: ProjectFrame;
     resourceframe: ResourceFrame;
     inspectorframe: InspectorFrame;
@@ -181,6 +192,7 @@ class MainFrame extends ScriptWidget {
     welcomeFrame: WelcomeFrame;
     inspectorlayout: Atomic.UILayout;
     mainToolbar: MainToolbar;
+    animationToolbar: AnimationToolbar;
     menu: MainFrameMenu;
 
 }

+ 22 - 0
Script/AtomicEditor/ui/frames/inspector/InspectorWidget.ts

@@ -21,6 +21,8 @@
 //
 
 import ScriptWidget = require("ui/ScriptWidget");
+import EditorUI = require("ui/EditorUI"); 
+
 
 class InspectorWidget extends ScriptWidget {
 
@@ -154,6 +156,26 @@ class InspectorWidget extends ScriptWidget {
 
     }
 
+    createPreviewAnimationButton(asset: ToolCore.Asset): Atomic.UIButton {
+
+        var button = new Atomic.UIButton();
+        button.fontDescription = this.attrFontDesc;
+        button.gravity = Atomic.UI_GRAVITY_RIGHT;
+        button.text = "Preview Animation";
+
+        button.onClick = function () {
+            this.onPreviewAnimation(asset);
+        }.bind(this);
+
+        return button;
+    }
+
+    onPreviewAnimation(asset: ToolCore.Asset) {
+
+        var mainFrame = EditorUI.getMainFrame();
+        mainFrame.showAnimationToolbar(asset);
+    }
+
     rootLayout:Atomic.UILayout;
     attrFontDesc:Atomic.UIFontDescription;
 }

+ 2 - 1
Script/AtomicEditor/ui/frames/inspector/ModelInspector.ts

@@ -134,7 +134,8 @@ class ModelInspector extends InspectorWidget {
         animationLayout.addChild(animLayout);
 
         this.createAnimationEntries();
-
+        // Animation preview button
+        rootLayout.addChild(this.createPreviewAnimationButton(this.asset));
         // apply button
         rootLayout.addChild(this.createApplyButton());
 

+ 1 - 0
Script/tsconfig.json

@@ -71,6 +71,7 @@
         "./AtomicEditor/ui/frames/ResourceFrame.ts",
         "./AtomicEditor/ui/frames/WelcomeFrame.ts",
         "./AtomicEditor/ui/MainToolbar.ts",
+        "./AtomicEditor/ui/AnimationToolbar.ts",
         "./AtomicEditor/ui/modal/About.ts",
         "./AtomicEditor/ui/modal/build/BuildComplete.ts",
         "./AtomicEditor/ui/modal/build/BuildOutput.ts",

+ 3 - 2
Source/Atomic/Graphics/AnimatedModel.cpp

@@ -72,7 +72,8 @@ AnimatedModel::AnimatedModel(Context* context) :
     isMaster_(true),
     loading_(false),
     assignBonesPending_(false),
-    forceAnimationUpdate_(false)
+    forceAnimationUpdate_(false),
+    boneCreationOverride_(true)
 {
 }
 
@@ -819,7 +820,7 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
 
 // ATOMIC BEGIN
         // Create scene nodes for the bones
-        if (createBones && !dontCreateBonesHack)
+        if ((createBones && !dontCreateBonesHack)|| boneCreationOverride_)
         {
 // ATOMIC END
 

+ 4 - 0
Source/Atomic/Graphics/AnimatedModel.h

@@ -94,6 +94,8 @@ public:
     void SetMorphWeight(const String& name, float weight);
     /// Set vertex morph weight by name hash.
     void SetMorphWeight(StringHash nameHash, float weight);
+    /// Set bone creation override. Useful for previewing animations in the editor scene view.
+    void SetBoneCreationOverride(bool enabled) { boneCreationOverride_ = enabled; }
     /// Reset all vertex morphs to zero.
     void ResetMorphWeights();
 
@@ -259,6 +261,8 @@ private:
     bool assignBonesPending_;
     /// Force animation update after becoming visible flag.
     bool forceAnimationUpdate_;
+    /// Override global bone creation flag, locally.
+    bool boneCreationOverride_;
 };
 
 }

+ 2 - 1
Source/ThirdParty/TurboBadger/tb_widgets.cpp

@@ -359,7 +359,8 @@ void TBWidget::AddChildRelative(TBWidget *child, WIDGET_Z_REL z, TBWidget *refer
 
 void TBWidget::RemoveChild(TBWidget *child, WIDGET_INVOKE_INFO info)
 {
-    assert(child->m_parent);
+    if (!child->m_parent)
+        return;
 
     if (info == WIDGET_INVOKE_INFO_NORMAL)
     {