animationPropertyGridComponent.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import * as React from "react";
  2. import { Observable, Observer } from "babylonjs/Misc/observable";
  3. import { Scene } from "babylonjs/scene";
  4. import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
  5. import { ButtonLineComponent } from "../../../../../sharedUiComponents/lines/buttonLineComponent";
  6. import { LineContainerComponent } from "../../../lineContainerComponent";
  7. import { SliderLineComponent } from "../../../lines/sliderLineComponent";
  8. import { LockObject } from "../../../../../sharedUiComponents/tabs/propertyGrids/lockObject";
  9. import { GlobalState } from "../../../../globalState";
  10. import { Animation } from "babylonjs/Animations/animation";
  11. import { Animatable } from "babylonjs/Animations/animatable";
  12. import { AnimationPropertiesOverride } from "babylonjs/Animations/animationPropertiesOverride";
  13. import { AnimationRange } from "babylonjs/Animations/animationRange";
  14. import { CheckBoxLineComponent } from "../../../../../sharedUiComponents/lines/checkBoxLineComponent";
  15. import { Nullable } from "babylonjs/types";
  16. import { FloatLineComponent } from "../../../lines/floatLineComponent";
  17. import { TextLineComponent } from "../../../../../sharedUiComponents/lines/textLineComponent";
  18. import { IAnimatable } from "babylonjs/Animations/animatable.interface";
  19. // import { AnimationCurveEditorComponent } from "../animations/animationCurveEditorComponent";
  20. // import { PopupComponent } from "../../../../popupComponent";
  21. interface IAnimationGridComponentProps {
  22. globalState: GlobalState;
  23. animatable: IAnimatable;
  24. scene: Scene;
  25. lockObject: LockObject;
  26. onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
  27. }
  28. export class AnimationGridComponent extends React.Component<IAnimationGridComponentProps, { currentFrame: number }> {
  29. private _animations: Nullable<Animation[]> = null;
  30. private _ranges: AnimationRange[];
  31. private _mainAnimatable: Nullable<Animatable>;
  32. private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
  33. private _isPlaying = false;
  34. private timelineRef: React.RefObject<SliderLineComponent>;
  35. // private _isCurveEditorOpen = false;
  36. private _animationControl = {
  37. from: 0,
  38. to: 0,
  39. loop: false,
  40. };
  41. constructor(props: IAnimationGridComponentProps) {
  42. super(props);
  43. this.state = { currentFrame: 0 };
  44. const animatableAsAny = this.props.animatable as any;
  45. this._ranges = animatableAsAny.getAnimationRanges ? animatableAsAny.getAnimationRanges() : [];
  46. if (animatableAsAny.getAnimatables) {
  47. const animatables = animatableAsAny.getAnimatables();
  48. this._animations = new Array<Animation>();
  49. animatables.forEach((animatable: IAnimatable) => {
  50. if (animatable.animations) {
  51. this._animations!.push(...animatable.animations);
  52. }
  53. });
  54. if (animatableAsAny.animations) {
  55. this._animations!.push(...animatableAsAny.animations);
  56. }
  57. // Extract from and to
  58. if (this._animations && this._animations.length) {
  59. this._animations.forEach((animation) => {
  60. let keys = animation.getKeys();
  61. if (keys && keys.length > 0) {
  62. if (keys[0].frame < this._animationControl.from) {
  63. this._animationControl.from = keys[0].frame;
  64. }
  65. const lastKeyIndex = keys.length - 1;
  66. if (keys[lastKeyIndex].frame > this._animationControl.to) {
  67. this._animationControl.to = keys[lastKeyIndex].frame;
  68. }
  69. }
  70. });
  71. }
  72. }
  73. this.timelineRef = React.createRef();
  74. }
  75. playOrPause() {
  76. const animatable = this.props.animatable;
  77. this._isPlaying = this.props.scene.getAllAnimatablesByTarget(animatable).length > 0;
  78. if (this._isPlaying) {
  79. this.props.scene.stopAnimation(this.props.animatable);
  80. this._mainAnimatable = null;
  81. } else {
  82. this._mainAnimatable = this.props.scene.beginAnimation(
  83. this.props.animatable,
  84. this._animationControl.from,
  85. this._animationControl.to,
  86. this._animationControl.loop
  87. );
  88. }
  89. this.forceUpdate();
  90. }
  91. componentDidMount() {
  92. this._onBeforeRenderObserver = this.props.scene.onBeforeRenderObservable.add(() => {
  93. if (!this._isPlaying || !this._mainAnimatable) {
  94. return;
  95. }
  96. this.setState({ currentFrame: this._mainAnimatable.masterFrame });
  97. });
  98. }
  99. componentWillUnmount() {
  100. if (this._onBeforeRenderObserver) {
  101. this.props.scene.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
  102. this._onBeforeRenderObserver = null;
  103. }
  104. }
  105. onCurrentFrameChange(value: number) {
  106. if (!this._mainAnimatable) {
  107. return;
  108. }
  109. this._mainAnimatable.goToFrame(value);
  110. this.setState({ currentFrame: value });
  111. }
  112. onChangeFromOrTo() {
  113. this.playOrPause();
  114. if (this._isPlaying) {
  115. this.playOrPause();
  116. }
  117. }
  118. // onOpenAnimationCurveEditor() {
  119. // this._isCurveEditorOpen = true;
  120. // }
  121. // onCloseAnimationCurveEditor(window: Window | null) {
  122. // this._isCurveEditorOpen = false;
  123. // if (window !== null) {
  124. // window.close();
  125. // }
  126. // }
  127. render() {
  128. const animatable = this.props.animatable;
  129. const animatableAsAny = this.props.animatable as any;
  130. let animatablesForTarget = this.props.scene.getAllAnimatablesByTarget(animatable);
  131. this._isPlaying = animatablesForTarget.length > 0;
  132. if (this._isPlaying && !this._mainAnimatable) {
  133. this._mainAnimatable = animatablesForTarget[0];
  134. if (this._mainAnimatable) {
  135. this._animationControl.from = this._mainAnimatable.fromFrame;
  136. this._animationControl.to = this._mainAnimatable.toFrame;
  137. this._animationControl.loop = this._mainAnimatable.loopAnimation;
  138. }
  139. }
  140. let animations = animatable.animations;
  141. return (
  142. <div>
  143. {this._ranges.length > 0 && (
  144. <LineContainerComponent globalState={this.props.globalState} title="ANIMATION RANGES">
  145. {this._ranges.map((range, i) => {
  146. return (
  147. <ButtonLineComponent
  148. key={range.name + i}
  149. label={range.name}
  150. onClick={() => {
  151. this._mainAnimatable = null;
  152. this.props.scene.beginAnimation(animatable, range.from, range.to, true);
  153. }}
  154. />
  155. );
  156. })}
  157. </LineContainerComponent>
  158. )}
  159. {animations && (
  160. <>
  161. <LineContainerComponent globalState={this.props.globalState} title="ANIMATIONS">
  162. <TextLineComponent label="Count" value={animations.length.toString()} />
  163. {/* <ButtonLineComponent label="Edit" onClick={() => this.onOpenAnimationCurveEditor()} />
  164. {animations.map((anim, i) => {
  165. return (
  166. <TextLineComponent
  167. key={anim.targetProperty + i}
  168. label={"#" + i + " >"}
  169. value={anim.targetProperty}
  170. />
  171. );
  172. })}
  173. {this._isCurveEditorOpen && (
  174. <PopupComponent
  175. id="curve-editor"
  176. title="Curve Animation Editor"
  177. size={{ width: 1024, height: 512 }}
  178. onOpen={(window: Window) => {}}
  179. onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}
  180. >
  181. <AnimationCurveEditorComponent
  182. scene={this.props.scene}
  183. entity={animatableAsAny}
  184. lockObject={this.props.lockObject}
  185. playOrPause={() => this.playOrPause()}
  186. globalState={this.props.globalState}
  187. />
  188. </PopupComponent>
  189. )} */}
  190. </LineContainerComponent>
  191. {animations.length > 0 && (
  192. <LineContainerComponent
  193. globalState={this.props.globalState}
  194. title="ANIMATION GENERAL CONTROL"
  195. >
  196. <FloatLineComponent
  197. lockObject={this.props.lockObject}
  198. isInteger={true}
  199. label="From"
  200. target={this._animationControl}
  201. propertyName="from"
  202. onChange={() => this.onChangeFromOrTo()}
  203. />
  204. <FloatLineComponent
  205. lockObject={this.props.lockObject}
  206. isInteger={true}
  207. label="To"
  208. target={this._animationControl}
  209. propertyName="to"
  210. onChange={() => this.onChangeFromOrTo()}
  211. />
  212. <CheckBoxLineComponent
  213. label="Loop"
  214. onSelect={(value) => (this._animationControl.loop = value)}
  215. isSelected={() => this._animationControl.loop}
  216. />
  217. {this._isPlaying && (
  218. <SliderLineComponent
  219. ref={this.timelineRef}
  220. label="Current frame"
  221. minimum={this._animationControl.from}
  222. maximum={this._animationControl.to}
  223. step={(this._animationControl.to - this._animationControl.from) / 1000.0}
  224. directValue={this.state.currentFrame}
  225. onInput={(value) => this.onCurrentFrameChange(value)}
  226. />
  227. )}
  228. <ButtonLineComponent
  229. label={this._isPlaying ? "Stop" : "Play"}
  230. onClick={() => this.playOrPause()}
  231. />
  232. {(this._ranges.length > 0 || (this._animations && this._animations.length > 0)) && (
  233. <>
  234. <CheckBoxLineComponent
  235. label="Enable override"
  236. onSelect={(value) => {
  237. if (value) {
  238. animatableAsAny.animationPropertiesOverride = new AnimationPropertiesOverride();
  239. animatableAsAny.animationPropertiesOverride.blendingSpeed = 0.05;
  240. } else {
  241. animatableAsAny.animationPropertiesOverride = null;
  242. }
  243. this.forceUpdate();
  244. }}
  245. isSelected={() => animatableAsAny.animationPropertiesOverride != null}
  246. onValueChanged={() => this.forceUpdate()}
  247. />
  248. {animatableAsAny.animationPropertiesOverride != null && (
  249. <div>
  250. <CheckBoxLineComponent
  251. label="Enable blending"
  252. target={animatableAsAny.animationPropertiesOverride}
  253. propertyName="enableBlending"
  254. onPropertyChangedObservable={this.props.onPropertyChangedObservable}
  255. />
  256. <SliderLineComponent
  257. label="Blending speed"
  258. target={animatableAsAny.animationPropertiesOverride}
  259. propertyName="blendingSpeed"
  260. minimum={0}
  261. maximum={0.1}
  262. step={0.01}
  263. onPropertyChangedObservable={this.props.onPropertyChangedObservable}
  264. />
  265. </div>
  266. )}
  267. </>
  268. )}
  269. </LineContainerComponent>
  270. )}
  271. </>
  272. )}
  273. </div>
  274. );
  275. }
  276. }