77 PropertyBinding ,
88 QuaternionKeyframeTrack ,
99 VectorKeyframeTrack ,
10- SkinnedMesh
10+ SkinnedMesh ,
11+ BooleanKeyframeTrack
1112} from 'three' ;
1213
1314/**
@@ -414,6 +415,29 @@ export class GLTFAnimationPointerExtension {
414415 // default
415416 __createTrack ( this . parser ) ;
416417
418+
419+ function isBooleanTarget ( node , trackPath ) {
420+ // trackPath like ".nodes.MyNode.visible" or ".materials.<uuid>.wireframe"
421+ try {
422+
423+ // get last token after '.'
424+ const sections = trackPath . split ( '.' ) . filter ( Boolean ) ;
425+ let last = sections [ sections . length - 1 ] ; // e.g., 'visible' or 'iridescenceThicknessRange[0]'
426+
427+ // strip indexers like [0]
428+ const propName = last . replace ( / \[ .* \] $ / , '' ) ;
429+
430+ // bail if not a direct property
431+ if ( ! ( propName in node ) ) return false ;
432+
433+ const val = node [ propName ] ;
434+ return typeof val === 'boolean' ;
435+ }
436+ catch {
437+ return false ;
438+ }
439+ }
440+
417441 /** Create a new track using the current parts array */
418442 function __createTrack ( parser ) {
419443
@@ -423,11 +447,31 @@ export class GLTFAnimationPointerExtension {
423447 console . log ( node , inputAccessor , outputAccessor , target , animationPointerPropertyPath ) ;
424448
425449 let TypedKeyframeTrack ;
426-
450+ let convertToBoolean = false ;
427451 switch ( outputAccessor . itemSize ) {
428452
429- case 1 :
430- TypedKeyframeTrack = NumberKeyframeTrack ;
453+ case 1 :
454+ // Get type of outputAccessor.array elements
455+ const arrayType = Object . prototype . toString . call ( outputAccessor . array ) ;
456+ const isUInt8 = ( arrayType === '[object Uint8Array]' ) ;
457+
458+ const looksLikeBool = isUInt8 && // UNSIGNED_BYTE as a strong hint
459+ isBooleanTarget ( node , animationPointerPropertyPath ) ;
460+
461+ // Log once per target property if it looks like a boolean
462+ if ( looksLikeBool ) {
463+ console . log ( 'Assuming boolean animation for' , animationPointerPropertyPath , 'based on property type and accessor type' ) ;
464+ }
465+ else
466+ {
467+ console . log ( 'Not a boolean target' , animationPointerPropertyPath ) ;
468+ }
469+ if ( looksLikeBool ) {
470+ TypedKeyframeTrack = BooleanKeyframeTrack ;
471+ convertToBoolean = true ; // map 0 => false, >0 => true
472+ } else {
473+ TypedKeyframeTrack = NumberKeyframeTrack ;
474+ }
431475 break ;
432476 case 2 :
433477 case 3 :
@@ -457,7 +501,11 @@ export class GLTFAnimationPointerExtension {
457501 if ( animationPointerPropertyPath . endsWith ( '.fov' ) ) {
458502
459503 outputArray = outputArray . map ( value => value / Math . PI * 180 ) ;
504+ }
460505
506+ if ( convertToBoolean ) {
507+ // boolean values are stored as ubyte in glTF, 0 = false, anything else = true
508+ outputArray = outputArray . map ( v => v > 0 ) ;
461509 }
462510
463511 const track = new TypedKeyframeTrack (
0 commit comments