@@ -13,9 +13,20 @@ function parse(gltf, { fileName = 'model', ...options } = {}) {
13
13
const animations = gltf . animations
14
14
const hasAnimations = animations . length > 0
15
15
16
+ /** @type {Record<string, Object3D[]> */
17
+ const slots = { }
18
+
16
19
// Collect all objects
17
20
const objects = [ ]
18
- gltf . scene . traverse ( ( child ) => objects . push ( child ) )
21
+ gltf . scene . traverse ( ( child ) => {
22
+ objects . push ( child ) ;
23
+
24
+ // Collect slots
25
+ const slot = child . userData ?. slot ;
26
+ const hasSlot = ( slot && typeof slot === "string" && slot . length > 0 ) ;
27
+ if ( hasSlot )
28
+ slots [ slot ] ? slots [ slot ] . push ( child ) : ( slots [ slot ] = [ child ] ) ;
29
+ } )
19
30
20
31
// Browse for duplicates
21
32
const duplicates = {
@@ -75,6 +86,11 @@ function parse(gltf, { fileName = 'model', ...options } = {}) {
75
86
return isVarName ( name ) ? `.${ name } ` : `['${ name } ']`
76
87
}
77
88
89
+ /** Ensure that a slot is a valid variable name e.g. must not contain spaces */
90
+ function sanitizeSlotName ( slotname ) {
91
+ return slotname . replaceAll ( / [ ^ a - z A - Z 0 - 9 ] / g, '' ) ;
92
+ }
93
+
78
94
const rNbr = ( number ) => {
79
95
return parseFloat ( number . toFixed ( Math . round ( options . precision || 2 ) ) )
80
96
}
@@ -220,17 +236,18 @@ function parse(gltf, { fileName = 'model', ...options } = {}) {
220
236
duplicates . geometries [ obj . geometry . uuid + obj . material . name ] &&
221
237
duplicates . geometries [ obj . geometry . uuid + obj . material . name ] . count > ( options . instanceall ? 0 : 1 )
222
238
let animated = gltf . animations && gltf . animations . length > 0
223
- return { type, node, instanced, animated }
239
+ const hasSlots = obj . userData ?. slot && typeof obj . userData . slot === "string" && obj . userData . slot . length > 0 ;
240
+ return { type, node, instanced, animated, hasSlots }
224
241
}
225
242
226
243
function equalOrNegated ( a , b ) {
227
244
return ( a . x === b . x || a . x === - b . x ) && ( a . y === b . y || a . y === - b . y ) && ( a . z === b . z || a . z === - b . z )
228
245
}
229
246
230
247
function prune ( obj , children , result , oldResult , silent ) {
231
- let { type, animated } = getInfo ( obj )
248
+ let { type, animated, hasSlots } = getInfo ( obj )
232
249
// Prune ...
233
- if ( ! obj . __removed && ! options . keepgroups && ! animated && ( type === 'group' || type === 'scene' ) ) {
250
+ if ( ! obj . __removed && ! options . keepgroups && ! animated && ! hasSlots && ( type === 'group' || type === 'scene' ) ) {
234
251
/** Empty or no-property groups
235
252
* <group>
236
253
* <mesh geometry={nodes.foo} material={materials.bar} />
@@ -370,8 +387,8 @@ function parse(gltf, { fileName = 'model', ...options } = {}) {
370
387
// Bail out if the object was pruned
371
388
if ( pruned !== undefined ) return pruned
372
389
373
- // Add custom slots if defined in the object's userData.
374
- // E.g. userData: { "slot" : "mySlot" } becomes `{ props. mySlot }`
390
+ // Add custom slots if defined in the object's userData
391
+ // E.g. userData: { "slot" : "mySlot" } becomes `{ mySlot }`
375
392
const slot = obj . userData ?. slot ;
376
393
const hasSlot = ( slot && typeof slot === "string" && slot . length > 0 ) ;
377
394
const hasContent = children . length || hasSlot ;
@@ -380,9 +397,9 @@ function parse(gltf, { fileName = 'model', ...options } = {}) {
380
397
result += `${ hasContent ? '>' : '/>' } \n`
381
398
382
399
// Add children
383
- if ( children . length ) result += `${ children } \n`
400
+ if ( children . length ) result += `${ children . trimEnd ( "\n" ) } \n`
384
401
// Add custom slot
385
- if ( hasSlot ) result += `{props. ${ slot } }\n` ;
402
+ if ( hasSlot ) result += `{${ sanitizeSlotName ( slot ) } }\n` ;
386
403
// Close tag
387
404
if ( hasContent ) result += `</${ type } >`
388
405
return result
@@ -447,10 +464,14 @@ function parse(gltf, { fileName = 'model', ...options } = {}) {
447
464
} catch ( e ) {
448
465
console . log ( 'Error while parsing glTF' , e )
449
466
}
467
+
468
+ const slotParams = Object . keys ( slots ) . length > 0 ? ( Object . keys ( slots ) . map ( sanitizeSlotName ) . join ( ", " ) + ", " ) : "" ;
469
+
450
470
const header = `/*
451
471
${ options . header ? options . header : 'Auto-generated by: https://github.com/pmndrs/gltfjsx' } ${
452
472
options . size ? `\nFiles: ${ options . size } ` : ''
453
473
}
474
+
454
475
${ parseExtras ( gltf . parser . json . asset && gltf . parser . json . asset . extras ) } */`
455
476
const result = `${ options . types ? `\nimport * as THREE from 'three'` : '' }
456
477
import React, { useRef ${ hasInstances ? ', useMemo, useContext, createContext' : '' } } from 'react'
@@ -466,7 +487,7 @@ ${parseExtras(gltf.parser.json.asset && gltf.parser.json.asset.extras)}*/`
466
487
hasInstances
467
488
? `
468
489
const context = createContext(${ options . types ? '{} as ContextType' : '' } )
469
- export function Instances({ children, ...props }${ options . types ? ': JSX.IntrinsicElements["group"]' : '' } ) {
490
+ export function Instances({ children, ${ slotParams } ...props }${ options . types ? ': JSX.IntrinsicElements["group"]' : '' } ) {
470
491
const { nodes } = useGLTF('${ url } '${ options . draco ? `, ${ JSON . stringify ( options . draco ) } ` : '' } )${
471
492
options . types ? ' as GLTFResult' : ''
472
493
}
@@ -485,7 +506,7 @@ ${parseExtras(gltf.parser.json.asset && gltf.parser.json.asset.extras)}*/`
485
506
: ''
486
507
}
487
508
488
- export ${ options . exportdefault ? 'default' : '' } function Model(props${
509
+ export ${ options . exportdefault ? 'default' : '' } function Model(${ slotParams } ... props${
489
510
options . types ? ": JSX.IntrinsicElements['group']" : ''
490
511
} ) {
491
512
${ hasInstances ? 'const instances = useContext(context);' : '' } ${
0 commit comments