瀏覽代碼

Merge pull request #8311 from toledoal/edit-animation-2

Animation Tree List
David Catuhe 5 年之前
父節點
當前提交
d23202c54b

+ 1 - 1
dist/preview release/what's new.md

@@ -8,7 +8,7 @@
 - Added HDR texture filtering tools to the sandbox ([Sebavan](https://github.com/sebavan/))
 - Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added editing of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
-- Added Curve editor to manage entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
+- Added Curve editor to manage selected entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
 - Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 - Added support for **thin instances** for faster mesh instances. [Doc](https://doc.babylonjs.com/how_to/how_to_use_thininstances) ([Popov72](https://github.com/Popov72))
 

+ 2 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/addAnimation.tsx

@@ -190,6 +190,8 @@ export class AddAnimation extends React.Component<IAddAnimationProps, {animation
                         this.props.entity.animations = updatedCollection;
                         this.props.changed();
                         this.props.close();
+                        //Cleaning form fields
+                        this.setState({ animationName: "", animationTargetPath: "", animationType: "Float", loopMode: Animation.ANIMATIONLOOPMODE_CYCLE, animationTargetProperty: ""});
                     }   
                 }
             } else {

+ 14 - 11
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -847,22 +847,25 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     * Core functions
     * This section handles main Curve Editor Functions.
     */
-    selectAnimation(animation: Animation) {
+    selectAnimation(animation: Animation, axis?: string) {
 
-        this.playStopAnimation();
+        if (!axis){
+            this.playStopAnimation();
 
-        this._svgKeyframes = [];
+            this._svgKeyframes = [];
 
-        const pathData = this.getPathData(animation);
+            const pathData = this.getPathData(animation);
 
-        let lastFrame = animation.getHighestFrame();
+            let lastFrame = animation.getHighestFrame();
 
-        if (pathData === "") {
-            console.log("no keyframes in this animation");
-        }
-
-        this.setState({ selected: animation, currentPathData: pathData, svgKeyframes: this._svgKeyframes, lastFrame: lastFrame });
+            if (pathData === "") {
+                console.log("no keyframes in this animation");
+            }
 
+            this.setState({ selected: animation, currentPathData: pathData, svgKeyframes: this._svgKeyframes, lastFrame: lastFrame });
+        } else {
+            console.log(axis); // This will handle animations that are not Float type
+        }
     }
 
     isAnimationPlaying() {
@@ -996,7 +999,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                     
                 <div className="content">
                     <div className="row">
-                        <EditorControls selectAnimation={(animation: Animation) => this.selectAnimation(animation)} 
+                        <EditorControls selectAnimation={(animation: Animation, axis?: string) => this.selectAnimation(animation, axis)} 
                         isTargetedAnimation={this._isTargetedAnimation} 
                         entity={this.props.entity} 
                         selected={this.state.selected} 

+ 163 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationListTree.tsx

@@ -0,0 +1,163 @@
+
+import * as React from "react";
+import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
+import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
+import { Observable } from "babylonjs/Misc/observable";
+import { PropertyChangedEvent } from "../../../../../components/propertyChangedEvent";
+import { Animation } from 'babylonjs/Animations/animation';
+import { IconButtonLineComponent } from '../../../lines/iconButtonLineComponent';
+import { Nullable } from 'babylonjs/types';
+
+interface IAnimationListTreeProps {
+    isTargetedAnimation: boolean;
+    entity: IAnimatable | TargetedAnimation;
+    selected: Animation | null
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    selectAnimation: (selected: Animation, axis?: string) => void;
+    empty: () => void;
+}
+
+interface Item {
+    index: number;
+    name: string;
+    property: string;
+    selected: boolean;
+    open: boolean;
+}
+
+export class AnimationListTree extends React.Component<IAnimationListTreeProps, { list:Item[] } >{
+    constructor(props: IAnimationListTreeProps) {
+        super(props);
+        let animationList = (this.props.entity as IAnimatable).animations && (this.props.entity as IAnimatable).animations?.map((animation, i) => {
+            return  ({ index: i, name: animation.name, property: animation.targetProperty, selected: false, open: false } as Item)
+        });
+        this.state = { list: animationList ?? [] }
+    }
+    
+    deleteAnimation() {
+        let currentSelected = this.props.selected;
+        if (this.props.entity instanceof TargetedAnimation) {
+            console.log("no animation remove allowed");
+        } else {
+            let animations = (this.props.entity as IAnimatable).animations;
+            if (animations) {
+                let updatedAnimations = animations.filter(anim => anim !== currentSelected);
+                (this.props.entity as IAnimatable).animations = updatedAnimations as Nullable<Animation[]>;
+                this.generateList();
+            }
+        }
+    }
+
+    generateList() {
+        let animationList = (this.props.entity as IAnimatable).animations && (this.props.entity as IAnimatable).animations?.map((animation, i) => {
+            return  ({ index: i, name: animation.name, property: animation.targetProperty, selected: false, open: false } as Item)
+        });
+        if (animationList?.length === 0){
+            this.props.empty();
+        }
+        this.setState({ list: animationList ?? [] });
+    }
+
+    editAnimation() {
+        console.log('Edit animation');// TODO. Implement the edit options here
+    }
+
+    toggleProperty(index: number) {
+        let item = this.state.list[index];
+        item.open = !item.open;
+    }
+
+    setListItem(animation: Animation, i: number) {
+        let element;
+
+        switch (animation.dataType) {
+            case Animation.ANIMATIONTYPE_FLOAT:
+                element = <li className={this.props.selected && this.props.selected.name === animation.name ? 'property active' : 'property'} key={i} onClick={() => this.props.selectAnimation(animation)}>
+                    <div className={`animation-bullet`}></div>
+                    <p>{animation.targetProperty}</p>
+                    <IconButtonLineComponent tooltip="Options" icon="small animation-options" onClick={() => this.editAnimation()} />
+                    {!(this.props.entity instanceof TargetedAnimation) ? this.props.selected && this.props.selected.name === animation.name ? <IconButtonLineComponent tooltip="Remove" icon="small animation-delete" onClick={() => this.deleteAnimation()} /> : <div className="spacer"></div> : null}
+                </li>
+                break;
+            case Animation.ANIMATIONTYPE_VECTOR2:
+                element = <li className={this.props.selected && this.props.selected.name === animation.name ? 'property active' : 'property'} key={i} onClick={() => this.props.selectAnimation(animation)}>
+                    <p>{animation.targetProperty}</p>
+                    <ul>
+                        <li key={`${i}_x`}>Property <strong>X</strong></li>
+                        <li key={`${i}_y`}>Property <strong>Y</strong></li>
+                    </ul>
+                </li>
+                break;
+            case Animation.ANIMATIONTYPE_VECTOR3:
+                element = <li className={this.props.selected && this.props.selected.name === animation.name ? 'property sub active' : 'property sub'} key={i} onClick={() => this.props.selectAnimation(animation, 'Vector3')}>
+                    <div className={`animation-arrow ${this.state.list[i].open ? '' : 'flip'}`} onClick={() => this.toggleProperty(i)}></div>
+                    <p>{animation.targetProperty}</p>
+                    <IconButtonLineComponent tooltip="Options" icon="small animation-options" onClick={() => this.editAnimation()} />
+                    {!(this.props.entity instanceof TargetedAnimation) ? this.props.selected && this.props.selected.name === animation.name ? <IconButtonLineComponent tooltip="Remove" icon="small animation-delete" onClick={() => this.deleteAnimation()} /> : <div className="spacer"></div> : null}
+                    <ul className={`sub-list ${this.state.list[i].open ? '' : 'hidden'}`}>
+                        <li key={`${i}_x`} className="property" style={{color: '#db3e3e'}} onClick={() => this.props.selectAnimation(animation, 'x')}><div className={`handle-indicator ${''}`}></div>{animation.targetProperty} X</li>
+                        <li key={`${i}_y`} className="property" style={{color: '#51e22d'}} onClick={() => this.props.selectAnimation(animation, 'y')}><div className={`handle-indicator ${''}`}></div>{animation.targetProperty} Y</li>
+                        <li key={`${i}_z`} className="property" style={{color: '#00a3ff'}} onClick={() => this.props.selectAnimation(animation, 'z')}><div className={`handle-indicator ${''}`}></div>{animation.targetProperty} Z</li>
+                    </ul>
+                </li>
+                break;
+            case Animation.ANIMATIONTYPE_QUATERNION:
+                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
+                    <ul>
+                        <li key={`${i}_x`}>Property <strong>X</strong></li>
+                        <li key={`${i}_y`}>Property <strong>Y</strong></li>
+                        <li key={`${i}_z`}>Property <strong>Z</strong></li>
+                        <li key={`${i}_w`}>Property <strong>W</strong></li>
+                    </ul>
+                </li>
+                break;
+            case Animation.ANIMATIONTYPE_COLOR3:
+                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
+                    <ul>
+                        <li key={`${i}_r`}>Property <strong>R</strong></li>
+                        <li key={`${i}_g`}>Property <strong>G</strong></li>
+                        <li key={`${i}_b`}>Property <strong>B</strong></li>
+                    </ul>
+                </li>
+                break;
+            case Animation.ANIMATIONTYPE_COLOR4:
+                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
+                    <ul>
+                        <li key={`${i}_r`}>Property <strong>R</strong></li>
+                        <li key={`${i}_g`}>Property <strong>G</strong></li>
+                        <li key={`${i}_b`}>Property <strong>B</strong></li>
+                        <li key={`${i}_a`}>Property <strong>A</strong></li>
+                    </ul>
+                </li>
+                break;
+            case Animation.ANIMATIONTYPE_SIZE:
+                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
+                    <ul>
+                        <li key={`${i}_width`}>Property <strong>Width</strong></li>
+                        <li key={`${i}_height`}>Property <strong>Height</strong></li>
+                    </ul>
+                </li>
+                break;
+            default: console.log("not recognized");
+                element = null;
+                break;
+        }
+
+        return element;
+    }
+
+    render() {
+        return (
+            <div className="object-tree">
+                    <ul>
+                        {
+                            this.props.isTargetedAnimation ? this.setListItem((this.props.entity as TargetedAnimation).animation, 0) :
+                                (this.props.entity as IAnimatable).animations && (this.props.entity as IAnimatable).animations?.map((animation, i) => {
+                                    return this.setListItem(animation, i);
+                                })}
+
+                    </ul>
+            </div>
+        )
+    }
+} 

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/assets/animationBulletIcon.svg

@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><defs><style>.cls-1{fill:#888;}.cls-2{fill:none;}</style></defs><g id="UI"><circle class="cls-1" cx="5" cy="5" r="2.89"/><rect class="cls-2" width="10" height="10"/></g></svg>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><defs><style>.cls-1{fill:#7a4ece;}.cls-2{fill:none;}</style></defs><g id="UI"><circle class="cls-1" cx="5" cy="5" r="2.89"/><rect class="cls-2" width="10" height="10"/></g></svg>

文件差異過大導致無法顯示
+ 90 - 40
inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss


+ 19 - 102
inspector/src/components/actionTabs/tabs/propertyGrids/animations/editorControls.tsx

@@ -7,9 +7,9 @@ import { Animation } from 'babylonjs/Animations/animation';
 import { IconButtonLineComponent } from '../../../lines/iconButtonLineComponent';
 import { NumericInputComponent } from '../../../lines/numericInputComponent';
 import { AddAnimation } from './addAnimation';
+import { AnimationListTree } from './animationListTree';
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
 import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
-import { Nullable } from 'babylonjs/types';
 
 interface IEditorControlsProps {
    isTargetedAnimation: boolean;
@@ -17,7 +17,7 @@ interface IEditorControlsProps {
    selected: Animation | null
    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
    setNotificationMessage: (message: string) => void;
-   selectAnimation: (selected: Animation) => void;
+   selectAnimation: (selected: Animation, axis?: string) => void;
 }
 
 export class EditorControls extends React.Component<IEditorControlsProps, {isAnimationTabOpen: boolean, isEditTabOpen: boolean, isLoadTabOpen: boolean, isSaveTabOpen: boolean, isLoopActive: boolean, animationsCount: number; framesPerSecond: number}>{ 
@@ -25,98 +25,15 @@ export class EditorControls extends React.Component<IEditorControlsProps, {isAni
     constructor(props: IEditorControlsProps) {
         super(props);
         let count = this.props.isTargetedAnimation ? 1 : (this.props.entity as IAnimatable).animations?.length ?? 0;
-        this.state = { isAnimationTabOpen: false, isEditTabOpen: false, isSaveTabOpen: false, isLoadTabOpen: false, isLoopActive: false, animationsCount: count, framesPerSecond: 60 }
+        this.state = { isAnimationTabOpen: count === 0 ? true : false, isEditTabOpen: count === 0 ? false : true, isSaveTabOpen: false, isLoadTabOpen: false, isLoopActive: false, animationsCount: count, framesPerSecond: 60 }
     }
 
-    animationsChanged(){
-        let recount = (this.props.entity as IAnimatable).animations?.length ?? 0;
-        this.setState( { animationsCount: recount } );
+    animationAdded(){
+        this.setState( { animationsCount: this.recountAnimations(), isEditTabOpen: true, isAnimationTabOpen: false } );
     }
 
-    deleteAnimation() {
-        let currentSelected = this.props.selected;
-        if (this.props.entity instanceof TargetedAnimation) {
-            console.log("no animation remove allowed");
-        } else {
-            let animations = (this.props.entity as IAnimatable).animations;
-            if (animations) {
-                let updatedAnimations = animations.filter(anim => anim !== currentSelected);
-                (this.props.entity as IAnimatable).animations = updatedAnimations as Nullable<Animation[]>;
-            }
-        }
-    }
-
-    setListItem(animation: Animation, i: number) {
-        let element;
-
-        switch (animation.dataType) {
-            case Animation.ANIMATIONTYPE_FLOAT:
-                element = <li className={this.props.selected && this.props.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.props.selectAnimation(animation)}>
-                    <p>{animation.name}&nbsp;
-                    <span>{animation.targetProperty}</span></p>
-                    {!(this.props.entity instanceof TargetedAnimation) ? this.props.selected && this.props.selected.name === animation.name ? <IconButtonLineComponent tooltip="Remove" icon="delete" onClick={() => this.deleteAnimation()} /> : null : null}
-                </li>
-                break;
-            case Animation.ANIMATIONTYPE_VECTOR2:
-                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
-                    <ul>
-                        <li key={`${i}_x`}>Property <strong>X</strong></li>
-                        <li key={`${i}_y`}>Property <strong>Y</strong></li>
-                    </ul>
-                </li>
-                break;
-            case Animation.ANIMATIONTYPE_VECTOR3:
-                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
-                    <ul>
-                        <li key={`${i}_x`}>Property <strong>X</strong></li>
-                        <li key={`${i}_y`}>Property <strong>Y</strong></li>
-                        <li key={`${i}_z`}>Property <strong>Z</strong></li>
-                    </ul>
-                </li>
-                break;
-            case Animation.ANIMATIONTYPE_QUATERNION:
-                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
-                    <ul>
-                        <li key={`${i}_x`}>Property <strong>X</strong></li>
-                        <li key={`${i}_y`}>Property <strong>Y</strong></li>
-                        <li key={`${i}_z`}>Property <strong>Z</strong></li>
-                        <li key={`${i}_w`}>Property <strong>W</strong></li>
-                    </ul>
-                </li>
-                break;
-            case Animation.ANIMATIONTYPE_COLOR3:
-                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
-                    <ul>
-                        <li key={`${i}_r`}>Property <strong>R</strong></li>
-                        <li key={`${i}_g`}>Property <strong>G</strong></li>
-                        <li key={`${i}_b`}>Property <strong>B</strong></li>
-                    </ul>
-                </li>
-                break;
-            case Animation.ANIMATIONTYPE_COLOR4:
-                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
-                    <ul>
-                        <li key={`${i}_r`}>Property <strong>R</strong></li>
-                        <li key={`${i}_g`}>Property <strong>G</strong></li>
-                        <li key={`${i}_b`}>Property <strong>B</strong></li>
-                        <li key={`${i}_a`}>Property <strong>A</strong></li>
-                    </ul>
-                </li>
-                break;
-            case Animation.ANIMATIONTYPE_SIZE:
-                element = <li className="property" key={i}><p>{animation.targetProperty}</p>
-                    <ul>
-                        <li key={`${i}_width`}>Property <strong>Width</strong></li>
-                        <li key={`${i}_height`}>Property <strong>Height</strong></li>
-                    </ul>
-                </li>
-                break;
-            default: console.log("not recognized");
-                element = null;
-                break;
-        }
-
-        return element;
+    recountAnimations() {
+        return (this.props.entity as IAnimatable).animations?.length ?? 0;
     }
 
     handleTabs(tab: number){
@@ -145,6 +62,10 @@ export class EditorControls extends React.Component<IEditorControlsProps, {isAni
         this.setState({framesPerSecond: fps});
     }
 
+    emptiedList(){
+        this.setState( { animationsCount: this.recountAnimations(), isEditTabOpen: false, isAnimationTabOpen: true } );
+    }
+
     render() { 
         return (
             <div className="animation-list">
@@ -171,7 +92,7 @@ export class EditorControls extends React.Component<IEditorControlsProps, {isAni
                     close={() => { this.setState({isAnimationTabOpen: false})}} 
                     entity={(this.props.entity as IAnimatable)} 
                     setNotificationMessage={(message: string) => { this.props.setNotificationMessage(message) }}
-                    changed={() => this.animationsChanged() }
+                    changed={() => this.animationAdded() }
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
             }
 
@@ -179,17 +100,13 @@ export class EditorControls extends React.Component<IEditorControlsProps, {isAni
 
             { this.state.isSaveTabOpen ? <div>Save</div> : null }
 
-            { this.state.isEditTabOpen ?
-                <div className="object-tree">
-                    <ul>
-                        {
-                            this.props.isTargetedAnimation ? this.setListItem((this.props.entity as TargetedAnimation).animation, 0) :
-                                (this.props.entity as IAnimatable).animations && (this.props.entity as IAnimatable).animations?.map((animation, i) => {
-                                    return this.setListItem(animation, i);
-                                })}
-
-                    </ul>
-                </div>
+            { this.state.isEditTabOpen ? <AnimationListTree 
+                isTargetedAnimation={this.props.isTargetedAnimation} 
+                entity={this.props.entity} 
+                selected={this.props.selected} 
+                onPropertyChangedObservable={this.props.onPropertyChangedObservable} 
+                empty={() => this.emptiedList() }
+                selectAnimation={this.props.selectAnimation}/>
             : null }
         </div>
         );