@@ -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
@@ -826,9 +839,7 @@ export function createViewer(
826839 scene . remove ( mannequin . root ) ;
827840 disposeTree ( mannequin . root ) ;
828841 mannequin = buildMannequin ( undefined , char . proportions ) ;
829- mannequin . root . traverse ( ( obj ) => {
830- if ( ( obj as THREE . Mesh ) . isMesh ) obj . visible = false ;
831- } ) ;
842+ setMeshVisibility ( mannequin . root , false ) ;
832843 scene . add ( mannequin . root ) ;
833844 scene . add ( char . group ) ;
834845 character = char ;
@@ -840,14 +851,27 @@ export function createViewer(
840851 else char . sync ( mannequin ) ;
841852 } )
842853 . catch ( ( ) => {
843- // Keep the procedural figure. Deliberately silent: an offline embed
844- // or a blocked CDN should degrade, not error.
854+ // Character failed (offline embed, blocked/404 CDN): reveal the
855+ // procedural figure we may have hidden, so the scene degrades to the
856+ // working fallback instead of staying blank. Deliberately silent.
857+ if ( deferProceduralMeshes ) setMeshVisibility ( mannequin . root , true ) ;
845858 } ) ;
846859 }
847860
848861 return api ;
849862}
850863
864+ /**
865+ * Show or hide a figure's meshes. The skeleton keeps driving animation and
866+ * bounding-box grounding regardless, so hiding only the meshes lets a hidden
867+ * procedural figure still pose the scene while its character stand-in loads.
868+ */
869+ function setMeshVisibility ( root : THREE . Object3D , visible : boolean ) : void {
870+ root . traverse ( ( obj ) => {
871+ if ( ( obj as THREE . Mesh ) . isMesh ) obj . visible = visible ;
872+ } ) ;
873+ }
874+
851875/** True for a finger bone id (thumb/index/middle/ring/pinky_left|right). */
852876function isFingerId ( id : string ) : boolean {
853877 return / ^ ( t h u m b | i n d e x | m i d d l e | r i n g | p i n k y ) _ / . test ( id ) ;
0 commit comments