#if GRIFFIN using UnityEngine; using System.Collections.Generic; using System.IO; #if UNITY_EDITOR using UnityEditor; #endif using Pinwheel.Griffin.Physic; namespace Pinwheel.Griffin.DataTool { public class GUnityTerrainGroupConverter { public enum GAlbedoUsage { None, ColorMap, VertexColor } public GameObject Root { get; set; } public GTerrainData DataTemplate { get; set; } public bool ImportGeometry { get; set; } public bool ImportSplats { get; set; } public bool ImportSplatsAsAlbedo { get; set; } public GAlbedoUsage AlbedoUsage { get; set; } public bool ImportTrees { get; set; } public bool ImportGrasses { get; set; } public bool SkipFoliageSnap { get; set; } public string DataDirectory { get; set; } private string conversionName; private string ConversionName { get { if (string.IsNullOrEmpty(conversionName)) { conversionName = System.DateTime.Now.Ticks.ToString(); } return conversionName; } set { conversionName = value; } } private List terrains; private List Terrains { get { if (terrains == null) { terrains = new List(); } return terrains; } set { terrains = value; } } private List convertedTerrains; private List ConvertedTerrains { get { if (convertedTerrains == null) { convertedTerrains = new List(); } return convertedTerrains; } set { convertedTerrains = value; } } private List splatGroups; private List SplatGroups { get { if (splatGroups == null) { splatGroups = new List(); } return splatGroups; } set { splatGroups = value; } } private List splatGroupIndices; private List SplatGroupIndices { get { if (splatGroupIndices == null) { splatGroupIndices = new List(); } return splatGroupIndices; } set { splatGroupIndices = value; } } private List treeGroups; private List TreeGroups { get { if (treeGroups == null) { treeGroups = new List(); } return treeGroups; } set { treeGroups = value; } } private List treeGroupIndices; private List TreeGroupIndices { get { if (treeGroupIndices == null) { treeGroupIndices = new List(); } return treeGroupIndices; } set { treeGroupIndices = value; } } private List grassGroups; private List GrassGroups { get { if (grassGroups == null) { grassGroups = new List(); } return grassGroups; } set { grassGroups = value; } } private List grassGroupIndices; private List GrassGroupIndices { get { if (grassGroupIndices == null) { grassGroupIndices = new List(); } return grassGroupIndices; } set { grassGroupIndices = value; } } public List Convert() { try { Validate(); Initialize(); if (DataTemplate == null) { CreateSharedData(); } ImportDataAndCreateTerrain(); FinishingUp(); } catch (GProgressCancelledException) { Debug.Log("Converting process canceled!"); } catch (System.Exception e) { Debug.LogError(e.ToString()); } finally { #if UNITY_EDITOR GCommonGUI.ClearProgressBar(); #endif } return ConvertedTerrains; } private void Validate() { if (Root == null) throw new System.ArgumentNullException("Root"); Terrain[] terrains = Root.GetComponentsInChildren(); if (terrains.Length == 0) { throw new System.Exception("No Terrain found under Root"); } bool hasData = false; for (int i = 0; i < terrains.Length; ++i) { if (terrains[i].terrainData != null) { hasData = true; } } if (!hasData) { throw new System.Exception("No Terrain with Terrain Data found under Root"); } #if UNITY_EDITOR if (!Application.isPlaying) { if (string.IsNullOrEmpty(DataDirectory) || !DataDirectory.StartsWith("Assets/")) { throw new System.ArgumentException("Data Directory must be related to Assets folder"); } } #endif } private void SaveAssets() { #if UNITY_EDITOR GCommonGUI.ProgressBar("Saving", "Saving unsaved assets...", 1f); AssetDatabase.SaveAssets(); GCommonGUI.ClearProgressBar(); #endif } private void Initialize() { Terrains = new List(); Terrain[] t = Root.GetComponentsInChildren(); for (int i = 0; i < t.Length; ++i) { if (t[i].terrainData != null) Terrains.Add(t[i]); } if (!Application.isPlaying) { GUtilities.EnsureDirectoryExists(DataDirectory); } } private void CreateSharedData() { if (ImportSplats) CreateSharedSplats(); if (ImportTrees) CreateSharedTrees(); if (ImportGrasses) CreateSharedGrasses(); //SaveAssets(); } private void CreateSharedSplats() { SplatGroups.Clear(); SplatGroupIndices.Clear(); for (int i = 0; i < Terrains.Count; ++i) { Terrain t = Terrains[i]; #if UNITY_2018_3_OR_NEWER TerrainLayer[] layers = t.terrainData.terrainLayers; #else SplatPrototype[] layers = t.terrainData.splatPrototypes; #endif int splatGroupIndex = -1; for (int j = 0; j < SplatGroups.Count; ++j) { if (SplatGroups[j].Equals(layers)) { splatGroupIndex = j; break; } } if (splatGroupIndex >= 0) { SplatGroupIndices.Add(splatGroupIndex); } else { GSplatPrototypeGroup group = GSplatPrototypeGroup.Create(layers); SplatGroups.Add(group); SplatGroupIndices.Add(SplatGroups.Count - 1); } } #if UNITY_EDITOR if (!Application.isPlaying) { GUtilities.EnsureDirectoryExists(DataDirectory); for (int i = 0; i < SplatGroups.Count; ++i) { GSplatPrototypeGroup group = SplatGroups[i]; group.name = string.Format("{0}{1}_{2}", "SharedSplats", i.ToString(), ConversionName); string assetPath = string.Format("{0}.asset", Path.Combine(DataDirectory, group.name)); AssetDatabase.CreateAsset(group, assetPath); } } #endif } private void CreateSharedTrees() { TreeGroups.Clear(); TreeGroupIndices.Clear(); for (int i = 0; i < Terrains.Count; ++i) { Terrain t = Terrains[i]; TreePrototype[] treePrototypes = t.terrainData.treePrototypes; int treeGroupIndex = -1; for (int j = 0; j < TreeGroups.Count; ++j) { if (TreeGroups[j].Equals(treePrototypes)) { treeGroupIndex = j; break; } } if (treeGroupIndex >= 0) { TreeGroupIndices.Add(treeGroupIndex); } else { GTreePrototypeGroup group = GTreePrototypeGroup.Create(treePrototypes); TreeGroups.Add(group); TreeGroupIndices.Add(TreeGroups.Count - 1); } } #if UNITY_EDITOR if (!Application.isPlaying) { GUtilities.EnsureDirectoryExists(DataDirectory); for (int i = 0; i < TreeGroups.Count; ++i) { GTreePrototypeGroup group = TreeGroups[i]; group.name = string.Format("{0}{1}_{2}", "SharedTrees", i.ToString(), ConversionName); string assetPath = string.Format("{0}.asset", Path.Combine(DataDirectory, group.name)); AssetDatabase.CreateAsset(group, assetPath); } } #endif } private void CreateSharedGrasses() { GrassGroups.Clear(); GrassGroupIndices.Clear(); for (int i = 0; i < Terrains.Count; ++i) { Terrain t = Terrains[i]; DetailPrototype[] detailPrototypes = t.terrainData.detailPrototypes; int grassGroupIndex = -1; for (int j = 0; j < GrassGroups.Count; ++j) { if (GrassGroups[j].Equals(detailPrototypes)) { grassGroupIndex = j; break; } } if (grassGroupIndex >= 0) { GrassGroupIndices.Add(grassGroupIndex); } else { GGrassPrototypeGroup group = GGrassPrototypeGroup.Create(detailPrototypes); GrassGroups.Add(group); GrassGroupIndices.Add(GrassGroups.Count - 1); } } #if UNITY_EDITOR if (!Application.isPlaying) { GUtilities.EnsureDirectoryExists(DataDirectory); for (int i = 0; i < GrassGroups.Count; ++i) { GGrassPrototypeGroup group = GrassGroups[i]; group.name = string.Format("{0}{1}_{2}", "SharedGrasses", i.ToString(), ConversionName); string assetPath = string.Format("{0}.asset", Path.Combine(DataDirectory, group.name)); AssetDatabase.CreateAsset(group, assetPath); } } #endif } private void ImportDataAndCreateTerrain() { GameObject terrainRoot = new GameObject(string.Format("{0}-{1}", Root.name, ConversionName)); terrainRoot.transform.parent = Root.transform.parent; terrainRoot.transform.position = Root.transform.position; for (int i = 0; i < Terrains.Count; ++i) { #if UNITY_EDITOR GCommonGUI.CancelableProgressBar("Converting", "Converting " + Terrains[i].name, 1f); #endif GTerrainData data = ScriptableObject.CreateInstance(); if (DataTemplate != null) { DataTemplate.Geometry.CopyTo(data.Geometry); DataTemplate.Shading.CopyTo(data.Shading); DataTemplate.Rendering.CopyTo(data.Rendering); DataTemplate.Foliage.CopyTo(data.Foliage); DataTemplate.Mask.CopyTo(data.Mask); } else { if (Application.isPlaying) //Reset() only called in edit mode { data.Reset(); data.Geometry.Reset(); data.Shading.Reset(); data.Rendering.Reset(); data.Foliage.Reset(); data.Mask.Reset(); } } #if UNITY_EDITOR if (!Application.isPlaying) { string assetName = "TerrainData_" + data.Id; AssetDatabase.CreateAsset(data, string.Format("{0}.asset", Path.Combine(DataDirectory, assetName))); } #endif Material templateMaterial = null; if (DataTemplate != null) { templateMaterial = DataTemplate.Shading.CustomMaterial; } Material material = null; if (templateMaterial != null) { material = new Material(templateMaterial.shader); } else { if (ImportSplats) { if (ImportSplatsAsAlbedo && AlbedoUsage == GAlbedoUsage.ColorMap) { material = GRuntimeSettings.Instance.terrainRendering.GetClonedMaterial(GCommon.CurrentRenderPipeline, GLightingModel.PBR, GTexturingModel.ColorMap); } else if (ImportSplatsAsAlbedo && AlbedoUsage == GAlbedoUsage.VertexColor) { material = GRuntimeSettings.Instance.terrainRendering.GetClonedMaterial(GCommon.CurrentRenderPipeline, GLightingModel.PBR, GTexturingModel.VertexColor); data.Geometry.AlbedoToVertexColorMode = GAlbedoToVertexColorMode.Sharp; } else { GSplatPrototypeGroup splats = DataTemplate ? DataTemplate.Shading.Splats : SplatGroups[SplatGroupIndices[i]]; GSplatsModel splatModel = (splats == null || splats.Prototypes.Count <= 4) ? GSplatsModel.Splats4Normals4 : GSplatsModel.Splats8; material = GRuntimeSettings.Instance.terrainRendering.GetClonedMaterial(GCommon.CurrentRenderPipeline, GLightingModel.PBR, GTexturingModel.Splat, splatModel); } } else { material = GRuntimeSettings.Instance.terrainRendering.GetClonedMaterial(GCommon.CurrentRenderPipeline, GLightingModel.PBR, GTexturingModel.Splat, GSplatsModel.Splats4); } } data.Shading.CustomMaterial = material; #if UNITY_EDITOR if (!Application.isPlaying && material != null) { string matName = "TerrainMaterial_" + data.Id; material.name = matName; AssetDatabase.CreateAsset(material, string.Format("{0}.mat", Path.Combine(DataDirectory, matName))); } #endif if (ImportSplats) { data.Shading.Splats = DataTemplate ? DataTemplate.Shading.Splats : SplatGroups[SplatGroupIndices[i]]; data.Shading.UpdateMaterials(); } if (ImportTrees) { data.Foliage.Trees = DataTemplate ? DataTemplate.Foliage.Trees : TreeGroups[TreeGroupIndices[i]]; } if (ImportGrasses) { data.Foliage.Grasses = DataTemplate ? DataTemplate.Foliage.Grasses : GrassGroups[GrassGroupIndices[i]]; } GUnityTerrainDataImporter importer = new GUnityTerrainDataImporter(); importer.SrcData = Terrains[i].terrainData; importer.SrcTerrain = Terrains[i]; importer.DesData = data; importer.DesTerrain = null; importer.ImportGeometry = ImportGeometry; importer.UseUnityTerrainSize = true; importer.ImportSplats = ImportSplats; importer.ImportSplatsAsAlbedo = ImportSplatsAsAlbedo; importer.ImportSplatControlMapsOnly = DataTemplate != null && DataTemplate.Shading.Splats != null; importer.ImportSplatControlMapResolution = DataTemplate == null; importer.CreateNewSplatPrototypesGroup = false; importer.ImportTrees = ImportTrees; importer.ImportTreeInstancesOnly = DataTemplate != null && DataTemplate.Foliage.Trees != null; importer.CreateNewTreePrototypesGroup = false; importer.ImportGrasses = ImportGrasses; importer.ImportGrassInstancesOnly = DataTemplate != null && DataTemplate.Foliage.Grasses != null; importer.CreateNewGrassPrototypesGroup = false; importer.GrassDensity = 1; importer.SkipFoliageSnap = SkipFoliageSnap; importer.Import(); GStylizedTerrain t = CreateTerrain(); t.transform.parent = terrainRoot.transform; t.transform.position = Terrains[i].transform.position; t.name = Terrains[i].name; #if UNITY_2018_3_OR_NEWER t.GroupId = Terrains[i].groupingID; #endif t.TerrainData = data; if (ImportTrees || ImportGrasses) { data.Foliage.SetTreeRegionDirty(GCommon.UnitRect); data.Foliage.SetGrassRegionDirty(GCommon.UnitRect); t.UpdateTreesPosition(); t.UpdateGrassPatches(); data.Foliage.ClearTreeDirtyRegions(); data.Foliage.ClearGrassDirtyRegions(); } ConvertedTerrains.Add(t); #if UNITY_EDITOR //SaveAssets(); GCommonGUI.ClearProgressBar(); #endif } } private GStylizedTerrain CreateTerrain() { GameObject g = new GameObject("Stylized Terrain"); g.transform.localPosition = Vector3.zero; g.transform.localRotation = Quaternion.identity; g.transform.localScale = Vector3.one; GStylizedTerrain terrain = g.AddComponent(); terrain.GroupId = 0; GameObject colliderGO = new GameObject("Tree Collider"); colliderGO.transform.parent = terrain.transform; colliderGO.transform.localPosition = Vector3.zero; colliderGO.transform.localRotation = Quaternion.identity; colliderGO.transform.localScale = Vector3.one; GTreeCollider collider = colliderGO.AddComponent(); collider.Terrain = terrain; return terrain; } private void FinishingUp() { #if UNITY_EDITOR GCommonGUI.ProgressBar("Finishing Up", "Matching geometry...", 1f); #endif for (int i = 0; i < ConvertedTerrains.Count; ++i) { ConvertedTerrains[i].ConnectNeighbor(); ConvertedTerrains[i].MatchEdges(); } Root.gameObject.SetActive(false); #if UNITY_EDITOR GCommonGUI.ClearProgressBar(); #endif } } } #endif