Explorar o código

Editor: Fix name conflict during glTF export. (#33699)

Michael Herzog hai 1 mes
pai
achega
0ccb67b625
Modificáronse 3 ficheiros con 141 adicións e 7 borrados
  1. 15 6
      editor/js/Animation.js
  2. 120 1
      editor/js/Menubar.File.js
  3. 6 0
      editor/js/Strings.js

+ 15 - 6
editor/js/Animation.js

@@ -367,7 +367,14 @@ function Animation( editor ) {
 
 			clipRow.addEventListener( 'click', function () {
 
-				editor.select( root );
+				if ( editor.selected !== root ) {
+
+					signals.objectSelected.remove( selectDefaultClip );
+					editor.select( root );
+					signals.objectSelected.add( selectDefaultClip );
+
+				}
+
 				selectClip( clip, root );
 				update(); // Refresh to update highlighting
 
@@ -578,10 +585,7 @@ function Animation( editor ) {
 
 	}
 
-	updateTime();
-
-	// Auto-select clip when an object with animations is selected
-	signals.objectSelected.add( function ( object ) {
+	function selectDefaultClip( object ) {
 
 		if ( object !== null && object.animations && object.animations.length > 0 ) {
 
@@ -590,7 +594,12 @@ function Animation( editor ) {
 
 		}
 
-	} );
+	}
+
+	updateTime();
+
+	// Auto-select clip when an object with animations is selected
+	signals.objectSelected.add( selectDefaultClip );
 
 	// Update when scene changes
 	signals.editorCleared.add( clear );

+ 120 - 1
editor/js/Menubar.File.js

@@ -1,5 +1,5 @@
 import { UIPanel, UIRow, UIHorizontalRule } from './libs/ui.js';
-import { FileLoader } from 'three';
+import { FileLoader, PropertyBinding } from 'three';
 
 function MenubarFile( editor ) {
 
@@ -276,6 +276,15 @@ function MenubarFile( editor ) {
 	option.onClick( async function () {
 
 		const scene = editor.scene;
+
+		if ( needsUniqueNames( scene ) ) { // see #25179
+
+			if ( confirm( strings.getKey( 'prompt/file/export/duplicateNames' ) ) === false ) return;
+
+			ensureUniqueNames( scene );
+
+		}
+
 		const animations = getAnimations( scene );
 
 		const optimizedAnimations = [];
@@ -307,6 +316,15 @@ function MenubarFile( editor ) {
 	option.onClick( async function () {
 
 		const scene = editor.scene;
+
+		if ( needsUniqueNames( scene ) ) { // see #25179
+
+			if ( confirm( strings.getKey( 'prompt/file/export/duplicateNames' ) ) === false ) return;
+
+			ensureUniqueNames( scene );
+
+		}
+
 		const animations = getAnimations( scene );
 
 		const optimizedAnimations = [];
@@ -460,6 +478,107 @@ function MenubarFile( editor ) {
 
 	}
 
+	function needsUniqueNames( scene ) {
+
+		const usedNames = new Set();
+		let duplicate = false;
+		let animated = false;
+
+		scene.traverse( function ( object ) {
+
+			if ( object.animations.length > 0 ) animated = true;
+
+			if ( object.name === '' ) return;
+
+			if ( usedNames.has( object.name ) ) duplicate = true;
+
+			usedNames.add( object.name );
+
+		} );
+
+		return duplicate && animated;
+
+	}
+
+	// Gives every object a unique name and keeps the animation tracks that
+	// reference them by name in sync. The renamed scene mirrors the result of a
+	// glTF round-trip, where the loader makes all names unique, too.
+
+	function ensureUniqueNames( scene ) {
+
+		// Resolve each track's target object up front, scoped to the object that
+		// owns the clip. This disambiguates colliding names before they change.
+
+		const trackBindings = [];
+
+		scene.traverse( function ( owner ) {
+
+			for ( const clip of owner.animations ) {
+
+				for ( const track of clip.tracks ) {
+
+					const nodeName = PropertyBinding.parseTrackName( track.name ).nodeName;
+					const target = PropertyBinding.findNode( owner, nodeName );
+
+					// References by UUID stay valid, so only track name-based ones.
+
+					if ( target !== null && target.name === nodeName ) {
+
+						trackBindings.push( { track, target, nodeName } );
+
+					}
+
+				}
+
+			}
+
+		} );
+
+		// Assign a unique name to every named object.
+
+		let changed = false;
+		const usedNames = new Set();
+
+		scene.traverse( function ( object ) {
+
+			if ( object.name === '' ) return;
+
+			if ( usedNames.has( object.name ) ) {
+
+				let suffix = 1, name;
+				do {
+
+					name = object.name + '_' + ( suffix ++ );
+
+				} while ( usedNames.has( name ) );
+
+				object.name = name;
+				changed = true;
+
+			}
+
+			usedNames.add( object.name );
+
+		} );
+
+		if ( changed === false ) return;
+
+		// Point the affected tracks at their renamed targets.
+
+		for ( const { track, target, nodeName } of trackBindings ) {
+
+			if ( target.name !== nodeName ) {
+
+				track.name = target.name + track.name.slice( nodeName.length );
+
+			}
+
+		}
+
+		editor.signals.sceneGraphChanged.dispatch();
+
+	}
+
 	return container;
 
 }

+ 6 - 0
editor/js/Strings.js

@@ -8,6 +8,7 @@ function Strings( config ) {
 			'prompt/file/failedToOpenProject': 'خطایی در باز کردن پروژه پیش آمده',
 			'prompt/file/export/noMeshSelected': 'هیچ Mesh ای انتخاب نکردید',
 			'prompt/file/export/noObjectSelected': 'هیچ آبجکتی انتخاب نکردید!',
+			'prompt/file/export/duplicateNames': 'Some objects share the same name. They will be renamed to ensure unique names. Are you sure?',
 			'prompt/script/remove': 'آیا اطمینان دارید؟',
 			'prompt/history/clear': 'هیستوری قبل و بعد (undo / redo) پاک خواهند شد آیا مطمئنید؟',
 			'prompt/history/preserve': 'The history will be preserved across sessions.\nThis can have an impact on performance when working with textures.',
@@ -456,6 +457,7 @@ function Strings( config ) {
 			'prompt/file/failedToOpenProject': 'Failed to open project!',
 			'prompt/file/export/noMeshSelected': 'No Mesh selected!',
 			'prompt/file/export/noObjectSelected': 'No Object selected!',
+			'prompt/file/export/duplicateNames': 'Some objects share the same name. They will be renamed to ensure unique names. Are you sure?',
 			'prompt/script/remove': 'Are you sure?',
 			'prompt/history/clear': 'The Undo/Redo History will be cleared. Are you sure?',
 			'prompt/history/preserve': 'The history will be preserved across sessions.\nThis can have an impact on performance when working with textures.',
@@ -905,6 +907,7 @@ function Strings( config ) {
 			'prompt/file/failedToOpenProject': 'Échec de l\'ouverture du projet !',
 			'prompt/file/export/noMeshSelected': 'Aucun maillage sélectionné !',
 			'prompt/file/export/noObjectSelected': 'Aucun objet sélectionné !',
+			'prompt/file/export/duplicateNames': 'Certains objets portent le même nom. Ils seront renommés afin de garantir des noms uniques. Êtes-vous sûr ?',
 			'prompt/script/remove': 'Es-tu sûr?',
 			'prompt/history/clear': 'L\'historique d\'annulation/rétablissement sera effacé Êtes-vous sûr ?',
 			'prompt/history/preserve': 'L\'histoire sera conservée entre les sessions.\nCela peut avoir un impact sur les performances lors de la manipulation des textures.',
@@ -1354,6 +1357,7 @@ function Strings( config ) {
 			'prompt/file/failedToOpenProject': '无法打开项目!',
 			'prompt/file/export/noMeshSelected': '未选择网格!',
 			'prompt/file/export/noObjectSelected': '未选择对象!',
+			'prompt/file/export/duplicateNames': '部分对象具有相同的名称。它们将被重命名以确保名称唯一。确定吗?',
 			'prompt/script/remove': '你确定吗?',
 			'prompt/history/clear': '撤销/重做历史记录将被清除。您确定吗?',
 			'prompt/history/preserve': '历史将在会话之间保留。\n这可能会影响在处理纹理时的性能。',
@@ -1803,6 +1807,7 @@ function Strings( config ) {
 			'prompt/file/failedToOpenProject': 'プロジェクトを開くことができませんでした!',
 			'prompt/file/export/noMeshSelected': 'メッシュが選択されていません!',
 			'prompt/file/export/noObjectSelected': 'オブジェクトが選択されていません!',
+			'prompt/file/export/duplicateNames': '一部のオブジェクトの名前が重複しています。名前を一意にするために変更します。よろしいですか?',
 			'prompt/script/remove': '本気ですか?',
 			'prompt/history/clear': '元に戻す/やり直しの履歴が消去されます。 本気ですか?',
 			'prompt/history/preserve': '履歴はセッションをまたいで保存されます。\nこれは、テクスチャを操作する際のパフォーマンスに影響を与える可能性があります。',
@@ -2251,6 +2256,7 @@ function Strings( config ) {
 			'prompt/file/failedToOpenProject': '프로젝트를 여는 데 실패했습니다!',
 			'prompt/file/export/noMeshSelected': '메시가 선택되지 않았습니다!',
 			'prompt/file/export/noObjectSelected': '객체가 선택되지 않았습니다!',
+			'prompt/file/export/duplicateNames': '일부 객체의 이름이 중복됩니다. 이름을 고유하게 만들기 위해 변경됩니다. 계속하시겠습니까?',
 			'prompt/script/remove': '삭제하시겠습니까?',
 			'prompt/history/clear': '되돌리기/다시하기 기록이 지워집니다. 진행하시겠습니까?',
 			'prompt/history/preserve': '기록은 세션을 통해 저장됩니다. 이는 텍스처를 조작할 때 성능에 영향을 미칠 수 있습니다.',

粤ICP备19079148号