Skip to content

Commit 498aba6

Browse files
Merge branch 'main' into ground-floor-fix
2 parents 706171e + aa52c68 commit 498aba6

1 file changed

Lines changed: 35 additions & 11 deletions

File tree

packages/posecode-render/src/index.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,12 @@ export interface ViewerOptions {
9393
clips?: Record<string, string>;
9494
/**
9595
* Keep the procedural figure visible while a skinned `characterUrl` loads.
96-
* This viewer always shows the procedural figure during load (and on load
97-
* failure), so the flag is accepted for API compatibility; the default
98-
* behavior already matches `true`.
96+
* Defaults to `true` (the procedural figure poses the scene during the load,
97+
* matching callers that never set this). Set `false` alongside a
98+
* `characterUrl` to hide the procedural meshes until the character resolves —
99+
* so a page load shows the skinned figure or nothing, never a blink of the
100+
* crude procedural figure. On load failure the procedural figure is revealed
101+
* regardless, so the scene never stays blank.
99102
*/
100103
showProceduralWhileLoading?: boolean;
101104
}
@@ -182,10 +185,20 @@ export function createViewer(
182185
enableShadows(mannequin.root);
183186
scene.add(mannequin.root);
184187

188+
// When a skinned character is requested and the caller opted out of the
189+
// procedural fallback during load, hide the procedural meshes up front so the
190+
// crude figure never flashes for the character's fetch time on a page load.
191+
// The skeleton still drives animation and grounding; only the meshes hide
192+
// (same as the post-load swap). Revealed again if the character fails to load.
193+
const deferProceduralMeshes =
194+
Boolean(opts.characterUrl) && opts.showProceduralWhileLoading === false;
195+
if (deferProceduralMeshes) setMeshVisibility(mannequin.root, false);
196+
185197
// Skinned character layer (optional). While loading (and on failure) the
186-
// procedural figure stays; once ready, the driver skeleton is rebuilt with
187-
// the character's proportions, its meshes are hidden (they keep feeding the
188-
// bounding-box grounding), and the character mirrors it every frame.
198+
// procedural figure stays — unless deferred above; once ready, the driver
199+
// skeleton is rebuilt with the character's proportions, its meshes are hidden
200+
// (they keep feeding the bounding-box grounding), and the character mirrors it
201+
// every frame.
189202
let character: Character | null = null;
190203

191204
// Mocap-clip layer (optional, character-only). When the loaded document
@@ -843,9 +856,7 @@ export function createViewer(
843856
scene.remove(mannequin.root);
844857
disposeTree(mannequin.root);
845858
mannequin = buildMannequin(undefined, char.proportions);
846-
mannequin.root.traverse((obj) => {
847-
if ((obj as THREE.Mesh).isMesh) obj.visible = false;
848-
});
859+
setMeshVisibility(mannequin.root, false);
849860
scene.add(mannequin.root);
850861
scene.add(char.group);
851862
character = char;
@@ -857,14 +868,27 @@ export function createViewer(
857868
else char.sync(mannequin);
858869
})
859870
.catch(() => {
860-
// Keep the procedural figure. Deliberately silent: an offline embed
861-
// or a blocked CDN should degrade, not error.
871+
// Character failed (offline embed, blocked/404 CDN): reveal the
872+
// procedural figure we may have hidden, so the scene degrades to the
873+
// working fallback instead of staying blank. Deliberately silent.
874+
if (deferProceduralMeshes) setMeshVisibility(mannequin.root, true);
862875
});
863876
}
864877

865878
return api;
866879
}
867880

881+
/**
882+
* Show or hide a figure's meshes. The skeleton keeps driving animation and
883+
* bounding-box grounding regardless, so hiding only the meshes lets a hidden
884+
* procedural figure still pose the scene while its character stand-in loads.
885+
*/
886+
function setMeshVisibility(root: THREE.Object3D, visible: boolean): void {
887+
root.traverse((obj) => {
888+
if ((obj as THREE.Mesh).isMesh) obj.visible = visible;
889+
});
890+
}
891+
868892
/** True for a finger bone id (thumb/index/middle/ring/pinky_left|right). */
869893
function isFingerId(id: string): boolean {
870894
return /^(thumb|index|middle|ring|pinky)_/.test(id);

0 commit comments

Comments
 (0)