sindicate 0.9.0 → 0.11.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sindicate",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Sindicate — open-world game engine for three.js WebGPU. Streaming worlds, characters, mounts & vehicles, missions, crowds.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -13,6 +13,12 @@ export function setAtlasEmissiveHook(fn) { atlasEmissiveHook = fn; }
13
13
  // it), not the engine — register per game: setClipCacheVersion(2). Defaults to 2
14
14
  // (western's current rebake generation).
15
15
  export function setClipCacheVersion(v) { CLIP_JSON_VER = v; }
16
+
17
+ // A registered CLIP PACK (e.g. Accentimations) is consulted BEFORE the per-game cooked
18
+ // cache: registerClipPack({ urlFor: (sourceStem) => url|null }). Misses fall through to
19
+ // the cooked cache, then the FBX parse — fully backwards compatible.
20
+ let clipPack = null;
21
+ export function registerClipPack(pack) { clipPack = pack; }
16
22
  // Patched loader from vendor/ — merges ALL FBX animation layers per stack. The
17
23
  // engine does NOT depend on a node_modules postinstall patch.
18
24
  import { FBXLoader } from '../vendor/FBXLoader.js';
@@ -547,6 +553,18 @@ export async function loadClip(url, name) {
547
553
  if (!clipCache.has(url)) {
548
554
  const jsonName = clipJsonName(url);
549
555
  const p = (async () => {
556
+ const packUrl = clipPack?.urlFor?.(url.split('/').pop().replace(/\.fbx$/i, ''));
557
+ if (packUrl) {
558
+ try {
559
+ const r = await fetch(packUrl);
560
+ if (r.ok) {
561
+ const clip = THREE.AnimationClip.parse(await r.json());
562
+ clip.name = name ?? url.split('/').pop().replace('.fbx', '');
563
+ tick(clip.name);
564
+ return clip;
565
+ }
566
+ } catch (e) { /* pack miss — fall through to the cooked cache */ }
567
+ }
550
568
  try {
551
569
  const r = await fetch(`/assets/clips/${jsonName}`);
552
570
  if (r.ok) {
@@ -193,7 +193,10 @@ export function buildRetargetContext(targetRoot, rigKey = null) {
193
193
  // Synty rigs, where Root and Hips are coincident) used to yield posScale = 0, which
194
194
  // multiplied every Hips.position key by ZERO: the pelvis got pinned to the root and the
195
195
  // character sank into the ground. Only rescale when BOTH hip heights are real; else 1:1.
196
- if (!(posScale > 0.01 && posScale < 100)) posScale = 1;
196
+ // BOUNDS: a metre-scale target fed cm clips has posScale 0.01 EXACTLY (fable's modular
197
+ // hero — the old `> 0.01` guard rejected its own legitimate ratio and the pelvis flew
198
+ // 87m up). 0.002 still excludes the genuine degenerate-bind zero case.
199
+ if (!(posScale > 0.002 && posScale < 500)) posScale = 1;
197
200
 
198
201
  // Same bone NAMES don't guarantee the same bind POSE: the Fantasy Rivals bosses each carry a
199
202
  // custom rest stance (bent legs, etc.) on anim-rig bone names. The clip can only be used as-is