Skip to content

Commit f62de5b

Browse files
committed
fix: support of animated weights for animation groups in cinematic editor
1 parent 41ca244 commit f62de5b

File tree

3 files changed

+133
-88
lines changed

3 files changed

+133
-88
lines changed

editor/src/editor/layout/cinematic/editor.tsx

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { getDefaultRenderingPipeline } from "../../rendering/default-pipeline";
3333
import { serializeCinematic } from "./serialization/serialize";
3434

3535
import { restoreSceneState, saveSceneState } from "./tools/state";
36+
import { syncAnimationGroupsToFrame, syncSoundsToFrame } from "./tools/sync";
3637

3738
import { CinematicEditorTimelineOptions } from "./timelines/options";
3839

@@ -228,6 +229,10 @@ export class CinematicEditor extends Component<ICinematicEditorProps, ICinematic
228229
group.goToFrame(time);
229230
group.pause();
230231

232+
syncAnimationGroupsToFrame(time, this.cinematic, {
233+
pauseAfterSync: true,
234+
});
235+
231236
this.editor.layout.preview.scene.lights.forEach((light) => {
232237
updateLightShadowMapRefreshRate(light);
233238
});
@@ -303,21 +308,9 @@ export class CinematicEditor extends Component<ICinematicEditorProps, ICinematic
303308
const group = this.createTemporaryAnimationGroup();
304309
group.start(false, 1.0, frame);
305310

