1+ using System ;
2+ using System . Diagnostics ;
3+ using System . IO ;
4+ using UnityEditor ;
5+ using UnityEditor . UIElements ;
6+ using UnityEngine ;
7+ using UnityEngine . UIElements ;
8+ using Object = UnityEngine . Object ;
9+
10+ namespace Editor . UnityBlenderDecimate
11+ {
12+ public class UnityBlenderDecimate : EditorWindow
13+ {
14+ [ MenuItem ( "Tools/Unity To Blender" ) ]
15+ public static void ShowExample ( )
16+ {
17+ UnityBlenderDecimate window = GetWindow < UnityBlenderDecimate > ( ) ;
18+ window . titleContent = new GUIContent ( "Unity Blender Decimate" ) ;
19+ }
20+
21+ [ SerializeField ] private VisualTreeAsset visualTreeAsset ;
22+ [ SerializeField ] private string blenderPath ;
23+ private Button BlenderPathSelectButton => rootVisualElement . Q < Button > ( "blenderPathSelect" ) ;
24+ private Label BlenderPathLabel => rootVisualElement . Q < Label > ( "blenderPath" ) ;
25+ private ObjectField PythonScriptField => rootVisualElement . Q < ObjectField > ( "pythonScript" ) ;
26+ private ObjectField InputObject => rootVisualElement . Q < ObjectField > ( "inputObject" ) ;
27+
28+ private GameObject selectedGameObject ;
29+ private string pythonScriptPath ;
30+
31+ public void CreateGUI ( )
32+ {
33+ rootVisualElement . Add ( visualTreeAsset . Instantiate ( ) ) ;
34+ #if UNITY_EDITOR_OSX
35+ SetupOSXBlenderPath ( ) ;
36+ #else
37+ BlenderPathSelectButton . RegisterCallback < ClickEvent > ( OnBlenderSelectPathClick ) ;
38+ #endif
39+
40+ PythonScriptField . RegisterValueChangedCallback ( OnPythonScriptSelect ) ;
41+ InputObject . RegisterValueChangedCallback ( CheckSelectedInputObject ) ;
42+ rootVisualElement . Q < Button > ( "execute" ) . RegisterCallback < ClickEvent > ( Execute ) ;
43+
44+ UpdateNextStep ( ) ;
45+ }
46+
47+ private void SetupOSXBlenderPath ( )
48+ {
49+ BlenderPathSelectButton . SetEnabled ( false ) ;
50+ BlenderPathLabel . text = IsAvailableBlender ( ) ? blenderPath : "Can't find Blender in Applications folder =(" ;
51+
52+ bool IsAvailableBlender ( )
53+ {
54+ blenderPath = Path . Combine ( "/Applications" , "Blender.app" , "Contents" , "MacOS" , "Blender" ) ;
55+ return File . Exists ( blenderPath ) ;
56+ }
57+ }
58+
59+ private void UpdateNextStep ( )
60+ {
61+ Func < ( bool isValidStep , string stepName ) > [ ] steps =
62+ {
63+ ( ) => ( File . Exists ( blenderPath ) , "blenderPathStep" ) ,
64+ ( ) => ( string . IsNullOrEmpty ( pythonScriptPath ) == false , "pythonScript" ) ,
65+ ( ) => ( selectedGameObject != null , "inputObjectStep" ) ,
66+ ( ) => ( true , "blenderExecuteStep" )
67+ } ;
68+
69+ var failedStep = Array . FindIndex ( steps , func => func . Invoke ( ) . isValidStep == false ) ;
70+ for ( var i = 0 ; i < steps . Length ; i ++ )
71+ {
72+ var stepName = steps [ i ] . Invoke ( ) . stepName ;
73+ var element = rootVisualElement . Q < VisualElement > ( stepName ) ;
74+ element ? . SetEnabled ( failedStep >= i || failedStep == - 1 ) ;
75+ }
76+ }
77+
78+ private void OnBlenderSelectPathClick ( ClickEvent @event )
79+ {
80+ var path = string . IsNullOrEmpty ( blenderPath ) ? Application . dataPath : blenderPath ;
81+ var executableExtension = "exe" ;
82+ blenderPath = EditorUtility . OpenFilePanel ( "Select Blender executable" , path , executableExtension ) ;
83+ BlenderPathLabel . text = blenderPath ;
84+
85+ UpdateNextStep ( ) ;
86+ }
87+
88+ private void OnPythonScriptSelect ( ChangeEvent < Object > @event )
89+ {
90+ var path = AssetDatabase . GetAssetPath ( @event . newValue ) ;
91+ pythonScriptPath = Path . GetExtension ( path ) == ".py" ? Path . GetFullPath ( path ) : null ;
92+
93+ UpdateNextStep ( ) ;
94+ }
95+
96+ private void CheckSelectedInputObject ( ChangeEvent < Object > @event )
97+ {
98+ PrefabAssetType prefabType = PrefabAssetType . NotAPrefab ;
99+ var go = @event . newValue as GameObject ;
100+ if ( go != null )
101+ {
102+ var prefabAssetType = PrefabUtility . GetPrefabAssetType ( go ) ;
103+ var fileExtension = Path . GetExtension ( AssetDatabase . GetAssetPath ( go ) ) ;
104+ var isObjModel = prefabAssetType == PrefabAssetType . Model && fileExtension == ".obj" ;
105+ prefabType = isObjModel ? PrefabAssetType . Model : PrefabAssetType . NotAPrefab ;
106+ }
107+ selectedGameObject = prefabType == PrefabAssetType . Model ? go : null ;
108+ InputObject . value = selectedGameObject ;
109+
110+ UpdateNextStep ( ) ;
111+ }
112+
113+ private void Execute ( ClickEvent @event )
114+ {
115+ var decimateValue = rootVisualElement . Q < Slider > ( "decimate" ) . value ;
116+ var absolutePathToObj = Path . GetFullPath ( AssetDatabase . GetAssetPath ( selectedGameObject ) ) ;
117+ absolutePathToObj = '"' + absolutePathToObj + '"' ;
118+ var blenderProcess = new Process ( ) ;
119+ blenderProcess . StartInfo . FileName = blenderPath ;
120+ var bgBlenderArgs = $ "--background --python { pythonScriptPath } -- { absolutePathToObj } { decimateValue } ";
121+ blenderProcess . StartInfo . Arguments = bgBlenderArgs ;
122+ blenderProcess . Start ( ) ;
123+ //TODO: Don't lock Unity
124+ blenderProcess . WaitForExit ( ) ;
125+ AssetDatabase . Refresh ( ImportAssetOptions . ForceUpdate ) ;
126+ }
127+ }
128+ }
0 commit comments