@volter/blender-engine 0.1.5 → 0.1.7
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/browser/runtime.ts +31 -2
- package/browser/session.py +563 -11
- package/browser/three/blender-frame-columns.ts +6 -0
- package/browser/three/blender-graph-material.ts +402 -0
- package/browser/three/blender-node-glsl.generated.ts +401 -0
- package/browser/three/blender-node-graph.ts +1026 -0
- package/browser/three/blender-physical-material.ts +162 -0
- package/browser/three/blender-presenter-change.ts +24 -0
- package/browser/three/blender-runtime-geometry.ts +112 -9
- package/browser/three/blender-runtime-lighting.ts +11 -0
- package/browser/three/blender-runtime-view.ts +237 -49
- package/browser/three/blender-texture-samplers.ts +79 -0
- package/browser/three/blender-world-volume.ts +235 -0
- package/browser/three/presenter.ts +6 -1
- package/package.json +1 -1
- package/wasm/BUNDLE.json +13 -25
- package/wasm/blender_browser.data.br +0 -0
- package/wasm/blender_browser.wasm.br +0 -0
package/browser/runtime.ts
CHANGED
|
@@ -27,6 +27,9 @@ type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : n
|
|
|
27
27
|
type Request = DistributiveOmit<WorkerRequest, 'id'>;
|
|
28
28
|
|
|
29
29
|
export interface BlenderRuntimeOptions {
|
|
30
|
+
/** Operation-only diagnostics, never request payloads. The optional observer
|
|
31
|
+
* must not affect work, even if it throws. End calls balance overlapping work. */
|
|
32
|
+
work?(label: string): () => void;
|
|
30
33
|
history?(entries: readonly NativeHistoryEntry[]): void;
|
|
31
34
|
/** Display a frame. A screenshot `capture` has its view REMEMBERED so the
|
|
32
35
|
* next document capture photographs what the agent asked for; a render
|
|
@@ -149,6 +152,7 @@ export class BlenderRuntime {
|
|
|
149
152
|
};
|
|
150
153
|
/** `performance.now()` at the `postMessage` of every outstanding call. */
|
|
151
154
|
readonly #callStarts = new Map<number, number>();
|
|
155
|
+
readonly #callWork = new Map<number, () => void>();
|
|
152
156
|
#lastCallMs: number | null = null;
|
|
153
157
|
#maxCompletedMs = 0;
|
|
154
158
|
#completedOver5s = 0;
|
|
@@ -198,6 +202,11 @@ export class BlenderRuntime {
|
|
|
198
202
|
return this.#project;
|
|
199
203
|
}
|
|
200
204
|
|
|
205
|
+
/** The startup document owned by this worker, including while boot is pending. */
|
|
206
|
+
get document(): string | null {
|
|
207
|
+
return this.#document;
|
|
208
|
+
}
|
|
209
|
+
|
|
201
210
|
/** Idempotent: the first call boots Blender at `project`'s
|
|
202
211
|
* absolute path; later calls await it (a different path is refused).
|
|
203
212
|
*
|
|
@@ -442,7 +451,8 @@ export class BlenderRuntime {
|
|
|
442
451
|
if (this.#terminated) return;
|
|
443
452
|
this.#terminated = true;
|
|
444
453
|
globalThis.removeEventListener?.('beforeunload', this.#beforeUnload);
|
|
445
|
-
this.#worker
|
|
454
|
+
const end = this.#work('terminating worker');
|
|
455
|
+
try { this.#worker.terminate(); } finally { end(); }
|
|
446
456
|
const error = new Error('The Blender session was terminated');
|
|
447
457
|
for (const id of [...this.#pending.keys()]) this.#settled(id);
|
|
448
458
|
for (const pending of this.#pending.values()) pending.reject(error);
|
|
@@ -489,6 +499,8 @@ export class BlenderRuntime {
|
|
|
489
499
|
|
|
490
500
|
/** Close a call's window, whether it answered, threw, or was terminated. */
|
|
491
501
|
#settled(id: number): void {
|
|
502
|
+
this.#callWork.get(id)?.();
|
|
503
|
+
this.#callWork.delete(id);
|
|
492
504
|
const started = this.#callStarts.get(id);
|
|
493
505
|
if (started === undefined) return;
|
|
494
506
|
this.#callStarts.delete(id);
|
|
@@ -510,10 +522,24 @@ export class BlenderRuntime {
|
|
|
510
522
|
const started = performance.now();
|
|
511
523
|
this.#callStarts.set(id, started);
|
|
512
524
|
this.#lastCallWindow = { start: started, end: null };
|
|
513
|
-
this.#
|
|
525
|
+
this.#callWork.set(id, this.#work(`waiting for worker ${request.op}`));
|
|
526
|
+
try {
|
|
527
|
+
this.#worker.postMessage({ ...request, id });
|
|
528
|
+
} catch (error) {
|
|
529
|
+
this.#pending.delete(id);
|
|
530
|
+
this.#settled(id);
|
|
531
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
532
|
+
}
|
|
514
533
|
});
|
|
515
534
|
}
|
|
516
535
|
|
|
536
|
+
#work(label: string): () => void {
|
|
537
|
+
try {
|
|
538
|
+
const end = this.#options.work?.(label);
|
|
539
|
+
return () => { try { end?.(); } catch { /* diagnostics never fail work */ } };
|
|
540
|
+
} catch { return () => {}; }
|
|
541
|
+
}
|
|
542
|
+
|
|
517
543
|
async #receive(reply: WorkerReply): Promise<void> {
|
|
518
544
|
if ('op' in reply) {
|
|
519
545
|
if (reply.op === 'document-dirty') {
|
|
@@ -539,6 +565,7 @@ export class BlenderRuntime {
|
|
|
539
565
|
const frame = reply.frame as { session?: unknown; revision?: unknown } | null;
|
|
540
566
|
if (typeof frame?.session === 'string' && typeof frame.revision === 'number')
|
|
541
567
|
this.#presented = { session: frame.session, revision: frame.revision };
|
|
568
|
+
const end = this.#work('presenting worker frame');
|
|
542
569
|
try {
|
|
543
570
|
const answer = await this.#options.present(reply.frame, reply.description, reply.capture);
|
|
544
571
|
this.#worker.postMessage({
|
|
@@ -555,6 +582,8 @@ export class BlenderRuntime {
|
|
|
555
582
|
id: reply.id,
|
|
556
583
|
error: error instanceof Error ? error.message : String(error),
|
|
557
584
|
} satisfies WorkerRequest);
|
|
585
|
+
} finally {
|
|
586
|
+
end();
|
|
558
587
|
}
|
|
559
588
|
return;
|
|
560
589
|
}
|