123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- import * as React from 'react';
- import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
- import { Observable } from 'babylonjs/Misc/observable';
- import { PropertyChangedEvent } from '../../../../../components/propertyChangedEvent';
- import { Animation } from 'babylonjs/Animations/animation';
- import { Vector2, Vector3, Quaternion } from 'babylonjs/Maths/math.vector';
- import { Size } from 'babylonjs/Maths/math.size';
- import { Color3, Color4 } from 'babylonjs/Maths/math.color';
- import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
- import { IAnimationKey } from 'babylonjs/Animations/animationKey';
- interface IAddAnimationProps {
- isOpen: boolean;
- close: () => void;
- entity: IAnimatable;
- onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
- setNotificationMessage: (message: string) => void;
- finishedUpdate: () => void;
- addedNewAnimation: () => void;
- fps: number;
- selectedToUpdate?: Animation | undefined;
- }
- export class AddAnimation extends React.Component<
- IAddAnimationProps,
- {
- animationName: string;
- animationTargetProperty: string;
- animationType: number;
- loopMode: number;
- animationTargetPath: string;
- isUpdating: boolean;
- }
- > {
- constructor(props: IAddAnimationProps) {
- super(props);
- this.state = this.setInitialState(this.props.selectedToUpdate);
- }
- setInitialState(editingAnimation?: Animation) {
- return {
- animationName: editingAnimation ? editingAnimation.name : '',
- animationTargetPath: '',
- animationType: editingAnimation
- ? editingAnimation.dataType
- : Animation.ANIMATIONTYPE_FLOAT,
- loopMode: editingAnimation
- ? editingAnimation.loopMode ?? Animation.ANIMATIONLOOPMODE_CYCLE
- : Animation.ANIMATIONLOOPMODE_CYCLE,
- animationTargetProperty: editingAnimation
- ? editingAnimation.targetProperty
- : '',
- isUpdating: editingAnimation ? true : false,
- };
- }
- componentWillReceiveProps(nextProps: IAddAnimationProps) {
- if (
- nextProps.selectedToUpdate !== undefined &&
- nextProps.selectedToUpdate !== this.props.selectedToUpdate
- ) {
- this.setState(this.setInitialState(nextProps.selectedToUpdate));
- } else {
- if (nextProps.isOpen === true && nextProps.isOpen !== this.props.isOpen)
- this.setState(this.setInitialState());
- }
- }
- updateAnimation() {
- if (this.props.selectedToUpdate !== undefined) {
- const oldNameValue = this.props.selectedToUpdate.name;
- this.props.selectedToUpdate.name = this.state.animationName;
- this.raiseOnPropertyUpdated(
- oldNameValue,
- this.state.animationName,
- 'name'
- );
- const oldLoopModeValue = this.props.selectedToUpdate.loopMode;
- this.props.selectedToUpdate.loopMode = this.state.loopMode;
- this.raiseOnPropertyUpdated(
- oldLoopModeValue,
- this.state.loopMode,
- 'loopMode'
- );
- const oldTargetPropertyValue = this.props.selectedToUpdate.targetProperty;
- this.props.selectedToUpdate.targetProperty = this.state.animationTargetProperty;
- this.raiseOnPropertyUpdated(
- oldTargetPropertyValue,
- this.state.animationTargetProperty,
- 'targetProperty'
- );
- this.props.finishedUpdate();
- }
- }
- getTypeAsString(type: number) {
- switch (type) {
- case Animation.ANIMATIONTYPE_FLOAT:
- return 'Float';
- case Animation.ANIMATIONTYPE_QUATERNION:
- return 'Quaternion';
- case Animation.ANIMATIONTYPE_VECTOR3:
- return 'Vector3';
- case Animation.ANIMATIONTYPE_VECTOR2:
- return 'Vector2';
- case Animation.ANIMATIONTYPE_SIZE:
- return 'Size';
- case Animation.ANIMATIONTYPE_COLOR3:
- return 'Color3';
- case Animation.ANIMATIONTYPE_COLOR4:
- return 'Color4';
- default:
- return 'Float';
- }
- }
- addAnimation() {
- if (
- this.state.animationName != '' &&
- this.state.animationTargetProperty != ''
- ) {
- let matchTypeTargetProperty = this.state.animationTargetProperty.split(
- '.'
- );
- let animationDataType = this.state.animationType;
- let matched = false;
- if (matchTypeTargetProperty.length === 1) {
- let match = (this.props.entity as any)[matchTypeTargetProperty[0]];
- if (match) {
- switch (match.constructor.name) {
- case 'Vector2':
- animationDataType === Animation.ANIMATIONTYPE_VECTOR2
- ? (matched = true)
- : (matched = false);
- break;
- case 'Vector3':
- animationDataType === Animation.ANIMATIONTYPE_VECTOR3
- ? (matched = true)
- : (matched = false);
- break;
- case 'Quaternion':
- animationDataType === Animation.ANIMATIONTYPE_QUATERNION
- ? (matched = true)
- : (matched = false);
- break;
- case 'Color3':
- animationDataType === Animation.ANIMATIONTYPE_COLOR3
- ? (matched = true)
- : (matched = false);
- break;
- case 'Color4':
- animationDataType === Animation.ANIMATIONTYPE_COLOR4
- ? (matched = true)
- : (matched = false);
- break;
- case 'Size':
- animationDataType === Animation.ANIMATIONTYPE_SIZE
- ? (matched = true)
- : (matched = false);
- break;
- }
- } else {
- this.props.setNotificationMessage(
- `The selected entity doesn't have a ${matchTypeTargetProperty[0]} property`
- );
- }
- } else if (matchTypeTargetProperty.length > 1) {
- let matchProp = (this.props.entity as any)[matchTypeTargetProperty[0]];
- if (matchProp) {
- let match = matchProp[matchTypeTargetProperty[1]];
- if (typeof match === 'number') {
- animationDataType === Animation.ANIMATIONTYPE_FLOAT
- ? (matched = true)
- : (matched = false);
- }
- }
- }
- if (matched) {
- let startValue;
- let outTangent;
- // Default start and end values for new animations
- switch (animationDataType) {
- case Animation.ANIMATIONTYPE_FLOAT:
- startValue = 1;
- outTangent = 0;
- break;
- case Animation.ANIMATIONTYPE_VECTOR2:
- startValue = new Vector2(1, 1);
- outTangent = Vector2.Zero();
- break;
- case Animation.ANIMATIONTYPE_VECTOR3:
- startValue = new Vector3(1, 1, 1);
- outTangent = Vector3.Zero();
- break;
- case Animation.ANIMATIONTYPE_QUATERNION:
- startValue = new Quaternion(1, 1, 1, 1);
- outTangent = Quaternion.Zero();
- break;
- case Animation.ANIMATIONTYPE_COLOR3:
- startValue = new Color3(1, 1, 1);
- outTangent = new Color3(0, 0, 0);
- break;
- case Animation.ANIMATIONTYPE_COLOR4:
- startValue = new Color4(1, 1, 1, 1);
- outTangent = new Color4(0, 0, 0, 0);
- break;
- case Animation.ANIMATIONTYPE_SIZE:
- startValue = new Size(1, 1);
- outTangent = Size.Zero();
- break;
- default:
- console.log('not recognized');
- break;
- }
- let alreadyAnimatedProperty = (this.props
- .entity as IAnimatable).animations?.find(
- (anim) => anim.targetProperty === this.state.animationTargetProperty,
- this
- );
- let alreadyAnimationName = (this.props
- .entity as IAnimatable).animations?.find(
- (anim) => anim.name === this.state.animationName,
- this
- );
- if (alreadyAnimatedProperty) {
- this.props.setNotificationMessage(
- `The property "${this.state.animationTargetProperty}" already has an animation`
- );
- } else if (alreadyAnimationName) {
- this.props.setNotificationMessage(
- `There is already an animation with the name: "${this.state.animationName}"`
- );
- } else {
- let animation = new Animation(
- this.state.animationName,
- this.state.animationTargetProperty,
- this.props.fps,
- animationDataType
- );
- // Start with two keyframes
- var keys: IAnimationKey[] = [];
- keys.push({
- frame: 0,
- value: startValue,
- outTangent: outTangent,
- });
- animation.setKeys(keys);
- if (this.props.entity.animations) {
- const store = this.props.entity.animations;
- const updatedCollection = [
- ...this.props.entity.animations,
- animation,
- ];
- this.raiseOnPropertyChanged(updatedCollection, store);
- this.props.entity.animations = updatedCollection;
- this.props.addedNewAnimation();
- //Cleaning form fields
- this.setState({
- animationName: '',
- animationTargetPath: '',
- animationType: Animation.ANIMATIONTYPE_FLOAT,
- loopMode: Animation.ANIMATIONLOOPMODE_CYCLE,
- animationTargetProperty: '',
- });
- }
- }
- } else {
- this.props.setNotificationMessage(
- `The property "${
- this.state.animationTargetProperty
- }" is not a "${this.getTypeAsString(this.state.animationType)}" type`
- );
- }
- } else {
- this.props.setNotificationMessage(
- `You need to provide a name and target property.`
- );
- }
- }
- raiseOnPropertyChanged(newValue: Animation[], previousValue: Animation[]) {
- if (!this.props.onPropertyChangedObservable) {
- return;
- }
- this.props.onPropertyChangedObservable.notifyObservers({
- object: this.props.entity,
- property: 'animations',
- value: newValue,
- initialValue: previousValue,
- });
- }
- raiseOnPropertyUpdated(
- newValue: string | number | undefined,
- previousValue: string | number,
- property: string
- ) {
- if (!this.props.onPropertyChangedObservable) {
- return;
- }
- this.props.onPropertyChangedObservable.notifyObservers({
- object: this.props.selectedToUpdate,
- property: property,
- value: newValue,
- initialValue: previousValue,
- });
- }
- handleNameChange(event: React.ChangeEvent<HTMLInputElement>) {
- event.preventDefault();
- this.setState({ animationName: event.target.value.trim() });
- }
- handlePathChange(event: React.ChangeEvent<HTMLInputElement>) {
- event.preventDefault();
- this.setState({ animationTargetPath: event.target.value.trim() });
- }
- handleTypeChange(event: React.ChangeEvent<HTMLSelectElement>) {
- event.preventDefault();
- this.setState({ animationType: parseInt(event.target.value) });
- }
- handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>) {
- event.preventDefault();
- this.setState({ animationTargetProperty: event.target.value });
- }
- handleLoopModeChange(event: React.ChangeEvent<HTMLSelectElement>) {
- event.preventDefault();
- this.setState({ loopMode: parseInt(event.target.value) });
- }
- render() {
- return (
- <div
- className='new-animation'
- style={{ display: this.props.isOpen ? 'block' : 'none' }}
- >
- <div className='sub-content'>
- {this.state.isUpdating ? null : (
- <div className='label-input'>
- <label>Target Path</label>
- <input
- type='text'
- value={this.state.animationTargetPath}
- onChange={(e) => this.handlePathChange(e)}
- disabled
- ></input>
- </div>
- )}
- <div className='label-input'>
- <label>Display Name</label>
- <input
- type='text'
- value={this.state.animationName}
- onChange={(e) => this.handleNameChange(e)}
- ></input>
- </div>
- <div className='label-input'>
- <label>Property</label>
- <input
- type='text'
- value={this.state.animationTargetProperty}
- onChange={(e) => this.handlePropertyChange(e)}
- ></input>
- </div>
- {this.state.isUpdating ? null : (
- <div className='label-input'>
- <label>Type</label>
- <select
- onChange={(e) => this.handleTypeChange(e)}
- value={this.state.animationType}
- >
- <option value={Animation.ANIMATIONTYPE_FLOAT}>Float</option>
- <option value={Animation.ANIMATIONTYPE_VECTOR3}>Vector3</option>
- <option value={Animation.ANIMATIONTYPE_VECTOR2}>Vector2</option>
- <option value={Animation.ANIMATIONTYPE_QUATERNION}>
- Quaternion
- </option>
- <option value={Animation.ANIMATIONTYPE_COLOR3}>Color3</option>
- <option value={Animation.ANIMATIONTYPE_COLOR4}>Color4</option>
- <option value={Animation.ANIMATIONTYPE_SIZE}>Size</option>
- </select>
- </div>
- )}
- <div className='label-input'>
- <label>Loop Mode</label>
- <select
- onChange={(e) => this.handleLoopModeChange(e)}
- value={this.state.loopMode}
- >
- <option value={Animation.ANIMATIONLOOPMODE_CYCLE}>Cycle</option>
- <option value={Animation.ANIMATIONLOOPMODE_RELATIVE}>
- Relative
- </option>
- <option value={Animation.ANIMATIONLOOPMODE_CONSTANT}>
- Constant
- </option>
- </select>
- </div>
- <div className='confirm-buttons'>
- <ButtonLineComponent
- label={this.state.isUpdating ? 'Update' : 'Create'}
- onClick={
- this.state.isUpdating
- ? () => this.updateAnimation()
- : () => this.addAnimation()
- }
- />
- </div>
- </div>
- </div>
- );
- }
- }
|