diff --git a/editor/js/Loader.js b/editor/js/Loader.js index 2733ff8baf69f0..7368b688d3b3ad 100644 --- a/editor/js/Loader.js +++ b/editor/js/Loader.js @@ -45,6 +45,14 @@ function Loader( editor ) { while ( normalized.startsWith( '../' ) ) normalized = normalized.slice( 3 ); while ( normalized.startsWith( '/' ) ) normalized = normalized.slice( 1 ); + try { + + normalized = decodeURIComponent( normalized ); + + } catch ( e ) { /* malformed URI — keep as-is */ } + + normalized = normalized.normalize( 'NFC' ); + return normalized; }; @@ -982,10 +990,22 @@ function Loader( editor ) { const zip = unzipSync( new Uint8Array( contents ) ); + // Build a lookup map with NFC-normalized keys to handle + // unicode normalization differences (e.g. NFD vs NFC) + + const zipLookup = {}; + + for ( const path in zip ) { + + zipLookup[ path.normalize( 'NFC' ) ] = zip[ path ]; + + } + const manager = new THREE.LoadingManager(); manager.setURLModifier( function ( url ) { - const file = zip[ url ]; + const normalized = decodeURIComponent( url ).normalize( 'NFC' ); + const file = zipLookup[ normalized ]; if ( file ) { diff --git a/src/loaders/LoadingManager.js b/src/loaders/LoadingManager.js index 02c3991d488a38..97301477fc455c 100644 --- a/src/loaders/LoadingManager.js +++ b/src/loaders/LoadingManager.js @@ -156,6 +156,11 @@ class LoadingManager { */ this.resolveURL = function ( url ) { + // Normalize to NFC so that Unicode URIs (e.g. from glTF) + // are percent-encoded correctly per RFC 3987. + + url = url.normalize( 'NFC' ); + if ( urlModifier ) { return urlModifier( url );