human2ai 0.1.3 → 0.1.4
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/README.en.md +36 -4
- package/README.md +36 -4
- package/dist/cli/main.js +26 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/database/style-library-repository.d.ts +14 -1
- package/dist/database/style-library-repository.js +59 -0
- package/dist/database/style-library-repository.js.map +1 -1
- package/dist/domain/composition/analysis.d.ts +3 -0
- package/dist/domain/composition/analysis.js +4 -0
- package/dist/domain/composition/analysis.js.map +1 -1
- package/dist/domain/composition/draft.d.ts +1 -0
- package/dist/domain/composition/draft.js +73 -2
- package/dist/domain/composition/draft.js.map +1 -1
- package/dist/domain/composition/frame.d.ts +1 -0
- package/dist/domain/composition/frame.js +6 -0
- package/dist/domain/composition/frame.js.map +1 -1
- package/dist/domain/composition/index.d.ts +4 -1
- package/dist/domain/composition/index.js +4 -1
- package/dist/domain/composition/index.js.map +1 -1
- package/dist/domain/composition/planning-intersections.d.ts +20 -0
- package/dist/domain/composition/planning-intersections.js +119 -0
- package/dist/domain/composition/planning-intersections.js.map +1 -0
- package/dist/domain/composition/planning.d.ts +25 -0
- package/dist/domain/composition/planning.js +152 -0
- package/dist/domain/composition/planning.js.map +1 -0
- package/dist/domain/composition/render.js +8 -0
- package/dist/domain/composition/render.js.map +1 -1
- package/dist/domain/composition/states.js +4 -1
- package/dist/domain/composition/states.js.map +1 -1
- package/dist/domain/composition/types.d.ts +46 -0
- package/dist/domain/spatial/camera-render-key.d.ts +7 -0
- package/dist/domain/spatial/camera-render-key.js +63 -0
- package/dist/domain/spatial/camera-render-key.js.map +1 -0
- package/dist/domain/spatial/lighting.js +3 -2
- package/dist/domain/spatial/lighting.js.map +1 -1
- package/dist/domain/spatial/model.d.ts +2 -0
- package/dist/domain/spatial/model.js +82 -27
- package/dist/domain/spatial/model.js.map +1 -1
- package/dist/domain/spatial/preview.d.ts +4 -0
- package/dist/domain/spatial/preview.js +23 -0
- package/dist/domain/spatial/preview.js.map +1 -0
- package/dist/domain/spatial/reference-lines.d.ts +12 -3
- package/dist/domain/spatial/reference-lines.js +37 -16
- package/dist/domain/spatial/reference-lines.js.map +1 -1
- package/dist/domain/spatial/render-inputs.d.ts +1 -1
- package/dist/domain/spatial/render-inputs.js +10 -2
- package/dist/domain/spatial/render-inputs.js.map +1 -1
- package/dist/domain/spatial/scene-cache.d.ts +13 -1
- package/dist/domain/spatial/scene-cache.js +88 -10
- package/dist/domain/spatial/scene-cache.js.map +1 -1
- package/dist/domain/spatial/scene-update.d.ts +6 -0
- package/dist/domain/spatial/scene-update.js +52 -0
- package/dist/domain/spatial/scene-update.js.map +1 -0
- package/dist/domain/spatial/scene.d.ts +3 -1
- package/dist/domain/spatial/scene.js +78 -20
- package/dist/domain/spatial/scene.js.map +1 -1
- package/dist/domain/style/model-input.d.ts +3 -0
- package/dist/domain/style/model-input.js +36 -0
- package/dist/domain/style/model-input.js.map +1 -0
- package/dist/domain/style/types.d.ts +7 -0
- package/dist/server/routes/spatial-sessions.js +28 -12
- package/dist/server/routes/spatial-sessions.js.map +1 -1
- package/dist/server/routes/style-library.js +39 -0
- package/dist/server/routes/style-library.js.map +1 -1
- package/dist/server/spatial-render-service.d.ts +21 -0
- package/dist/server/spatial-render-service.js +67 -0
- package/dist/server/spatial-render-service.js.map +1 -0
- package/dist/server/spatial-render-worker.d.ts +1 -0
- package/dist/server/spatial-render-worker.js +15 -0
- package/dist/server/spatial-render-worker.js.map +1 -0
- package/dist/server/spatial-render.d.ts +14 -2
- package/dist/server/spatial-render.js +38 -7
- package/dist/server/spatial-render.js.map +1 -1
- package/locales/en/common.json +60 -4
- package/migrations/0010_style_preview_models.sql +8 -0
- package/package.json +1 -1
- package/schemas/composition-draft.schema.json +457 -0
- package/skills/human2ai/SKILL.md +12 -4
- package/skills/human2ai/references/composition.md +192 -1
- package/skills/human2ai/references/spatial-modeling.md +20 -0
- package/web/out/404/index.html +1 -1
- package/web/out/404.html +1 -1
- package/web/out/__next.__PAGE__.txt +6 -6
- package/web/out/__next._full.txt +6 -6
- package/web/out/__next._tree.txt +3 -3
- package/web/out/_next/static/chunks/101.6ef34adad3e9815b.js +1 -0
- package/web/out/_next/static/chunks/130-7e9e715a084569bb.js +1 -0
- package/web/out/_next/static/chunks/166-96a8c6ae1e9aed59.js +1 -0
- package/web/out/_next/static/chunks/218.7b89674409b344b8.js +1 -0
- package/web/out/_next/static/chunks/484.92b016222d6749f2.js +1 -0
- package/web/out/_next/static/chunks/{773-24ba65074da7fa71.js → 635-611c009924b842be.js} +50 -50
- package/web/out/_next/static/chunks/653-6dfb97fec9a2d91b.js +1 -0
- package/web/out/_next/static/chunks/678-32d3aa1859450d63.js +1 -0
- package/web/out/_next/static/chunks/83607670-fd2f339702a222e4.js +1 -0
- package/web/out/_next/static/chunks/app/_not-found/page-b92817fdc4609975.js +1 -0
- package/web/out/_next/static/chunks/app/composition/page-6c12ee8472715448.js +1 -0
- package/web/out/_next/static/chunks/app/{layout-3b5f82ced220ef42.js → layout-1dd9196cde94389c.js} +1 -1
- package/web/out/_next/static/chunks/app/{page-d41d1cdb1d6ed018.js → page-741ecaf9ee8c3c07.js} +1 -1
- package/web/out/_next/static/chunks/app/spatial/page-5ba0940650df6c7f.js +1 -0
- package/web/out/_next/static/chunks/app/styles/page-1ccfc2c5fbc1e646.js +1 -0
- package/web/out/_next/static/chunks/app/ui-sketch/page-ef4f2e531583e684.js +1 -0
- package/web/out/_next/static/chunks/{c3f2cda1-9e279dab137b7272.js → c3f2cda1-ef85a0897724e46b.js} +1 -1
- package/web/out/_next/static/chunks/{main-app-ec9e35d71481270a.js → main-app-cf37a257d91ce62e.js} +1 -1
- package/web/out/_next/static/chunks/next/dist/client/components/builtin/global-error-33b4f58f02e82215.js +1 -0
- package/web/out/_next/static/chunks/webpack-d16ce1f994e98178.js +1 -0
- package/web/out/_next/static/css/06743b5635914999.css +5 -0
- package/web/out/_next/static/css/0b43ff4a0439c97e.css +1 -0
- package/web/out/_next/static/css/39e8cace36461a15.css +1 -0
- package/web/out/_not-found/__next._full.txt +3 -3
- package/web/out/_not-found/__next._not-found.__PAGE__.txt +3 -3
- package/web/out/_not-found/__next._tree.txt +1 -1
- package/web/out/_not-found/index.html +1 -1
- package/web/out/_not-found/index.txt +3 -3
- package/web/out/composition/__next._full.txt +6 -6
- package/web/out/composition/__next._tree.txt +3 -3
- package/web/out/composition/__next.composition.__PAGE__.txt +6 -6
- package/web/out/composition/index.html +1 -1
- package/web/out/composition/index.txt +6 -6
- package/web/out/index.html +1 -1
- package/web/out/index.txt +6 -6
- package/web/out/spatial/__next._full.txt +6 -6
- package/web/out/spatial/__next._tree.txt +3 -3
- package/web/out/spatial/__next.spatial.__PAGE__.txt +6 -6
- package/web/out/spatial/index.html +1 -1
- package/web/out/spatial/index.txt +6 -6
- package/web/out/styles/__next._full.txt +6 -6
- package/web/out/styles/__next._tree.txt +3 -3
- package/web/out/styles/__next.styles.__PAGE__.txt +6 -6
- package/web/out/styles/index.html +1 -1
- package/web/out/styles/index.txt +6 -6
- package/web/out/ui-sketch/__next._full.txt +6 -6
- package/web/out/ui-sketch/__next._tree.txt +3 -3
- package/web/out/ui-sketch/__next.ui-sketch.__PAGE__.txt +6 -6
- package/web/out/ui-sketch/index.html +1 -1
- package/web/out/ui-sketch/index.txt +6 -6
- package/web/out/_next/static/chunks/130-027b022c82d30077.js +0 -1
- package/web/out/_next/static/chunks/678-6f33e7277564a5df.js +0 -1
- package/web/out/_next/static/chunks/728-7597352c3a7eeaf9.js +0 -1
- package/web/out/_next/static/chunks/83607670-bc1279370476e59f.js +0 -1
- package/web/out/_next/static/chunks/app/_not-found/page-58bf95050b473956.js +0 -1
- package/web/out/_next/static/chunks/app/composition/page-30184e9c21352991.js +0 -1
- package/web/out/_next/static/chunks/app/spatial/page-fc3c66e1f94ed210.js +0 -1
- package/web/out/_next/static/chunks/app/styles/page-194a16109270e08d.js +0 -1
- package/web/out/_next/static/chunks/app/ui-sketch/page-c28eeab67fea766e.js +0 -1
- package/web/out/_next/static/chunks/next/dist/client/components/builtin/global-error-f62e60e5a6e74304.js +0 -1
- package/web/out/_next/static/chunks/webpack-fdd377b620f7ea99.js +0 -1
- package/web/out/_next/static/css/2ed56cbf8f662d23.css +0 -5
- package/web/out/_next/static/css/96839f63fcc5e50d.css +0 -1
- /package/web/out/_next/static/{Wakl9xK54qWYuP4Hpi0W9 → n4UcxqMmr_2CBmSxBp52z}/_buildManifest.js +0 -0
- /package/web/out/_next/static/{Wakl9xK54qWYuP4Hpi0W9 → n4UcxqMmr_2CBmSxBp52z}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { canonicalJson } from "../fingerprint.js";
|
|
2
|
+
const keys = new WeakMap();
|
|
2
3
|
/** Authored names and instructions do not alter spatial geometry or camera output. */
|
|
3
|
-
export function spatialEntityRenderKey(
|
|
4
|
-
|
|
4
|
+
export function spatialEntityRenderKey(entity) {
|
|
5
|
+
const { name: _name, note: _note, ...data } = entity;
|
|
6
|
+
// A cheap snapshot also detects in-place edits; reference identity alone is unsafe.
|
|
7
|
+
const raw = JSON.stringify(data), previous = keys.get(entity);
|
|
8
|
+
if (previous?.raw === raw)
|
|
9
|
+
return previous.key;
|
|
10
|
+
const key = canonicalJson(data);
|
|
11
|
+
keys.set(entity, { raw, key });
|
|
12
|
+
return key;
|
|
5
13
|
}
|
|
6
14
|
//# sourceMappingURL=render-inputs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-inputs.js","sourceRoot":"","sources":["../../../src/domain/spatial/render-inputs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,sFAAsF;AACtF,MAAM,UAAU,sBAAsB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"render-inputs.js","sourceRoot":"","sources":["../../../src/domain/spatial/render-inputs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,IAAI,GAAG,IAAI,OAAO,EAAkE,CAAC;AAE3F,sFAAsF;AACtF,MAAM,UAAU,sBAAsB,CAAC,MAAwC;IAC7E,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IACrD,oFAAoF;IACpF,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,QAAQ,EAAE,GAAG,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC,GAAG,CAAC;IAC/C,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import { Group } from "three";
|
|
1
|
+
import { Color, Group, Mesh } from "three";
|
|
2
2
|
import type { SpatialDraft } from "./types.ts";
|
|
3
|
+
interface SceneCacheOptions {
|
|
4
|
+
shade?(draft: SpatialDraft): Promise<Float32Array[]>;
|
|
5
|
+
onShaded?(): void;
|
|
6
|
+
}
|
|
3
7
|
/** Editor-owned meshes; deterministic exports continue to use createSpatialScene. */
|
|
4
8
|
export declare class SpatialSceneCache {
|
|
9
|
+
private options;
|
|
5
10
|
readonly scene: Group<import("three").Object3DEventMap>;
|
|
6
11
|
private entries;
|
|
12
|
+
readonly baseColors: WeakMap<Mesh<import("three").BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>, import("three").Material<import("three").MaterialEventMap> | import("three").Material<import("three").MaterialEventMap>[], import("three").Object3DEventMap>, Color>;
|
|
7
13
|
private shadedKey?;
|
|
8
14
|
private shadedTopology?;
|
|
9
15
|
private colors?;
|
|
16
|
+
private desired?;
|
|
17
|
+
private shading;
|
|
18
|
+
private disposed;
|
|
19
|
+
constructor(options?: SceneCacheOptions);
|
|
10
20
|
update(draft: SpatialDraft, options?: {
|
|
11
21
|
showRig?: boolean;
|
|
12
22
|
dragging?: boolean;
|
|
13
23
|
}): boolean;
|
|
24
|
+
private scheduleShading;
|
|
14
25
|
dispose(): void;
|
|
15
26
|
}
|
|
27
|
+
export {};
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { Group } from "three";
|
|
2
|
-
import { createSpatialScene, disposeSpatialScene } from "./scene.js";
|
|
2
|
+
import { createSpatialScene, disposeSpatialScene, updateSpatialRig } from "./scene.js";
|
|
3
3
|
import { applySpatialContactShading, spatialSurfaces } from "./lighting.js";
|
|
4
4
|
import { spatialEntityRenderKey } from "./render-inputs.js";
|
|
5
|
+
import { entityMatrix, rememberSpatialColors, updateSpatialGeometry } from "./scene-update.js";
|
|
6
|
+
import { updateCharacterModel } from "./model.js";
|
|
5
7
|
/** Editor-owned meshes; deterministic exports continue to use createSpatialScene. */
|
|
6
8
|
export class SpatialSceneCache {
|
|
9
|
+
options;
|
|
7
10
|
scene = new Group();
|
|
8
11
|
entries = new Map();
|
|
12
|
+
baseColors = new WeakMap();
|
|
9
13
|
shadedKey;
|
|
10
14
|
shadedTopology;
|
|
11
15
|
colors;
|
|
16
|
+
desired;
|
|
17
|
+
shading = false;
|
|
18
|
+
disposed = false;
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
12
22
|
update(draft, options = {}) {
|
|
13
23
|
const next = new Map();
|
|
14
24
|
let changed = false;
|
|
@@ -16,14 +26,49 @@ export class SpatialSceneCache {
|
|
|
16
26
|
const key = JSON.stringify([spatialEntityRenderKey(entity), Boolean(options.showRig)]);
|
|
17
27
|
let entry = this.entries.get(entity.id);
|
|
18
28
|
if (entry?.key !== key) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
const shapeKey = JSON.stringify([spatialEntityRenderKey({ ...entity, position: [0, 0, 0], rotation: [0, 0, 0] }), Boolean(options.showRig)]);
|
|
30
|
+
if (entry?.shapeKey === shapeKey) {
|
|
31
|
+
entry.group.matrixAutoUpdate = false;
|
|
32
|
+
entry.group.matrix.copy(entityMatrix(entity).multiply(entry.origin));
|
|
33
|
+
entry.group.matrixWorldNeedsUpdate = true;
|
|
34
|
+
entry.key = key;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const part = "bones" in entity
|
|
38
|
+
? { ...draft, characters: [entity], objects: [] }
|
|
39
|
+
: { ...draft, characters: [], objects: [entity] };
|
|
40
|
+
const rig = entry?.group.children.filter(object => object.userData.rig) ?? [];
|
|
41
|
+
if (rig.length)
|
|
42
|
+
entry.group.remove(...rig);
|
|
43
|
+
const model = entry?.group.children[0];
|
|
44
|
+
if (entry && "bones" in entity && model instanceof Group && updateCharacterModel(model, entity)) {
|
|
45
|
+
entry.group.matrixAutoUpdate = true;
|
|
46
|
+
entry.group.matrix.identity();
|
|
47
|
+
entry = { key, shapeKey, group: entry.group, origin: entityMatrix(entity).invert() };
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const fresh = createSpatialScene(part);
|
|
51
|
+
if (entry && updateSpatialGeometry(entry.group, fresh)) {
|
|
52
|
+
disposeSpatialScene(fresh);
|
|
53
|
+
entry = { key, shapeKey, group: entry.group, origin: entityMatrix(entity).invert() };
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
if (entry) {
|
|
57
|
+
this.scene.remove(entry.group);
|
|
58
|
+
disposeSpatialScene(entry.group);
|
|
59
|
+
}
|
|
60
|
+
entry = { key, shapeKey, group: fresh, origin: entityMatrix(entity).invert() };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if ("bones" in entity && options.showRig && entity.appearance !== "geometric") {
|
|
64
|
+
if (rig.length)
|
|
65
|
+
entry.group.add(...rig);
|
|
66
|
+
updateSpatialRig(entry.group, entity, true);
|
|
67
|
+
}
|
|
68
|
+
else if (rig.length)
|
|
69
|
+
disposeSpatialScene(new Group().add(...rig));
|
|
70
|
+
rememberSpatialColors(entry.group, this.baseColors);
|
|
22
71
|
}
|
|
23
|
-
const part = "bones" in entity
|
|
24
|
-
? { ...draft, characters: [entity], objects: [] }
|
|
25
|
-
: { ...draft, characters: [], objects: [entity] };
|
|
26
|
-
entry = { key, group: createSpatialScene(part, options) };
|
|
27
72
|
changed = true;
|
|
28
73
|
}
|
|
29
74
|
next.set(entity.id, entry);
|
|
@@ -45,10 +90,22 @@ export class SpatialSceneCache {
|
|
|
45
90
|
if (changed)
|
|
46
91
|
this.scene.updateMatrixWorld(true);
|
|
47
92
|
// Cameras, selection, helpers and light switches do not affect contact rays.
|
|
48
|
-
const surfaceKey = JSON.stringify([
|
|
93
|
+
const surfaceKey = JSON.stringify([...next.values()].map(entry => entry.key));
|
|
49
94
|
const meshes = spatialSurfaces(this.scene);
|
|
50
95
|
const topology = meshes.map(mesh => JSON.stringify([mesh.userData, mesh.geometry.getAttribute("position").count])).join("/");
|
|
51
|
-
|
|
96
|
+
const needsShading = !this.colors || topology !== this.shadedTopology || surfaceKey !== this.shadedKey;
|
|
97
|
+
this.desired = !options.dragging && needsShading ? { draft, key: surfaceKey, topology } : undefined;
|
|
98
|
+
if (this.options.shade) {
|
|
99
|
+
if (!this.colors || topology !== this.shadedTopology) {
|
|
100
|
+
this.colors = meshes.map(mesh => new Float32Array(mesh.geometry.getAttribute("position").count * 3).fill(1));
|
|
101
|
+
this.shadedTopology = topology;
|
|
102
|
+
this.shadedKey = undefined;
|
|
103
|
+
}
|
|
104
|
+
if (changed)
|
|
105
|
+
applySpatialContactShading(this.scene, draft, this.colors);
|
|
106
|
+
this.scheduleShading();
|
|
107
|
+
}
|
|
108
|
+
else if (!this.colors || topology !== this.shadedTopology || (!options.dragging && needsShading)) {
|
|
52
109
|
this.colors = applySpatialContactShading(this.scene, draft);
|
|
53
110
|
this.shadedKey = surfaceKey;
|
|
54
111
|
this.shadedTopology = topology;
|
|
@@ -58,7 +115,28 @@ export class SpatialSceneCache {
|
|
|
58
115
|
}
|
|
59
116
|
return changed;
|
|
60
117
|
}
|
|
118
|
+
scheduleShading() {
|
|
119
|
+
if (this.shading || !this.desired || this.disposed)
|
|
120
|
+
return;
|
|
121
|
+
const request = this.desired;
|
|
122
|
+
this.shading = true;
|
|
123
|
+
void this.options.shade(request.draft).then(colors => {
|
|
124
|
+
if (this.disposed || this.desired?.key !== request.key || this.desired.topology !== request.topology)
|
|
125
|
+
return;
|
|
126
|
+
this.colors = applySpatialContactShading(this.scene, request.draft, colors);
|
|
127
|
+
this.shadedKey = request.key;
|
|
128
|
+
this.shadedTopology = request.topology;
|
|
129
|
+
this.desired = undefined;
|
|
130
|
+
this.options.onShaded?.();
|
|
131
|
+
}).catch(() => {
|
|
132
|
+
// Keep the last valid colors; a subsequent scene change can retry the worker.
|
|
133
|
+
if (this.desired?.key === request.key)
|
|
134
|
+
this.desired = undefined;
|
|
135
|
+
}).finally(() => { this.shading = false; this.scheduleShading(); });
|
|
136
|
+
}
|
|
61
137
|
dispose() {
|
|
138
|
+
this.disposed = true;
|
|
139
|
+
this.desired = undefined;
|
|
62
140
|
disposeSpatialScene(this.scene);
|
|
63
141
|
this.scene.clear();
|
|
64
142
|
this.entries.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-cache.js","sourceRoot":"","sources":["../../../src/domain/spatial/scene-cache.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"scene-cache.js","sourceRoot":"","sources":["../../../src/domain/spatial/scene-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Color, Group, Matrix4, Mesh } from "three";
|
|
2
|
+
import type { SpatialCharacter, SpatialObject } from "./types.ts";
|
|
3
|
+
export declare const entityMatrix: (entity: SpatialCharacter | SpatialObject) => Matrix4;
|
|
4
|
+
/** Preserve GPU buffers and picking identity when authored topology stays the same. */
|
|
5
|
+
export declare function updateSpatialGeometry(target: Group, source: Group): boolean;
|
|
6
|
+
export declare function rememberSpatialColors(group: Group, colors: WeakMap<Mesh, Color>): void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { LineSegments, Matrix4, Mesh, MeshLambertMaterial, Vector3 } from "three";
|
|
2
|
+
import { quaternion, vector } from "./kinematics.js";
|
|
3
|
+
export const entityMatrix = (entity) => new Matrix4().compose(vector(entity.position), quaternion(entity.rotation), new Vector3(1, 1, 1));
|
|
4
|
+
const drawable = (object) => object instanceof Mesh || object instanceof LineSegments;
|
|
5
|
+
/** Preserve GPU buffers and picking identity when authored topology stays the same. */
|
|
6
|
+
export function updateSpatialGeometry(target, source) {
|
|
7
|
+
const before = [], after = [];
|
|
8
|
+
target.traverse(object => before.push(object));
|
|
9
|
+
source.traverse(object => after.push(object));
|
|
10
|
+
if (before.length !== after.length || before.some((object, i) => {
|
|
11
|
+
const next = after[i];
|
|
12
|
+
if (object.type !== next.type || JSON.stringify(object.userData) !== JSON.stringify(next.userData))
|
|
13
|
+
return true;
|
|
14
|
+
if (!drawable(object) || !drawable(next))
|
|
15
|
+
return false;
|
|
16
|
+
return object.geometry.getAttribute("position").count !== next.geometry.getAttribute("position").count
|
|
17
|
+
|| object.geometry.index?.count !== next.geometry.index?.count
|
|
18
|
+
|| Object.entries(next.geometry.attributes).some(([name, attribute]) => object.geometry.getAttribute(name)?.array.length !== attribute.array.length);
|
|
19
|
+
}))
|
|
20
|
+
return false;
|
|
21
|
+
before.forEach((object, i) => {
|
|
22
|
+
const next = after[i];
|
|
23
|
+
object.position.copy(next.position);
|
|
24
|
+
object.quaternion.copy(next.quaternion);
|
|
25
|
+
object.scale.copy(next.scale);
|
|
26
|
+
object.matrixAutoUpdate = next.matrixAutoUpdate;
|
|
27
|
+
object.matrix.copy(next.matrix);
|
|
28
|
+
if (!drawable(object) || !drawable(next))
|
|
29
|
+
return;
|
|
30
|
+
for (const [name, attribute] of Object.entries(next.geometry.attributes)) {
|
|
31
|
+
const previous = object.geometry.getAttribute(name);
|
|
32
|
+
previous.array.set(attribute.array);
|
|
33
|
+
previous.needsUpdate = true;
|
|
34
|
+
}
|
|
35
|
+
if (object.geometry.index && next.geometry.index) {
|
|
36
|
+
object.geometry.index.array.set(next.geometry.index.array);
|
|
37
|
+
object.geometry.index.needsUpdate = true;
|
|
38
|
+
}
|
|
39
|
+
object.geometry.boundingBox = null;
|
|
40
|
+
object.geometry.boundingSphere = null;
|
|
41
|
+
if (!Array.isArray(object.material) && !Array.isArray(next.material))
|
|
42
|
+
object.material.copy(next.material);
|
|
43
|
+
});
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
export function rememberSpatialColors(group, colors) {
|
|
47
|
+
group.traverse(object => {
|
|
48
|
+
if (object instanceof Mesh && object.material instanceof MeshLambertMaterial)
|
|
49
|
+
colors.set(object, object.material.color.clone());
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=scene-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-update.js","sourceRoot":"","sources":["../../../src/domain/spatial/scene-update.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Group, OrthographicCamera, PerspectiveCamera } from "three";
|
|
2
|
-
import type { SpatialCamera, SpatialDraft } from "./types.ts";
|
|
2
|
+
import type { SpatialCamera, SpatialCharacter, SpatialDraft } from "./types.ts";
|
|
3
3
|
export declare function createOutputCamera(source: SpatialCamera): OrthographicCamera | PerspectiveCamera;
|
|
4
4
|
export declare function createSpatialScene(draft: SpatialDraft, options?: {
|
|
5
5
|
showRig?: boolean;
|
|
6
6
|
handCreases?: boolean;
|
|
7
7
|
}): Group;
|
|
8
|
+
/** Unit helper geometry survives pose/proportion changes; only transforms change. */
|
|
9
|
+
export declare function updateSpatialRig(scene: Group, actor: SpatialCharacter, batch?: boolean): void;
|
|
8
10
|
export declare function disposeSpatialScene(scene: Group): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BoxGeometry, Color, CylinderGeometry, DoubleSide, Group, Mesh, MeshLambertMaterial, LineSegments, OrthographicCamera, PerspectiveCamera, Quaternion, SphereGeometry, Vector3, } from "three";
|
|
1
|
+
import { BoxGeometry, Color, CylinderGeometry, DoubleSide, Group, InstancedMesh, Matrix4, Mesh, MeshLambertMaterial, LineSegments, OrthographicCamera, PerspectiveCamera, Quaternion, SphereGeometry, Vector3, } from "three";
|
|
2
2
|
import { createCharacterModel } from "./model.js";
|
|
3
3
|
import { jointWorldTransforms, quaternion, vector } from "./kinematics.js";
|
|
4
4
|
import { fingerPart } from "./hands.js";
|
|
@@ -27,25 +27,7 @@ export function createSpatialScene(draft, options = {}) {
|
|
|
27
27
|
scene.add(createCharacterModel(actor, options));
|
|
28
28
|
if (!options.showRig || actor.appearance === "geometric")
|
|
29
29
|
continue;
|
|
30
|
-
|
|
31
|
-
const overlay = (mesh) => {
|
|
32
|
-
mesh.material.depthTest = false;
|
|
33
|
-
mesh.renderOrder = 2;
|
|
34
|
-
};
|
|
35
|
-
for (const joint of actor.joints.filter(j => !j.terminal || Boolean(fingerPart(actor.bones.find(b => b.endJointId === j.id)?.modelPart ?? "")))) {
|
|
36
|
-
overlay(add(new SphereGeometry(joint.radius, 12, 8), "#e6a65c", world[joint.id].position, world[joint.id].rotation, { characterId: actor.id, jointId: joint.id, rig: "joint" }));
|
|
37
|
-
}
|
|
38
|
-
for (const bone of actor.bones) {
|
|
39
|
-
const finger = fingerPart(bone.modelPart);
|
|
40
|
-
if (finger?.segment === 0)
|
|
41
|
-
continue;
|
|
42
|
-
const start = world[bone.startJointId].position, end = world[bone.endJointId].position;
|
|
43
|
-
const direction = end.clone().sub(start), length = direction.length();
|
|
44
|
-
const data = { characterId: actor.id, boneId: bone.id, rig: "bone" };
|
|
45
|
-
const radius = actor.height * (finger ? .0018 : .009);
|
|
46
|
-
const mesh = add(new CylinderGeometry(radius, radius, length, 10), "#728aa1", start.clone().add(end).multiplyScalar(.5), new Quaternion().setFromUnitVectors(new Vector3(0, 1, 0), direction.normalize()), data);
|
|
47
|
-
overlay(mesh);
|
|
48
|
-
}
|
|
30
|
+
updateSpatialRig(scene, actor);
|
|
49
31
|
}
|
|
50
32
|
for (const object of draft.objects) {
|
|
51
33
|
const size = vector(object.size);
|
|
@@ -57,9 +39,85 @@ export function createSpatialScene(draft, options = {}) {
|
|
|
57
39
|
scene.updateMatrixWorld(true);
|
|
58
40
|
return scene;
|
|
59
41
|
}
|
|
42
|
+
/** Unit helper geometry survives pose/proportion changes; only transforms change. */
|
|
43
|
+
export function updateSpatialRig(scene, actor, batch = false) {
|
|
44
|
+
const parts = spatialRigParts(actor);
|
|
45
|
+
if (batch) {
|
|
46
|
+
const matrix = new Matrix4();
|
|
47
|
+
for (const rig of ["joint", "bone"]) {
|
|
48
|
+
const instances = parts.filter(part => part.data.rig === rig);
|
|
49
|
+
let mesh = scene.children.find(object => object instanceof InstancedMesh && object.userData.characterId === actor.id && object.userData.rig === rig);
|
|
50
|
+
if (mesh && mesh.count !== instances.length) {
|
|
51
|
+
scene.remove(mesh);
|
|
52
|
+
disposeSpatialScene(new Group().add(mesh));
|
|
53
|
+
mesh = undefined;
|
|
54
|
+
}
|
|
55
|
+
if (!mesh) {
|
|
56
|
+
mesh = new InstancedMesh(rigGeometry(rig), new MeshLambertMaterial({ side: DoubleSide, depthTest: false }), instances.length);
|
|
57
|
+
mesh.renderOrder = 2;
|
|
58
|
+
scene.add(mesh);
|
|
59
|
+
}
|
|
60
|
+
mesh.userData = { characterId: actor.id, rig, instances: instances.map(part => part.data) };
|
|
61
|
+
const color = new Color(rig === "joint" ? "#e6a65c" : "#728aa1");
|
|
62
|
+
instances.forEach((part, i) => {
|
|
63
|
+
mesh.setMatrixAt(i, matrix.compose(part.position, part.rotation, part.scale));
|
|
64
|
+
mesh.setColorAt(i, color);
|
|
65
|
+
});
|
|
66
|
+
mesh.instanceMatrix.needsUpdate = true;
|
|
67
|
+
if (mesh.instanceColor)
|
|
68
|
+
mesh.instanceColor.needsUpdate = true;
|
|
69
|
+
mesh.computeBoundingSphere();
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const existing = new Map(scene.children.filter(object => object.userData.rig && object.userData.characterId === actor.id)
|
|
74
|
+
.map(object => [`${object.userData.rig}/${object.userData.jointId ?? object.userData.boneId}`, object]));
|
|
75
|
+
const add = (rig, id) => {
|
|
76
|
+
const key = `${rig}/${id}`;
|
|
77
|
+
let mesh = existing.get(key);
|
|
78
|
+
existing.delete(key);
|
|
79
|
+
if (!mesh) {
|
|
80
|
+
mesh = new Mesh(rigGeometry(rig), new MeshLambertMaterial({ side: DoubleSide, depthTest: false }));
|
|
81
|
+
mesh.userData = { characterId: actor.id, rig, [rig === "joint" ? "jointId" : "boneId"]: id };
|
|
82
|
+
mesh.renderOrder = 2;
|
|
83
|
+
scene.add(mesh);
|
|
84
|
+
}
|
|
85
|
+
mesh.material.color.set(rig === "joint" ? "#e6a65c" : "#728aa1");
|
|
86
|
+
return mesh;
|
|
87
|
+
};
|
|
88
|
+
for (const part of parts) {
|
|
89
|
+
const mesh = add(part.data.rig, part.data.jointId ?? part.data.boneId);
|
|
90
|
+
mesh.position.copy(part.position);
|
|
91
|
+
mesh.quaternion.copy(part.rotation);
|
|
92
|
+
mesh.scale.copy(part.scale);
|
|
93
|
+
}
|
|
94
|
+
for (const mesh of existing.values()) {
|
|
95
|
+
scene.remove(mesh);
|
|
96
|
+
mesh.geometry.dispose();
|
|
97
|
+
mesh.material.dispose();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const rigGeometry = (rig) => rig === "joint" ? new SphereGeometry(1, 12, 8) : new CylinderGeometry(1, 1, 1, 10);
|
|
101
|
+
function spatialRigParts(actor) {
|
|
102
|
+
const world = jointWorldTransforms(actor), parts = [];
|
|
103
|
+
for (const joint of actor.joints.filter(j => !j.terminal || Boolean(fingerPart(actor.bones.find(b => b.endJointId === j.id)?.modelPart ?? "")))) {
|
|
104
|
+
parts.push({ data: { characterId: actor.id, rig: "joint", jointId: joint.id }, position: world[joint.id].position, rotation: world[joint.id].rotation, scale: new Vector3().setScalar(joint.radius) });
|
|
105
|
+
}
|
|
106
|
+
for (const bone of actor.bones) {
|
|
107
|
+
const finger = fingerPart(bone.modelPart);
|
|
108
|
+
if (finger?.segment === 0)
|
|
109
|
+
continue;
|
|
110
|
+
const start = world[bone.startJointId].position, end = world[bone.endJointId].position;
|
|
111
|
+
const direction = end.clone().sub(start), length = direction.length(), radius = actor.height * (finger ? .0018 : .009);
|
|
112
|
+
parts.push({ data: { characterId: actor.id, rig: "bone", boneId: bone.id }, position: start.clone().add(end).multiplyScalar(.5), rotation: new Quaternion().setFromUnitVectors(new Vector3(0, 1, 0), direction.normalize()), scale: new Vector3(radius, length, radius) });
|
|
113
|
+
}
|
|
114
|
+
return parts;
|
|
115
|
+
}
|
|
60
116
|
export function disposeSpatialScene(scene) {
|
|
61
117
|
scene.traverse(object => {
|
|
62
118
|
if (object instanceof Mesh || object instanceof LineSegments) {
|
|
119
|
+
if (object instanceof InstancedMesh)
|
|
120
|
+
object.dispose();
|
|
63
121
|
object.geometry.dispose();
|
|
64
122
|
for (const material of Array.isArray(object.material) ? object.material : [object.material])
|
|
65
123
|
material.dispose();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene.js","sourceRoot":"","sources":["../../../src/domain/spatial/scene.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"scene.js","sourceRoot":"","sources":["../../../src/domain/spatial/scene.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const MAX_STYLE_MODEL_BYTES = 10 * 1024 * 1024;
|
|
2
|
+
/** Accept a self-contained, static GLB example, never executable generator code. */
|
|
3
|
+
export function validStyleModel(data) {
|
|
4
|
+
if (!Buffer.isBuffer(data) || data.length < 28 || data.length > MAX_STYLE_MODEL_BYTES)
|
|
5
|
+
return false;
|
|
6
|
+
if (data.readUInt32LE(0) !== 0x46546c67 || data.readUInt32LE(4) !== 2 || data.readUInt32LE(8) !== data.length)
|
|
7
|
+
return false;
|
|
8
|
+
const jsonLength = data.readUInt32LE(12);
|
|
9
|
+
if (jsonLength % 4 || 20 + jsonLength > data.length || data.readUInt32LE(16) !== 0x4e4f534a)
|
|
10
|
+
return false;
|
|
11
|
+
try {
|
|
12
|
+
const document = JSON.parse(data.subarray(20, 20 + jsonLength).toString("utf8"));
|
|
13
|
+
if (document.asset?.version !== "2.0" || !document.scenes?.length || !document.nodes?.length || !document.meshes?.length)
|
|
14
|
+
return false;
|
|
15
|
+
// Textures and geometry must live in this file, with no decoder dependencies.
|
|
16
|
+
if (document.extensionsRequired?.some((name) => ["KHR_draco_mesh_compression", "EXT_meshopt_compression", "KHR_texture_basisu"].includes(name)))
|
|
17
|
+
return false;
|
|
18
|
+
if (JSON.stringify(document).includes('"uri":'))
|
|
19
|
+
return false;
|
|
20
|
+
const binaryOffset = 20 + jsonLength;
|
|
21
|
+
if (binaryOffset + 8 > data.length || data.readUInt32LE(binaryOffset + 4) !== 0x004e4942)
|
|
22
|
+
return false;
|
|
23
|
+
const binaryLength = data.readUInt32LE(binaryOffset);
|
|
24
|
+
if (binaryLength % 4 || binaryOffset + 8 + binaryLength !== data.length)
|
|
25
|
+
return false;
|
|
26
|
+
if (document.buffers?.length !== 1 || !Number.isInteger(document.buffers[0].byteLength) || document.buffers[0].byteLength <= 0 || document.buffers[0].byteLength > binaryLength)
|
|
27
|
+
return false;
|
|
28
|
+
return Array.isArray(document.bufferViews) && document.bufferViews.every((view) => (view.buffer === 0 && Number.isInteger(view.byteOffset ?? 0) && (view.byteOffset ?? 0) >= 0
|
|
29
|
+
&& Number.isInteger(view.byteLength) && view.byteLength > 0
|
|
30
|
+
&& (view.byteOffset ?? 0) + view.byteLength <= document.buffers[0].byteLength));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=model-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-input.js","sourceRoot":"","sources":["../../../src/domain/style/model-input.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtD,oFAAoF;AACpF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB;QAAE,OAAO,KAAK,CAAC;IACpG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,UAAU,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5H,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACzC,IAAI,UAAU,GAAG,CAAC,IAAI,EAAE,GAAG,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAC1G,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACjF,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;YAAE,OAAO,KAAK,CAAC;QACvI,8EAA8E;QAC9E,IAAI,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,4BAA4B,EAAE,yBAAyB,EAAE,oBAAoB,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QACtK,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;QAC9D,MAAM,YAAY,GAAG,EAAE,GAAG,UAAU,CAAC;QACrC,IAAI,YAAY,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,YAAY,GAAG,CAAC,CAAC,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QACvG,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACrD,IAAI,YAAY,GAAG,CAAC,IAAI,YAAY,GAAG,CAAC,GAAG,YAAY,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACtF,IAAI,QAAQ,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,YAAY;YAAE,OAAO,KAAK,CAAC;QAC9L,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAiE,EAAE,EAAE,CAAC,CAC9I,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC;eACvF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC;eACxD,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAC9E,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -22,10 +22,17 @@ export interface StyleEntry {
|
|
|
22
22
|
description: string;
|
|
23
23
|
promptSummary: string;
|
|
24
24
|
referenceImages: StyleReferenceImage[];
|
|
25
|
+
previewModel?: StylePreviewModel;
|
|
25
26
|
revision: number;
|
|
26
27
|
createdAt: string;
|
|
27
28
|
updatedAt: string;
|
|
28
29
|
}
|
|
30
|
+
export interface StylePreviewModel {
|
|
31
|
+
id: string;
|
|
32
|
+
originalFilename: string;
|
|
33
|
+
byteSize: number;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
}
|
|
29
36
|
export interface SessionStyleState {
|
|
30
37
|
session: Session;
|
|
31
38
|
style: StyleEntry | null;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import schema from "../../../schemas/spatial-draft.schema.json" with { type: "json" };
|
|
2
2
|
import { createSpatialDraft, SpatialConstraintError } from "../../domain/spatial/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { SpatialRenderService } from "../spatial-render-service.js";
|
|
4
|
+
import { SpatialCameraRenderKeys } from "../../domain/spatial/camera-render-key.js";
|
|
5
|
+
import { cameraBoxView } from "../../domain/spatial/camera-box.js";
|
|
4
6
|
import { SPATIAL_BOX_FACES, SPATIAL_RENDER_PASSES } from "../../domain/spatial/types.js";
|
|
5
7
|
import { parseSpatialBoxViews } from "../../domain/spatial/camera-box-sheet.js";
|
|
6
8
|
import en from "../../../locales/en/common.json" with { type: "json" };
|
|
@@ -10,15 +12,25 @@ export function registerSpatialSessionRoutes(server, repository) {
|
|
|
10
12
|
server.addSchema(fastifySchema);
|
|
11
13
|
registerDraftVersionRoutes(server, { routePrefix: "/api/v1/sessions/:sessionId/spatial", draftSchema: { $ref: schema.$id }, repository });
|
|
12
14
|
const cache = new Map();
|
|
15
|
+
const inFlight = new Map();
|
|
16
|
+
const renderer = new SpatialRenderService(), renderKeys = new SpatialCameraRenderKeys();
|
|
17
|
+
server.addHook("onClose", () => renderer.close());
|
|
13
18
|
const cached = (key, render) => {
|
|
14
|
-
|
|
19
|
+
const image = cache.get(key);
|
|
20
|
+
if (image) {
|
|
21
|
+
cache.delete(key);
|
|
22
|
+
cache.set(key, image);
|
|
23
|
+
return Promise.resolve(image);
|
|
24
|
+
}
|
|
25
|
+
let pending = inFlight.get(key);
|
|
15
26
|
if (!pending) {
|
|
16
|
-
pending = render()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
pending = render().then(image => {
|
|
28
|
+
cache.set(key, image);
|
|
29
|
+
if (cache.size > 24)
|
|
30
|
+
cache.delete(cache.keys().next().value);
|
|
31
|
+
return image;
|
|
32
|
+
}).finally(() => { inFlight.delete(key); });
|
|
33
|
+
inFlight.set(key, pending);
|
|
22
34
|
}
|
|
23
35
|
return pending;
|
|
24
36
|
};
|
|
@@ -46,8 +58,8 @@ export function registerSpatialSessionRoutes(server, repository) {
|
|
|
46
58
|
if (!camera)
|
|
47
59
|
return reply.code(404).send({ code: "SPATIAL_CAMERA_NOT_FOUND", message: "Camera not found" });
|
|
48
60
|
const pass = request.query.pass ?? "color";
|
|
49
|
-
const key = JSON.stringify([sessionId,
|
|
50
|
-
const pending = cached(key, () =>
|
|
61
|
+
const key = JSON.stringify([sessionId, cameraId, renderKeys.key(draft, camera, pass)]);
|
|
62
|
+
const pending = cached(key, () => renderer.render({ draft, camera, pass }));
|
|
51
63
|
return reply.header("cache-control", request.query.revision ? "private, max-age=31536000, immutable" : "no-cache")
|
|
52
64
|
.header("x-spatial-revision", version?.revision ?? 0).header("x-spatial-render-pass", pass).type("image/png").send(await pending);
|
|
53
65
|
}
|
|
@@ -71,8 +83,12 @@ export function registerSpatialSessionRoutes(server, repository) {
|
|
|
71
83
|
if (!box || !version)
|
|
72
84
|
return reply.code(404).send({ code: "SPATIAL_CAMERA_NOT_FOUND", message: "Camera box not found" });
|
|
73
85
|
const pass = request.query.pass ?? "color", view = views ?? request.query.view ?? "sheet";
|
|
74
|
-
const
|
|
75
|
-
const
|
|
86
|
+
const faces = typeof view === "string" ? view === "sheet" ? SPATIAL_BOX_FACES : [view] : view;
|
|
87
|
+
const key = JSON.stringify([sessionId, "camera-box", boxId, view, faces.map(face => {
|
|
88
|
+
const { source, camera } = cameraBoxView(box, face);
|
|
89
|
+
return renderKeys.key(version.draft, source, pass, camera);
|
|
90
|
+
})]);
|
|
91
|
+
const png = cached(key, () => renderer.render({ draft: version.draft, box, view, pass }));
|
|
76
92
|
return reply.header("cache-control", request.query.revision ? "private, max-age=31536000, immutable" : "no-cache")
|
|
77
93
|
.header("x-spatial-revision", version.revision).header("x-spatial-render-pass", pass).header("x-spatial-box-view", typeof view === "string" ? view : "sheet")
|
|
78
94
|
.header("x-spatial-box-views", views?.join(",") ?? (view === "sheet" ? SPATIAL_BOX_FACES.join(",") : view)).type("image/png").send(await png);
|