@flighthq/mesh 0.2.0 → 0.2.1-next.410.a23f189

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 CHANGED
@@ -8,4 +8,5 @@ export * from './meshGeometryOperations';
8
8
  export * from './meshGeometrySubset';
9
9
  export * from './meshGeometryTransforms';
10
10
  export * from './meshGeometryUvs';
11
+ export * from './morphMeshGeometry';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -8,4 +8,5 @@ export * from './meshGeometryOperations';
8
8
  export * from './meshGeometrySubset';
9
9
  export * from './meshGeometryTransforms';
10
10
  export * from './meshGeometryUvs';
11
+ export * from './morphMeshGeometry';
11
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { MeshGeometry, MeshSubset, PrimitiveTopology, VertexAttributeLayout } from '@flighthq/types';
1
+ import type { MeshGeometry, MeshMorphBindPose, MeshSkinBindPose, MeshSubset, PrimitiveTopology, VertexAttributeLayout } from '@flighthq/types';
2
2
  export interface MeshGeometryOptions {
3
3
  indices?: Readonly<Uint16Array<ArrayBuffer>> | Readonly<Uint32Array<ArrayBuffer>> | null;
4
4
  layout: VertexAttributeLayout;
@@ -11,5 +11,10 @@ export declare function createMeshGeometry(options: Readonly<MeshGeometryOptions
11
11
  export declare function destroyMeshGeometryGlData(geometry: Readonly<MeshGeometry>): void;
12
12
  export declare function destroyMeshGeometryWgpuData(geometry: Readonly<MeshGeometry>): void;
13
13
  export declare function getMeshGeometryIndexCount(geometry: Readonly<MeshGeometry>): number;
14
+ export declare function getMeshGeometryMorphBindPose(geometry: Readonly<MeshGeometry>): MeshMorphBindPose | null;
15
+ export declare function getMeshGeometrySkinBindPose(geometry: Readonly<MeshGeometry>): MeshSkinBindPose | null;
14
16
  export declare function getMeshGeometryVertexCount(geometry: Readonly<MeshGeometry>): number;
17
+ export declare function hasMeshGeometrySkin(geometry: Readonly<MeshGeometry>): boolean;
18
+ export declare function setMeshGeometryMorphBindPose(geometry: Readonly<MeshGeometry>, bindPose: MeshMorphBindPose | null): void;
19
+ export declare function setMeshGeometrySkinBindPose(geometry: Readonly<MeshGeometry>, bindPose: MeshSkinBindPose | null): void;
15
20
  //# sourceMappingURL=meshGeometry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"meshGeometry.d.ts","sourceRoot":"","sources":["../src/meshGeometry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EAEZ,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAQzB,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC;IACzF,MAAM,EAAE,qBAAqB,CAAC;IAC9B,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;CACrC;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,YAAY,CAkC9E;AAOD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GAAG,YAAY,CAwBvF;AAMD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,IAAI,CAGhF;AAMD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,IAAI,CAGlF;AAGD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,MAAM,CAElF;AAID,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,MAAM,CAEnF"}
1
+ {"version":3,"file":"meshGeometry.d.ts","sourceRoot":"","sources":["../src/meshGeometry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EAEZ,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAQzB,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC;IACzF,MAAM,EAAE,qBAAqB,CAAC;IAC9B,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;CACrC;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,YAAY,CAkC9E;AAOD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GAAG,YAAY,CAwBvF;AAMD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,IAAI,CAGhF;AAMD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,IAAI,CAGlF;AAGD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,MAAM,CAElF;AAKD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,iBAAiB,GAAG,IAAI,CAGvG;AAKD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,gBAAgB,GAAG,IAAI,CAGrG;AAID,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,MAAM,CAEnF;AAKD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,OAAO,CAM7E;AAKD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,EAChC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,GACjC,IAAI,CAGN;AAKD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAGrH"}
@@ -85,11 +85,52 @@ export function destroyMeshGeometryWgpuData(geometry) {
85
85
  export function getMeshGeometryIndexCount(geometry) {
86
86
  return geometry.indices ? geometry.indices.length : 0;
87
87
  }
88
+ // Returns the CPU-morph base pose cached on the geometry's runtime, or null before the first morph
89
+ // capture. The morph deform subsystem (mesh/scene) owns this slot; a non-morphed geometry never fills
90
+ // it, so plain meshes pay nothing. Sibling of getMeshGeometrySkinBindPose.
91
+ export function getMeshGeometryMorphBindPose(geometry) {
92
+ const runtime = geometry[EntityRuntimeKey];
93
+ return runtime ? runtime.morphBindPose : null;
94
+ }
95
+ // Returns the CPU-skinning bind pose cached on the geometry's runtime, or null before the first
96
+ // skin capture. The deform subsystem (skeleton3d/scene) owns this slot; a rigid geometry never
97
+ // fills it, so plain meshes pay nothing.
98
+ export function getMeshGeometrySkinBindPose(geometry) {
99
+ const runtime = geometry[EntityRuntimeKey];
100
+ return runtime ? runtime.skinBindPose : null;
101
+ }
88
102
  // Returns the number of vertices, derived from the interleaved vertex stream and the layout
89
103
  // stride (stride is in bytes; a Float32 is 4 bytes).
90
104
  export function getMeshGeometryVertexCount(geometry) {
91
105
  return getVertexCountFromLayout(geometry.vertices, geometry.layout);
92
106
  }
107
+ // Returns true when the geometry's vertex layout carries the joints0 skinning channel — the signal
108
+ // that it can be skeletally deformed (CPU via skinMeshGeometry, or the GPU HAS_SKIN shader variant).
109
+ // The renderer selects the skinned program variant off this, exactly as it would off uv1.
110
+ export function hasMeshGeometrySkin(geometry) {
111
+ const attributes = geometry.layout.attributes;
112
+ for (let i = 0; i < attributes.length; i++) {
113
+ if (attributes[i].semantic === 'joints0')
114
+ return true;
115
+ }
116
+ return false;
117
+ }
118
+ // Stores (or clears, with null) the CPU-morph base pose on the geometry's runtime. Called once by the
119
+ // morph deform glue when a morphed geometry is first captured; pass null to force a recapture (e.g.
120
+ // after the vertex layout or base data changes). Sibling of setMeshGeometrySkinBindPose.
121
+ export function setMeshGeometryMorphBindPose(geometry, bindPose) {
122
+ const runtime = geometry[EntityRuntimeKey];
123
+ if (runtime)
124
+ runtime.morphBindPose = bindPose;
125
+ }
126
+ // Stores (or clears, with null) the CPU-skinning bind pose on the geometry's runtime. Called once
127
+ // by the deform glue when a skinned geometry is first captured; pass null to force a recapture
128
+ // (e.g. after the vertex layout or bind-pose data changes).
129
+ export function setMeshGeometrySkinBindPose(geometry, bindPose) {
130
+ const runtime = geometry[EntityRuntimeKey];
131
+ if (runtime)
132
+ runtime.skinBindPose = bindPose;
133
+ }
93
134
  // Allocates a MeshGeometry entity with a runtime carrying empty (null) GPU upload slots. This is
94
135
  // the single construction point so every MeshGeometry shares the same runtime shape.
95
136
  function createMeshGeometryRuntime(fields) {
@@ -102,7 +143,13 @@ function createMeshGeometryRuntime(fields) {
102
143
  version: fields.version,
103
144
  vertices: fields.vertices,
104
145
  });
105
- const runtime = { binding: null, webglData: null, webgpuData: null };
146
+ const runtime = {
147
+ binding: null,
148
+ morphBindPose: null,
149
+ skinBindPose: null,
150
+ webglData: null,
151
+ webgpuData: null,
152
+ };
106
153
  geometry[EntityRuntimeKey] = runtime;
107
154
  return geometry;
108
155
  }
@@ -1 +1 @@
1
- {"version":3,"file":"meshGeometry.js","sourceRoot":"","sources":["../src/meshGeometry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"meshGeometry.js","sourceRoot":"","sources":["../src/meshGeometry.ts"],"names":[],"mappings":"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"}
@@ -560,7 +560,7 @@ export function createTorusMeshGeometry(radius = 0.5, tube = 0.2, radialSegments
560
560
  const b = a + 1;
561
561
  const c = a + rowStride;
562
562
  const d = c + 1;
563
- indices.push(a, c, b, b, c, d);
563
+ indices.push(a, b, c, b, d, c);
564
564
  }
565
565
  }
566
566
  return buildCanonicalMeshGeometry(positions, normals, uvs, indices);
@@ -1,4 +1,5 @@
1
1
  import type { MeshGeometry, VertexAttributeLayout } from '@flighthq/types';
2
2
  export declare function convertMeshGeometryLayout(source: Readonly<MeshGeometry>, targetLayout: Readonly<VertexAttributeLayout>): MeshGeometry;
3
3
  export declare const CANONICAL_MESH_GEOMETRY_LAYOUT: VertexAttributeLayout;
4
+ export declare const CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT: VertexAttributeLayout;
4
5
  //# sourceMappingURL=meshGeometryLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"meshGeometryLayout.d.ts","sourceRoot":"","sources":["../src/meshGeometryLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAc3E,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,YAAY,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAC5C,YAAY,CA8Cd;AAgBD,eAAO,MAAM,8BAA8B,EAAE,qBAQ5C,CAAC"}
1
+ {"version":3,"file":"meshGeometryLayout.d.ts","sourceRoot":"","sources":["../src/meshGeometryLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAc3E,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,YAAY,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAC5C,YAAY,CA8Cd;AAgBD,eAAO,MAAM,8BAA8B,EAAE,qBAQ5C,CAAC;AAUF,eAAO,MAAM,sCAAsC,EAAE,qBAUpD,CAAC"}
@@ -80,4 +80,23 @@ export const CANONICAL_MESH_GEOMETRY_LAYOUT = {
80
80
  ],
81
81
  stride: 48,
82
82
  };
83
+ // The canonical interleaved skinned-mesh layout: the PBR record above extended past uv0 with the
84
+ // two 4-influence skinning channels — joints0 (the 4 influencing joint indices) and weights0 (their
85
+ // blend weights) — for 20 floats / 80 bytes. Both are float32x4 so the whole record stays in one
86
+ // Float32Array (geometry.vertices), the format CPU skinning reads directly and GPU skinning uploads
87
+ // as float attributes at locations 6–7; the packed uint8x4/unorm8x4 formats remain available for a
88
+ // later compaction pass but are not required. position/normal keep offsets 0/12, so normal/bounds
89
+ // compute and the rigid draw path work unchanged — a skinned geometry differs only by carrying the
90
+ // extra channels a skin reads. Pass to createMeshGeometry when building geometry a Skin will deform.
91
+ export const CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT = {
92
+ attributes: [
93
+ { byteOffset: 0, format: 'float32x3', semantic: 'position' },
94
+ { byteOffset: 12, format: 'float32x3', semantic: 'normal' },
95
+ { byteOffset: 24, format: 'float32x4', semantic: 'tangent' },
96
+ { byteOffset: 40, format: 'float32x2', semantic: 'uv0' },
97
+ { byteOffset: 48, format: 'float32x4', semantic: 'joints0' },
98
+ { byteOffset: 64, format: 'float32x4', semantic: 'weights0' },
99
+ ],
100
+ stride: 80,
101
+ };
83
102
  //# sourceMappingURL=meshGeometryLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"meshGeometryLayout.js","sourceRoot":"","sources":["../src/meshGeometryLayout.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"meshGeometryLayout.js","sourceRoot":"","sources":["../src/meshGeometryLayout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { MeshGeometry, MeshMorph, MeshMorphBindPose } from '@flighthq/types';
2
+ export declare function blendMeshGeometryMorph(geometry: MeshGeometry, morph: Readonly<MeshMorph>, bindPose: Readonly<MeshMorphBindPose>): void;
3
+ export declare function captureMeshMorphBindPose(geometry: Readonly<MeshGeometry>): MeshMorphBindPose;
4
+ //# sourceMappingURL=morphMeshGeometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"morphMeshGeometry.d.ts","sourceRoot":"","sources":["../src/morphMeshGeometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAalF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,EAC1B,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GACpC,IAAI,CAoDN;AAUD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,iBAAiB,CAyC5F"}
@@ -0,0 +1,116 @@
1
+ import { getVertexAttributeFloatOffset } from './meshGeometryAttributes';
2
+ // Blends a morphed MeshGeometry in place for the current weights: computes base + Σ wᵢ·targetᵢ
3
+ // (an additive blend) for position, and — where the base carries them — normal and tangent, into the
4
+ // base pose's scratch, then writes the blended attributes back into the interleaved `geometry.vertices`
5
+ // and bumps `geometry.version` so the backends re-upload. `bindPose` is the capture the caller holds on
6
+ // the geometry runtime; only the position/normal/tangent-xyz channels are rewritten, so uv0/color0/the
7
+ // tangent handedness `w` stay intact. A target whose normal/tangent deltas are null contributes only to
8
+ // position. A zero (or unset) weight contributes nothing, so a settled morph is base-cost. Scene-free by
9
+ // design — the mesh-level glue that owns the runtime slot lives in @flighthq/scene (updateMeshMorph).
10
+ // Sibling of skinMeshGeometry; the two compose (skin over morph) when a mesh carries both.
11
+ export function blendMeshGeometryMorph(geometry, morph, bindPose) {
12
+ const { blendedNormals, blendedPositions, blendedTangents, normals, positions, tangents } = bindPose;
13
+ const vertexCount = (positions.length / 3) | 0;
14
+ const floats = vertexCount * 3;
15
+ blendedPositions.set(positions.subarray(0, floats));
16
+ if (blendedNormals !== null && normals !== null)
17
+ blendedNormals.set(normals.subarray(0, floats));
18
+ if (blendedTangents !== null && tangents !== null)
19
+ blendedTangents.set(tangents.subarray(0, floats));
20
+ const targets = morph.targets;
21
+ const weights = morph.weights;
22
+ const targetCount = Math.min(targets.length, weights.length);
23
+ for (let t = 0; t < targetCount; t++) {
24
+ const weight = weights[t];
25
+ if (weight === 0)
26
+ continue;
27
+ const target = targets[t];
28
+ accumulateDeltas(blendedPositions, target.positionDeltas, weight, floats);
29
+ if (blendedNormals !== null && target.normalDeltas !== null) {
30
+ accumulateDeltas(blendedNormals, target.normalDeltas, weight, floats);
31
+ }
32
+ if (blendedTangents !== null && target.tangentDeltas !== null) {
33
+ accumulateDeltas(blendedTangents, target.tangentDeltas, weight, floats);
34
+ }
35
+ }
36
+ const { layout, vertices } = geometry;
37
+ const floatsPerVertex = layout.stride / 4;
38
+ const positionOffset = getVertexAttributeFloatOffset(layout, 'position');
39
+ const normalOffset = getVertexAttributeFloatOffset(layout, 'normal');
40
+ const tangentOffset = getVertexAttributeFloatOffset(layout, 'tangent');
41
+ for (let v = 0; v < vertexCount; v++) {
42
+ const dst = v * floatsPerVertex;
43
+ const s = v * 3;
44
+ if (positionOffset >= 0) {
45
+ vertices[dst + positionOffset] = blendedPositions[s];
46
+ vertices[dst + positionOffset + 1] = blendedPositions[s + 1];
47
+ vertices[dst + positionOffset + 2] = blendedPositions[s + 2];
48
+ }
49
+ if (blendedNormals !== null && normalOffset >= 0) {
50
+ vertices[dst + normalOffset] = blendedNormals[s];
51
+ vertices[dst + normalOffset + 1] = blendedNormals[s + 1];
52
+ vertices[dst + normalOffset + 2] = blendedNormals[s + 2];
53
+ }
54
+ if (blendedTangents !== null && tangentOffset >= 0) {
55
+ vertices[dst + tangentOffset] = blendedTangents[s];
56
+ vertices[dst + tangentOffset + 1] = blendedTangents[s + 1];
57
+ vertices[dst + tangentOffset + 2] = blendedTangents[s + 2];
58
+ }
59
+ }
60
+ geometry.version++;
61
+ }
62
+ // Captures the de-interleaved CPU-morph base pose for one MeshGeometry: the base (rest)
63
+ // position/normal/tangent attributes read out of the interleaved `geometry.vertices` through
64
+ // `geometry.layout`, plus reusable blended-output scratch. Allocates the SoA arrays once; the
65
+ // per-frame blend (blendMeshGeometryMorph) then allocates nothing. Call once, before the first blend,
66
+ // and store the result on the geometry's runtime (MeshGeometryRuntime.morphBindPose). Normal/tangent
67
+ // arrays are null when the base layout omits that channel, so the blend rewrites only the channels the
68
+ // geometry carries. The tangent is captured as its xyz direction (3 floats); the handedness `w` stays
69
+ // in geometry.vertices and is not morphed. Sibling of captureMeshSkinBindPose.
70
+ export function captureMeshMorphBindPose(geometry) {
71
+ const { layout, vertices } = geometry;
72
+ const floatsPerVertex = layout.stride / 4;
73
+ const vertexCount = floatsPerVertex > 0 ? (vertices.length / floatsPerVertex) | 0 : 0;
74
+ const positionOffset = getVertexAttributeFloatOffset(layout, 'position');
75
+ const normalOffset = getVertexAttributeFloatOffset(layout, 'normal');
76
+ const tangentOffset = getVertexAttributeFloatOffset(layout, 'tangent');
77
+ const positions = new Float32Array(vertexCount * 3);
78
+ const normals = normalOffset >= 0 ? new Float32Array(vertexCount * 3) : null;
79
+ const tangents = tangentOffset >= 0 ? new Float32Array(vertexCount * 3) : null;
80
+ for (let v = 0; v < vertexCount; v++) {
81
+ const base = v * floatsPerVertex;
82
+ const p = v * 3;
83
+ if (positionOffset >= 0) {
84
+ positions[p] = vertices[base + positionOffset];
85
+ positions[p + 1] = vertices[base + positionOffset + 1];
86
+ positions[p + 2] = vertices[base + positionOffset + 2];
87
+ }
88
+ if (normals !== null) {
89
+ normals[p] = vertices[base + normalOffset];
90
+ normals[p + 1] = vertices[base + normalOffset + 1];
91
+ normals[p + 2] = vertices[base + normalOffset + 2];
92
+ }
93
+ if (tangents !== null) {
94
+ tangents[p] = vertices[base + tangentOffset];
95
+ tangents[p + 1] = vertices[base + tangentOffset + 1];
96
+ tangents[p + 2] = vertices[base + tangentOffset + 2];
97
+ }
98
+ }
99
+ return {
100
+ blendedNormals: normals !== null ? new Float32Array(vertexCount * 3) : null,
101
+ blendedPositions: new Float32Array(vertexCount * 3),
102
+ blendedTangents: tangents !== null ? new Float32Array(vertexCount * 3) : null,
103
+ normals,
104
+ positions,
105
+ tangents,
106
+ };
107
+ }
108
+ // Adds `weight * deltas[i]` into `accumulator[i]` for the first `count` floats. `deltas` shorter than
109
+ // `count` contributes only over its own length (a sparse target padded to fewer floats), leaving the
110
+ // rest of the accumulator at its base value.
111
+ function accumulateDeltas(accumulator, deltas, weight, count) {
112
+ const n = Math.min(count, deltas.length);
113
+ for (let i = 0; i < n; i++)
114
+ accumulator[i] += weight * deltas[i];
115
+ }
116
+ //# sourceMappingURL=morphMeshGeometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"morphMeshGeometry.js","sourceRoot":"","sources":["../src/morphMeshGeometry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/mesh",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-next.410.a23f189",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/flighthq/flight.git",
@@ -32,9 +32,9 @@
32
32
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
33
33
  },
34
34
  "dependencies": {
35
- "@flighthq/entity": "0.2.0",
36
- "@flighthq/geometry": "0.2.0",
37
- "@flighthq/types": "0.2.0"
35
+ "@flighthq/entity": "0.2.1-next.410.a23f189",
36
+ "@flighthq/geometry": "0.2.1-next.410.a23f189",
37
+ "@flighthq/types": "0.2.1-next.410.a23f189"
38
38
  },
39
39
  "devDependencies": {
40
40
  "typescript": "^5.3.0"
@@ -1,4 +1,4 @@
1
- import type { MeshGeometryRuntime, VertexAttributeLayout } from '@flighthq/types';
1
+ import type { MeshGeometryRuntime, MeshMorphBindPose, MeshSkinBindPose, VertexAttributeLayout } from '@flighthq/types';
2
2
  import { EntityRuntimeKey } from '@flighthq/types';
3
3
 
4
4
  import {
@@ -7,7 +7,12 @@ import {
7
7
  destroyMeshGeometryGlData,
8
8
  destroyMeshGeometryWgpuData,
9
9
  getMeshGeometryIndexCount,
10
+ getMeshGeometryMorphBindPose,
11
+ getMeshGeometrySkinBindPose,
10
12
  getMeshGeometryVertexCount,
13
+ hasMeshGeometrySkin,
14
+ setMeshGeometryMorphBindPose,
15
+ setMeshGeometrySkinBindPose,
11
16
  } from './meshGeometry';
12
17
 
13
18
  const CANONICAL_LAYOUT: VertexAttributeLayout = {
@@ -147,9 +152,81 @@ describe('getMeshGeometryIndexCount', () => {
147
152
  });
148
153
  });
149
154
 
155
+ describe('getMeshGeometryMorphBindPose', () => {
156
+ it('returns null before any capture', () => {
157
+ const geometry = createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: makeVertices(3) });
158
+ expect(getMeshGeometryMorphBindPose(geometry)).toBeNull();
159
+ });
160
+ });
161
+
162
+ describe('getMeshGeometrySkinBindPose', () => {
163
+ it('returns null before any capture', () => {
164
+ const geometry = createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: makeVertices(3) });
165
+ expect(getMeshGeometrySkinBindPose(geometry)).toBeNull();
166
+ });
167
+ });
168
+
150
169
  describe('getMeshGeometryVertexCount', () => {
151
170
  it('derives the vertex count from stride', () => {
152
171
  const geometry = createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: makeVertices(5) });
153
172
  expect(getMeshGeometryVertexCount(geometry)).toBe(5);
154
173
  });
