@threlte/gltf 3.0.5 → 3.0.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @threlte/gltf
2
2
 
3
+ ## 3.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - e854fb5: Add multiple SkinnedMesh support
8
+
9
+ ## 3.0.6
10
+
11
+ ### Patch Changes
12
+
13
+ - 766701b: Fixed -p flag collision between --printwidth and --precision
14
+ - 766701b: Fixed --printwidth flag being ignored
15
+ - 766701b: Fixed ReferenceError crash when using --degrade flag
16
+
3
17
  ## 3.0.5
4
18
 
5
19
  ### Patch Changes
package/cli.js CHANGED
@@ -47,7 +47,7 @@ const cli = meow(
47
47
  keepnames: { type: 'boolean', shortFlag: 'k' },
48
48
  keepgroups: { type: 'boolean', shortFlag: 'K' },
49
49
  shadows: { type: 'boolean', shortFlag: 's' },
50
- printwidth: { type: 'number', shortFlag: 'p', default: 120 },
50
+ printwidth: { type: 'number', shortFlag: 'w', default: 120 },
51
51
  meta: { type: 'boolean', shortFlag: 'm' },
52
52
  precision: { type: 'number', shortFlag: 'p', default: 2 },
53
53
  isolated: { type: 'boolean', shortFlag: 'i', default: false },
@@ -62,6 +62,7 @@ const cli = meow(
62
62
  simplify: { type: 'boolean', shortFlag: 'S', default: false },
63
63
  keepmeshes: { type: 'boolean', shortFlag: 'j', default: false },
64
64
  keepmaterials: { type: 'boolean', shortFlag: 'M', default: false },
65
+ format: { type: 'string', shortFlag: 'f', default: 'webp' },
65
66
  ratio: { type: 'number', default: 0.75 },
66
67
  error: { type: 'number', default: 0.001 },
67
68
  debug: { type: 'boolean', shortFlag: 'D' }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threlte/gltf",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "GLTF to Threlte converter",
5
5
  "type": "module",
6
6
  "keywords": [
package/src/index.js CHANGED
@@ -39,7 +39,7 @@ export default async function (file, output, options) {
39
39
  singleQuote: true,
40
40
  trailingComma: 'es5',
41
41
  semi: false,
42
- printWidth: 100,
42
+ printWidth: options.printwidth ?? 100,
43
43
  parser: 'svelte',
44
44
  plugins: [prettierPluginSvelte],
45
45
  overrides: [
@@ -19,4 +19,14 @@ export type Options = {
19
19
  ratio?: number
20
20
  error?: number
21
21
  debug?: boolean
22
+ keepmeshes?: boolean
23
+ keepmaterials?: boolean
24
+ format?: string
25
+ degrade?: string
26
+ degraderesolution?: number
27
+ console?: boolean
28
+ header?: string
29
+ showLog?: (log: string) => void
30
+ timeout?: number
31
+ delay?: number
22
32
  }
@@ -21,6 +21,9 @@ export function parse(fileName, gltf, options = {}) {
21
21
  const objects = []
22
22
  gltf.scene.traverse((child) => objects.push(child))
23
23
 
24
+ // Detect skinned meshes — these need per-instance cloning
25
+ const hasSkinnedMeshes = objects.some((o) => o.isSkinnedMesh)
26
+
24
27
  // Browse for duplicates
25
28
  const duplicates = {
26
29
  names: {},
@@ -53,7 +56,7 @@ export function parse(fileName, gltf, options = {}) {
53
56
  }
54
57
 
55
58
  if (child.geometry) {
56
- const key = child.geometry.uuid + child.material?.name ?? ''
59
+ const key = child.geometry.uuid + (child.material?.name ?? '')
57
60
  if (!duplicates.geometries[key]) {
58
61
  let name = (child.name || 'Part').replace(/[^a-zA-Z]/g, '')
59
62
  name = name.charAt(0).toUpperCase() + name.slice(1)
@@ -77,7 +80,7 @@ export function parse(fileName, gltf, options = {}) {
77
80
  }
78
81
 
79
82
  function sanitizeName(name) {
80
- return isVarName(name) ? `.${name}` : `['${name}']`
83
+ return isVarName(name) ? `.${name}` : `['${name.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}']`
81
84
  }
82
85
 
83
86
  const rNbr = (number) => {
@@ -184,14 +187,15 @@ export function parse(fileName, gltf, options = {}) {
184
187
  else result += `material={gltf.${node}.material} `
185
188
  }
186
189
 
187
- if (obj.skeleton) result += `skeleton={gltf.${node}.skeleton} `
190
+ if (obj.skeleton)
191
+ result += `skeleton={${hasSkinnedMeshes ? `clonedNodes${sanitizeName(obj.name)}` : `gltf.${node}`}.skeleton} `
188
192
  if (obj.visible === false) result += `visible={false} `
189
193
  if (obj.castShadow === true) result += `castShadow `
190
194
  if (obj.receiveShadow === true) result += `receiveShadow `
191
195
  if (obj.morphTargetDictionary)
192
- result += `morphTargetDictionary={gltf.${node}.morphTargetDictionary} `
196
+ result += `morphTargetDictionary={${hasSkinnedMeshes ? `clonedNodes${sanitizeName(obj.name)}` : `gltf.${node}`}.morphTargetDictionary} `
193
197
  if (obj.morphTargetInfluences)
194
- result += `morphTargetInfluences={gltf.${node}.morphTargetInfluences} `
198
+ result += `morphTargetInfluences={${hasSkinnedMeshes ? `clonedNodes${sanitizeName(obj.name)}` : `gltf.${node}`}.morphTargetInfluences} `
195
199
  if (obj.intensity && rNbr(obj.intensity)) result += `intensity={${rNbr(obj.intensity)}} `
196
200
  //if (obj.power && obj.power !== 4 * Math.PI) result += `power={${rNbr(obj.power)}} `
197
201
  if (obj.angle && obj.angle !== Math.PI / 3) result += `angle={${rDeg(obj.angle)}} `
@@ -402,7 +406,7 @@ export function parse(fileName, gltf, options = {}) {
402
406
 
403
407
  // Bail out on lights and bones
404
408
  if (type === 'bone') {
405
- return `<T is={gltf.${node}} />\n`
409
+ return `<T is={${hasSkinnedMeshes ? `clonedNodes${sanitizeName(obj.name)}` : `gltf.${node}`}} />\n`
406
410
  }
407
411
 
408
412
  // Collect children
@@ -495,6 +499,7 @@ export function parse(fileName, gltf, options = {}) {
495
499
  const imports = `
496
500
  ${options.types ? `\nimport type * as THREE from 'three'` : ''}
497
501
  ${hasAnimations ? `import { Group } from 'three'` : ''}
502
+ ${hasSkinnedMeshes ? `import { clone as cloneSkeleton } from 'three/examples/jsm/utils/SkeletonUtils.js'` : ''}
498
503
  ${options.types ? `import type { Snippet } from 'svelte'` : ''}
499
504
  import { ${['T', options.types && !options.isolated ? 'type Props' : '']
500
505
  .filter(Boolean)
@@ -528,7 +533,7 @@ ${parseExtras(gltf.parser.json.asset && gltf.parser.json.asset.extras)}-->
528
533
  ${
529
534
  options.preload
530
535
  ? `
531
- <script context="module"${options.types ? ' lang="ts"' : ''}>
536
+ <script module${options.types ? ' lang="ts"' : ''}>
532
537
  ${imports}
533
538
 
534
539
  ${options.types ? printThrelteTypes(objects, animations) : ''}
@@ -575,6 +580,19 @@ ${
575
580
 
576
581
  ${!options.preload ? `const gltf = ${useGltf}` : 'const gltf = load()'}
577
582
 
583
+ ${
584
+ hasSkinnedMeshes
585
+ ? `function cloneScene(scene${options.types ? ': THREE.Group' : ''}) {
586
+ const clone = cloneSkeleton(scene)
587
+ const nodes${options.types ? ': Record<string, THREE.Object3D>' : ''} = {}
588
+ clone.traverse((child) => {
589
+ if (child.name) nodes[child.name] = child
590
+ })
591
+ return nodes${options.types ? ' as unknown as GLTFResult["nodes"]' : ''}
592
+ }`
593
+ : ''
594
+ }
595
+
578
596
  ${
579
597
  hasAnimations
580
598
  ? `export const { actions, mixer } = useGltfAnimations${
@@ -588,6 +606,7 @@ ${
588
606
  {#await gltf}
589
607
  {@render fallback?.()}
590
608
  {:then gltf}
609
+ ${hasSkinnedMeshes ? '{@const clonedNodes = cloneScene(gltf.scene)}' : ''}
591
610
  ${scene}
592
611
  {:catch err}
593
612
  {@render error?.({ error: err })}
@@ -83,7 +83,7 @@ export async function transform(file, output, config = {}) {
83
83
  encoder: sharp,
84
84
  pattern: new RegExp(`^(?=${config.degrade}).*$`),
85
85
  targetFormat: config.format,
86
- resize: [degradeResolution, degradeResolution]
86
+ resize: [config.degraderesolution ?? 512, config.degraderesolution ?? 512]
87
87
  }),
88
88
  textureCompress({
89
89
  encoder: sharp,