Alejandro Toledo 5 years ago
parent
commit
01672b572d

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

@@ -9,7 +9,7 @@
 - Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added SubSurfaceScattering on PBR materials ([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 selected entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
+- Added Curve editor to manage 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))
 

+ 146 - 45
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -38,8 +38,8 @@ interface ICanvasAxis {
 }
 
 export interface IActionableKeyFrame {
-  frame: number;
-  value: any;
+  frame?: number;
+  value?: any;
 }
 
 interface ICurveData {
@@ -183,7 +183,7 @@ export class AnimationCurveEditorComponent extends React.Component<
       panningY: 0,
       panningX: 0,
       repositionCanvas: false,
-      actionableKeyframe: { frame: 0, value: 0 },
+      actionableKeyframe: { frame: undefined, value: undefined },
     };
   }
 
@@ -296,24 +296,44 @@ export class AnimationCurveEditorComponent extends React.Component<
     }
   }
 
+  getKeyframeValueFromAnimation(id: string) {
+    const animation = this.state.selected as Animation;
+    const index = parseInt(id.split('_')[3]);
+    const coordinate = parseInt(id.split('_')[2]);
+    const keys = [...animation.getKeys()];
+
+    const key = keys.find((_, i) => i === index);
+
+    if (key) {
+      const valueAsArray = this.getValueAsArray(animation.dataType, key.value);
+      return { frame: key?.frame, value: valueAsArray[coordinate] };
+    } else {
+      return undefined;
+    }
+  }
+
   /**
    * Keyframe Manipulation
    * This section handles events from SvgDraggableArea.
    */
   selectKeyframe(id: string, multiselect: boolean) {
-    let selectedKeyFrame = this.state.svgKeyframes?.find((kf) => kf.id === id)
+    const frameValue = this.getKeyframeValueFromAnimation(id);
+    const selectedKeyFrame = this.state.svgKeyframes?.find((kf) => kf.id === id)
       ?.selected;
     if (!multiselect) {
       this.deselectKeyframes();
     }
 
-    let updatedKeyframes = this.state.svgKeyframes?.map((kf) => {
+    const updatedKeyframes = this.state.svgKeyframes?.map((kf) => {
       if (kf.id === id) {
         kf.selected = !selectedKeyFrame;
       }
       return kf;
     });
-    this.setState({ svgKeyframes: updatedKeyframes });
+    this.setState({
+      svgKeyframes: updatedKeyframes,
+      actionableKeyframe: frameValue ?? this.state.actionableKeyframe,
+    });
   }
 
   selectedControlPoint(type: string, id: string) {
@@ -341,7 +361,10 @@ export class AnimationCurveEditorComponent extends React.Component<
       kf.selected = false;
       return kf;
     });
-    this.setState({ svgKeyframes: updatedKeyframes });
+    this.setState({
+      svgKeyframes: updatedKeyframes,
+      actionableKeyframe: { frame: undefined, value: undefined },
+    });
   }
 
   updateValuePerCoordinate(
@@ -492,13 +515,15 @@ export class AnimationCurveEditorComponent extends React.Component<
         this._heightScale) *
       2; // this value comes inverted svg from 0 = 100 to 100 = 0
 