155
174
  });
175
+
176
+ describe('hasMeshGeometrySkin', () => {
177
+ it('is false for the canonical (rigid) layout and true when joints0 is present', () => {
178
+ const rigid = createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: makeVertices(3) });
179
+ expect(hasMeshGeometrySkin(rigid)).toBe(false);
180
+
181
+ const skinnedLayout: VertexAttributeLayout = {
182
+ attributes: [
183
+ { byteOffset: 0, format: 'float32x3', semantic: 'position' },
184
+ { byteOffset: 12, format: 'float32x4', semantic: 'joints0' },
185
+ { byteOffset: 28, format: 'float32x4', semantic: 'weights0' },
186
+ ],
187
+ stride: 44,
188
+ };
189
+ const skinned = createMeshGeometry({ layout: skinnedLayout, vertices: new Float32Array(11) });
190
+ expect(hasMeshGeometrySkin(skinned)).toBe(true);
191
+ });
192
+ });
193
+
194
+ describe('setMeshGeometryMorphBindPose', () => {
195
+ it('stores and clears the morph base pose on the runtime slot', () => {
196
+ const geometry = createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: makeVertices(3) });
197
+ const bindPose: MeshMorphBindPose = {
198
+ blendedNormals: new Float32Array(9),
199
+ blendedPositions: new Float32Array(9),
200
+ blendedTangents: new Float32Array(9),
201
+ normals: new Float32Array(9),
202
+ positions: new Float32Array(9),
203
+ tangents: new Float32Array(9),
204
+ };
205
+
206
+ setMeshGeometryMorphBindPose(geometry, bindPose);
207
+ expect(getMeshGeometryMorphBindPose(geometry)).toBe(bindPose);
208
+
209
+ setMeshGeometryMorphBindPose(geometry, null);
210
+ expect(getMeshGeometryMorphBindPose(geometry)).toBeNull();
211
+ });
212
+ });
213
+
214
+ describe('setMeshGeometrySkinBindPose', () => {
215
+ it('stores and clears the skinning bind pose on the runtime slot', () => {
216
+ const geometry = createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: makeVertices(3) });
217
+ const bindPose: MeshSkinBindPose = {
218
+ joints: new Float32Array(4),
219
+ normals: new Float32Array(3),
220
+ positions: new Float32Array(3),
221
+ skinnedNormals: new Float32Array(3),
222
+ skinnedPositions: new Float32Array(3),
223
+ weights: new Float32Array(4),
224
+ };
225
+
226
+ setMeshGeometrySkinBindPose(geometry, bindPose);
227
+ expect(getMeshGeometrySkinBindPose(geometry)).toBe(bindPose);
228
+
229
+ setMeshGeometrySkinBindPose(geometry, null);
230
+ expect(getMeshGeometrySkinBindPose(geometry)).toBeNull();
231
+ });
232
+ });
@@ -42,6 +42,39 @@ function expectUnitTangents(geometry: Readonly<MeshGeometry>): void {
42
42
  }
