Spaces:
Sleeping
Sleeping
| using System; | |
| using System.IO; | |
| using System.Text; | |
| using System.Collections.Generic; | |
| using UnityEditor; | |
| using UnityEngine; | |
| using ZEPETO.Asset; | |
| using UnityMeshSimplifier; // Pastikan library ada di folder Assets | |
| public class TestBuilder | |
| { | |
| public static void ManualConvert() | |
| { | |
| EditorApplication.LockReloadAssemblies(); | |
| Debug.Log("==============================================================="); | |
| Debug.Log("๐ ๏ธ [ZEPETO BUILDER] Smart Minifier & Texture (Original Material Guard)"); | |
| Debug.Log("==============================================================="); | |
| try | |
| { | |
| // --- KUNCI PENYELESAIAN FATAL ERROR (CRASH) --- | |
| string[] badPackages = Directory.GetFiles("Assets", "package.json", SearchOption.AllDirectories); | |
| foreach (string badPkg in badPackages) | |
| { | |
| File.Delete(badPkg); | |
| if (File.Exists(badPkg + ".meta")) File.Delete(badPkg + ".meta"); | |
| Debug.Log($"๐๏ธ [FIX] Menghapus package.json nyasar: {badPkg}"); | |
| } | |
| // 1. Parsing Argumen Command Line | |
| string[] args = Environment.GetCommandLineArgs(); | |
| string z_id = ""; | |
| string z_pw = ""; | |
| int targetTris = 9900; | |
| for (int i = 0; i < args.Length; i++) | |
| { | |
| if (args[i] == "-id" && i + 1 < args.Length) z_id = args[i + 1]; | |
| if (args[i] == "-password" && i + 1 < args.Length) z_pw = args[i + 1]; | |
| if (args[i] == "-maxtris" && i + 1 < args.Length) targetTris = int.Parse(args[i + 1]); | |
| } | |
| // 2. Tentukan Path | |
| string inputPath = "Assets/InputRaw"; | |
| string outputPath = "Assets/OptimizedOutput"; | |
| if (!Directory.Exists(inputPath)) | |
| { | |
| Debug.LogError($"โ [BUILDER] Folder tidak ditemukan: {inputPath}"); | |
| EditorApplication.UnlockReloadAssemblies(); | |
| EditorApplication.Exit(1); | |
| return; | |
| } | |
| string[] prefabs = Directory.GetFiles(inputPath, "*.prefab", SearchOption.AllDirectories); | |
| if (prefabs.Length == 0) | |
| { | |
| Debug.LogError("โ [BUILDER] Tidak ada file .prefab ditemukan."); | |
| EditorApplication.UnlockReloadAssemblies(); | |
| EditorApplication.Exit(1); | |
| return; | |
| } | |
| string targetPrefabPath = prefabs[0]; | |
| string prefabFileName = Path.GetFileName(targetPrefabPath); | |
| // 3. Buat Folder Output yang Bersih | |
| if (Directory.Exists(outputPath)) Directory.Delete(outputPath, true); | |
| Directory.CreateDirectory(outputPath); | |
| AssetDatabase.Refresh(); | |
| // 4. Muat Prefab Asli dan Instansiasi ke Memori Scene | |
| GameObject originalPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(targetPrefabPath); | |
| GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(originalPrefab); | |
| // ==================================================================== | |
| // --- TAHAP PERSIAPAN MEGA PREFAB (WADAH) --- | |
| // ==================================================================== | |
| string finalPrefabPath = $"{outputPath}/{prefabFileName}"; | |
| GameObject tempObj = new GameObject("TempContainer"); | |
| PrefabUtility.SaveAsPrefabAsset(tempObj, finalPrefabPath); | |
| UnityEngine.Object.DestroyImmediate(tempObj); | |
| // ==================================================================== | |
| // --- TAHAP SMART TEXTURE SCALER (Target Max 950 KB) --- | |
| // ==================================================================== | |
| Debug.Log("๐ผ๏ธ [TEXTURE] Memulai Kalkulasi Smart Texture Scaler (Target: 950KB)..."); | |
| var allRenderers = instance.GetComponentsInChildren<Renderer>(true); | |
| HashSet<Texture2D> uniqueTextures = new HashSet<Texture2D>(); | |
| foreach (var rend in allRenderers) | |
| { | |
| foreach (var mat in rend.sharedMaterials) | |
| { | |
| if (mat == null) continue; | |
| string[] texNames = mat.GetTexturePropertyNames(); | |
| foreach (var tName in texNames) | |
| { | |
| Texture tex = mat.GetTexture(tName); | |
| if (tex is Texture2D t2d) uniqueTextures.Add(t2d); | |
| } | |
| } | |
| } | |
| long totalPixels = 0; | |
| foreach (var tex in uniqueTextures) | |
| { | |
| totalPixels += (tex.width * tex.height); | |
| } | |
| long maxSafePixels = 950 * 1024; | |
| float textureScaleRatio = 1.0f; | |
| if (totalPixels > maxSafePixels) | |
| { | |
| textureScaleRatio = Mathf.Sqrt((float)maxSafePixels / (float)totalPixels); | |
| Debug.Log($"๐ [TEXTURE MATH] Menerapkan Smart Scale Ratio: {textureScaleRatio:P2}"); | |
| } | |
| foreach (var tex in uniqueTextures) | |
| { | |
| string texPath = AssetDatabase.GetAssetPath(tex); | |
| if (string.IsNullOrEmpty(texPath)) continue; | |
| TextureImporter importer = AssetImporter.GetAtPath(texPath) as TextureImporter; | |
| if (importer != null) | |
| { | |
| int currentMaxDim = Mathf.Max(tex.width, tex.height); | |
| importer.mipmapEnabled = false; | |
| importer.textureCompression = TextureImporterCompression.CompressedLQ; | |
| if (textureScaleRatio < 1.0f) | |
| { | |
| int targetDim = (int)(currentMaxDim * textureScaleRatio); | |
| int powerOf2 = 32; | |
| while (powerOf2 * 2 <= targetDim && powerOf2 < 2048) | |
| { | |
| powerOf2 *= 2; | |
| } | |
| importer.maxTextureSize = powerOf2; | |
| } | |
| else | |
| { | |
| importer.maxTextureSize = Mathf.NextPowerOfTwo(currentMaxDim); | |
| } | |
| importer.SaveAndReimport(); | |
| Debug.Log($"โ [TEXTURE] {tex.name} divalidasi pada resolusi: {importer.maxTextureSize}px"); | |
| } | |
| } | |
| // ==================================================================== | |
| // --- TAHAP PENJAGAAN MATERIAL BAWAAN --- | |
| // Kita biarkan material terhubung ke file .mat aslinya. Tidak ada kloning! | |
| // ==================================================================== | |
| Debug.Log("๐จ [MATERIAL] Material bawaan & Custom Shader dipertahankan sepenuhnya."); | |
| // ==================================================================== | |
| // --- TAHAP SMART MINIFIER POLIGON + EMBEDDING MESH --- | |
| // ==================================================================== | |
| var skinnedRenderers = instance.GetComponentsInChildren<SkinnedMeshRenderer>(true); | |
| var meshFilters = instance.GetComponentsInChildren<MeshFilter>(true); | |
| int totalOriginalTris = 0; | |
| // BYPASS DRM ZEPETO: Menghitung poligon dengan GetIndexCount | |
| foreach (var smr in skinnedRenderers) | |
| { | |
| if (smr.sharedMesh != null) | |
| { | |
| for (int i = 0; i < smr.sharedMesh.subMeshCount; i++) | |
| { | |
| totalOriginalTris += (int)(smr.sharedMesh.GetIndexCount(i) / 3); | |
| } | |
| } | |
| } | |
| foreach (var mf in meshFilters) | |
| { | |
| if (mf.sharedMesh != null) | |
| { | |
| for (int i = 0; i < mf.sharedMesh.subMeshCount; i++) | |
| { | |
| totalOriginalTris += (int)(mf.sharedMesh.GetIndexCount(i) / 3); | |
| } | |
| } | |
| } | |
| float quality = (totalOriginalTris > targetTris) ? (float)targetTris / (float)totalOriginalTris : 1.0f; | |
| Debug.Log($"๐ [MESH] Poligon Asli: {totalOriginalTris} tris | Target: {targetTris} | Rasio: {quality}"); | |
| foreach (var smr in skinnedRenderers) | |
| { | |
| if (smr.sharedMesh == null) continue; | |
| var meshSimplifier = new UnityMeshSimplifier.MeshSimplifier(); | |
| meshSimplifier.Initialize(smr.sharedMesh); | |
| meshSimplifier.SimplifyMesh(quality); | |
| Mesh optimizedMesh = meshSimplifier.ToMesh(); | |
| if (optimizedMesh != null) | |
| { | |
| optimizedMesh.name = smr.sharedMesh.name + "_embedded"; | |
| AssetDatabase.AddObjectToAsset(optimizedMesh, finalPrefabPath); | |
| smr.sharedMesh = optimizedMesh; | |
| } | |
| } | |
| foreach (var mf in meshFilters) | |
| { | |
| if (mf.sharedMesh == null) continue; | |
| var meshSimplifier = new UnityMeshSimplifier.MeshSimplifier(); | |
| meshSimplifier.Initialize(mf.sharedMesh); | |
| meshSimplifier.SimplifyMesh(quality); | |
| Mesh optimizedMesh = meshSimplifier.ToMesh(); | |
| if (optimizedMesh != null) | |
| { | |
| optimizedMesh.name = mf.sharedMesh.name + "_embedded"; | |
| AssetDatabase.AddObjectToAsset(optimizedMesh, finalPrefabPath); | |
| mf.sharedMesh = optimizedMesh; | |
| } | |
| } | |
| // ==================================================================== | |
| // --- FINALISASI & PACKING --- | |
| // ==================================================================== | |
| GameObject optimizedPrefab = PrefabUtility.SaveAsPrefabAsset(instance, finalPrefabPath); | |
| UnityEngine.Object.DestroyImmediate(instance); | |
| AssetDatabase.SaveAssets(); | |
| Debug.Log($"โ [BUILDER] Prefab siap dengan mesh terpadatkan dan material bawaan utuh!"); | |
| Debug.Log("๐ [PACKING] Menjalankan ZepetoAssetPackage.Pack..."); | |
| var result = new StringBuilder(); | |
| var statistics = ZepetoAssetBundleInfo.Create(optimizedPrefab); | |
| if (statistics != null) | |
| { | |
| result.AppendLine("[ZEPETO STUDIO ARCHIVE RESULT]"); | |
| result.AppendLine(JsonUtility.ToJson(statistics, true)); | |
| } | |
| var packResult = ZepetoAssetPackage.Pack(optimizedPrefab); | |
| result.Append(packResult); | |
| Debug.Log(result.ToString()); | |
| Debug.Log("โ [BUILDER] Seluruh proses konversi sukses sempurna."); | |
| EditorApplication.UnlockReloadAssemblies(); | |
| EditorApplication.Exit(0); | |
| } | |
| catch (Exception e) | |
| { | |
| Debug.LogError($"๐ฅ [BUILDER] Error: {e.Message}"); | |
| Debug.LogError($"StackTrace: {e.StackTrace}"); | |
| EditorApplication.UnlockReloadAssemblies(); | |
| EditorApplication.Exit(1); | |
| } | |
| } | |
| } |