-    keys[index].value = this.updateValuePerCoordinate(
+    const updatedValueInCoordinate = this.updateValuePerCoordinate(
       animation.dataType,
       keys[index].value,
       updatedValue,
       coordinate
     );
 
+    keys[index].value = updatedValueInCoordinate;
+
     this.updateLeftControlPoint(
       updatedSvgKeyFrame,
       keys[index],
@@ -514,6 +539,10 @@ export class AnimationCurveEditorComponent extends React.Component<
 
     animation.setKeys(keys);
 
+    this.setState({
+      actionableKeyframe: { frame: newFrame, value: updatedValueInCoordinate },
+    });
+
     this.selectAnimation(animation, coordinate);
   }
 
@@ -602,33 +631,35 @@ export class AnimationCurveEditorComponent extends React.Component<
     event.preventDefault();
     let frame = 0;
 
-    if (
-      isNaN(event.target.valueAsNumber) ||
-      event.target.value.indexOf('.') !== -1
-    ) {
-      this.setState({
-        notification: 'Frame input only accepts integer values',
-      });
-    } else {
-      if (this.state.selected !== null) {
-        let animation = this.state.selected;
-        let keys = animation.getKeys();
-        frame = parseInt(event.target.value);
+    if (event.target.value !== '-') {
+      if (
+        isNaN(event.target.valueAsNumber) ||
+        event.target.value.indexOf('.') !== -1
+      ) {
+        this.setState({
+          notification: 'Frame input only accepts integer values',
+        });
+      } else {
+        if (this.state.selected !== null) {
+          let animation = this.state.selected;
+          let keys = animation.getKeys();
+          frame = parseInt(event.target.value);
 
-        let isKeyframe = keys.find((k) => k.frame === frame);
+          let isKeyframe = keys.find((k) => k.frame === frame);
 
-        let value = this.state.actionableKeyframe.value;
+          let value = this.state.actionableKeyframe.value;
 
-        if (isKeyframe) {
-          value = isKeyframe.value;
-        }
+          if (isKeyframe) {
+            value = isKeyframe.value;
+          }
 
-        this.setState({
-          actionableKeyframe: {
-            frame: frame,
-            value: value,
-          },
-        });
+          this.setState({
+            actionableKeyframe: {
+              frame: frame,
+              value: value,
+            },
+          });
+        }
       }
     }
   }
@@ -636,16 +667,18 @@ export class AnimationCurveEditorComponent extends React.Component<
   handleValueChange(event: React.ChangeEvent<HTMLInputElement>) {
     event.preventDefault();
 
-    if (isNaN(event.target.valueAsNumber)) {
-      this.setState({
-        notification: 'Value input only numeric values',
-      });
-    } else {
+    let value: string | number = '';
+
+    if (event.target.value !== '-') {
+      if (event.target.value !== '') {
+        value = parseFloat(event.target.value);
+      }
+
       this.setState(
         {
           actionableKeyframe: {
             frame: this.state.actionableKeyframe.frame,
-            value: parseFloat(parseFloat(event.target.value).toFixed(3)),
+            value: value,
           },
         },
         () => {
@@ -714,16 +747,53 @@ export class AnimationCurveEditorComponent extends React.Component<
       let currentAnimation = this.state.selected;
 
       if (currentAnimation.dataType === Animation.ANIMATIONTYPE_FLOAT) {
-        let keys = currentAnimation.getKeys();
-        let x = this.state.actionableKeyframe.frame;
-        let y = this.state.actionableKeyframe.value;
+        if (
+          this.state.actionableKeyframe.frame &&
+          this.state.actionableKeyframe.value
+        ) {
+          let keys = currentAnimation.getKeys();
+          let x = this.state.actionableKeyframe.frame;
+          let y = this.state.actionableKeyframe.value;
+
+          console.log(this.state.selectedCoordinate);
+          // check if value exists...
+          let arrayValue: any = [];
+          let emptyValue = this.returnZero(currentAnimation.dataType);
+          let existValue = keys.find((k) => k.frame === x);
+          if (existValue) {
+            arrayValue = this.getValueAsArray(
+              currentAnimation.dataType,
+              existValue.value
+            );
+          } else {
+            // Set empty if doesn't exist
+            if (emptyValue) {
+              arrayValue = this.getValueAsArray(
+                currentAnimation.dataType,
+                emptyValue
+              );
+            }
+          }
 
-        keys.push({ frame: x, value: y, inTangent: 0, outTangent: 0 });
-        keys.sort((a, b) => a.frame - b.frame);
+          arrayValue[this.state.selectedCoordinate] = y;
 
-        currentAnimation.setKeys(keys);
+          let actualValue = this.setValueAsType(
+            currentAnimation.dataType,
+            arrayValue
+          );
 
-        this.selectAnimation(currentAnimation);
+          keys.push({
+            frame: x,
+            value: actualValue,
+            inTangent: 0,
+            outTangent: 0,
+          });
+          keys.sort((a, b) => a.frame - b.frame);
+
+          currentAnimation.setKeys(keys);
+
+          this.selectAnimation(currentAnimation);
+        }
       }
     }
   }
@@ -926,6 +996,37 @@ export class AnimationCurveEditorComponent extends React.Component<
     return valueAsArray;
   }
 
+  setValueAsType(valueType: number, arrayValue: number[]) {
+    switch (valueType) {
+      case Animation.ANIMATIONTYPE_FLOAT:
+        return arrayValue[0];
+      case Animation.ANIMATIONTYPE_VECTOR3:
+        return new Vector3(arrayValue[0], arrayValue[1], arrayValue[2]);
+      case Animation.ANIMATIONTYPE_VECTOR2:
+        return new Vector2(arrayValue[0], arrayValue[1]);
+      case Animation.ANIMATIONTYPE_QUATERNION:
+        return new Quaternion(
+          arrayValue[0],
+          arrayValue[1],
+          arrayValue[2],
+          arrayValue[3]
+        );
+      case Animation.ANIMATIONTYPE_COLOR3:
+        return new Color3(arrayValue[0], arrayValue[1], arrayValue[2]);
+      case Animation.ANIMATIONTYPE_COLOR4:
+        return new Color4(
+          arrayValue[0],
+          arrayValue[1],
+          arrayValue[2],
+          arrayValue[3]
+        );
+      case Animation.ANIMATIONTYPE_SIZE:
+        return new Size(arrayValue[0], arrayValue[1]);
+      default:
+        return arrayValue[0];
+    }
+  }
+
   getPathData(animation: Animation | null) {
     if (animation === null) {
       return undefined;
@@ -1472,7 +1573,7 @@ export class AnimationCurveEditorComponent extends React.Component<
 
         this.setState({
           currentFrame: frame,
-          currentValue: parseFloat(currentValue.toFixed(3)),
+          currentValue: currentValue,
           currentPoint: currentP,
           isPlaying: false,
         });

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

@@ -122,7 +122,7 @@ export class AnimationListTree extends React.Component<
     index: number
   ) {
     this.setState({ selectedCoordinate: coordinate, selectedAnimation: index });
-    this.props.selectAnimation(animation, SelectedCoordinate.x);
+    this.props.selectAnimation(animation, coordinate);
   }
 
   coordinateItem(

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss

@@ -485,7 +485,7 @@
       align-items: center;
       margin-right: 8px;
       input {
-        width: 75px;
+        width: 78px;
         height: 24px;
         color: white;
         font-size: 12px;

+ 11 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar.tsx

@@ -23,6 +23,14 @@ export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
     super(props);
   }
 
+  onBlur(e: React.ChangeEvent<HTMLInputElement>) {
+    e.preventDefault();
+    if (this.props.actionableKeyframe.value === '') {
+      e.target.value = '0';
+      this.props.handleValueChange(e);
+    }
+  }
+
   render() {
     return (
       <div className='actions-wrapper'>
@@ -38,15 +46,16 @@ export class GraphActionsBar extends React.Component<IGraphActionsBarProps> {
             <input
               type='number'
               onChange={this.props.handleFrameChange}
-              value={this.props.actionableKeyframe.frame}
+              value={this.props.actionableKeyframe.frame ?? '-'}
               step='1'
             />
           </div>
           <div className='action-input'>
             <input
               type='number'
-              value={this.props.actionableKeyframe.value}
+              value={this.props.actionableKeyframe.value ?? '-'}
               onChange={this.props.handleValueChange}
+              onBlur={(e) => this.onBlur(e)}
               step='0.01'
             />
           </div>

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

@@ -251,13 +251,13 @@ export class SvgDraggableArea extends React.Component<
     this._draggableArea.current?.focus();
 
     if ((e.target as SVGSVGElement).className.baseVal == 'linear pannable') {
-      if (this.isControlPointActive()) {
+      if (this.isNotControlPointActive()) {
         this.props.deselectKeyframes();
       }
     }
   }
 
-  isControlPointActive() {
+  isNotControlPointActive() {
     const activeControlPoints = this.props.keyframeSvgPoints.filter(
       (x) => x.isLeftActive || x.isRightActive
     );