@flighthq/skeleton3d 0.3.1-next.975.01aea94 → 0.4.0-edge.1908.751021c
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/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/enableSkeleton3DGuards.d.ts +4 -0
- package/dist/enableSkeleton3DGuards.d.ts.map +1 -0
- package/dist/enableSkeleton3DGuards.js +29 -0
- package/dist/enableSkeleton3DGuards.js.map +1 -0
- package/dist/skeleton3d.d.ts +1 -0
- package/dist/skeleton3d.d.ts.map +1 -1
- package/dist/skeleton3d.js +24 -3
- package/dist/skeleton3d.js.map +1 -1
- package/package.json +7 -6
- package/src/enableSkeleton3DGuards.test.ts +66 -0
- package/src/skeleton3d.test.ts +43 -0
package/dist/contract.d.ts
CHANGED
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
package/dist/contract.js
CHANGED
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,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":"enableSkeleton3DGuards.d.ts","sourceRoot":"","sources":["../src/enableSkeleton3DGuards.ts"],"names":[],"mappings":"AAMA,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAKD,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { logOnce } from '@flighthq/log/contract';
|
|
2
|
+
import { LogLevel } from '@flighthq/types/contract';
|
|
3
|
+
import { setSkeleton3DBindPoseGuard } from './skeleton3d';
|
|
4
|
+
export function areSkeleton3DGuardsEnabled() {
|
|
5
|
+
return skeleton3DGuardsEnabled;
|
|
6
|
+
}
|
|
7
|
+
export function disableSkeleton3DGuards() {
|
|
8
|
+
setSkeleton3DBindPoseGuard(null);
|
|
9
|
+
skeleton3DGuardsEnabled = false;
|
|
10
|
+
}
|
|
11
|
+
// Installs opt-in diagnostics for skeleton operations that substitute a value rather than compute one.
|
|
12
|
+
// The core modules stay message-free, so an application that omits this one sheds both the text and
|
|
13
|
+
// @flighthq/log.
|
|
14
|
+
export function enableSkeleton3DGuards() {
|
|
15
|
+
setSkeleton3DBindPoseGuard(warnOnDegenerateSkeleton3DBindPose);
|
|
16
|
+
skeleton3DGuardsEnabled = true;
|
|
17
|
+
}
|
|
18
|
+
// The substituted identity is a deliberate recovery, not a computed result: that one joint captures no
|
|
19
|
+
// bind pose and renders undeformed while the rest of the rig works. Nothing else reports it, and the
|
|
20
|
+
// symptom — one limb that will not deform — points at the mesh rather than at the joint's scale.
|
|
21
|
+
function warnOnDegenerateSkeleton3DBindPose(skeleton, jointIndex) {
|
|
22
|
+
logOnce(`skeleton3d:degenerate-bind-pose:${jointIndex}`, LogLevel.Warn, {
|
|
23
|
+
joint: skeleton.joints[jointIndex]?.name ?? '<unnamed>',
|
|
24
|
+
jointIndex,
|
|
25
|
+
message: 'setSkeleton3DBindPose: a joint has no invertible world matrix, so its inverse-bind entry was set to identity and that joint will not deform — a zero scale on the joint or an ancestor is the usual cause, and an imported rig can carry one.',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
let skeleton3DGuardsEnabled = false;
|
|
29
|
+
//# sourceMappingURL=enableSkeleton3DGuards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enableSkeleton3DGuards.js","sourceRoot":"","sources":["../src/enableSkeleton3DGuards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAE1D,MAAM,UAAU,0BAA0B;IACxC,OAAO,uBAAuB,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,0BAA0B,CAAC,IAAI,CAAC,CAAC;IACjC,uBAAuB,GAAG,KAAK,CAAC;AAClC,CAAC;AAED,uGAAuG;AACvG,oGAAoG;AACpG,iBAAiB;AACjB,MAAM,UAAU,sBAAsB;IACpC,0BAA0B,CAAC,kCAAkC,CAAC,CAAC;IAC/D,uBAAuB,GAAG,IAAI,CAAC;AACjC,CAAC;AAED,uGAAuG;AACvG,qGAAqG;AACrG,iGAAiG;AACjG,SAAS,kCAAkC,CAAC,QAA8B,EAAE,UAAkB;IAC5F,OAAO,CAAC,mCAAmC,UAAU,EAAE,EAAE,QAAQ,CAAC,IAAI,EAAE;QACtE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,IAAI,IAAI,WAAW;QACvD,UAAU;QACV,OAAO,EACL,+OAA+O;KAClP,CAAC,CAAC;AACL,CAAC;AAED,IAAI,uBAAuB,GAAG,KAAK,CAAC"}
|
package/dist/skeleton3d.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export declare function getSkeleton3DJointIndexByName(skeleton: Readonly<Skeleto
|
|
|
9
9
|
export declare function getSkeleton3DJointWorldMatrix(out: Matrix4Like, skeleton: Readonly<Skeleton3D>, jointIndex: number): boolean;
|
|
10
10
|
export declare function getSkeleton3DJointWorldMatrixByName(out: Matrix4Like, skeleton: Readonly<Skeleton3D>, name: string): boolean;
|
|
11
11
|
export declare function setSkeleton3DBindPose(skeleton: Readonly<Skeleton3D>): void;
|
|
12
|
+
export declare function setSkeleton3DBindPoseGuard(guard: ((skeleton: Readonly<Skeleton3D>, jointIndex: number) => void) | null): void;
|
|
12
13
|
export declare function validateSkeleton3D(skeleton: Readonly<Skeleton3D>): Skeleton3DValidationDiagnostic | null;
|
|
13
14
|
//# sourceMappingURL=skeleton3d.d.ts.map
|
package/dist/skeleton3d.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skeleton3d.d.ts","sourceRoot":"","sources":["../src/skeleton3d.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"skeleton3d.d.ts","sourceRoot":"","sources":["../src/skeleton3d.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAEhH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,CAS1E;AAOD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,UAAU,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,KAAK,MAAM,GAClE,UAAU,CAuBZ;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAqBnF;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EAAE,EAChB,mBAAmB,CAAC,EAAE,YAAY,EAClC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAC/B,UAAU,CAWZ;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAG5D;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAe1F;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlG;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,UAAU,EAAE,MAAM,GACjB,OAAO,CAKT;AAED,wBAAgB,mCAAmC,CACjD,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9B,IAAI,EAAE,MAAM,GACX,OAAO,CAET;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAmB1E;AAKD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,GAC3E,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,8BAA8B,GAAG,IAAI,CAWxG"}
|
package/dist/skeleton3d.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createEntity } from '@flighthq/entity/contract';
|
|
2
|
-
import { copyMatrix4, createMatrix3, createMatrix4, inverseMatrix4, multiplyMatrix4, setMatrix3NormalFromMatrix4, } from '@flighthq/geometry/contract';
|
|
2
|
+
import { copyMatrix4, createMatrix3, createMatrix4, inverseMatrix4, multiplyMatrix4, setMatrix3NormalFromMatrix4, setMatrix4Identity, } from '@flighthq/geometry/contract';
|
|
3
3
|
import { addNodeChild, getNodeParent, getNodeWorldMatrix4 } from '@flighthq/node/contract';
|
|
4
4
|
export function cloneSkeleton3D(skeleton) {
|
|
5
5
|
const clone = createEntity({
|
|
@@ -124,10 +124,30 @@ export function getSkeleton3DJointWorldMatrixByName(out, skeleton, name) {
|
|
|
124
124
|
export function setSkeleton3DBindPose(skeleton) {
|
|
125
125
|
const { inverseBindMatrices, joints } = skeleton;
|
|
126
126
|
for (let j = 0; j < joints.length; j++) {
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
// A joint's world matrix is singular when anything in its chain carries a zero scale, and that value
|
|
128
|
+
// is not necessarily the app's: glTF import writes node scale straight from unvalidated file data
|
|
129
|
+
// and joints ARE nodes, so a collapsed rig arrives from a file we do not control.
|
|
130
|
+
//
|
|
131
|
+
// Identity per joint, matching the AWD2 bind-pose recovery rather than declining the whole call: one
|
|
132
|
+
// degenerate joint costs its own bind pose and leaves it undeformed, while every other joint keeps
|
|
133
|
+
// working. Writing the failed inverse instead puts NaN in the palette, and NaN there does not stay
|
|
134
|
+
// put — it reaches the skinned vertices and takes the mesh with it, silently.
|
|
135
|
+
if (inverseMatrix4(_result, getNodeWorldMatrix4(joints[j]))) {
|
|
136
|
+
inverseBindMatrices.set(_result.m, j * 16);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
setMatrix4Identity(_result);
|
|
140
|
+
inverseBindMatrices.set(_result.m, j * 16);
|
|
141
|
+
skeleton3DBindPoseGuard?.(skeleton, j);
|
|
142
|
+
}
|
|
129
143
|
}
|
|
130
144
|
}
|
|
145
|
+
// The seam the guard layer installs into. This package has no importdiagnostics sink — it is not an
|
|
146
|
+
// importer, even though the degenerate value usually originates in one — so the caller-facing message
|
|
147
|
+
// lives in the guard layer, like `reparentNode`'s, rather than as a tally.
|
|
148
|
+
export function setSkeleton3DBindPoseGuard(guard) {
|
|
149
|
+
skeleton3DBindPoseGuard = guard;
|
|
150
|
+
}
|
|
131
151
|
export function validateSkeleton3D(skeleton) {
|
|
132
152
|
const jointCount = skeleton.joints.length;
|
|
133
153
|
const expectedInverseBindMatricesLength = jointCount * 16;
|
|
@@ -144,4 +164,5 @@ export function validateSkeleton3D(skeleton) {
|
|
|
144
164
|
const _invBind = createMatrix4();
|
|
145
165
|
const _result = createMatrix4();
|
|
146
166
|
const _normal = createMatrix3();
|
|
167
|
+
let skeleton3DBindPoseGuard = null;
|
|
147
168
|
//# sourceMappingURL=skeleton3d.js.map
|
package/dist/skeleton3d.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skeleton3d.js","sourceRoot":"","sources":["../src/skeleton3d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,2BAA2B,
|
|
1
|
+
{"version":3,"file":"skeleton3d.js","sourceRoot":"","sources":["../src/skeleton3d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG3F,MAAM,UAAU,eAAe,CAAC,QAA8B;IAC5D,MAAM,KAAK,GAAG,YAAY,CAAC;QACzB,mBAAmB,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QACnE,aAAa,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC;QACvD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE;QAC/B,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE;QACzG,cAAc,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC;KAC1D,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gGAAgG;AAChG,qGAAqG;AACrG,kGAAkG;AAClG,mGAAmG;AACnG,wGAAwG;AACxG,MAAM,UAAU,6BAA6B,CAC3C,QAA8B,EAC9B,UAAmE;IAEnE,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,KAAK,CAAS,YAAY,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;QAClB,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,IAAI,YAAY,KAAK,IAAI;YAAE,SAAS;QACpC,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACrD,IAAI,WAAW,KAAK,SAAS;YAAE,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,YAAY,CAAC;QAClB,mBAAmB,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QACnE,aAAa,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC;QACvD,MAAM;QACN,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE;QACzG,cAAc,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC;KAC1D,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,QAA8B;IAC3E,MAAM,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,QAAQ,CAAC;IAChF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;YAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAC3E,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QACnE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACnC,kGAAkG;QAClG,qFAAqF;QACrF,2BAA2B,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,kGAAkG;QAClG,yEAAyE;QACzE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,cAAc,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACrC,cAAc,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7C,cAAc,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7C,cAAc,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,MAAgB,EAChB,mBAAkC,EAClC,KAAgC;IAEhC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5B,MAAM,QAAQ,GAAG,YAAY,CAAC;QAC5B,mBAAmB,EAAE,mBAAmB,IAAI,IAAI,YAAY,CAAC,KAAK,GAAG,EAAE,CAAC;QACxE,aAAa,EAAE,IAAI,YAAY,CAAC,KAAK,GAAG,EAAE,CAAC;QAC3C,MAAM;QACN,KAAK,EAAE,KAAK,IAAI,IAAI;QACpB,cAAc,EAAE,IAAI,YAAY,CAAC,KAAK,GAAG,EAAE,CAAC;KAC7C,CAAC,CAAC;IACH,IAAI,mBAAmB,KAAK,SAAS;QAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACvE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAoB;IACpD,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3B,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,CAAuB,EAAE,CAAuB;IAC/E,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACtD,IAAI,CAAC,CAAC,mBAAmB,CAAC,MAAM,KAAK,CAAC,CAAC,mBAAmB,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAChF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,mBAAmB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtD,IAAI,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAC1E,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;IAC/B,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;IAC/B,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,KAAK,MAAM,CAAC;IACjE,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAC5C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,QAA8B,EAAE,IAAY;IACxF,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;IAC3B,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,CAAC,CAAC,CAAC;IAC7B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,GAAgB,EAChB,QAA8B,EAC9B,UAAkB;IAElB,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;IAC5B,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAChE,WAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,mCAAmC,CACjD,GAAgB,EAChB,QAA8B,EAC9B,IAAY;IAEZ,OAAO,6BAA6B,CAAC,GAAG,EAAE,QAAQ,EAAE,6BAA6B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;AACrG,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAA8B;IAClE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;IACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,qGAAqG;QACrG,kGAAkG;QAClG,kFAAkF;QAClF,EAAE;QACF,qGAAqG;QACrG,mGAAmG;QACnG,mGAAmG;QACnG,8EAA8E;QAC9E,IAAI,cAAc,CAAC,OAAO,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,kBAAkB,CAAC,OAAO,CAAC,CAAC;YAC5B,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;YAC3C,uBAAuB,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;AACH,CAAC;AAED,oGAAoG;AACpG,sGAAsG;AACtG,2EAA2E;AAC3E,MAAM,UAAU,0BAA0B,CACxC,KAA4E;IAE5E,uBAAuB,GAAG,KAAK,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAA8B;IAC/D,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IAC1C,MAAM,iCAAiC,GAAG,UAAU,GAAG,EAAE,CAAC;IAC1D,MAAM,yBAAyB,GAAG,QAAQ,CAAC,mBAAmB,CAAC,MAAM,CAAC;IACtE,IAAI,yBAAyB,KAAK,iCAAiC;QAAE,OAAO,IAAI,CAAC;IACjF,OAAO;QACL,iCAAiC;QACjC,yBAAyB;QACzB,UAAU;QACV,OAAO,EAAE,yCAAyC,yBAAyB,8BAA8B,UAAU,WAAW,iCAAiC,GAAG;KACnK,CAAC;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;AACjC,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAChC,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAEhC,IAAI,uBAAuB,GAA0E,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/skeleton3d",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-edge.1908.751021c",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,11 +38,12 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/entity": "0.
|
|
42
|
-
"@flighthq/geometry": "0.
|
|
43
|
-
"@flighthq/
|
|
44
|
-
"@flighthq/
|
|
45
|
-
"@flighthq/
|
|
41
|
+
"@flighthq/entity": "0.4.0-edge.1908.751021c",
|
|
42
|
+
"@flighthq/geometry": "0.4.0-edge.1908.751021c",
|
|
43
|
+
"@flighthq/log": "0.4.0-edge.1908.751021c",
|
|
44
|
+
"@flighthq/mesh": "0.4.0-edge.1908.751021c",
|
|
45
|
+
"@flighthq/node": "0.4.0-edge.1908.751021c",
|
|
46
|
+
"@flighthq/types": "0.4.0-edge.1908.751021c"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"@flighthq/scene3d": "*",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { setVector3 } from '@flighthq/geometry/contract';
|
|
2
|
+
import { setLogSink } from '@flighthq/log/contract';
|
|
3
|
+
import { invalidateNodeLocalTransform } from '@flighthq/node/contract';
|
|
4
|
+
import { createNode3D } from '@flighthq/scene3d/contract';
|
|
5
|
+
import type { LogEntry } from '@flighthq/types/contract';
|
|
6
|
+
import { afterEach, describe, expect, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { areSkeleton3DGuardsEnabled, disableSkeleton3DGuards, enableSkeleton3DGuards } from './enableSkeleton3DGuards';
|
|
9
|
+
import { createSkeleton3D, setSkeleton3DBindPose } from './skeleton3d';
|
|
10
|
+
|
|
11
|
+
describe('areSkeleton3DGuardsEnabled', () => {
|
|
12
|
+
test('reports whether the guards are installed', () => {
|
|
13
|
+
expect(areSkeleton3DGuardsEnabled()).toBe(false);
|
|
14
|
+
enableSkeleton3DGuards();
|
|
15
|
+
expect(areSkeleton3DGuardsEnabled()).toBe(true);
|
|
16
|
+
disableSkeleton3DGuards();
|
|
17
|
+
expect(areSkeleton3DGuardsEnabled()).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('disableSkeleton3DGuards', () => {
|
|
22
|
+
test('leaves the core silent again once removed', () => {
|
|
23
|
+
enableSkeleton3DGuards();
|
|
24
|
+
disableSkeleton3DGuards();
|
|
25
|
+
expect(captureLog(() => setSkeleton3DBindPose(collapsedSkeleton()))).toHaveLength(0);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('enableSkeleton3DGuards', () => {
|
|
30
|
+
// `logOnce` keys on the joint index and has no reset, so only ONE test may trigger a given index — a
|
|
31
|
+
// repeat would be deduplicated away and assert nothing while still looking green.
|
|
32
|
+
test('warns, naming the joint, when a bind pose cannot be captured', () => {
|
|
33
|
+
enableSkeleton3DGuards();
|
|
34
|
+
const entries = captureLog(() => setSkeleton3DBindPose(collapsedSkeleton()));
|
|
35
|
+
expect(entries).toHaveLength(1);
|
|
36
|
+
expect(entries[0].data).toMatchObject({ jointIndex: 0 });
|
|
37
|
+
expect(String((entries[0].data as { message: string }).message)).toContain('will not deform');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('stays silent for a rig whose joints all invert', () => {
|
|
41
|
+
enableSkeleton3DGuards();
|
|
42
|
+
expect(captureLog(() => setSkeleton3DBindPose(createSkeleton3D([createNode3D()])))).toHaveLength(0);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
disableSkeleton3DGuards();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function captureLog(run: () => void): LogEntry[] {
|
|
51
|
+
const entries: LogEntry[] = [];
|
|
52
|
+
setLogSink((entry) => entries.push(entry));
|
|
53
|
+
try {
|
|
54
|
+
run();
|
|
55
|
+
} finally {
|
|
56
|
+
setLogSink(null);
|
|
57
|
+
}
|
|
58
|
+
return entries;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function collapsedSkeleton(): ReturnType<typeof createSkeleton3D> {
|
|
62
|
+
const collapsed = createNode3D();
|
|
63
|
+
setVector3(collapsed.scale, 0, 0, 0);
|
|
64
|
+
invalidateNodeLocalTransform(collapsed);
|
|
65
|
+
return createSkeleton3D([collapsed]);
|
|
66
|
+
}
|
package/src/skeleton3d.test.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getSkeleton3DJointWorldMatrix,
|
|
15
15
|
getSkeleton3DJointWorldMatrixByName,
|
|
16
16
|
setSkeleton3DBindPose,
|
|
17
|
+
setSkeleton3DBindPoseGuard,
|
|
17
18
|
validateSkeleton3D,
|
|
18
19
|
} from './skeleton3d';
|
|
19
20
|
|
|
@@ -284,6 +285,29 @@ describe('getSkeleton3DJointWorldMatrixByName', () => {
|
|
|
284
285
|
});
|
|
285
286
|
|
|
286
287
|
describe('setSkeleton3DBindPose', () => {
|
|
288
|
+
// A zero-scale joint makes its world matrix singular. Writing the failed inverse puts NaN in the
|
|
289
|
+
// palette, which reaches the skinned vertices silently; identity costs that one joint its bind pose
|
|
290
|
+
// and leaves every other joint working — the AWD2 recovery, applied per joint.
|
|
291
|
+
it('substitutes identity for a joint with no invertible world matrix, leaving the rest of the rig', () => {
|
|
292
|
+
const collapsed = createNode3D();
|
|
293
|
+
const healthy = createNode3D();
|
|
294
|
+
const skeleton = createSkeleton3D([collapsed, healthy]);
|
|
295
|
+
setVector3(collapsed.scale, 0, 0, 0);
|
|
296
|
+
setVector3(healthy.position, 3, 0, 0);
|
|
297
|
+
invalidateNodeLocalTransform(collapsed);
|
|
298
|
+
invalidateNodeLocalTransform(healthy);
|
|
299
|
+
|
|
300
|
+
setSkeleton3DBindPose(skeleton);
|
|
301
|
+
|
|
302
|
+
for (const value of skeleton.inverseBindMatrices) expect(Number.isFinite(value)).toBe(true);
|
|
303
|
+
// The collapsed joint got identity...
|
|
304
|
+
expect(Array.from(skeleton.inverseBindMatrices.slice(0, 16))).toEqual([
|
|
305
|
+
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
|
|
306
|
+
]);
|
|
307
|
+
// ...and the healthy joint still got its real inverse bind.
|
|
308
|
+
expect(skeleton.inverseBindMatrices[28]).toBeCloseTo(-3);
|
|
309
|
+
});
|
|
310
|
+
|
|
287
311
|
it('rebinds so the current pose becomes the rest pose', () => {
|
|
288
312
|
const joint = createNode3D();
|
|
289
313
|
const skeleton = createSkeleton3D([joint]);
|
|
@@ -297,6 +321,25 @@ describe('setSkeleton3DBindPose', () => {
|
|
|
297
321
|
});
|
|
298
322
|
});
|
|
299
323
|
|
|
324
|
+
describe('setSkeleton3DBindPoseGuard', () => {
|
|
325
|
+
it('is notified for the degenerate joint only', () => {
|
|
326
|
+
const seen: number[] = [];
|
|
327
|
+
setSkeleton3DBindPoseGuard((_skeleton, jointIndex) => seen.push(jointIndex));
|
|
328
|
+
try {
|
|
329
|
+
const collapsed = createNode3D();
|
|
330
|
+
const healthy = createNode3D();
|
|
331
|
+
const skeleton = createSkeleton3D([healthy, collapsed]);
|
|
332
|
+
setVector3(collapsed.scale, 0, 0, 0);
|
|
333
|
+
invalidateNodeLocalTransform(collapsed);
|
|
334
|
+
invalidateNodeLocalTransform(healthy);
|
|
335
|
+
setSkeleton3DBindPose(skeleton);
|
|
336
|
+
expect(seen).toEqual([1]);
|
|
337
|
+
} finally {
|
|
338
|
+
setSkeleton3DBindPoseGuard(null);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
300
343
|
describe('validateSkeleton3D', () => {
|
|
301
344
|
it('returns null for a well-formed skeleton', () => {
|
|
302
345
|
expect(validateSkeleton3D(createSkeleton3D([createNode3D(), createNode3D()]))).toBeNull();
|