@flighthq/skeleton3d 0.2.1-next.585.c7219b8 → 0.2.1-next.640.ec2ea9a
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/prepareSceneSkinning.d.ts +4 -0
- package/dist/prepareSceneSkinning.d.ts.map +1 -0
- package/dist/prepareSceneSkinning.js +71 -0
- package/dist/prepareSceneSkinning.js.map +1 -0
- package/dist/skinMeshGeometry.js +2 -2
- package/dist/skinMeshGeometry.js.map +1 -1
- package/dist/updateMeshDeformation.d.ts.map +1 -1
- package/dist/updateMeshDeformation.js +5 -1
- package/dist/updateMeshDeformation.js.map +1 -1
- package/dist/updateMeshSkin.d.ts.map +1 -1
- package/dist/updateMeshSkin.js +4 -2
- package/dist/updateMeshSkin.js.map +1 -1
- package/package.json +6 -7
- package/src/prepareSceneSkinning.test.ts +97 -0
- package/src/updateMeshSkin.test.ts +5 -2
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareSceneSkinning.d.ts","sourceRoot":"","sources":["../src/prepareSceneSkinning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAe,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAwBlE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAoB9D;AAcD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAcnE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createAabb } from '@flighthq/geometry';
|
|
2
|
+
import { getMeshGeometrySkinBindPose, setMeshGeometrySkinBindPose } from '@flighthq/mesh';
|
|
3
|
+
import { getNodeRuntime } from '@flighthq/node';
|
|
4
|
+
import { getMeshSkinConservativeBounds } from './getMeshSkinBounds';
|
|
5
|
+
import { computeSkeleton3DJointMatrices } from './skeleton3d';
|
|
6
|
+
import { captureMeshSkinBindPose } from './skinMeshGeometry';
|
|
7
|
+
// Readies one skinned mesh for a GPU-skinned frame WITHOUT CPU-posing its vertices. It recomputes the
|
|
8
|
+
// skeleton's joint palette from the joints' current world transforms (what the HAS_SKIN shader variant
|
|
9
|
+
// deforms from) and writes the mesh's POSED local-space bounds into the node runtime's
|
|
10
|
+
// deformedLocalBounds slot, so cull and picking test the posed silhouette instead of the static
|
|
11
|
+
// bind-pose box a GPU-skinned mesh's geometry still holds. A non-skinned mesh is a no-op.
|
|
12
|
+
//
|
|
13
|
+
// It deliberately does NOT call skinMeshGeometry — a GPU-skinned mesh must keep bind-pose vertices in
|
|
14
|
+
// geometry.vertices, the buffer the shader deforms, so CPU-posing them here would double-skin the draw
|
|
15
|
+
// or clobber the upload. The bind pose the bounds sweep needs is obtained by CAPTURE, which only reads:
|
|
16
|
+
// captureMeshSkinBindPose de-interleaves the current vertices into its own arrays and writes nothing
|
|
17
|
+
// back. That read-only capture is what lets a pure-GPU skin path have posed bounds at all. (If the app
|
|
18
|
+
// also drives CPU skinning through updateMeshSkin, the bind pose was already captured from pristine
|
|
19
|
+
// vertices by that path and this capture is a no-op.)
|
|
20
|
+
//
|
|
21
|
+
// The sweep is joint-driven, not per-vertex: getMeshSkinConservativeBounds unions the rest box
|
|
22
|
+
// transformed by each referenced joint's palette matrix, costing one box-transform per joint rather
|
|
23
|
+
// than an O(vertices) pass — cheap enough to run before the cull, which is the only ordering that culls
|
|
24
|
+
// a posed mesh correctly. This is the per-mesh primitive; prepareSceneSkinning drives a whole scene.
|
|
25
|
+
export function prepareMeshSkinning(mesh) {
|
|
26
|
+
const skin = mesh.skin;
|
|
27
|
+
if (skin == null)
|
|
28
|
+
return;
|
|
29
|
+
computeSkeleton3DJointMatrices(skin.skeleton);
|
|
30
|
+
const geometry = mesh.geometry;
|
|
31
|
+
let bindPose = getMeshGeometrySkinBindPose(geometry);
|
|
32
|
+
if (bindPose === null) {
|
|
33
|
+
bindPose = captureMeshSkinBindPose(geometry);
|
|
34
|
+
setMeshGeometrySkinBindPose(geometry, bindPose);
|
|
35
|
+
}
|
|
36
|
+
const runtime = getNodeRuntime(mesh);
|
|
37
|
+
let bounds = runtime.deformedLocalBounds;
|
|
38
|
+
if (bounds == null) {
|
|
39
|
+
bounds = createAabb();
|
|
40
|
+
runtime.deformedLocalBounds = bounds;
|
|
41
|
+
}
|
|
42
|
+
getMeshSkinConservativeBounds(bounds, bindPose, skin.skeleton);
|
|
43
|
+
}
|
|
44
|
+
// Runs prepareMeshSkinning over every enabled mesh in the subtree rooted at `scene` — the per-frame
|
|
45
|
+
// skinning-prep pass. Call it BEFORE prepareSceneRender: the render prepare pass culls against the
|
|
46
|
+
// posed bounds this writes, so driving it afterwards would cull one frame stale. A skinned+morphed app
|
|
47
|
+
// also calls the morph pass (prepareSceneMorph in @flighthq/scene); the two are independent so each
|
|
48
|
+
// deformer's consumers pay only for the pass they use — a morph-only app never imports this package.
|
|
49
|
+
//
|
|
50
|
+
// This is a separate caller-invoked pass rather than something prepareSceneRender does itself so that
|
|
51
|
+
// @flighthq/render never depends on @flighthq/skeleton3d: cull consumes the posed bounds as plain data
|
|
52
|
+
// off the node runtime, and a rigid or 2D bundle pays nothing for skinning it does not use. The walk
|
|
53
|
+
// itself needs only @flighthq/node + @flighthq/mesh (both already skeleton3d dependencies), so it
|
|
54
|
+
// carries no @flighthq/scene dependency at all. A scene with no skinned meshes may skip this call; it
|
|
55
|
+
// is a no-op for rigid meshes, and disabled subtrees are skipped whole (matching the render walk).
|
|
56
|
+
export function prepareSceneSkinning(scene) {
|
|
57
|
+
if (!scene.enabled)
|
|
58
|
+
return;
|
|
59
|
+
// Structural mesh test (carries geometry), so it holds for meshes created with a custom kind — the
|
|
60
|
+
// same discrimination collectVisibleMeshes uses.
|
|
61
|
+
const mesh = scene;
|
|
62
|
+
if (mesh.geometry != null)
|
|
63
|
+
prepareMeshSkinning(mesh);
|
|
64
|
+
const children = getNodeRuntime(scene).children;
|
|
65
|
+
if (children !== null) {
|
|
66
|
+
for (let i = 0; i < children.length; i++) {
|
|
67
|
+
prepareSceneSkinning(children[i]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=prepareSceneSkinning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareSceneSkinning.js","sourceRoot":"","sources":["../src/prepareSceneSkinning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,sGAAsG;AACtG,uGAAuG;AACvG,uFAAuF;AACvF,gGAAgG;AAChG,0FAA0F;AAC1F,EAAE;AACF,sGAAsG;AACtG,uGAAuG;AACvG,wGAAwG;AACxG,qGAAqG;AACrG,uGAAuG;AACvG,oGAAoG;AACpG,sDAAsD;AACtD,EAAE;AACF,+FAA+F;AAC/F,oGAAoG;AACpG,wGAAwG;AACxG,qGAAqG;AACrG,MAAM,UAAU,mBAAmB,CAAC,IAAoB;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO;IAEzB,8BAA8B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,IAAI,QAAQ,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,QAAQ,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAC7C,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,OAAO,GAAG,cAAc,CAAC,IAAe,CAAgB,CAAC;IAC/D,IAAI,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IACzC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,MAAM,GAAG,UAAU,EAAE,CAAC;QACtB,OAAO,CAAC,mBAAmB,GAAG,MAAM,CAAC;IACvC,CAAC;IACD,6BAA6B,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED,oGAAoG;AACpG,mGAAmG;AACnG,uGAAuG;AACvG,oGAAoG;AACpG,qGAAqG;AACrG,EAAE;AACF,sGAAsG;AACtG,uGAAuG;AACvG,qGAAqG;AACrG,kGAAkG;AAClG,sGAAsG;AACtG,mGAAmG;AACnG,MAAM,UAAU,oBAAoB,CAAC,KAAwB;IAC3D,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO;IAE3B,mGAAmG;IACnG,iDAAiD;IACjD,MAAM,IAAI,GAAG,KAAwB,CAAC;IACtC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI;QAAE,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAErD,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC;IAChD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/skinMeshGeometry.js
CHANGED
|
@@ -60,8 +60,8 @@ export function captureMeshSkinBindPose(geometry) {
|
|
|
60
60
|
// first) into the bind pose's scratch, writes the skinned positions/normals back into the interleaved
|
|
61
61
|
// `geometry.vertices`, and bumps `geometry.version` so the backends re-upload. `bindPose` is the
|
|
62
62
|
// capture the caller holds on the geometry runtime; only the position/normal channels are rewritten,
|
|
63
|
-
// so tangent/uv0/joints0/weights0 stay intact. Scene-free by design — the
|
|
64
|
-
// the runtime slot and drives the palette
|
|
63
|
+
// so tangent/uv0/joints0/weights0 stay intact. Scene-free by design — the node-level glue that owns
|
|
64
|
+
// the runtime slot and drives the palette is updateMeshSkin, beside this file.
|
|
65
65
|
export function skinMeshGeometry(geometry, skeleton, bindPose) {
|
|
66
66
|
skinVertices(bindPose.skinnedPositions, bindPose.skinnedNormals, bindPose.positions, bindPose.normals, bindPose.joints, bindPose.weights, skeleton.jointMatrices);
|
|
67
67
|
const { layout, vertices } = geometry;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skinMeshGeometry.js","sourceRoot":"","sources":["../src/skinMeshGeometry.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"skinMeshGeometry.js","sourceRoot":"","sources":["../src/skinMeshGeometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateMeshDeformation.d.ts","sourceRoot":"","sources":["../src/updateMeshDeformation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"updateMeshDeformation.d.ts","sourceRoot":"","sources":["../src/updateMeshDeformation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAa5C,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAGhE"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { updateMeshMorph } from '@flighthq/
|
|
1
|
+
import { updateMeshMorph } from '@flighthq/mesh';
|
|
2
2
|
import { updateMeshSkin } from './updateMeshSkin';
|
|
3
3
|
// Applies a mesh's CPU deformation in the one valid composition order: morph first, then skin. Each
|
|
4
4
|
// deformer remains a standalone primitive and is a no-op when absent; this small composition prevents
|
|
5
5
|
// callers from freezing the first morph into skin's captured input or accidentally reversing the two.
|
|
6
|
+
//
|
|
7
|
+
// Both halves are same-layer primitives: morph needs only geometry, so it lives in @flighthq/mesh;
|
|
8
|
+
// skin needs the joint palette, so it lives here. Composing them here — at the upper of the two —
|
|
9
|
+
// keeps skeleton3d below @flighthq/scene with no cycle.
|
|
6
10
|
// Backends using GPU skinning should continue to update morph CPU-side and upload the joint palette,
|
|
7
11
|
// rather than calling this CPU skin path and then skinning again in the shader.
|
|
8
12
|
export function updateMeshDeformation(mesh) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateMeshDeformation.js","sourceRoot":"","sources":["../src/updateMeshDeformation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"updateMeshDeformation.js","sourceRoot":"","sources":["../src/updateMeshDeformation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,oGAAoG;AACpG,sGAAsG;AACtG,sGAAsG;AACtG,EAAE;AACF,mGAAmG;AACnG,kGAAkG;AAClG,wDAAwD;AACxD,qGAAqG;AACrG,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CAAC,IAAoB;IACxD,eAAe,CAAC,IAAI,CAAC,CAAC;IACtB,cAAc,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateMeshSkin.d.ts","sourceRoot":"","sources":["../src/updateMeshSkin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAiB5C,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"updateMeshSkin.d.ts","sourceRoot":"","sources":["../src/updateMeshSkin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAiB5C,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAqBzD"}
|
package/dist/updateMeshSkin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getMeshGeometrySkinBindPose,
|
|
1
|
+
import { getMeshGeometrySkinBindPose, setMeshGeometrySkinBindPose } from '@flighthq/mesh';
|
|
2
2
|
import { computeSkeleton3DJointMatrices } from './skeleton3d';
|
|
3
3
|
import { captureMeshSkinBindPose, skinMeshGeometry, updateMeshSkinBindPoseDeformInput } from './skinMeshGeometry';
|
|
4
4
|
// Deforms a skinned mesh into its geometry for the current joint pose — the explicit per-frame
|
|
@@ -30,6 +30,8 @@ export function updateMeshSkin(mesh) {
|
|
|
30
30
|
updateMeshSkinBindPoseDeformInput(bindPose, geometry);
|
|
31
31
|
}
|
|
32
32
|
skinMeshGeometry(geometry, skin.skeleton, bindPose);
|
|
33
|
-
|
|
33
|
+
// Bounds are deliberately NOT refreshed here. skinMeshGeometry bumped geometry.version, which marks
|
|
34
|
+
// the bounds cache stale; ensureMeshGeometryBounds does the sweep on the first query. A skinned mesh
|
|
35
|
+
// that is only uploaded and never culled or picked never pays for a box it does not use.
|
|
34
36
|
}
|
|
35
37
|
//# sourceMappingURL=updateMeshSkin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateMeshSkin.js","sourceRoot":"","sources":["../src/updateMeshSkin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,
|
|
1
|
+
{"version":3,"file":"updateMeshSkin.js","sourceRoot":"","sources":["../src/updateMeshSkin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAG1F,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,iCAAiC,EAAE,MAAM,oBAAoB,CAAC;AAElH,+FAA+F;AAC/F,gGAAgG;AAChG,+FAA+F;AAC/F,0FAA0F;AAC1F,8FAA8F;AAC9F,oGAAoG;AACpG,oGAAoG;AACpG,iGAAiG;AACjG,EAAE;AACF,sGAAsG;AACtG,sGAAsG;AACtG,wFAAwF;AACxF,MAAM,UAAU,cAAc,CAAC,IAAoB;IACjD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO;IAEzB,8BAA8B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,IAAI,QAAQ,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,QAAQ,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAC7C,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC;SAAM,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QAC9B,4FAA4F;QAC5F,iGAAiG;QACjG,iCAAiC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACxD,CAAC;IAED,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACpD,oGAAoG;IACpG,qGAAqG;IACrG,yFAAyF;AAC3F,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/skeleton3d",
|
|
3
|
-
"version": "0.2.1-next.
|
|
3
|
+
"version": "0.2.1-next.640.ec2ea9a",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -32,12 +32,11 @@
|
|
|
32
32
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@flighthq/entity": "0.2.1-next.
|
|
36
|
-
"@flighthq/geometry": "0.2.1-next.
|
|
37
|
-
"@flighthq/mesh": "0.2.1-next.
|
|
38
|
-
"@flighthq/node": "0.2.1-next.
|
|
39
|
-
"@flighthq/
|
|
40
|
-
"@flighthq/types": "0.2.1-next.585.c7219b8"
|
|
35
|
+
"@flighthq/entity": "0.2.1-next.640.ec2ea9a",
|
|
36
|
+
"@flighthq/geometry": "0.2.1-next.640.ec2ea9a",
|
|
37
|
+
"@flighthq/mesh": "0.2.1-next.640.ec2ea9a",
|
|
38
|
+
"@flighthq/node": "0.2.1-next.640.ec2ea9a",
|
|
39
|
+
"@flighthq/types": "0.2.1-next.640.ec2ea9a"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"@flighthq/scene": "*",
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { setVector3 } from '@flighthq/geometry';
|
|
2
|
+
import {
|
|
3
|
+
CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT,
|
|
4
|
+
createMeshGeometry,
|
|
5
|
+
getMeshGeometrySkinBindPose,
|
|
6
|
+
} from '@flighthq/mesh';
|
|
7
|
+
import { addNodeChild, getNodeRuntime, invalidateNodeLocalTransform } from '@flighthq/node';
|
|
8
|
+
import { createMesh, createSceneNode } from '@flighthq/scene';
|
|
9
|
+
import type { MeshRuntime } from '@flighthq/types';
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
|
|
12
|
+
import { prepareMeshSkinning, prepareSceneSkinning } from './prepareSceneSkinning';
|
|
13
|
+
import { createSkeleton3D } from './skeleton3d';
|
|
14
|
+
|
|
15
|
+
function createOneVertexSkinnedMesh() {
|
|
16
|
+
const vertices = new Float32Array(20);
|
|
17
|
+
vertices[0] = 1; // position x
|
|
18
|
+
vertices[4] = 1; // normal y
|
|
19
|
+
vertices[16] = 1; // weights0[0]
|
|
20
|
+
const geometry = createMeshGeometry({ layout: CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT, vertices });
|
|
21
|
+
return createMesh(geometry, []);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function deformedBounds(mesh: ReturnType<typeof createOneVertexSkinnedMesh>) {
|
|
25
|
+
return (getNodeRuntime(mesh) as MeshRuntime).deformedLocalBounds;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('prepareMeshSkinning', () => {
|
|
29
|
+
it('captures the bind pose and writes posed conservative bounds without CPU-posing the vertices', () => {
|
|
30
|
+
const mesh = createOneVertexSkinnedMesh();
|
|
31
|
+
const joint = createSceneNode();
|
|
32
|
+
mesh.skin = { skeleton: createSkeleton3D([joint]) };
|
|
33
|
+
setVector3(joint.position, 0, 5, 0);
|
|
34
|
+
invalidateNodeLocalTransform(joint);
|
|
35
|
+
const versionBefore = mesh.geometry.version;
|
|
36
|
+
|
|
37
|
+
prepareMeshSkinning(mesh);
|
|
38
|
+
|
|
39
|
+
// The bind pose is captured (read-only) so the bounds sweep has a rest pose to work from.
|
|
40
|
+
expect(getMeshGeometrySkinBindPose(mesh.geometry)).not.toBeNull();
|
|
41
|
+
// The GPU deforms the vertices in-shader, so the CPU buffer must stay at bind pose and NOT bump.
|
|
42
|
+
expect(mesh.geometry.vertices[0]).toBe(1);
|
|
43
|
+
expect(mesh.geometry.vertices[1]).toBe(0);
|
|
44
|
+
expect(mesh.geometry.version).toBe(versionBefore);
|
|
45
|
+
// The posed conservative bounds cover the joint-translated vertex (y ≈ 5).
|
|
46
|
+
const bounds = deformedBounds(mesh);
|
|
47
|
+
expect(bounds).not.toBeNull();
|
|
48
|
+
expect(bounds!.max.y).toBeGreaterThanOrEqual(5 - 1e-6);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('is a no-op for a rigid mesh and leaves the slot empty', () => {
|
|
52
|
+
const mesh = createMesh(
|
|
53
|
+
createMeshGeometry({ layout: CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT, vertices: new Float32Array(20) }),
|
|
54
|
+
[],
|
|
55
|
+
);
|
|
56
|
+
prepareMeshSkinning(mesh);
|
|
57
|
+
expect(deformedBounds(mesh) ?? null).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('reuses the same bounds AABB instance across frames', () => {
|
|
61
|
+
const mesh = createOneVertexSkinnedMesh();
|
|
62
|
+
const joint = createSceneNode();
|
|
63
|
+
mesh.skin = { skeleton: createSkeleton3D([joint]) };
|
|
64
|
+
setVector3(joint.position, 0, 5, 0);
|
|
65
|
+
invalidateNodeLocalTransform(joint);
|
|
66
|
+
|
|
67
|
+
prepareMeshSkinning(mesh);
|
|
68
|
+
const first = deformedBounds(mesh);
|
|
69
|
+
prepareMeshSkinning(mesh);
|
|
70
|
+
expect(deformedBounds(mesh)).toBe(first);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('prepareSceneSkinning', () => {
|
|
75
|
+
it('poses every skinned mesh in the subtree and skips disabled subtrees', () => {
|
|
76
|
+
const scene = createSceneNode();
|
|
77
|
+
const skinned = createOneVertexSkinnedMesh();
|
|
78
|
+
const joint = createSceneNode();
|
|
79
|
+
skinned.skin = { skeleton: createSkeleton3D([joint]) };
|
|
80
|
+
setVector3(joint.position, 0, 5, 0);
|
|
81
|
+
invalidateNodeLocalTransform(joint);
|
|
82
|
+
addNodeChild(scene, skinned);
|
|
83
|
+
|
|
84
|
+
const disabledGroup = createSceneNode(undefined, { enabled: false });
|
|
85
|
+
const hidden = createOneVertexSkinnedMesh();
|
|
86
|
+
hidden.skin = { skeleton: createSkeleton3D([createSceneNode()]) };
|
|
87
|
+
addNodeChild(disabledGroup, hidden);
|
|
88
|
+
addNodeChild(scene, disabledGroup);
|
|
89
|
+
|
|
90
|
+
prepareSceneSkinning(scene);
|
|
91
|
+
|
|
92
|
+
expect(deformedBounds(skinned)).not.toBeNull();
|
|
93
|
+
// A disabled subtree is skipped whole, so its skinned mesh is never posed.
|
|
94
|
+
expect(getMeshGeometrySkinBindPose(hidden.geometry)).toBeNull();
|
|
95
|
+
expect(deformedBounds(hidden) ?? null).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -2,6 +2,7 @@ import { setVector3 } from '@flighthq/geometry';
|
|
|
2
2
|
import {
|
|
3
3
|
CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT,
|
|
4
4
|
createMeshGeometry,
|
|
5
|
+
ensureMeshGeometryBounds,
|
|
5
6
|
getMeshGeometrySkinBindPose,
|
|
6
7
|
} from '@flighthq/mesh';
|
|
7
8
|
import { invalidateNodeLocalTransform } from '@flighthq/node';
|
|
@@ -34,8 +35,10 @@ describe('updateMeshSkin', () => {
|
|
|
34
35
|
updateMeshSkin(mesh);
|
|
35
36
|
|
|
36
37
|
expect(mesh.geometry.vertices[1]).toBeCloseTo(5);
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
// Bounds are a dirty-gated cache: the skin marks them stale, the ensure recomputes them.
|
|
39
|
+
const bounds = ensureMeshGeometryBounds(mesh.geometry);
|
|
40
|
+
expect(bounds?.min.y).toBeCloseTo(5);
|
|
41
|
+
expect(bounds?.max.y).toBeCloseTo(5);
|
|
39
42
|
expect(mesh.geometry.version).toBe(versionBefore + 1);
|
|
40
43
|
expect(getMeshGeometrySkinBindPose(mesh.geometry)).not.toBeNull();
|
|
41
44
|
});
|