306-
// Start all sounds that were created before the current frame
307-
this.cinematic.tracks.forEach((track) => {
308-
track.sounds?.forEach((sound) => {
309-
const endFrame = sound.frame + (sound.endFrame - sound.startFrame);
310-
if (sound.frame > frame || endFrame < frame) {
311-
return;
312-
}
313-
314-
const frameDiff = frame - sound.frame;
315-
316-
if (frameDiff > 0) {
317-
const offset = frameDiff / this.cinematic.framesPerSecond;
318-
track.sound?.play(0, offset);
319-
}
320-
});
311+
syncSoundsToFrame(frame, this.cinematic);
312+
syncAnimationGroupsToFrame(frame, this.cinematic, {
313+
pauseAfterSync: false,
321314
});
322315

323316
scene.beginDirectAnimation(this, [this._playAnimation], currentTime, maxFrame, false, 1.0);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { AnimationGroup, Sound } from "babylonjs";
2+
3+
import { ICinematic } from "babylonjs-editor-tools";
4+
5+
export interface ISyncAnimationGroupsToFrameOptions {
6+
pauseAfterSync: boolean;
7+
}
8+
9+
export function syncAnimationGroupsToFrame(frame: number, cinematic: ICinematic, options: ISyncAnimationGroupsToFrameOptions) {
10+
cinematic.tracks.forEach((track) => {
11+
const animationGroup = track.animationGroup as AnimationGroup;
12+
if (!animationGroup) {
13+
return;
14+
}
15+
16+
track.animationGroups?.forEach((configuration) => {
17+
const endFrame = configuration.frame + (configuration.endFrame - configuration.startFrame) / configuration.speed;
18+
if (configuration.frame > frame || endFrame < frame) {
19+
return;
20+
}
21+
22+
const frameDiff = frame - configuration.frame;
23+
24+
if (frameDiff > 0) {
25+
const offset = frameDiff * configuration.speed;
26+
27+
animationGroup.play(false);
28+
animationGroup.goToFrame(offset);
29+
30+
if (options.pauseAfterSync) {
31+
animationGroup.stop();
32+
}
33+
}
34+
});
35+
});
36+
}
37+
38+
export function syncSoundsToFrame(frame: number, cinematic: ICinematic) {
39+
cinematic.tracks.forEach((track) => {
40+
const sound = track.sound as Sound;
41+
if (!sound) {
42+
return;
43+
}
44+
45+
track.sounds?.forEach((configuration) => {
46+
const endFrame = configuration.frame + (configuration.endFrame - configuration.startFrame);
47+
if (configuration.frame > frame || endFrame < frame) {
48+
return;
49+
}
50+
51+
const frameDiff = frame - configuration.frame;
52+
53+
if (frameDiff > 0) {
54+
const offset = frameDiff / cinematic.framesPerSecond;
55+
sound.play(0, offset);
56+
}
57+
});
58+
});
59+
}

tools/src/cinematic/generate.ts

Lines changed: 66 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Scene } from "@babylonjs/core/scene";
2-
import { Tools } from "@babylonjs/core/Misc/tools";
32
import { Sound } from "@babylonjs/core/Audio/sound";
43
import { Animation } from "@babylonjs/core/Animations/animation";
54
import { IAnimationKey } from "@babylonjs/core/Animations/animationKey";
@@ -19,7 +18,7 @@ import { handleApplyImpulseEvent } from "./events/apply-impulse";
1918
import { ICinematic } from "./typings";
2019
import { Cinematic } from "./cinematic";
2120
import { isCinematicKey, isCinematicKeyCut } from "./guards";
22-
import { cloneKey, getPropertyValue, registerAfterAnimationCallback } from "./tools";
21+
import { getPropertyValue, registerAfterAnimationCallback } from "./tools";
2322

2423
export type GenerateCinematicAnimationGroupOptions = {
2524
/**
@@ -42,85 +41,79 @@ export function generateCinematicAnimationGroup(cinematic: ICinematic, scene: Sc
4241
cinematic.tracks.forEach((track) => {
4342
// Animation groups
4443
const animationGroup = track.animationGroup as AnimationGroup;
45-
if (animationGroup) {
46-
const groupedAnimations: Animation[] = [];
47-
48-
track.animationGroups?.forEach((configuration) => {
49-
animationGroup.targetedAnimations.forEach((targetedAnimation) => {
50-
let animation: Animation | null = null;
51-
52-
defer: {
53-
const existingTargetedAnimations = result.targetedAnimations.filter((ta2) => ta2.target === targetedAnimation.target);
54-
if (existingTargetedAnimations.length) {
55-
const existingTargetedAnimationsPair = existingTargetedAnimations.find(
56-
(et) => et.animation.targetProperty === targetedAnimation.animation.targetProperty
57-
);
58-
if (existingTargetedAnimationsPair) {
59-
animation = existingTargetedAnimationsPair.animation;
60-
break defer;
61-
}
62-
}
44+
if (animationGroup && track.animationGroups?.length) {
45+
const effectiveAnimationGroup = animationGroup.clone(`${animationGroup.name}-cinematic-temp`);
6346

64-
animation = targetedAnimation.animation.clone();
65-
animation.setKeys([]);
66-
animation.name = Tools.RandomId();
67-
animation.framePerSecond = cinematic.framesPerSecond;
68-
}
47+
const index = scene.animationGroups.indexOf(effectiveAnimationGroup);
48+
if (index !== -1) {
49+
scene.animationGroups.splice(index, 1);
50+
}
6951

70-
const keys = animation.getKeys();
71-
const sourceKeys = targetedAnimation.animation.getKeys();
52+
result.onAnimationEndObservable.add(() => {
53+
animationGroup.stop();
54+
effectiveAnimationGroup.stop();
55+
});
7256

73-
const speed = configuration.speed;
74-
const normalizedFps = cinematic.framesPerSecond / targetedAnimation.animation.framePerSecond / speed;
57+
const minFrame = track.animationGroups.reduce((prev, curr) => Math.min(prev, curr.frame), Number.MAX_VALUE) ?? 0;
58+
const maxFrame = track.animationGroups.reduce((prev, curr) => Math.max(prev, curr.frame + curr.endFrame), 0) ?? 0;
7559

76-
sourceKeys.forEach((k) => {
77-
if (k.frame >= configuration.startFrame && k.frame <= configuration.endFrame) {
78-
keys.push({
79-
...cloneKey(targetedAnimation.animation.dataType, k),
80-
frame: configuration.frame + k.frame * normalizedFps,
81-
});
82-
}
83-
});
60+
const dummyObject = {
61+
frame: minFrame,
62+
};
8463

85-
animation.setKeys(keys);
86-
result.addTargetedAnimation(animation, targetedAnimation.target);
64+
const animationGroupsAnimation = new Animation(effectiveAnimationGroup.name, "frame", 60, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE, false);
8765

88-
groupedAnimations.push(animation);
89-
});
66+
animationGroupsAnimation.setKeys([
67+
{ frame: minFrame, value: 0 },
68+
{ frame: maxFrame, value: maxFrame },
69+
]);
70+
71+
track.animationGroups.forEach((configuration) => {
72+
animationGroupsAnimation.addEvent(
73+
new AnimationEvent(configuration.frame, () => {
74+
effectiveAnimationGroup.from = configuration.startFrame;
75+
effectiveAnimationGroup.to = configuration.endFrame;
76+
effectiveAnimationGroup.speedRatio = configuration.speed;
77+
effectiveAnimationGroup.stop();
78+
effectiveAnimationGroup.goToFrame(configuration.startFrame);
79+
effectiveAnimationGroup.play(false);
80+
})
81+
);
9082
});
9183

92-
// TODO: fix that
93-
// if (groupedAnimations.length && (track.animationGroupWeight?.length ?? 0) >= 2) {
94-
// const dummyObject = {
95-
// weight: 0,
96-
// };
97-
98-
// const weightAnimation = new Animation(`${animationGroup.name}-weights`, "weight", 60, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE, false);
99-
// const weightKeys: IAnimationKey[] = [];
100-
101-
// track.animationGroupWeight!.forEach((keyFrame) => {
102-
// if (isCinematicKeyCut(keyFrame)) {
103-
// weightKeys.push(keyFrame.key1);
104-
// weightKeys.push(keyFrame.key2);
105-
// } else {
106-
// weightKeys.push(keyFrame);
107-
// }
108-
// });
109-
110-
// weightAnimation.setKeys(weightKeys);
111-
// result.addTargetedAnimation(weightAnimation, dummyObject);
112-
113-
// registerAfterAnimationCallback(result, scene, () => {
114-
// result.animatables.forEach((animatable) => {
115-
// for (const animation of animatable.getAnimations()) {
116-
// if (groupedAnimations.includes(animation.animation)) {
117-
// animatable.weight = dummyObject.weight;
118-
// break;
119-
// }
120-
// }
121-
// });
122-
// });
123-
// }
84+
result.addTargetedAnimation(animationGroupsAnimation, dummyObject);
85+
86+
if ((track.animationGroupWeight?.length ?? 0) >= 2) {
87+
const dummyObject = {
88+
weight: 0,
89+
};
90+
91+
const weightAnimation = new Animation(
92+
`${effectiveAnimationGroup.name}-weights`,
93+
"weight",
94+
60,
95+
Animation.ANIMATIONTYPE_FLOAT,
96+
Animation.ANIMATIONLOOPMODE_CYCLE,
97+
false
98+
);
99+
const weightKeys: IAnimationKey[] = [];
100+
101+
track.animationGroupWeight!.forEach((keyFrame) => {
102+
if (isCinematicKeyCut(keyFrame)) {
103+
weightKeys.push(keyFrame.key1);
104+
weightKeys.push(keyFrame.key2);
105+
} else {
106+
weightKeys.push(keyFrame);
107+
}
108+
});
109+
110+
weightAnimation.setKeys(weightKeys);
111+
result.addTargetedAnimation(weightAnimation, dummyObject);
112+
113+
registerAfterAnimationCallback(result, scene, () => {
114+
effectiveAnimationGroup.weight = dummyObject.weight;
115+
});
116+
}
124117
}
125118

126119
const sound = track.sound as Sound;

0 commit comments

Comments
 (0)