43
43
  }
44
44
 
45
+ // Asserts a triangle's geometric face normal agrees with its vertices' averaged shading normal, so
46
+ // the winding (CCW front-facing) matches the outward-pointing normals the builder emits. A back-
47
+ // facing wind would be culled and expose the interior surface instead. Scans from the first triangle
48
+ // to the first non-degenerate one, skipping the zero-area pole triangles UV-sphere fans produce.
49
+ function expectWindingMatchesNormals(geometry: Readonly<MeshGeometry>): void {
50
+ const stride = 12;
51
+ const p = (index: number, offset: number): number => geometry.vertices[index * stride + offset];
52
+ const indices = geometry.indices;
53
+ expect(indices).not.toBeNull();
54
+ if (indices === null) return;
55
+ const indexCount = indices.length;
56
+ for (let t = 0; t + 2 < indexCount; t += 3) {
57
+ const i0 = indices[t];
58
+ const i1 = indices[t + 1];
59
+ const i2 = indices[t + 2];
60
+ // Right-hand rule over the winding order: (p1 - p0) × (p2 - p0) faces front for CCW triangles.
61
+ const ex = [p(i1, 0) - p(i0, 0), p(i1, 1) - p(i0, 1), p(i1, 2) - p(i0, 2)];
62
+ const ey = [p(i2, 0) - p(i0, 0), p(i2, 1) - p(i0, 1), p(i2, 2) - p(i0, 2)];
63
+ const faceNormal = [ex[1] * ey[2] - ex[2] * ey[1], ex[2] * ey[0] - ex[0] * ey[2], ex[0] * ey[1] - ex[1] * ey[0]];
64
+ if (Math.hypot(faceNormal[0], faceNormal[1], faceNormal[2]) < 1e-6) continue;
65
+ const avgNormal = [0, 0, 0];
66
+ for (const index of [i0, i1, i2]) {
67
+ avgNormal[0] += p(index, 3);
68
+ avgNormal[1] += p(index, 4);
69
+ avgNormal[2] += p(index, 5);
70
+ }
71
+ const dot = faceNormal[0] * avgNormal[0] + faceNormal[1] * avgNormal[1] + faceNormal[2] * avgNormal[2];
72
+ expect(dot).toBeGreaterThan(0);
73
+ return;
74
+ }
75
+ throw new Error('no non-degenerate triangle found');
76
+ }
77
+
45
78
  describe('createBoxMeshGeometry', () => {
46
79
  it('builds 24 vertices, 36 indices, and bounds at the half-extents', () => {
47
80
  const geometry = createBoxMeshGeometry(2, 4, 6);
@@ -190,6 +223,7 @@ describe('createSphereMeshGeometry', () => {
190
223
  expect(r).toBeCloseTo(1, 4);
191
224
  }
192
225
  expectUnitNormals(geometry);
226
+ expectWindingMatchesNormals(geometry);
193
227
  });
194
228
  });
195
229
 
@@ -216,5 +250,6 @@ describe('createTorusMeshGeometry', () => {
216
250
  expect(geometry.bounds!.max.x).toBeCloseTo(0.7, 2);
217
251
  expect(geometry.bounds!.max.z).toBeCloseTo(0.2, 2);
218
252
  expectUnitNormals(geometry);
253
+ expectWindingMatchesNormals(geometry);
219
254
  });
220
255
  });
