Skip to content

Commit d6af701

Browse files
committed
GLTFLoader/GLTFExporter: Add support for single root scenes
1 parent 5bf53ee commit d6af701

File tree

2 files changed

+37
-12
lines changed

2 files changed

+37
-12
lines changed

examples/jsm/exporters/GLTFExporter.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,8 @@ class GLTFWriter {
646646
onlyVisible: true,
647647
maxTextureSize: Infinity,
648648
animations: [],
649-
includeCustomExtensions: false
649+
includeCustomExtensions: false,
650+
allowSingleRoot: false,
650651
}, options );
651652

652653
if ( this.options.animations.length > 0 ) {
@@ -2540,6 +2541,10 @@ class GLTFWriter {
25402541

25412542
if ( objectsWithoutScene.length > 0 ) {
25422543

2544+
if ( options.allowSingleRoot && input.length === 1 ) {
2545+
this.extensionsUsed[ 'GODOT_single_root' ] = true;
2546+
}
2547+
25432548
await this.processObjectsAsync( objectsWithoutScene );
25442549

25452550
}
@@ -3567,6 +3572,7 @@ GLTFExporter.Utils = {
35673572
* @property {number} [maxTextureSize=Infinity] - Restricts the image maximum size (both width and height) to the given value.
35683573
* @property {Array<AnimationClip>} [animations=[]] - List of animations to be included in the export.
35693574
* @property {boolean} [includeCustomExtensions=false] - Export custom glTF extensions defined on an object's `userData.gltfExtensions` property.
3575+
* @property {boolean} [allowSingleRoot=false] - When exporting a single object, flag it as the single root of the glTF file using the `GODOT_single_root` extension. This is highly recommended, but is false by default for backwards compatibility.
35703576
**/
35713577

35723578
/**

examples/jsm/loaders/GLTFLoader.js

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ class GLTFLoader extends Loader {
405405
}
406406

407407
/**
408-
* Parses the given FBX data and returns the resulting group.
408+
* Parses the given glTF data and returns the generated data to `onLoad`, with the root Object3D in the `.scene` property.
409409
*
410410
* @param {string|ArrayBuffer} data - The raw glTF data.
411411
* @param {string} path - The URL base path.
@@ -619,7 +619,8 @@ const EXTENSIONS = {
619619
EXT_TEXTURE_WEBP: 'EXT_texture_webp',
620620
EXT_TEXTURE_AVIF: 'EXT_texture_avif',
621621
EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
622-
EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing'
622+
EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing',
623+
GODOT_SINGLE_ROOT: 'GODOT_single_root',
623624
};
624625

625626
/**
@@ -4479,18 +4480,28 @@ class GLTFParser {
44794480

44804481
const extensions = this.extensions;
44814482
const sceneDef = this.json.scenes[ sceneIndex ];
4483+
const nodeIds = sceneDef.nodes || [];
44824484
const parser = this;
4485+
const extensionsUsed = this.json.extensionsUsed;
4486+
const isSingleRoot = Array.isArray( extensionsUsed ) ? extensionsUsed.includes( EXTENSIONS.GODOT_SINGLE_ROOT ) : false;
44834487

4484-
// Loader returns Group, not Scene.
4485-
// See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172
4486-
const scene = new Group();
4487-
if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name );
4488+
let scene;
4489+
if ( isSingleRoot ) {
44884490

4489-
assignExtrasToUserData( scene, sceneDef );
4491+
if ( nodeIds.length !== 1 ) {
4492+
throw new Error( 'THREE.GLTFLoader: glTF file with the single root flag must have exactly one scene root node. File is invalid.' );
4493+
}
44904494

4491-
if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef );
4495+
} else {
4496+
// Loader returns Group, not Scene.
4497+
// See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172
4498+
scene = new Group();
4499+
if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name );
44924500

4493-
const nodeIds = sceneDef.nodes || [];
4501+
assignExtrasToUserData( scene, sceneDef );
4502+
4503+
if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef );
4504+
}
44944505

44954506
const pending = [];
44964507

@@ -4502,9 +4513,17 @@ class GLTFParser {
45024513

45034514
return Promise.all( pending ).then( function ( nodes ) {
45044515

4505-
for ( let i = 0, il = nodes.length; i < il; i ++ ) {
4516+
if ( isSingleRoot ) {
45064517

4507-
scene.add( nodes[ i ] );
4518+
scene = nodes[ 0 ];
4519+
4520+
} else {
4521+
4522+
for ( let i = 0, il = nodes.length; i < il; i ++ ) {
4523+
4524+
scene.add( nodes[ i ] );
4525+
4526+
}
45084527

45094528
}
45104529

0 commit comments

Comments
 (0)