@volter/editor-model-play 0.5.194 → 0.5.196
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/play-script.ts +55 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/editor-model-play",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.196",
|
|
4
4
|
"author": "Volter AI, Inc.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
]
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@volter/editor-sdk": "0.5.
|
|
31
|
+
"@volter/editor-sdk": "0.5.196"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"react": "^19.0.0",
|
package/src/play-script.ts
CHANGED
|
@@ -86,6 +86,7 @@ import {
|
|
|
86
86
|
takeModelPlayStep,
|
|
87
87
|
} from './model-play';
|
|
88
88
|
import { beginModelPlayLog } from './play-log';
|
|
89
|
+
import type { DocumentPlayAnimation } from '@volter/editor-sdk/kit/document-play-extension';
|
|
89
90
|
import { materialOverrides } from './play-materials';
|
|
90
91
|
interface PlayComposition {
|
|
91
92
|
readonly entries: readonly string[];
|
|
@@ -157,6 +158,29 @@ export interface ModelPlayContext {
|
|
|
157
158
|
* });
|
|
158
159
|
*/
|
|
159
160
|
autoplay(bot: ModelPlayAutoplayController | Readonly<Record<string, ModelPlayAutoplayController>> | null): void;
|
|
161
|
+
/**
|
|
162
|
+
* Set the action an object's armature plays, as Blender's `animation_data.action` does: any
|
|
163
|
+
* action in the file that animates that armature's bones, by name. `object` is the armature, its
|
|
164
|
+
* skinned mesh, or an object above or below them, or its name. Each armature starts a run playing
|
|
165
|
+
* the action the file assigns it. Setting another crossfades over `fade` seconds (0.2), loops
|
|
166
|
+
* unless `loop: false` (which holds the last frame), and plays at `speed`; setting the action
|
|
167
|
+
* already playing does nothing, so a script may set it every update from the character's state.
|
|
168
|
+
* `null` fades it out. Actions run on the game's clock. Each change is an `action` entry in the
|
|
169
|
+
* play log; a name the armature lacks is `action-unknown` once and returns false.
|
|
170
|
+
*
|
|
171
|
+
* play.setAction('Hero', moving ? 'Run' : 'Idle');
|
|
172
|
+
*/
|
|
173
|
+
setAction(object: THREE.Object3D | string, action: string | null, options?: ModelPlayActionOptions): boolean;
|
|
174
|
+
/** The actions an object's armature can play, by name (empty without an armature). */
|
|
175
|
+
actions(object: THREE.Object3D | string): readonly string[];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface ModelPlayActionOptions {
|
|
179
|
+
readonly loop?: boolean;
|
|
180
|
+
readonly fade?: number;
|
|
181
|
+
readonly speed?: number;
|
|
182
|
+
/** Start again from the first frame when this clip is already playing. */
|
|
183
|
+
readonly restart?: boolean;
|
|
160
184
|
}
|
|
161
185
|
|
|
162
186
|
/** What the bot is handed before each `update` it drives. */
|
|
@@ -290,8 +314,11 @@ export function runPlayScript(options: {
|
|
|
290
314
|
readonly ready: () => void;
|
|
291
315
|
readonly returning: () => void;
|
|
292
316
|
readonly ownMaterial?: ((material: THREE.Material) => THREE.Material | null) | undefined;
|
|
317
|
+
/** The document's skins and clips bound to `root`; absent, characters stand in their exported pose. */
|
|
318
|
+
readonly animation?: DocumentPlayAnimation | undefined;
|
|
293
319
|
}): () => void {
|
|
294
320
|
const { blend, root, camera, onFrame } = options;
|
|
321
|
+
const options_ = options;
|
|
295
322
|
const modulePath = playScriptPath(blend);
|
|
296
323
|
// A fresh copy is a fresh run: its log starts empty, its clock at zero. Writes go through
|
|
297
324
|
// this run's handle, which is inert once the run has ended.
|
|
@@ -385,6 +412,32 @@ export function runPlayScript(options: {
|
|
|
385
412
|
const behaviors = botBehaviors(bot);
|
|
386
413
|
if (alive.value) alive.bot = behaviors;
|
|
387
414
|
},
|
|
415
|
+
setAction(object, action, options) {
|
|
416
|
+
if (!alive.value) return false;
|
|
417
|
+
const name = typeof object === 'string' ? object : object.name;
|
|
418
|
+
const target = typeof object === 'string' ? root.getObjectByName(object) ?? null : object;
|
|
419
|
+
const unknown = (why: string): false => {
|
|
420
|
+
const key = `${name}\u0000${action}`;
|
|
421
|
+
if (!alive.unknown.has(key)) { alive.unknown.add(key); run.append('play', 'action-unknown', { object: name, action, why }); }
|
|
422
|
+
return false;
|
|
423
|
+
};
|
|
424
|
+
if (!target) return unknown('the model has no such object');
|
|
425
|
+
if (!options_.animation) return unknown('this document lends no animation');
|
|
426
|
+
const before = options_.animation.playing(target);
|
|
427
|
+
if (action === null) {
|
|
428
|
+
options_.animation.stop(target, options?.fade);
|
|
429
|
+
if (before !== null) run.append('play', 'action', { object: target.name, action: null, from: before });
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
const answer = options_.animation.play(target, action, options);
|
|
433
|
+
if (!answer.ok) return unknown(answer.why);
|
|
434
|
+
if (before !== action || options?.restart) run.append('play', 'action', { armature: answer.armature, action, from: before });
|
|
435
|
+
return true;
|
|
436
|
+
},
|
|
437
|
+
actions(object) {
|
|
438
|
+
const target = typeof object === 'string' ? root.getObjectByName(object) ?? null : object;
|
|
439
|
+
return target && options_.animation ? options_.animation.clips(target) : [];
|
|
440
|
+
},
|
|
388
441
|
});
|
|
389
442
|
const scripts = new WeakMap<ModelPlayGame, Script>();
|
|
390
443
|
let stopped = false;
|
|
@@ -566,6 +619,8 @@ export function runPlayScript(options: {
|
|
|
566
619
|
// A paused frame that runs an update is a Step; its entry carries the step's own tick.
|
|
567
620
|
const stepping = modelPlayClock(options.documentId).paused;
|
|
568
621
|
const tick = (dt: number): void => {
|
|
622
|
+
// The characters' clips move on the game's clock, one update's `dt` at a time.
|
|
623
|
+
options.animation?.update(dt);
|
|
569
624
|
run.advance(dt);
|
|
570
625
|
ran += 1;
|
|
571
626
|
simulated += dt;
|