#if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; /// /// Imports the authored battlefield component FBX/XPbr files and creates one /// reusable prefab per component. Ground is built separately so its collider /// and read/write mesh settings cannot be confused with decoration assets. /// public static class BuildBattlefieldAssets { const string Root = "Assets/Bundles/Scenes/Prototype/Battlefield/A/Components"; const string GroundRoot = "Assets/Bundles/Scenes/Prototype/Battlefield/A/Ground"; const string GroundPrefab = "Assets/Bundles/Scenes/Prototype/Battlefield/GreenfieldGround.prefab"; const string XpbrShader = "Test/XPbr"; static readonly string[] Parts = { "tree", "rock", "bridge", "sign", "shrub" }; public static string BuildComponents() { AssetDatabase.Refresh(); var reports = new List(); foreach (var type in Parts) reports.Add(BuildPart(type)); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return string.Join(" | ", reports); } /// /// Builds the independently authored ground asset once Hunyuan has produced /// Ground/ground.fbx and its XPbr textures. The ground keeps a readable mesh /// so the authored prefab can carry a non-convex MeshCollider. /// public static string BuildGround() { AssetDatabase.Refresh(); string fbxPath = GroundRoot + "/ground.fbx"; if (!File.Exists(fbxPath)) return "ground: missing fbx"; string basePath = GroundRoot + "/ground_base.png"; string mixPath = GroundRoot + "/ground_mix.png"; ConfigureTexture(basePath, true, true); ConfigureTexture(mixPath, false, false); var importer = AssetImporter.GetAtPath(fbxPath) as ModelImporter; if (importer != null) { importer.globalScale = 1f; importer.useFileScale = false; importer.bakeAxisConversion = true; importer.materialImportMode = ModelImporterMaterialImportMode.ImportViaMaterialDescription; importer.meshCompression = ModelImporterMeshCompression.Medium; importer.isReadable = true; importer.addCollider = false; importer.SaveAndReimport(); } var mat = GetOrCreateMaterial(GroundRoot + "/ground.mat", "ground", basePath, mixPath); var model = AssetDatabase.LoadAssetAtPath(fbxPath); if (model == null) return "ground: fbx not loaded"; var root = new GameObject("GreenfieldGround"); try { var instance = (GameObject)PrefabUtility.InstantiatePrefab(model); instance.name = "GroundMesh"; instance.transform.SetParent(root.transform, false); PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); foreach (var renderer in instance.GetComponentsInChildren(true)) renderer.sharedMaterial = mat; foreach (var filter in instance.GetComponentsInChildren(true)) { if (filter.sharedMesh == null) continue; var collider = filter.gameObject.GetComponent(); if (collider == null) collider = filter.gameObject.AddComponent(); collider.convex = false; collider.sharedMesh = filter.sharedMesh; } PrefabUtility.SaveAsPrefabAsset(root, GroundPrefab); } finally { UnityEngine.Object.DestroyImmediate(root); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return "ground: " + GroundPrefab; } public static string BuildAll() { var components = BuildComponents(); var ground = BuildGround(); return components + " | " + ground; } static string BuildPart(string type) { string dir = Root + "/" + type; string fbxPath = dir + "/" + type + ".fbx"; string matPath = dir + "/" + type + ".mat"; string basePath = dir + "/" + type + "_base.png"; string mixPath = dir + "/" + type + "_mix.png"; string prefabPath = dir + "/" + type + ".prefab"; if (!File.Exists(fbxPath)) return type + ": missing fbx"; ConfigureTexture(basePath, true, true); ConfigureTexture(mixPath, false, false); var importer = AssetImporter.GetAtPath(fbxPath) as ModelImporter; if (importer != null) { importer.globalScale = 1f; importer.useFileScale = false; importer.bakeAxisConversion = true; importer.materialImportMode = ModelImporterMaterialImportMode.ImportViaMaterialDescription; importer.meshCompression = ModelImporterMeshCompression.Medium; importer.isReadable = false; importer.addCollider = false; importer.SaveAndReimport(); } var mat = GetOrCreateMaterial(matPath, type, basePath, mixPath); var model = AssetDatabase.LoadAssetAtPath(fbxPath); if (model == null) return type + ": fbx not loaded"; var instance = (GameObject)PrefabUtility.InstantiatePrefab(model); instance.name = type; foreach (var renderer in instance.GetComponentsInChildren()) renderer.sharedMaterial = mat; instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = Quaternion.identity; instance.transform.localScale = Vector3.one; PrefabUtility.SaveAsPrefabAsset(instance, prefabPath); UnityEngine.Object.DestroyImmediate(instance); return type + ": " + prefabPath; } static Material GetOrCreateMaterial(string matPath, string name, string basePath, string mixPath) { var baseMap = AssetDatabase.LoadAssetAtPath(basePath); var mixMap = AssetDatabase.LoadAssetAtPath(mixPath); var shader = Shader.Find(XpbrShader) ?? Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard"); var mat = AssetDatabase.LoadAssetAtPath(matPath); if (mat == null) { mat = new Material(shader) { name = name }; AssetDatabase.CreateAsset(mat, matPath); } else mat.shader = shader; if (mat.HasProperty("_MainTex")) mat.SetTexture("_MainTex", baseMap); if (mat.HasProperty("_MixTex")) mat.SetTexture("_MixTex", mixMap); if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", Color.white); if (mat.HasProperty("_Metallic")) mat.SetFloat("_Metallic", 1f); if (mat.HasProperty("_Roughness")) mat.SetFloat("_Roughness", 1f); if (mat.HasProperty("_AO")) mat.SetFloat("_AO", 1f); EditorUtility.SetDirty(mat); return mat; } static void ConfigureTexture(string path, bool srgb, bool alpha) { if (!File.Exists(path)) return; var importer = AssetImporter.GetAtPath(path) as TextureImporter; if (importer == null) return; importer.textureType = TextureImporterType.Default; importer.sRGBTexture = srgb; importer.mipmapEnabled = true; importer.alphaIsTransparency = alpha; importer.SaveAndReimport(); } } #endif