@@ -29,7 +29,7 @@ public static void ShowWindow()
2929 {
3030 GetWindow < DecimaterMain > ( "Mesh Optimizer GUI" ) ;
3131 }
32-
32+
3333 private void OnEnable ( )
3434 {
3535 LoadShaders ( ) ;
@@ -71,11 +71,7 @@ private void OnGUI()
7171 decimateLevel = EditorGUILayout . Slider ( "Optimize Level" , decimateLevel , 0.1f , 1.0f ) ;
7272 if ( EditorGUI . EndChangeCheck ( ) )
7373 {
74- Mesh currentMesh = GetCurrentMesh ( ) ;
75- if ( currentMesh != null )
76- {
77- MeshRevertManager . StoreDecimateLevel ( currentMesh , decimateLevel ) ;
78- }
74+ // NOTHING HERE
7975 }
8076
8177 GUILayout . Space ( 10 ) ;
@@ -84,12 +80,12 @@ private void OnGUI()
8480 ApplyDecimation ( ) ;
8581 }
8682
87- if ( GUILayout . Button ( "Revert " ) )
83+ if ( GUILayout . Button ( "Undo " ) )
8884 {
8985 RevertDecimation ( ) ;
9086 }
9187
92- if ( GUILayout . Button ( "Revert to Original" ) )
88+ if ( GUILayout . Button ( "Reset To Original Mesh " ) )
9389 {
9490 RevertToOriginalMesh ( ) ;
9591 }
@@ -101,15 +97,41 @@ private void OnGUI()
10197 private void ApplyDecimation ( )
10298 {
10399 bool isSkinnedMeshRenderer = selectedGameObject . GetComponent < SkinnedMeshRenderer > ( ) != null ;
100+ int boneCount = 0 ;
101+
102+ if ( isSkinnedMeshRenderer )
103+ {
104+ SkinnedMeshRenderer skinnedMeshRenderer = selectedGameObject . GetComponent < SkinnedMeshRenderer > ( ) ;
105+ boneCount = skinnedMeshRenderer . bones . Length ;
106+ }
104107
105- EnableReadWrite ( decimatedMesh ) ;
106- MeshDecimaterUtility . DecimateMesh ( originalMesh , decimatedMesh , decimateLevel , isSkinnedMeshRenderer , originalSubmeshCount ) ;
108+ EnableReadWrite ( originalMesh ) ;
109+
110+ // save "level" to history
111+ MeshRevertManager . PushDecimateLevel ( originalMesh , decimateLevel ) ;
112+
113+ MeshDecimaterUtility . DecimateMesh ( originalMesh , decimatedMesh , decimateLevel , isSkinnedMeshRenderer , originalSubmeshCount , boneCount ) ;
114+
115+ decimatedMesh . RecalculateNormals ( ) ;
116+ decimatedMesh . RecalculateBounds ( ) ;
107117
108118 SaveDecimatedMesh ( ) ;
109119
110120 MeshRevertManager . StoreOriginalMesh ( decimatedMesh , originalMesh ) ;
111121
112- MeshRevertManager . StoreDecimateLevel ( decimatedMesh , decimateLevel ) ;
122+ EditorApplication . delayCall += ( ) =>
123+ {
124+ ApplyMeshToRenderer ( isSkinnedMeshRenderer ) ;
125+ } ;
126+
127+ meshPreviewer . UpdatePreviewMesh ( selectedGameObject ) ;
128+
129+ isFirstDecimation = false ;
130+ }
131+
132+ private void ApplyMeshToRenderer ( bool isSkinnedMeshRenderer )
133+ {
134+ if ( selectedGameObject == null ) return ;
113135
114136 if ( selectedGameObject . GetComponent < MeshFilter > ( ) != null )
115137 {
@@ -121,43 +143,71 @@ private void ApplyDecimation()
121143 else if ( isSkinnedMeshRenderer )
122144 {
123145 SkinnedMeshRenderer skinnedMeshRenderer = selectedGameObject . GetComponent < SkinnedMeshRenderer > ( ) ;
146+
147+ skinnedMeshRenderer . sharedMesh = null ;
148+
124149 skinnedMeshRenderer . sharedMesh = decimatedMesh ;
125150 skinnedMeshRenderer . sharedMaterials = originalMaterials ;
151+
152+ skinnedMeshRenderer . updateWhenOffscreen = true ;
153+
154+ Animator animator = selectedGameObject . GetComponent < Animator > ( ) ;
155+ if ( animator != null )
156+ {
157+ animator . Rebind ( ) ;
158+ }
159+
160+ ValidateBoneWeights ( skinnedMeshRenderer ) ;
126161 }
162+ }
127163
128- meshPreviewer . UpdatePreviewMesh ( selectedGameObject ) ;
164+ private void ValidateBoneWeights ( SkinnedMeshRenderer skinnedMeshRenderer )
165+ {
166+ Mesh mesh = skinnedMeshRenderer . sharedMesh ;
167+ int boneCount = skinnedMeshRenderer . bones . Length ;
129168
130- // errorめんどいの
131- if ( isFirstDecimation )
169+ for ( int i = 0 ; i < mesh . boneWeights . Length ; i ++ )
132170 {
133- isFirstDecimation = false ;
134- Debug . LogWarning ( "First decimation performed. Applying decimation again to prevent mesh data mismatch error." ) ;
135- ApplyDecimation ( ) ;
171+ BoneWeight bw = mesh . boneWeights [ i ] ;
172+ if ( bw . boneIndex0 >= boneCount || bw . boneIndex1 >= boneCount || bw . boneIndex2 >= boneCount || bw . boneIndex3 >= boneCount )
173+ {
174+ Debug . LogError ( $ "Invalid bone index detected in boneWeights at vertex { i } : boneIndex0={ bw . boneIndex0 } , boneIndex1={ bw . boneIndex1 } , boneIndex2={ bw . boneIndex2 } , boneIndex3={ bw . boneIndex3 } ") ;
175+ }
136176 }
137177 }
138178
139179 private void RevertDecimation ( )
140180 {
141- if ( selectedGameObject . GetComponent < MeshFilter > ( ) ! = null )
181+ if ( originalMesh = = null )
142182 {
143- MeshFilter meshFilter = selectedGameObject . GetComponent < MeshFilter > ( ) ;
144- meshFilter . sharedMesh = originalMesh ;
145- MeshRenderer meshRenderer = selectedGameObject . GetComponent < MeshRenderer > ( ) ;
146- meshRenderer . sharedMaterials = originalMaterials ;
183+ Debug . LogWarning ( "Original mesh not found." ) ;
184+ return ;
147185 }
148- else if ( selectedGameObject . GetComponent < SkinnedMeshRenderer > ( ) != null )
186+
187+ float ? previousDecimateLevel = MeshRevertManager . RevertDecimateLevel ( originalMesh ) ;
188+ if ( previousDecimateLevel . HasValue )
149189 {
150- SkinnedMeshRenderer skinnedMeshRenderer = selectedGameObject . GetComponent < SkinnedMeshRenderer > ( ) ;
151- skinnedMeshRenderer . sharedMesh = originalMesh ;
152- skinnedMeshRenderer . sharedMaterials = originalMaterials ;
153- }
190+ decimateLevel = previousDecimateLevel . Value ;
191+ MeshRevertManager . PushDecimateLevel ( originalMesh , decimateLevel ) ;
154192
155- decimateLevel = DEFAULT_DECIMATE_LEVEL ;
156- MeshRevertManager . StoreDecimateLevel ( originalMesh , decimateLevel ) ;
193+ ApplyDecimation ( ) ;
157194
158- meshPreviewer . UpdatePreviewMesh ( selectedGameObject ) ;
195+ Debug . Log ( $ "Reverted to previous decimate level: { decimateLevel } ") ;
196+ }
197+ else
198+ {
199+ if ( decimateLevel == DEFAULT_DECIMATE_LEVEL )
200+ {
201+ EditorUtility . DisplayDialog ( "Revert Failed" , "Not able to revert. Already at the default decimate level." , "OK" ) ;
202+ }
203+ else
204+ {
205+ EditorUtility . DisplayDialog ( "Revert Failed" , "Nothing to revert." , "OK" ) ;
206+ }
207+ Debug . LogWarning ( "Revert failed: No previous decimate level available." ) ;
208+ }
159209
160- isFirstDecimation = true ;
210+ meshPreviewer . UpdatePreviewMesh ( selectedGameObject ) ;
161211 }
162212
163213 private void RevertToOriginalMesh ( )
@@ -188,15 +238,15 @@ private void RevertToOriginalMesh()
188238 Debug . Log ( "Reverted to original mesh." ) ;
189239
190240 decimateLevel = DEFAULT_DECIMATE_LEVEL ;
191- MeshRevertManager . StoreDecimateLevel ( originalMeshFromManager , decimateLevel ) ;
241+ MeshRevertManager . PushDecimateLevel ( originalMeshFromManager , decimateLevel ) ;
192242 meshInfoDisplay . SetOriginalMesh ( originalMeshFromManager ) ;
193243 }
194244 else
195245 {
196246 Debug . LogWarning ( "Original mesh not found." ) ;
197247
198248 decimateLevel = DEFAULT_DECIMATE_LEVEL ;
199- MeshRevertManager . StoreDecimateLevel ( currentMesh , decimateLevel ) ;
249+ MeshRevertManager . PushDecimateLevel ( currentMesh , decimateLevel ) ;
200250 meshInfoDisplay . SetOriginalMesh ( currentMesh ) ;
201251 }
202252
@@ -219,18 +269,34 @@ private void UpdateSelection(GameObject newSelectedGameObject)
219269
220270 if ( newSelectedGameObject != null )
221271 {
222- MeshFilter meshFilter = newSelectedGameObject . GetComponent < MeshFilter > ( ) ;
223- SkinnedMeshRenderer skinnedMeshRenderer = newSelectedGameObject . GetComponent < SkinnedMeshRenderer > ( ) ;
224-
225- if ( meshFilter != null )
272+ Mesh original = GetOriginalMesh ( newSelectedGameObject ) ;
273+ if ( original != null )
226274 {
227275 selectedGameObject = newSelectedGameObject ;
228- originalMesh = meshFilter . sharedMesh ;
276+ originalMesh = original ;
229277 EnableReadWrite ( originalMesh ) ;
230278
231279 decimateLevel = MeshRevertManager . GetDecimateLevel ( originalMesh ) ;
232280
233- decimatedMesh = Instantiate ( originalMesh ) ;
281+ string actualMeshName = GetActualMeshName ( ) ;
282+ string originalPath = AssetDatabase . GetAssetPath ( originalMesh ) ;
283+ string directory = Path . GetDirectoryName ( originalPath ) ;
284+ string newFileName = $ "{ actualMeshName } { MESH_SUFFIX } .asset";
285+ string newPath = $ "{ directory } /{ newFileName } ";
286+
287+ Mesh existingDecimatedMesh = AssetDatabase . LoadAssetAtPath < Mesh > ( newPath ) ;
288+ if ( existingDecimatedMesh != null )
289+ {
290+ decimatedMesh = existingDecimatedMesh ;
291+ }
292+ else
293+ {
294+ decimatedMesh = new Mesh ( ) ;
295+ decimatedMesh . name = $ "{ actualMeshName } { MESH_SUFFIX } ";
296+ AssetDatabase . CreateAsset ( decimatedMesh , newPath ) ;
297+ AssetDatabase . SaveAssets ( ) ;
298+ }
299+
234300 meshInfoDisplay . SetOriginalMesh ( originalMesh ) ;
235301
236302 Renderer renderer = newSelectedGameObject . GetComponent < Renderer > ( ) ;
@@ -245,29 +311,36 @@ private void UpdateSelection(GameObject newSelectedGameObject)
245311 Debug . Log ( $ "Selected Mesh: { AssetDatabase . GetAssetPath ( originalMesh ) } ") ;
246312 Repaint ( ) ;
247313 }
248- else if ( skinnedMeshRenderer != null )
249- {
250- selectedGameObject = newSelectedGameObject ;
251- originalMesh = skinnedMeshRenderer . sharedMesh ;
252- EnableReadWrite ( originalMesh ) ;
253-
254- decimateLevel = MeshRevertManager . GetDecimateLevel ( originalMesh ) ;
255-
256- decimatedMesh = Instantiate ( originalMesh ) ;
257- meshInfoDisplay . SetOriginalMesh ( originalMesh ) ;
314+ }
315+ }
258316
259- originalMaterials = skinnedMeshRenderer . sharedMaterials ;
260- originalSubmeshCount = new int [ originalMesh . subMeshCount ] ;
261- for ( int i = 0 ; i < originalMesh . subMeshCount ; i ++ )
262- {
263- originalSubmeshCount [ i ] = originalMesh . GetTriangles ( i ) . Length / 3 ;
264- }
317+ private Mesh GetOriginalMesh ( GameObject gameObject )
318+ {
319+ MeshFilter meshFilter = gameObject . GetComponent < MeshFilter > ( ) ;
320+ if ( meshFilter != null && meshFilter . sharedMesh != null )
321+ {
322+ Mesh mesh = meshFilter . sharedMesh ;
323+ if ( mesh . name . EndsWith ( MESH_SUFFIX ) )
324+ {
325+ Mesh original = MeshRevertManager . GetOriginalMesh ( mesh ) ;
326+ return original != null ? original : mesh ;
327+ }
328+ return mesh ;
329+ }
265330
266- Selection . activeObject = originalMesh ;
267- Debug . Log ( $ "Selected Mesh: { AssetDatabase . GetAssetPath ( originalMesh ) } ") ;
268- Repaint ( ) ;
331+ SkinnedMeshRenderer skinnedMeshRenderer = gameObject . GetComponent < SkinnedMeshRenderer > ( ) ;
332+ if ( skinnedMeshRenderer != null && skinnedMeshRenderer . sharedMesh != null )
333+ {
334+ Mesh mesh = skinnedMeshRenderer . sharedMesh ;
335+ if ( mesh . name . EndsWith ( MESH_SUFFIX ) )
336+ {
337+ Mesh original = MeshRevertManager . GetOriginalMesh ( mesh ) ;
338+ return original != null ? original : mesh ;
269339 }
340+ return mesh ;
270341 }
342+
343+ return null ;
271344 }
272345
273346 private void SaveDecimatedMesh ( )
@@ -277,7 +350,13 @@ private void SaveDecimatedMesh()
277350 string originalPath = AssetDatabase . GetAssetPath ( originalMesh ) ;
278351 string directory = Path . GetDirectoryName ( originalPath ) ;
279352 string newFileName = $ "{ actualMeshName } { MESH_SUFFIX } .asset";
280- string newPath = Path . Combine ( directory , newFileName ) ;
353+
354+ if ( actualMeshName . EndsWith ( MESH_SUFFIX ) )
355+ {
356+ newFileName = $ "{ RemoveDecimatedSuffix ( actualMeshName ) } { MESH_SUFFIX } .asset";
357+ }
358+
359+ string newPath = $ "{ directory } /{ newFileName } ";
281360
282361 Mesh existingMesh = AssetDatabase . LoadAssetAtPath < Mesh > ( newPath ) ;
283362 if ( existingMesh != null )
@@ -288,6 +367,7 @@ private void SaveDecimatedMesh()
288367 }
289368 else
290369 {
370+ decimatedMesh . name = $ "{ RemoveDecimatedSuffix ( actualMeshName ) } { MESH_SUFFIX } ";
291371 AssetDatabase . CreateAsset ( decimatedMesh , newPath ) ;
292372 AssetDatabase . SaveAssets ( ) ;
293373 Debug . Log ( $ "Decimated mesh saved: { newPath } ") ;
@@ -303,18 +383,42 @@ private string GetActualMeshName()
303383 MeshFilter meshFilter = selectedGameObject . GetComponent < MeshFilter > ( ) ;
304384 if ( meshFilter != null && meshFilter . sharedMesh != null )
305385 {
306- return meshFilter . sharedMesh . name ;
386+ return RemoveCloneAndDecimatedSuffix ( meshFilter . sharedMesh . name ) ;
307387 }
308388
309389 SkinnedMeshRenderer skinnedMeshRenderer = selectedGameObject . GetComponent < SkinnedMeshRenderer > ( ) ;
310390 if ( skinnedMeshRenderer != null && skinnedMeshRenderer . sharedMesh != null )
311391 {
312- return skinnedMeshRenderer . sharedMesh . name ;
392+ return RemoveCloneAndDecimatedSuffix ( skinnedMeshRenderer . sharedMesh . name ) ;
313393 }
314394
315395 return "Unknown" ;
316396 }
317397
398+ private string RemoveCloneAndDecimatedSuffix ( string meshName )
399+ {
400+ // erase "(Clone)"
401+ if ( meshName . EndsWith ( "(Clone)" ) )
402+ {
403+ meshName = meshName . Substring ( 0 , meshName . Length - "(Clone)" . Length ) ;
404+ }
405+ // erase "_decimated"
406+ if ( meshName . EndsWith ( MESH_SUFFIX ) )
407+ {
408+ meshName = meshName . Substring ( 0 , meshName . Length - MESH_SUFFIX . Length ) ;
409+ }
410+ return meshName ;
411+ }
412+
413+ private string RemoveDecimatedSuffix ( string meshName )
414+ {
415+ if ( meshName . EndsWith ( MESH_SUFFIX ) )
416+ {
417+ return meshName . Substring ( 0 , meshName . Length - MESH_SUFFIX . Length ) ;
418+ }
419+ return meshName ;
420+ }
421+
318422 private Mesh GetCurrentMesh ( )
319423 {
320424 if ( selectedGameObject != null )
@@ -339,8 +443,11 @@ private void EnableReadWrite(Mesh mesh)
339443 ModelImporter modelImporter = AssetImporter . GetAtPath ( path ) as ModelImporter ;
340444 if ( modelImporter != null )
341445 {
342- modelImporter . isReadable = true ;
343- AssetDatabase . ImportAsset ( path , ImportAssetOptions . ForceUpdate ) ;
446+ if ( ! modelImporter . isReadable )
447+ {
448+ modelImporter . isReadable = true ;
449+ AssetDatabase . ImportAsset ( path , ImportAssetOptions . ForceUpdate ) ;
450+ }
344451 }
345452 }
346453
0 commit comments