@@ -1,7 +1,11 @@
1
1
  import type { VertexAttributeLayout } from '@flighthq/types';
2
2
 
3
3
  import { createMeshGeometry } from './meshGeometry';
4
- import { CANONICAL_MESH_GEOMETRY_LAYOUT, convertMeshGeometryLayout } from './meshGeometryLayout';
4
+ import {
5
+ CANONICAL_MESH_GEOMETRY_LAYOUT,
6
+ CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT,
7
+ convertMeshGeometryLayout,
8
+ } from './meshGeometryLayout';
5
9
 
6
10
  // Minimal position-only layout: 3 floats / 12 bytes.
7
11
  const POSITION_ONLY_LAYOUT: VertexAttributeLayout = {
@@ -54,6 +58,28 @@ describe('CANONICAL_MESH_GEOMETRY_LAYOUT', () => {
54
58
  });
55
59
  });
56
60
 
61
+ describe('CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT', () => {
62
+ it('extends the canonical PBR record to stride 80', () => {
63
+ expect(CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT.stride).toBe(80);
64
+ });
65
+ it('keeps position and normal at the canonical offsets so rigid compute still works', () => {
66
+ const attributes = CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT.attributes;
67
+ const position = attributes.find((a) => a.semantic === 'position');
68
+ const normal = attributes.find((a) => a.semantic === 'normal');
69
+ expect(position?.byteOffset).toBe(0);
70
+ expect(normal?.byteOffset).toBe(12);
71
+ });
72
+ it('adds joints0 and weights0 as float32x4 after uv0', () => {
73
+ const attributes = CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT.attributes;
74
+ const joints = attributes.find((a) => a.semantic === 'joints0');
75
+ const weights = attributes.find((a) => a.semantic === 'weights0');
76
+ expect(joints?.byteOffset).toBe(48);
77
+ expect(joints?.format).toBe('float32x4');
78
+ expect(weights?.byteOffset).toBe(64);
79
+ expect(weights?.format).toBe('float32x4');
80
+ });
81
+ });
82
+
57
83
  describe('convertMeshGeometryLayout', () => {
58
84
  it('strips to position-only layout', () => {
59
85
  const source = makeCanonicalGeometry();
@@ -0,0 +1,142 @@
1
+ import type { MeshMorph, VertexAttributeLayout } from '@flighthq/types';
2
+
3
+ import { createMeshGeometry } from './meshGeometry';
4
+ import { blendMeshGeometryMorph, captureMeshMorphBindPose } from './morphMeshGeometry';
5
+
6
+ // A canonical PBR record (position + normal + tangent(w) + uv0), stride 48 bytes.
7
+ const CANONICAL_LAYOUT: VertexAttributeLayout = {
8
+ attributes: [
9
+ { byteOffset: 0, format: 'float32x3', semantic: 'position' },
10
+ { byteOffset: 12, format: 'float32x3', semantic: 'normal' },
11
+ { byteOffset: 24, format: 'float32x4', semantic: 'tangent' },
12
+ { byteOffset: 40, format: 'float32x2', semantic: 'uv0' },
13
+ ],
14
+ stride: 48,
15
+ };
16
+
17
+ // A minimal position-only record, stride 12 bytes — exercises the null normal/tangent path.
18
+ const POSITION_LAYOUT: VertexAttributeLayout = {
19
+ attributes: [{ byteOffset: 0, format: 'float32x3', semantic: 'position' }],
20
+ stride: 12,
21
+ };
22
+
23
+ function positionOnlyGeometry(positions: readonly number[]) {
24
+ return createMeshGeometry({ layout: POSITION_LAYOUT, vertices: new Float32Array(positions) });
25
+ }
26
+
27
+ describe('morphMeshGeometry', () => {
28
+ describe('blendMeshGeometryMorph', () => {
29
+ it('applies base + Σ wᵢ·targetᵢ to positions', () => {
30
+ const geometry = positionOnlyGeometry([0, 0, 0, 1, 0, 0]);
31
+ const bindPose = captureMeshMorphBindPose(geometry);
32
+ const morph: MeshMorph = {
33
+ targets: [
34
+ { normalDeltas: null, positionDeltas: new Float32Array([0, 1, 0, 0, 2, 0]), tangentDeltas: null },
35
+ { normalDeltas: null, positionDeltas: new Float32Array([0, 0, 3, 0, 0, 4]), tangentDeltas: null },
36
+ ],
37
+ weights: new Float32Array([0.5, 2]),
38
+ };
39
+
40
+ blendMeshGeometryMorph(geometry, morph, bindPose);
41
+
42
+ // vertex 0: (0,0,0) + 0.5*(0,1,0) + 2*(0,0,3) = (0, 0.5, 6)
43
+ // vertex 1: (1,0,0) + 0.5*(0,2,0) + 2*(0,0,4) = (1, 1, 8)
44
+ expect(Array.from(geometry.vertices)).toEqual([0, 0.5, 6, 1, 1, 8]);
45
+ });
46
+
47
+ it('bumps geometry.version so backends re-upload', () => {
48
+ const geometry = positionOnlyGeometry([0, 0, 0]);
49
+ const bindPose = captureMeshMorphBindPose(geometry);
50
+ const before = geometry.version;
51
+ blendMeshGeometryMorph(
52
+ geometry,
53
+ {
54
+ targets: [{ normalDeltas: null, positionDeltas: new Float32Array([1, 1, 1]), tangentDeltas: null }],
55
+ weights: new Float32Array([1]),
56
+ },
57
+ bindPose,
58
+ );
59
+ expect(geometry.version).toBe(before + 1);
60
+ });
61
+
62
+ it('leaves the base pose when all weights are zero', () => {
63
+ const geometry = positionOnlyGeometry([2, 3, 4]);
64
+ const bindPose = captureMeshMorphBindPose(geometry);
65
+ blendMeshGeometryMorph(
66
+ geometry,
67
+ {
68
+ targets: [{ normalDeltas: null, positionDeltas: new Float32Array([9, 9, 9]), tangentDeltas: null }],
69
+ weights: new Float32Array([0]),
70
+ },
71
+ bindPose,
72
+ );
73
+ expect(Array.from(geometry.vertices)).toEqual([2, 3, 4]);
74
+ });
75
+
76
+ it('is idempotent across frames — reblends from the base, not the last result', () => {
77
+ const geometry = positionOnlyGeometry([0, 0, 0]);
78
+ const bindPose = captureMeshMorphBindPose(geometry);
79
+ const morph: MeshMorph = {
80
+ targets: [{ normalDeltas: null, positionDeltas: new Float32Array([1, 0, 0]), tangentDeltas: null }],
81
+ weights: new Float32Array([1]),
82
+ };
83
+ blendMeshGeometryMorph(geometry, morph, bindPose);
84
+ blendMeshGeometryMorph(geometry, morph, bindPose);
85
+ expect(Array.from(geometry.vertices)).toEqual([1, 0, 0]);
86
+ });
87
+
88
+ it('morphs normals and tangent xyz but leaves tangent w and uv0 intact', () => {
89
+ // One vertex: position (0,0,0), normal (0,0,1), tangent (1,0,0, w=1), uv0 (0.25,0.75).
90
+ const geometry = createMeshGeometry({
91
+ layout: CANONICAL_LAYOUT,
92
+ vertices: new Float32Array([0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0.25, 0.75]),
93
+ });
94
+ const bindPose = captureMeshMorphBindPose(geometry);
95
+ blendMeshGeometryMorph(
96
+ geometry,
97
+ {
98
+ targets: [
99
+ {
100
+ normalDeltas: new Float32Array([0, 1, 0]),
101
+ positionDeltas: new Float32Array([5, 0, 0]),
102
+ tangentDeltas: new Float32Array([0, 0, 2]),
103
+ },
104
+ ],
105
+ weights: new Float32Array([1]),
106
+ },
107
+ bindPose,
108
+ );
109
+
110
+ const v = Array.from(geometry.vertices);
111
+ expect(v.slice(0, 3)).toEqual([5, 0, 0]); // position
112
+ expect(v.slice(3, 6)).toEqual([0, 1, 1]); // normal
113
+ expect(v.slice(6, 10)).toEqual([1, 0, 2, 1]); // tangent xyz morphed, w=1 intact
114
+ expect(v.slice(10, 12)).toEqual([0.25, 0.75]); // uv0 intact
115
+ });
116
+ });
117
+
118
+ describe('captureMeshMorphBindPose', () => {
119
+ it('captures the base position pose and allocates blend scratch', () => {
120
+ const geometry = positionOnlyGeometry([1, 2, 3, 4, 5, 6]);
121
+ const bindPose = captureMeshMorphBindPose(geometry);
122
+ expect(Array.from(bindPose.positions)).toEqual([1, 2, 3, 4, 5, 6]);
123
+ expect(bindPose.blendedPositions.length).toBe(6);
124
+ expect(bindPose.normals).toBeNull();
125
+ expect(bindPose.tangents).toBeNull();
126
+ expect(bindPose.blendedNormals).toBeNull();
127
+ expect(bindPose.blendedTangents).toBeNull();
128
+ });
129
+
130
+ it('captures normal and tangent-xyz base attributes when the layout carries them', () => {
131
+ const geometry = createMeshGeometry({
132
+ layout: CANONICAL_LAYOUT,
133
+ vertices: new Float32Array([0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0]),
134
+ });
135
+ const bindPose = captureMeshMorphBindPose(geometry);
136
+ expect(Array.from(bindPose.normals!)).toEqual([0, 1, 0]);
137
+ expect(Array.from(bindPose.tangents!)).toEqual([1, 0, 0]);
138
+ expect(bindPose.blendedNormals!.length).toBe(3);
139
+ expect(bindPose.blendedTangents!.length).toBe(3);
140
+ });
141
+ });
142
+ });