@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 +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/meshGeometry.d.ts +6 -1
- package/dist/meshGeometry.d.ts.map +1 -1
- package/dist/meshGeometry.js +48 -1
- package/dist/meshGeometry.js.map +1 -1
- package/dist/meshGeometryBuilders.js +1 -1
- package/dist/meshGeometryLayout.d.ts +1 -0
- package/dist/meshGeometryLayout.d.ts.map +1 -1
- package/dist/meshGeometryLayout.js +19 -0
- package/dist/meshGeometryLayout.js.map +1 -1
- package/dist/morphMeshGeometry.d.ts +4 -0
- package/dist/morphMeshGeometry.d.ts.map +1 -0
- package/dist/morphMeshGeometry.js +116 -0
- package/dist/morphMeshGeometry.js.map +1 -0
- package/package.json +4 -4
- package/src/meshGeometry.test.ts +78 -1
- package/src/meshGeometryBuilders.test.ts +35 -0
- package/src/meshGeometryLayout.test.ts +27 -1
- package/src/morphMeshGeometry.test.ts +142 -0
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,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
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"}
|
package/dist/meshGeometry.d.ts
CHANGED
|
@@ -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"}
|
package/dist/meshGeometry.js
CHANGED
|
@@ -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 = {
|
|
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
|
}
|
package/dist/meshGeometry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meshGeometry.js","sourceRoot":"","sources":["../src/meshGeometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
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,
|
|
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":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,gGAAgG;AAChG,iGAAiG;AACjG,0FAA0F;AAC1F,iGAAiG;AACjG,6FAA6F;AAC7F,6BAA6B;AAC7B,EAAE;AACF,8FAA8F;AAC9F,wFAAwF;AACxF,6DAA6D;AAC7D,MAAM,UAAU,yBAAyB,CACvC,MAA8B,EAC9B,YAA6C;IAE7C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IACvC,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC;IACtC,MAAM,kBAAkB,GAAG,SAAS,GAAG,CAAC,CAAC;IACzC,MAAM,kBAAkB,GAAG,SAAS,GAAG,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzG,MAAM,WAAW,GAAG,IAAI,YAAY,CAAC,WAAW,GAAG,kBAAkB,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAEjC,yEAAyE;IACzE,8EAA8E;IAC9E,MAAM,OAAO,GAAiF,EAAE,CAAC;IACjG,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAC9C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,SAAS;QACpD,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,GAAG,CAAC,CAAC;QAC9C,MAAM,cAAc,GAAG,wBAAwB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAChE,IAAI,cAAc,KAAK,CAAC;YAAE,SAAS;QACnC,kDAAkD;QAClD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CACzE,CAAC;QACF,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,OAAO,CAAC,IAAI,CAAC;YACX,cAAc;YACd,cAAc;YACd,cAAc,EAAE,OAAO,CAAC,UAAU,GAAG,CAAC;SACvC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,CAAC,GAAG,kBAAkB,CAAC;QACvC,MAAM,OAAO,GAAG,CAAC,GAAG,kBAAkB,CAAC;QACvC,KAAK,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,IAAI,OAAO,EAAE,CAAC;YACzE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC;gBACxC,WAAW,CAAC,OAAO,GAAG,cAAc,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC;YACrF,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,kBAAkB,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,SAAS;QACpC,MAAM,EAAE,YAAY;QACpB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,WAAW;KACtB,CAAC,CAAC;AACL,CAAC;AAED,gGAAgG;AAChG,2DAA2D;AAC3D,SAAS,wBAAwB,CAAC,MAAc;IAC9C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,wFAAwF;AACxF,+FAA+F;AAC/F,iGAAiG;AACjG,oEAAoE;AACpE,MAAM,CAAC,MAAM,8BAA8B,GAA0B;IACnE,UAAU,EAAE;QACV,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE;QAC5D,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE;QAC3D,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE;QAC5D,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE;KACzD;IACD,MAAM,EAAE,EAAE;CACX,CAAC"}
|
|
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.
|
|
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.
|
|
36
|
-
"@flighthq/geometry": "0.2.
|
|
37
|
-
"@flighthq/types": "0.2.
|
|
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"
|
package/src/meshGeometry.test.ts
CHANGED
|
@@ -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 {
|
|
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
|
+
});
|