using UnityEngine; using UnityEditor; using System; using System.IO; using System.Linq; using System.Reflection; using UnityMeshSimplifier; public class TestBuilder { // ========================================== // FASE 1: MINIFY MESH // ========================================== public static void ManualConvert() { try { // --- WORKER LIMIT --- Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobWorkerCount = 4; Debug.Log($"⚙️ [CLI] Job Worker Count: {Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobWorkerCount}"); // --- BACKEND FIX --- PlayerSettings.SetScriptingBackend(BuildTargetGroup.Standalone, ScriptingImplementation.Mono2x); PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.Mono2x); PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, ScriptingImplementation.IL2CPP); // --- PROSES PERAMPINGAN MESH --- string inputPath = "Assets/InputRaw"; if (Directory.Exists(inputPath)) { string[] prefabs = Directory.GetFiles(inputPath, "*.prefab", SearchOption.AllDirectories); foreach (string path in prefabs) { GameObject go = PrefabUtility.LoadPrefabContents(path); if (go != null) { Debug.Log($"📦 Merampingkan Mesh: {path}"); SimplifyMesh(go, path); PrefabUtility.SaveAsPrefabAsset(go, path); PrefabUtility.UnloadPrefabContents(go); } } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log("✅ [CLI] Fase 1 (Minify) selesai dengan sukses. Menutup instance untuk masuk Fase Upload."); EditorApplication.Exit(0); } catch (Exception e) { Debug.LogError($"💀 [ERROR FATAL FASE 1] {e.ToString()}"); EditorApplication.Exit(1); } } // ========================================== // FASE 2: UPLOAD ZEPETO // ========================================== public static void ZepetoUploadPhase() { try { Debug.Log("🚀 [CLI] Memulai Fase 2: Eksekusi Pengepakan & Upload ZEPETO..."); Type zepetoType = null; // Pencarian Aman (Safe Reflection) foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { try { var types = assembly.GetTypes(); var foundType = types.FirstOrDefault(t => t != null && t.Name == "ZepetoPackageCliBuilder"); if (foundType != null) { zepetoType = foundType; break; } } catch (ReflectionTypeLoadException e) { var foundType = e.Types.FirstOrDefault(t => t != null && t.Name == "ZepetoPackageCliBuilder"); if (foundType != null) { zepetoType = foundType; break; } } catch (Exception) {} } if (zepetoType != null) { MethodInfo buildMethod = zepetoType.GetMethod("BuildWithArgs", BindingFlags.Public | BindingFlags.Static); if (buildMethod != null) { Debug.Log("✅ [CLI] Memanggil ZepetoPackageCliBuilder.BuildWithArgs()..."); buildMethod.Invoke(null, null); // Dibiarkan tanpa Exit(0) agar thread asinkron ZEPETO bisa berjalan untuk Upload } else { Debug.LogError("💀 [ERROR FASE 2] Method BuildWithArgs tidak ditemukan di class ZepetoPackageCliBuilder!"); EditorApplication.Exit(1); } } else { Debug.LogError("💀 [ERROR FASE 2] Class ZepetoPackageCliBuilder tidak ditemukan! Pastikan library Zepeto sudah ter-load di Fase 2."); EditorApplication.Exit(1); } } catch (Exception e) { Debug.LogError($"💀 [ERROR FATAL FASE 2 ASLI] {e.ToString()}"); EditorApplication.Exit(1); } } static void SimplifyMesh(GameObject go, string path) { var renderers = go.GetComponentsInChildren(); foreach (var ren in renderers) { Mesh src = (ren is MeshRenderer mr) ? ren.GetComponent()?.sharedMesh : (ren is SkinnedMeshRenderer smr) ? smr.sharedMesh : null; if (src != null && !src.name.Contains("_Internal")) { string assetPath = AssetDatabase.GetAssetPath(src); if (!string.IsNullOrEmpty(assetPath)) { ModelImporter importer = AssetImporter.GetAtPath(assetPath) as ModelImporter; if (importer != null && !importer.isReadable) { importer.isReadable = true; importer.SaveAndReimport(); } } var simplifier = new MeshSimplifier(); simplifier.Initialize(src); simplifier.SimplifyMesh(0.05f); // Sisa 5% poligon Mesh newMesh = simplifier.ToMesh(); newMesh.name = src.name + "_Internal"; string directory = Path.GetDirectoryName(path); string newMeshPath = Path.Combine(directory, newMesh.name + "_" + Guid.NewGuid().ToString().Substring(0, 5) + ".asset"); newMeshPath = newMeshPath.Replace("\\", "/"); AssetDatabase.CreateAsset(newMesh, newMeshPath); if (ren is MeshRenderer) ren.GetComponent().sharedMesh = newMesh; else if (ren is SkinnedMeshRenderer s) s.sharedMesh = newMesh; } } } }