Files
KopMap/Assets/NatureManufacture Assets/WorldStreamer/Scritps/Terrain/Editor/TerrainManager.cs
2025-09-02 18:55:19 +08:00

1689 lines
72 KiB
C#

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using UnityEngine.Rendering;
//using UnityEngine.Experimental.TerrainAPI;
using System.IO;
using UnityEditor.SceneManagement;
using UnityEditor.Build;
using UnityEngine.SceneManagement;
namespace WorldStreamer2
{
/// <summary>
/// Split terrain.
/// </summary>
public class TerrainManager : Editor
{
private string _currentScene;
private TerrainManagerSettings _terrainManagerSettings;
private Terrain _parentTerrain;
private float _minHeightTerrain;
private float _maxHeightTerrain;
/// <summary>
/// The scroll position.
/// </summary>
private Vector2 _scrollPos;
private List<MeshFilter> _meshFiltersToFix = new();
private readonly TerrainMapsExporterHdrp _terrainMapsExporterHdrp;
private readonly TerrainMapsExporter _terrainMapsExporter;
public TerrainManager()
{
_terrainMapsExporterHdrp = new TerrainMapsExporterHdrp(this);
_terrainMapsExporter = new TerrainMapsExporter(this);
}
public TerrainManagerSettings ManagerSettings
{
get => _terrainManagerSettings;
set => _terrainManagerSettings = value;
}
public float MaxHeightTerrain
{
get => _maxHeightTerrain;
set => _maxHeightTerrain = value;
}
public float MinHeightTerrain
{
get => _minHeightTerrain;
set => _minHeightTerrain = value;
}
public TerrainMapsExporterHdrp TerrainMapsExporterHdrp
{
get { return _terrainMapsExporterHdrp; }
}
public TerrainMapsExporter TerrainMapsExporter
{
get { return _terrainMapsExporter; }
}
public void OnEnable()
{
if (RenderPipelineManager.currentPipeline == null)
{
}
else
{
BuildTarget buildTarget = EditorUserBuildSettings.activeBuildTarget;
BuildTargetGroup targetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
NamedBuildTarget namedBuildTarget = UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(targetGroup);
string srpType = GraphicsSettings.defaultRenderPipeline.GetType().ToString();
if (srpType.Contains("HDRenderPipelineAsset"))
{
string defineHdrp = "NM_HDRP";
string define = PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget);
if (!define.Contains(defineHdrp))
PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, define + " " + defineHdrp);
}
else if (srpType.Contains("UniversalRenderPipelineAsset") ||
srpType.Contains("LightweightRenderPipelineAsset"))
{
string defineUrp = "NM_URP";
string define = PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget);
if (!define.Contains(defineUrp))
PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, define + " " + defineUrp);
}
}
}
public void OnGUI()
{
if (_currentScene != SceneManager.GetActiveScene().path || ManagerSettings == null)
{
SceneChanged();
}
if (ManagerSettings == null)
return;
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
if (ManagerSettings.colorSpaceLast == ColorSpace.Uninitialized)
{
ManagerSettings.ambientLightColor = PlayerSettings.colorSpace == ColorSpace.Gamma ? new Color(1.23f, 1.23f, 1.23f) : new Color(1f, 1f, 1f);
ManagerSettings.colorSpaceLast = PlayerSettings.colorSpace;
}
else if (ManagerSettings.colorSpaceLast != PlayerSettings.colorSpace)
{
if (ManagerSettings.colorSpaceLast == ColorSpace.Gamma)
{
ManagerSettings.ambientLightColor.r /= 1.23f;
ManagerSettings.ambientLightColor.g /= 1.23f;
ManagerSettings.ambientLightColor.b /= 1.23f;
}
else
{
ManagerSettings.ambientLightColor.r *= 1.23f;
ManagerSettings.ambientLightColor.g *= 1.23f;
ManagerSettings.ambientLightColor.b *= 1.23f;
}
ManagerSettings.colorSpaceLast = PlayerSettings.colorSpace;
}
Terrain[] terrains = Terrain.activeTerrains;
SerializedObject serializedManagerSettings = new SerializedObject(ManagerSettings);
SerializedProperty listTerrainTrees = serializedManagerSettings.FindProperty("terrainTrees");
for (int i = 0; i < terrains.Length; i++)
{
if (terrains[i] != null && terrains[i].terrainData != null)
{
TreePrototype[] prototypes = terrains[i].terrainData.treePrototypes;
foreach (var treePrototype in prototypes)
{
bool toAdd = true;
foreach (var terrainTree in ManagerSettings.terrainTrees)
{
if (treePrototype.prefab == terrainTree.tree)
{
toAdd = false;
break;
}
}
if (toAdd)
{
ManagerSettings.terrainTrees.Add(new TerrainTrees() { tree = treePrototype.prefab });
}
}
}
}
EditorGUILayout.Space();
#if NM_HDRP
GUILayout.Label("[HDRP]", EditorStyles.boldLabel);
EditorGUILayout.Space();
#endif
#if NM_URP
GUILayout.Label("[URP]", EditorStyles.boldLabel);
EditorGUILayout.Space();
#endif
////Vertical
GUILayout.Label("Terrains settings", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
TerrainSettingsUI.TerrainSettingsGUI(this);
EditorGUILayout.Space();
if (GUILayout.Button("Set settings for selected terrains"))
{
TerrainSettingsSetter.SetTerrainSettings(ManagerSettings);
}
if (GUILayout.Button("Set settings for all terrains"))
{
TerrainSettingsSetter.SetTerrainSettings(ManagerSettings, true);
}
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
////Vertical
GUILayout.Label("Terrain splitter", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.Space();
ManagerSettings.terrainsDataPath =
EditorGUILayout.TextField("Terrain create folder", ManagerSettings.terrainsDataPath);
ManagerSettings.splitSize =
Mathf.NextPowerOfTwo(EditorGUILayout.IntSlider("Split size", ManagerSettings.splitSize, 2, 32));
ManagerSettings.addTerrainCulling =
EditorGUILayout.Toggle("Add terrain culling", ManagerSettings.addTerrainCulling);
ManagerSettings.splitterType = (TerrainManagerSettings.SplitterType)EditorGUILayout.EnumPopup("Splitter type", ManagerSettings.splitterType);
if (ManagerSettings.splitterType == TerrainManagerSettings.SplitterType.GPU)
{
EditorGUILayout.HelpBox("GPU splitter is still in preview.", MessageType.Warning);
}
if (GUILayout.Button("Split selected terrains"))
{
if (ManagerSettings.splitterType == TerrainManagerSettings.SplitterType.CPU)
TerrainSplitterCPU.SplitTerrain(ManagerSettings);
else
TerrainSplitter.SplitTerrain(ManagerSettings);
}
if (GUILayout.Button("Split all terrains"))
{
if (ManagerSettings.splitterType == TerrainManagerSettings.SplitterType.CPU)
TerrainSplitterCPU.SplitTerrain(ManagerSettings, true);
else
TerrainSplitter.SplitTerrain(ManagerSettings, true);
}
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
////Vertical
GUILayout.Label("Terrain Low Poly Mesh Generator", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.Space();
ManagerSettings.terrainPath =
EditorGUILayout.TextField("Terrain create folder", ManagerSettings.terrainPath);
ManagerSettings.ambientLightColor = EditorGUILayout.ColorField(new GUIContent("Ambient color"),
ManagerSettings.ambientLightColor, true, false, true);
EditorGUILayout.Space();
ManagerSettings.terrainPrefixName =
EditorGUILayout.TextField("Terrain Mesh Name", ManagerSettings.terrainPrefixName);
ManagerSettings.terrainLod =
EditorGUILayout.IntSlider("Terrain lod", ManagerSettings.terrainLod, 0, 8);
EditorGUILayout.Space();
ManagerSettings.basemapResolution = Mathf.NextPowerOfTwo(
EditorGUILayout.IntSlider("Basemap Resolution", ManagerSettings.basemapResolution, 128, 4096));
ManagerSettings.useBaseMap =
EditorGUILayout.Toggle("Use base map", ManagerSettings.useBaseMap);
ManagerSettings.useSmoothness =
EditorGUILayout.Toggle("Use smoothness map", ManagerSettings.useSmoothness);
#if NM_URP
ManagerSettings.useMaskSmoothnessURP = EditorGUILayout.Toggle("Use mask map", ManagerSettings.useMaskSmoothnessURP);
#endif
ManagerSettings.terrainNormalDetails = Mathf.NextPowerOfTwo(
EditorGUILayout.IntSlider("Normal Details", ManagerSettings.terrainNormalDetails, 1, 32));
ManagerSettings.terrainNormalStrength = EditorGUILayout.Slider("Normal strength",
ManagerSettings.terrainNormalStrength, 1, 10);
ManagerSettings.useTerrainNormal = EditorGUILayout.Toggle("Create normal from shape",
ManagerSettings.useTerrainNormal);
ManagerSettings.useTextureNormal = EditorGUILayout.Toggle("Create normal from textures",
ManagerSettings.useTextureNormal);
EditorGUILayout.Space();
ManagerSettings.yOffset = EditorGUILayout.FloatField("Y offset", ManagerSettings.yOffset);
ManagerSettings.addVerticesDown =
EditorGUILayout.Toggle("Add vertices down", ManagerSettings.addVerticesDown);
if (ManagerSettings.addVerticesDown)
ManagerSettings.verticesDownDistance = EditorGUILayout.Slider("Vertices down distance",
ManagerSettings.verticesDownDistance, 1, 100);
EditorGUILayout.Space();
SerializedProperty trainglesTreesMax = serializedManagerSettings.FindProperty("trainglesTreesMax");
EditorGUILayout.PropertyField(trainglesTreesMax,
new GUIContent("Trees Max Traingles", "Trees Max Traingles to Export"));
listTerrainTrees.isExpanded = EditorGUILayout.Foldout(listTerrainTrees.isExpanded,
new GUIContent("Tree Prototypes", "Tree Prototypes to generate mesh lod"));
EditorGUI.indentLevel++;
if (listTerrainTrees.isExpanded)
{
//EditorGUILayout.PropertyField(listTerrainTrees.FindPropertyRelative("Array.size"));
for (int i = 0; i < listTerrainTrees.arraySize; i++)
{
SerializedProperty scriptable = listTerrainTrees.GetArrayElementAtIndex(i);
EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(false));
if (scriptable.FindPropertyRelative("tree").objectReferenceValue != null)
{
scriptable.isExpanded = EditorGUILayout.Foldout(scriptable.isExpanded, new GUIContent(scriptable.FindPropertyRelative("tree").objectReferenceValue.name), false);
EditorGUILayout.PropertyField(scriptable.FindPropertyRelative("active"), new GUIContent(""),
GUILayout.ExpandWidth(false), GUILayout.MaxWidth(40));
}
else
{
GUIStyle style = new GUIStyle
{
richText = true
};
EditorGUILayout.LabelField($"<color=red><b>Missing Tree id: {i}</b></color>", style);
}
//EditorGUILayout.LabelField();
EditorGUILayout.EndHorizontal();
if (scriptable.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(scriptable.FindPropertyRelative("tree"));
EditorGUI.indentLevel--;
EditorGUILayout.Space();
}
}
}
if (GUILayout.Button("Refresh tree prototypes"))
{
ManagerSettings.terrainTrees.Clear();
}
EditorGUILayout.Space();
if (GUILayout.Button("Export selected Terrains data: shape and trees into LP Mesh"))
{
TerrainToMesh(true);
}
if (GUILayout.Button("Export selected Terrains data: shape into LP Mesh"))
{
TerrainToMesh();
}
if (GUILayout.Button("Export selected Terrains data: trees into LP Mesh"))
{
ExportTreesForSelectedTerrain();
}
EditorGUILayout.Space();
if (GUILayout.Button("Export all Terrains data: shape and trees into LP Mesh"))
{
TerrainToMesh(true, true);
}
if (GUILayout.Button("Export all Terrains data: shape into LP Mesh"))
{
TerrainToMesh(false, true);
}
if (GUILayout.Button("Export all Terrains data: trees into LP Mesh"))
{
ExportTreesForSelectedTerrain(true);
}
EditorGUILayout.Space();
if (GUILayout.Button("Export all Terrains Rim"))
{
TerrainToMeshRim(true);
}
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
EditorGUILayout.EndScrollView();
serializedManagerSettings.ApplyModifiedProperties();
}
/// <summary>
/// Creates the settings gameObject.
/// </summary>
private void SceneChanged()
{
_currentScene = EditorSceneManager.GetActiveScene().path;
#if UNITY_2022_1_OR_NEWER && !Unity_2021_3_1 && !Unity_2021_3_2 && !Unity_2021_3_3 && !Unity_2021_3_4 && !Unity_2021_3_5 && !Unity_2021_3_6 && !Unity_2021_3_7 && !Unity_2021_3_8 && !Unity_2021_3_9 && !Unity_2021_3_10 && !Unity_2021_3_11 && !Unity_2021_3_12 && !Unity_2021_3_13 && !Unity_2021_3_14 && !Unity_2021_3_15 && !Unity_2021_3_16 && !Unity_2021_3_17
ManagerSettings = FindAnyObjectByType<TerrainManagerSettings>();
#else
ManagerSettings = FindObjectOfType<TerrainManagerSettings>();
#endif
if (ManagerSettings != null) return;
GameObject gameObject = new GameObject("_TerrainManagerSettings");
ManagerSettings = gameObject.AddComponent<TerrainManagerSettings>();
ManagerSettings.materialTemplate =
AssetDatabase.GetBuiltinExtraResource<Material>("Default-Terrain-Standard.mat");
gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSaveInBuild;
gameObject.tag = "EditorOnly";
}
private void TerrainToMeshRim(bool allTerrains = false)
{
_meshFiltersToFix.Clear();
if (!Directory.Exists("Assets/" + ManagerSettings.terrainPath))
{
Directory.CreateDirectory("Assets/" + ManagerSettings.terrainPath);
}
Terrain[] terrains = Selection.GetFiltered<Terrain>(SelectionMode.TopLevel);
if (allTerrains)
terrains = Terrain.activeTerrains;
int idTerrain = 0;
int countTerrain = terrains.Length;
foreach (var terrain in terrains)
{
EditorUtility.DisplayProgressBar("Terrain mesh generation",
"Exporting terrain " + idTerrain + "/" + countTerrain + "\n Exporting mesh",
idTerrain / (float)countTerrain);
TerrainData terrainData = terrain.terrainData;
float[,] heightmapData = terrainData.GetHeights(0, 0, terrainData.heightmapResolution,
terrainData.heightmapResolution);
float sizeX = terrainData.size.x;
float sizeY = terrainData.size.y;
float sizeZ = terrainData.size.z;
float terrainTowidth = (1 / sizeX * (terrainData.heightmapResolution - 1));
float terrainToheight = (1 / sizeZ * (terrainData.heightmapResolution - 1));
Vector3 position = Vector3.zero;
int lod = 1;
Vector4[,] positionArray = new Vector4[heightmapData.GetLength(0) + 2, heightmapData.GetLength(1) + 2];
int addxz = 1;
for (int x = 0; x < heightmapData.GetLength(0); x += lod)
{
//List<Vector3> positionArrayRow = new List<Vector3>();
for (int z = 0; z < heightmapData.GetLength(1); z += lod)
{
position.x = z / terrainToheight; //, polygonHeight
position.y = heightmapData[x, z] * sizeY;
position.z = x / terrainTowidth;
positionArray[x / lod + addxz, z / lod + addxz] =
new Vector4(position.x, position.y, position.z);
if (x == 0)
positionArray[0, z / lod + addxz] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == heightmapData.GetLength(0) - 1)
positionArray[x / lod + 2, z / lod + addxz] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (z == 0)
positionArray[x / lod + 1, 0] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (z == heightmapData.GetLength(1) - 1)
positionArray[x / lod + addxz, z / lod + 2] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == 0 && z == 0)
positionArray[0, 0] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == 0 && z == heightmapData.GetLength(1) - 1)
positionArray[0, z / lod + 2] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == heightmapData.GetLength(0) - 1 && z == 0)
positionArray[x / lod + 2, 0] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == heightmapData.GetLength(0) - 1 && z == heightmapData.GetLength(1) - 1)
positionArray[x / lod + 2, z / lod + 2] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
}
//positionArray.Add(positionArrayRow);
}
Mesh meshTerrain = new Mesh();
meshTerrain.indexFormat = IndexFormat.UInt32;
List<Vector3> vertices = new();
List<Vector3> normals = new();
List<Vector2> uvs = new();
List<int> triangles = new();
Vector3 normal;
Vector3 vert;
int id = 0;
Vector2 uv;
for (int x = 0; x < positionArray.GetLength(0); x++)
{
for (int z = 0; z < positionArray.GetLength(1); z++)
{
if (x > 1 && x < positionArray.GetLength(0) - 2 && z > 1 && z < positionArray.GetLength(1) - 2)
continue;
vert = positionArray[x, z];
positionArray[x, z].w = id;
id++;
vertices.Add(vert);
uv = new Vector2(vert.x / sizeX, vert.z / sizeZ);
if (uv.x > 0.99)
uv.x = 1;
if (uv.y > 0.99)
uv.y = 1;
if (uv.x < 0.01)
uv.x = 0;
if (uv.y < 0.01)
uv.y = 0;
uvs.Add(uv);
normal = terrainData.GetInterpolatedNormal(vert.x / sizeX, vert.z / sizeZ);
if (x == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 1, z].x / sizeX,
positionArray[x + 1, z].z / sizeZ);
if (x == positionArray.GetLength(0) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 1, z].x / sizeX,
positionArray[x - 1, z].z / sizeZ);
if (z == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z + 1].x / sizeX,
positionArray[x, z + 1].z / sizeZ);
if (z == positionArray.GetLength(1) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z - 1].x / sizeX,
positionArray[x, z - 1].z / sizeZ);
if (x == 0 && z == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 1, z + 1].x / sizeX,
positionArray[x + 1, z + 1].z / sizeZ);
if (x == 0 && z == positionArray.GetLength(1) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 1, z - 1].x / sizeX,
positionArray[x, z - 1].z / sizeZ);
if (x == positionArray.GetLength(0) - 1 && z == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 1, z + 1].x / sizeX,
positionArray[x - 1, z + 1].z / sizeZ);
if (x == positionArray.GetLength(0) - 1 && z == heightmapData.GetLength(1) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 1, z - 1].x / sizeX,
positionArray[x - 1, z - 1].z / sizeZ);
if (x == 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 2, z].x / sizeX,
positionArray[x + 2, z].z / sizeZ);
if (x == positionArray.GetLength(0) - 2)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 2, z].x / sizeX,
positionArray[x - 2, z].z / sizeZ);
if (z == 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z + 2].x / sizeX,
positionArray[x, z + 2].z / sizeZ);
if (z == positionArray.GetLength(1) - 2)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z - 2].x / sizeX,
positionArray[x, z - 2].z / sizeZ);
if (x == 1 && z == 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 2, z + 2].x / sizeX,
positionArray[x + 2, z + 2].z / sizeZ);
if (x == 1 && z == positionArray.GetLength(1) - 2)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 2, z - 2].x / sizeX,
positionArray[x, z - 2].z / sizeZ);
if (x == positionArray.GetLength(0) - 2 && z == 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 2, z + 2].x / sizeX,
positionArray[x - 2, z + 2].z / sizeZ);
if (x == positionArray.GetLength(0) - 2 && z == heightmapData.GetLength(1) - 2)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 2, z - 2].x / sizeX,
positionArray[x - 2, z - 2].z / sizeZ);
normals.Add(normal);
}
}
int rowPositionCount = positionArray.GetLength(1);
Debug.Log(positionArray.GetLength(0) + " " + positionArray.GetLength(1));
for (int i = 0; i < positionArray.GetLength(1) - 1; i++)
{
for (int j = 0; j < positionArray.GetLength(0) - 1; j++)
{
if (j > 0 && j < positionArray.GetLength(0) - 2 && i > 0 && i < positionArray.GetLength(1) - 2)
continue;
triangles.Add((int)positionArray[j, i].w);
triangles.Add((int)positionArray[(j + 1), i].w);
triangles.Add((int)positionArray[j, (i + 1)].w);
triangles.Add((int)positionArray[(j + 1), i].w);
triangles.Add((int)positionArray[(j + 1), (i + 1)].w);
triangles.Add((int)positionArray[j, (i + 1)].w);
}
}
//if (j > 0 && j < positionArray.GetLength(1) - 2 && i > 0 && i < positionArray.GetLength(0) - 2)
// continue;
meshTerrain.SetVertices(vertices);
meshTerrain.SetTriangles(triangles, 0);
meshTerrain.SetUVs(0, uvs);
meshTerrain.SetNormals(normals);
//meshTerrain.RecalculateNormals();
meshTerrain.RecalculateTangents();
meshTerrain.RecalculateBounds();
EditorUtility.DisplayProgressBar("Terrain mesh generation",
$"Exporting terrain {idTerrain}/{countTerrain}\n Exporting textures",
(idTerrain + 0.5f) / countTerrain);
string terrainName = ManagerSettings.terrainPrefixName + terrain.gameObject.name;
GameObject meshGo = new GameObject(terrainName);
MeshFilter meshfilter = meshGo.AddComponent<MeshFilter>();
MeshRenderer meshRenderer = meshGo.AddComponent<MeshRenderer>();
Material terrainMaterial = new Material(Shader.Find("Standard"));
if (RenderPipelineManager.currentPipeline == null)
{
terrainMaterial = new Material(Shader.Find("Standard"));
}
else
{
var srpType = GraphicsSettings.defaultRenderPipeline.GetType().ToString();
if (srpType.Contains("HDRenderPipelineAsset"))
{
terrainMaterial = new Material(Shader.Find("HDRP/TerrainLit"));
}
else if (srpType.Contains("UniversalRenderPipelineAsset"))
{
terrainMaterial = new Material(Shader.Find("Universal Render Pipeline/Terrain/Lit"));
}
}
MaterialPropertyBlock block = new MaterialPropertyBlock();
terrain.GetSplatMaterialPropertyBlock(block);
Texture mask;
Texture basemap = TerrainMapsExporter.ExportBaseMap(terrain, terrainName, out mask);
terrainMaterial.SetTexture("_MainTex", basemap);
Texture texture = ExportNormalMapHeightMap(terrain, terrainName);
terrainMaterial.SetTexture("_BumpMap", texture);
terrainMaterial.SetInt("_SmoothnessTextureChannel", 1);
terrainMaterial.SetFloat("_Glossiness", .2f);
terrainMaterial.SetFloat("_GlossMapScale", 0.5f);
terrainMaterial.SetFloat("_Metallic", .0750f);
terrainMaterial.EnableKeyword("_NORMALMAP");
#if NM_URP
terrainMaterial.SetFloat("_Smoothness", 1f);
UnityEditor.Rendering.Universal.ShaderGUI.LitGUI.SetMaterialKeywords(terrainMaterial);
//CoreUtils.SetKeyword(terrainMaterial, "_SPECULAR_SETUP", false);
//CoreUtils.SetKeyword(terrainMaterial, "_METALLICSPECGLOSSMAP", true);
#endif
string name = ManagerSettings.terrainPath + "/M_" + terrainName + ".mat";
string path = "Assets/" + name;
AssetDatabase.CreateAsset(terrainMaterial, path);
terrainMaterial = AssetDatabase.LoadAssetAtPath<Material>(path);
meshRenderer.sharedMaterial = terrainMaterial;
meshGo.transform.position = terrain.transform.position;
meshfilter.sharedMesh = meshTerrain;
_meshFiltersToFix.Add(meshfilter);
AssetDatabase.Refresh();
idTerrain++;
}
EditorUtility.ClearProgressBar();
Debug.Log("Clear fix normals");
FixMeshNormals(_meshFiltersToFix);
string pathMesh;
foreach (var item in _meshFiltersToFix)
{
name = ManagerSettings.terrainPath + item.name + ".asset";
pathMesh = "Assets/" + name;
item.sharedMesh.UploadMeshData(true);
AssetDatabase.CreateAsset(item.sharedMesh, pathMesh);
}
AssetDatabase.SaveAssets();
System.GC.Collect();
}
private void TerrainToMesh(bool exportTrees = false, bool allTerrains = false)
{
_meshFiltersToFix.Clear();
if (!Directory.Exists("Assets/" + ManagerSettings.terrainPath))
{
Directory.CreateDirectory("Assets/" + ManagerSettings.terrainPath);
}
Terrain[] terrains = Selection.GetFiltered<Terrain>(SelectionMode.TopLevel);
if (allTerrains)
terrains = Terrain.activeTerrains;
int idTerrain = 0;
int countTerrain = terrains.Length;
foreach (var terrain in terrains)
{
EditorUtility.DisplayProgressBar("Terrain mesh generation",
"Exporting terrain " + idTerrain + "/" + countTerrain + "\n Exporting mesh",
idTerrain / (float)countTerrain);
TerrainData terrainData = terrain.terrainData;
float[,] heightmapData = terrainData.GetHeights(0, 0, terrainData.heightmapResolution,
terrainData.heightmapResolution);
float sizeX = terrainData.size.z;
float sizeY = terrainData.size.y;
float sizeZ = terrainData.size.x;
float terrainTowidth = (1 / sizeX * (terrainData.heightmapResolution - 1));
float terrainToheight = (1 / sizeZ * (terrainData.heightmapResolution - 1));
Vector3 position = Vector3.zero;
int lod = (int)Mathf.Pow(2, ManagerSettings.terrainLod);
Vector3[,] positionArray =
new Vector3[heightmapData.GetLength(0) / lod + (lod == 1 ? -1 : 0) + 1 +
(ManagerSettings.addVerticesDown ? 2 : 0), heightmapData.GetLength(1) / lod +
(lod == 1 ? -1 : 0) + 1 + (ManagerSettings.addVerticesDown ? 2 : 0)];
int addxz = +(ManagerSettings.addVerticesDown ? 1 : 0);
for (int x = 0; x < heightmapData.GetLength(0); x += lod)
{
//List<Vector3> positionArrayRow = new List<Vector3>();
for (int z = 0; z < heightmapData.GetLength(1); z += lod)
{
position.x = z / terrainToheight; //, polygonHeight
position.y = heightmapData[x, z] * sizeY;
position.z = x / terrainTowidth;
positionArray[x / lod + addxz, z / lod + addxz] =
new Vector4(position.x, position.y, position.z);
if (ManagerSettings.addVerticesDown)
{
if (x == 0)
positionArray[0, z / lod + addxz] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == heightmapData.GetLength(0) - 1)
positionArray[x / lod + 2, z / lod + addxz] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (z == 0)
positionArray[x / lod + 1, 0] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (z == heightmapData.GetLength(1) - 1)
positionArray[x / lod + addxz, z / lod + 2] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == 0 && z == 0)
positionArray[0, 0] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == 0 && z == heightmapData.GetLength(1) - 1)
positionArray[0, z / lod + 2] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == heightmapData.GetLength(0) - 1 && z == 0)
positionArray[x / lod + 2, 0] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
if (x == heightmapData.GetLength(0) - 1 && z == heightmapData.GetLength(1) - 1)
positionArray[x / lod + 2, z / lod + 2] = new Vector4(position.x,
position.y - ManagerSettings.verticesDownDistance, position.z);
}
}
//positionArray.Add(positionArrayRow);
}
Mesh meshTerrain = new Mesh();
meshTerrain.indexFormat = IndexFormat.UInt32;
List<Vector3> vertices = new();
List<Vector3> normals = new();
List<Vector2> uvs = new();
List<int> triangles = new();
Vector3 normal;
Vector3 vert;
//Debug.Log(sizeX);
Vector2 uv;
for (int x = 0; x < positionArray.GetLength(0); x++)
{
for (int z = 0; z < positionArray.GetLength(1); z++)
{
vert = positionArray[x, z];
vertices.Add(vert);
uv = new Vector2(vert.x / sizeZ, vert.z / sizeX);
uvs.Add(uv);
normal = terrainData.GetInterpolatedNormal(vert.x / sizeZ, vert.z / sizeX);
//Debug.Log($" vert {vert} sizeX {sizeX} sizeZ {sizeZ} normal {normal}");
if (x == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 1, z].x / sizeZ,
positionArray[x + 1, z].z / sizeX);
if (x == positionArray.GetLength(0) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 1, z].x / sizeZ,
positionArray[x - 1, z].z / sizeX);
if (z == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z + 1].x / sizeZ,
positionArray[x, z + 1].z / sizeX);
if (z == positionArray.GetLength(1) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z - 1].x / sizeZ,
positionArray[x, z - 1].z / sizeX);
if (x == 0 && z == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 1, z + 1].x / sizeZ,
positionArray[x + 1, z + 1].z / sizeX);
if (x == 0 && z == positionArray.GetLength(1) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 1, z - 1].x / sizeZ,
positionArray[x, z - 1].z / sizeX);
if (x == positionArray.GetLength(0) - 1 && z == 0)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 1, z + 1].x / sizeZ,
positionArray[x - 1, z + 1].z / sizeX);
if (x == positionArray.GetLength(0) - 1 && z == heightmapData.GetLength(1) - 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 1, z - 1].x / sizeZ,
positionArray[x - 1, z - 1].z / sizeX);
if (ManagerSettings.addVerticesDown)
{
if (x == 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x + 2, z].x / sizeZ,
positionArray[x + 2, z].z / sizeX);
if (x == positionArray.GetLength(0) - 2)
normal = terrainData.GetInterpolatedNormal(positionArray[x - 2, z].x / sizeZ,
positionArray[x - 2, z].z / sizeX);
if (z == 1)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z + 2].x / sizeZ,
positionArray[x, z + 2].z / sizeX);
if (z == positionArray.GetLength(1) - 2)
normal = terrainData.GetInterpolatedNormal(positionArray[x, z - 2].x / sizeZ,
positionArray[x, z - 2].z / sizeX);
if (x == 1 && z == 1)
normal = terrainData.GetInterpolatedNormal(
positionArray[x + 2, z + 2].x / sizeZ,
positionArray[x + 2, z + 2].z / sizeX);
if (x == 1 && z == positionArray.GetLength(1) - 2)
normal = terrainData.GetInterpolatedNormal(
positionArray[x + 2, z - 2].x / sizeZ,
positionArray[x, z - 2].z / sizeX);
if (x == positionArray.GetLength(0) - 2 && z == 1)
normal = terrainData.GetInterpolatedNormal(
positionArray[x - 2, z + 2].x / sizeZ,
positionArray[x - 2, z + 2].z / sizeX);
if (x == positionArray.GetLength(0) - 2 && z == heightmapData.GetLength(1) - 2)
normal = terrainData.GetInterpolatedNormal(
positionArray[x - 2, z - 2].x / sizeZ,
positionArray[x - 2, z - 2].z / sizeX);
}
normals.Add(normal);
}
}
int rowPositionCount = positionArray.GetLength(1);
for (int i = 0; i < positionArray.GetLength(1) - 1; i++)
{
for (int j = 0; j < positionArray.GetLength(0) - 1; j++)
{
triangles.Add(j + i * rowPositionCount);
triangles.Add(j + (i + 1) * rowPositionCount);
triangles.Add((j + 1) + i * rowPositionCount);
triangles.Add((j + 1) + i * rowPositionCount);
triangles.Add(j + (i + 1) * rowPositionCount);
triangles.Add((j + 1) + (i + 1) * rowPositionCount);
}
}
meshTerrain.SetVertices(vertices);
meshTerrain.SetTriangles(triangles, 0);
meshTerrain.SetUVs(0, uvs);
meshTerrain.SetNormals(normals);
//meshTerrain.RecalculateNormals();
meshTerrain.RecalculateTangents();
meshTerrain.RecalculateBounds();
EditorUtility.DisplayProgressBar("Terrain mesh generation",
"Exporting terrain " + idTerrain + "/" + countTerrain + "\n Exporting textures",
(idTerrain + 0.5f) / countTerrain);
string terrainName = ManagerSettings.terrainPrefixName + terrain.gameObject.name;
GameObject meshGo = new GameObject(terrainName);
MeshFilter meshfilter = meshGo.AddComponent<MeshFilter>();
MeshRenderer meshRenderer = meshGo.AddComponent<MeshRenderer>();
Texture texture = null;
if (ManagerSettings.useTerrainNormal || ManagerSettings.useTextureNormal)
{
texture = ExportNormalMapHeightMap(terrain, terrainName);
}
Material terrainMaterial = new Material(Shader.Find("Standard"));
if (RenderPipelineManager.currentPipeline == null)
{
terrainMaterial = new Material(Shader.Find("Standard"));
Texture mask;
Texture basemap = TerrainMapsExporter.ExportBaseMap(terrain, terrainName, out mask);
terrainMaterial.SetTexture("_MainTex", basemap);
if (ManagerSettings.useTerrainNormal || ManagerSettings.useTextureNormal)
{
terrainMaterial.SetTexture("_BumpMap", texture);
}
terrainMaterial.SetInt("_SmoothnessTextureChannel", 1);
terrainMaterial.SetFloat("_Glossiness", .2f);
terrainMaterial.SetFloat("_GlossMapScale", 0.5f);
terrainMaterial.SetFloat("_Metallic", .0750f);
terrainMaterial.SetFloat("_Smoothness", 1f);
terrainMaterial.EnableKeyword("_NORMALMAP");
terrainMaterial.EnableKeyword("_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A");
}
else
{
var srpType = GraphicsSettings.defaultRenderPipeline.GetType().ToString();
if (srpType.Contains("HDRenderPipelineAsset"))
{
terrainMaterial = new Material(Shader.Find("HDRP/Lit"));
Texture mask = TerrainMapsExporterHdrp.ExportMask(terrain, terrainName);
Texture basemap = TerrainMapsExporterHdrp.ExportBaseMap(terrain, terrainName);
terrainMaterial.SetTexture("_BaseColorMap", basemap);
terrainMaterial.SetTexture("_MaskMap", mask);
terrainMaterial.SetFloat("_Metallic", 1f);
terrainMaterial.SetFloat("_MetallicRemapMin", 0f);
terrainMaterial.SetFloat("_MetallicRemapMax", 1f);
if (ManagerSettings.useTerrainNormal || ManagerSettings.useTextureNormal)
{
terrainMaterial.SetTexture("_NormalMap", texture);
}
terrainMaterial.EnableKeyword("_NORMALMAP");
terrainMaterial.EnableKeyword("_MASKMAP");
}
else if (srpType.Contains("UniversalRenderPipelineAsset") ||
srpType.Contains("LightweightRenderPipelineAsset"))
{
terrainMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit"));
Texture mask;
Texture basemap = TerrainMapsExporter.ExportBaseMap(terrain, terrainName, out mask);
Debug.Log(basemap);
terrainMaterial.SetTexture("_BaseMap", basemap);
terrainMaterial.SetTexture("_MainTex", basemap);
terrainMaterial.SetTexture("_MetallicGlossMap", mask);
terrainMaterial.SetTexture("_OcclusionMap", mask);
if (ManagerSettings.useTerrainNormal || ManagerSettings.useTextureNormal)
{
terrainMaterial.SetTexture("_BumpMap", texture);
}
if (ManagerSettings.useMaskSmoothnessURP)
terrainMaterial.SetInt("_SmoothnessTextureChannel", 0);
else
terrainMaterial.SetInt("_SmoothnessTextureChannel", 1);
terrainMaterial.SetFloat("_Glossiness", .2f);
terrainMaterial.SetFloat("_GlossMapScale", 0.5f);
terrainMaterial.SetFloat("_Metallic", .0750f);
terrainMaterial.EnableKeyword("_NORMALMAP");
#if NM_URP
terrainMaterial.SetFloat("_Smoothness", 1f);
UnityEditor.Rendering.Universal.ShaderGUI.LitGUI.SetMaterialKeywords(terrainMaterial);
//CoreUtils.SetKeyword(terrainMaterial, "_SPECULAR_SETUP", false);
//CoreUtils.SetKeyword(terrainMaterial, "_METALLICSPECGLOSSMAP", true);
#endif
}
}
MaterialPropertyBlock block = new MaterialPropertyBlock();
terrain.GetSplatMaterialPropertyBlock(block);
string name = ManagerSettings.terrainPath + "/M_" + terrainName + ".mat";
string path = "Assets/" + name;
AssetDatabase.CreateAsset(terrainMaterial, path);
terrainMaterial = AssetDatabase.LoadAssetAtPath<Material>(path);
meshRenderer.sharedMaterial = terrainMaterial;
meshGo.transform.position =
terrain.transform.position + new Vector3(0, ManagerSettings.yOffset, 0);
meshfilter.sharedMesh = meshTerrain;
if (exportTrees)
{
EditorUtility.DisplayProgressBar("Terrain mesh generation",
"Exporting terrain " + idTerrain + "/" + countTerrain + "\n Exporting trees",
(idTerrain + 0.75f) / countTerrain);
List<GameObject> trees = ExportTrees(terrain);
foreach (var treeBatcher in trees)
{
treeBatcher.transform.parent = meshGo.transform;
treeBatcher.transform.localPosition = Vector3.zero;
}
}
_meshFiltersToFix.Add(meshfilter);
AssetDatabase.Refresh();
idTerrain++;
}
EditorUtility.ClearProgressBar();
FixMeshNormals(_meshFiltersToFix);
string pathMesh;
foreach (var item in _meshFiltersToFix)
{
name = ManagerSettings.terrainPath + item.name + ".asset";
pathMesh = "Assets/" + name;
item.sharedMesh.UploadMeshData(true);
AssetDatabase.CreateAsset(item.sharedMesh, pathMesh);
}
AssetDatabase.SaveAssets();
Resources.UnloadUnusedAssets();
System.GC.Collect();
}
private void FixMeshNormals(List<MeshFilter> meshFiltersToFix)
{
MeshFilter meshFilter;
Mesh mesh;
Vector3[] vertices;
Vector3[] normals;
Bounds meshBounds;
Matrix4x4 matrix4;
MeshFilter meshFilterSecond;
Mesh meshSecond;
Vector3[] verticesSecond;
Vector3[] normalsSecond;
Bounds meshBoundsSecond;
Matrix4x4 matrix4Second;
Vector3 pos;
Vector4 posRob;
Vector3 pos2;
double editorTime = EditorApplication.timeSinceStartup;
try
{
bool cancel = false;
AssetDatabase.StartAssetEditing();
for (int i = 0; i < meshFiltersToFix.Count - 1; i++)
{
meshFilter = meshFiltersToFix[i];
mesh = meshFilter.sharedMesh;
vertices = mesh.vertices;
normals = mesh.normals;
meshBounds = meshFilter.GetComponent<Renderer>().bounds;
matrix4 = meshFilter.transform.localToWorldMatrix;
cancel = EditorUtility.DisplayCancelableProgressBar("Terrain mesh generation",
"Normals calculating " + i + "/" + meshFiltersToFix.Count,
(i) / (float)meshFiltersToFix.Count);
for (int j = i + 1; j < meshFiltersToFix.Count; j++)
{
meshFilterSecond = meshFiltersToFix[j];
meshBoundsSecond = meshFilterSecond.GetComponent<Renderer>().bounds;
Vector3 baseSize = meshBoundsSecond.size;
meshBoundsSecond.size = baseSize * 0.99f;
bool outer = meshBounds.Intersects(meshBoundsSecond);
meshBoundsSecond.size = baseSize * 1.01f;
bool inner = meshBounds.Intersects(meshBoundsSecond);
if (!outer && inner)
{
meshSecond = meshFilterSecond.sharedMesh;
verticesSecond = meshSecond.vertices;
normalsSecond = meshSecond.normals;
matrix4Second = meshFilterSecond.transform.localToWorldMatrix;
for (int v1 = 0; v1 < vertices.Length; v1++)
{
posRob = vertices[v1];
posRob.w = 1;
pos = matrix4 * posRob;
if (meshBoundsSecond.SqrDistance(pos) < 1)
{
for (int v2 = 0; v2 < verticesSecond.Length; v2++)
{
if (v2 % 1000 == 0)
cancel = EditorUtility.DisplayCancelableProgressBar(
"Terrain mesh generation",
"Normals calculating " + i + "/" + meshFiltersToFix.Count,
(i + v1 / (float)vertices.Length) / meshFiltersToFix.Count);
posRob = verticesSecond[v2];
posRob.w = 1;
pos2 = matrix4Second * posRob;
if ((pos - pos2).sqrMagnitude < 0.01f)
{
normalsSecond[v2] = normals[v1];
}
if (cancel)
break;
}
meshSecond.normals = normalsSecond;
}
if (cancel)
break;
}
}
if (cancel)
break;
}
mesh.RecalculateTangents();
if (cancel)
break;
}
EditorUtility.ClearProgressBar();
}
finally
{
AssetDatabase.StopAssetEditing();
}
}
private Texture2D ExportNormalMapHeightMap(Terrain terrain, string terrainName)
{
int heightMapResolution = terrain.terrainData.heightmapResolution;
float[,] rawHeights = terrain.terrainData.GetHeights(0, 0, heightMapResolution, heightMapResolution);
int myIndex = 0;
float min = float.MaxValue;
float max = float.MinValue;
float height = 0;
for (int y = 0; y < heightMapResolution; y++)
{
for (int x = 0; x < heightMapResolution; x++)
{
rawHeights[y, x] = Mathf.Clamp01(rawHeights[y, x]);
height = rawHeights[y, x];
if (height > max)
max = height;
if (height < min)
min = height;
myIndex++;
}
}
if (max > MaxHeightTerrain)
MaxHeightTerrain = max;
if (min < MinHeightTerrain)
MinHeightTerrain = min;
for (int y = 0; y < heightMapResolution; y++)
{
for (int x = 0; x < heightMapResolution; x++)
{
//Debug.Log($"{rawHeights[y, x]} {minHeightTerrain} {maxHeightTerrain}");
rawHeights[y, x] = 1 - (rawHeights[y, x] - MinHeightTerrain) /
(MaxHeightTerrain - MinHeightTerrain);
}
}
string name = ManagerSettings.terrainPath + "/T_" + terrainName + "_N.png";
string path = Application.dataPath + name;
var extension = Path.GetExtension(path);
Texture2D normalTerrain = WorldStreamer2.TerrainManagerUtils.GetNormalMap(rawHeights, 20 * ManagerSettings.terrainNormalStrength);
Texture2D normalTextures = null;
if (RenderPipelineManager.currentPipeline == null)
{
normalTextures = TerrainMapsExporter.ExportTextureNormalmap(terrain, terrainName);
}
else
{
var srpType = GraphicsSettings.defaultRenderPipeline.GetType().ToString();
if (srpType.Contains("HDRenderPipelineAsset"))
{
normalTextures = TerrainMapsExporterHdrp.ExportTextureNormalmap(terrain);
}
else if (srpType.Contains("UniversalRenderPipelineAsset") ||
srpType.Contains("LightweightRenderPipelineAsset"))
{
normalTextures = TerrainMapsExporter.ExportTextureNormalmap(terrain, terrainName);
}
}
Texture2D normalFinal = new Texture2D(normalTextures.width, normalTextures.height);
float resize = normalTextures.width / (float)normalTerrain.width;
Vector4 colorNormalTexture = Vector4.zero;
Vector2 blueCount;
for (int x = 0; x < normalTextures.width; x++)
{
for (int y = 0; y < normalTextures.height; y++)
{
if (ManagerSettings.useTerrainNormal && ManagerSettings.useTextureNormal)
{
Vector4 colorNormal =
normalTerrain.GetPixel(Mathf.FloorToInt(x / resize), Mathf.FloorToInt(y / resize));
colorNormalTexture = normalTextures.GetPixel(x, y);
colorNormalTexture = WorldStreamer2.TerrainManagerUtils.LinearLightAddSub(colorNormalTexture, colorNormal);
//colorNormalTexture.z = colorNormal.z;
//colorNormalTexture.z = Mathf.Sqrt(1 - Mathf.Clamp01(colorNormalTexture.x * colorNormalTexture.x + colorNormalTexture.y * colorNormalTexture.y)) * 0.5f + 0.5f;
blueCount = new Vector2(colorNormalTexture.x, colorNormalTexture.y);
blueCount = blueCount * 2 - Vector2.one;
colorNormalTexture.z = Mathf.Clamp01(Vector2.Dot(blueCount, blueCount));
colorNormalTexture.z = Mathf.Sqrt(Mathf.Sqrt(1 - colorNormalTexture.z));
//colorNormalTexture.Normalize();
colorNormalTexture.w = 1;
}
if (!ManagerSettings.useTerrainNormal && ManagerSettings.useTextureNormal)
{
colorNormalTexture = normalTextures.GetPixel(x, y);
blueCount = new Vector2(colorNormalTexture.x, colorNormalTexture.y);
blueCount = blueCount * 2 - Vector2.one;
colorNormalTexture.z = Mathf.Clamp01(Vector2.Dot(blueCount, blueCount));
colorNormalTexture.z = Mathf.Sqrt(Mathf.Sqrt(1 - colorNormalTexture.z));
//colorNormalTexture = LinearLightAddSub(colorNormalTexture, colorNormalTexture);
}
if (ManagerSettings.useTerrainNormal && !ManagerSettings.useTextureNormal)
{
colorNormalTexture =
normalTerrain.GetPixel(Mathf.FloorToInt(x / resize), Mathf.FloorToInt(y / resize));
blueCount = new Vector2(colorNormalTexture.x, colorNormalTexture.y);
blueCount = blueCount * 2 - Vector2.one;
colorNormalTexture.z = Mathf.Clamp01(Vector2.Dot(blueCount, blueCount));
colorNormalTexture.z = Mathf.Sqrt(Mathf.Sqrt(1 - colorNormalTexture.z));
}
normalFinal.SetPixel(x, y, colorNormalTexture);
}
}
byte[]
pngData = normalFinal
.EncodeToPNG(); // GetNormalMap(rawHeights, 20 * terrainManagerSettings.terrainNormalStrength).EncodeToPNG();
//byte[] pngData = normalTextures.EncodeToPNG();// GetNormalMap(rawHeights, 20 * terrainManagerSettings.terrainNormalStrength).EncodeToPNG();
File.WriteAllBytes(path, pngData);
//Debug.Log(path);
AssetDatabase.Refresh();
TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath("Assets/" + name);
importer.textureType = TextureImporterType.NormalMap;
importer.wrapMode = TextureWrapMode.Clamp;
importer.mipmapFilter = TextureImporterMipFilter.BoxFilter;
importer.streamingMipmaps = true;
importer.anisoLevel = 8;
importer.SaveAndReimport();
//importer.mipMapsPreserveCoverage = true;
//importer.alphaTestReferenceValue = 0.85f;
//importer.convertToNormalmap = true;
//importer.normalmapFilter = TextureImporterNormalFilter.Sobel;
//importer.heightmapScale = 0.2f;
AssetDatabase.ImportAsset("Assets/" + name, ImportAssetOptions.ForceUpdate);
AssetDatabase.Refresh();
return (Texture2D)AssetDatabase.LoadAssetAtPath("assets" + name, typeof(Texture2D));
}
private void SetInstancing(bool instanced, bool allTerrains = false)
{
Terrain[] terrains = Selection.GetFiltered<Terrain>(SelectionMode.TopLevel);
if (allTerrains)
terrains = Terrain.activeTerrains;
foreach (var terrain in terrains)
{
terrain.drawInstanced = instanced;
}
}
private void ExportTreesForSelectedTerrain(bool allTerrains = false)
{
Terrain[] terrains = Selection.GetFiltered<Terrain>(SelectionMode.TopLevel);
if (allTerrains)
terrains = Terrain.activeTerrains;
foreach (var terrain in terrains)
{
ExportTrees(terrain);
}
}
private List<GameObject> ExportTrees(Terrain terrain)
{
if (!Directory.Exists("Assets/" + ManagerSettings.terrainPath))
{
Directory.CreateDirectory("Assets/" + ManagerSettings.terrainPath);
}
if (terrain == null)
return null;
List<GameObject> treePrototypes = new();
foreach (var item in ManagerSettings.terrainTrees)
{
if (item.active)
treePrototypes.Add(item.tree);
}
List<GameObject> treesBatchList = new();
bool backFace = Physics.queriesHitBackfaces;
Physics.queriesHitBackfaces = true;
TerrainData data = terrain.terrainData;
float width = data.size.x;
float height = data.size.z;
float y = data.size.y;
GameObject[] trees = new GameObject[data.treePrototypes.Length];
string pathMesh;
// Debug.Log("Exporting " + data.treeInstances.Length + " trees");
List<TreeInstance> treeInstanceNew = new();
for (int tID = 0; tID < data.treePrototypes.Length; tID++)
{
if (!treePrototypes.Contains(data.treePrototypes[tID].prefab))
continue;
//Debug.Log(tID);
List<MeshFilter> meshFilters = new();
foreach (TreeInstance tree in data.treeInstances)
{
if (tree.prototypeIndex == tID)
{
Vector3 position = new Vector3(tree.position.x * width, tree.position.y * y,
tree.position.z * height); // + _terrain.transform.position;
bool treeInRange = true;
if (treeInRange)
{
//Debug.Log(treeInRange);
if (trees[tree.prototypeIndex] == null)
{
GameObject treePrefab = data.treePrototypes[tree.prototypeIndex].prefab;
LODGroup lOdGroup = treePrefab.GetComponent<LODGroup>();
if (lOdGroup != null)
{
LOD[] lods = lOdGroup.GetLODs();
trees[tree.prototypeIndex] = lods[lods.Length - 1].renderers[0].gameObject;
}
else
{
trees[tree.prototypeIndex] = treePrefab;
}
}
if (trees[tID].GetComponent<MeshRenderer>())
{
GameObject treeG = GameObject.Instantiate(trees[tree.prototypeIndex]);
//Debug.Log(treeG.name);
treeG.transform.position = position;
treeG.transform.rotation = Quaternion.Euler(0, tree.rotation, 0);
treeG.transform.localScale =
new Vector3(tree.widthScale, tree.heightScale, tree.widthScale);
meshFilters.Add(treeG.GetComponent<MeshFilter>());
}
}
}
}
if (trees[tID] != null && trees[tID].GetComponent<MeshRenderer>() != null)
{
//List<Matrix4x4> matrices = new List<Matrix4x4>();
Material treeMaterial = Instantiate(trees[tID].GetComponent<MeshRenderer>().sharedMaterial);
string shaderName = treeMaterial.shader.name;
if (shaderName.Contains("NatureManufacture") && shaderName.Contains("Cross"))
{
// Debug.Log(RenderPipelineManager.currentPipeline);
if (RenderPipelineManager.currentPipeline == null)
{
if (shaderName.Contains("Snow"))
treeMaterial.shader = Shader.Find("NatureManufacture Shaders/Trees/Cross Snow WS");
else
treeMaterial.shader = Shader.Find("NatureManufacture Shaders/Trees/Cross WS");
}
else
{
var srpType = GraphicsSettings.defaultRenderPipeline.GetType().ToString();
if (srpType.Contains("HDRenderPipelineAsset"))
{
if (shaderName.Contains("Snow"))
treeMaterial.shader = Shader.Find("NatureManufacture/HDRP/Foliage/Cross Snow WS");
else if (shaderName.Contains(" 2"))
treeMaterial.shader = Shader.Find("NatureManufacture/HDRP/Foliage/Cross 2 WS");
else
treeMaterial.shader = Shader.Find("NatureManufacture/HDRP/Foliage/Cross WS");
}
else if (srpType.Contains("UniversalRenderPipelineAsset") ||
srpType.Contains("LightweightRenderPipelineAsset"))
{
if (shaderName.Contains("Snow"))
treeMaterial.shader = Shader.Find("NatureManufacture/URP/Foliage/Cross Snow WS");
else if (shaderName.Contains(" 2"))
treeMaterial.shader = Shader.Find("NatureManufacture/URP/Foliage/Cross 2 WS");
else
treeMaterial.shader = Shader.Find("NatureManufacture/URP/Foliage/Cross WS");
}
}
}
name = ManagerSettings.terrainPath + data.treePrototypes[tID].prefab.name + "_" +
treeMaterial.name + ".mat";
pathMesh = "Assets/" + name;
Material mat = AssetDatabase.LoadAssetAtPath<Material>(pathMesh);
if (mat == null)
AssetDatabase.CreateAsset(treeMaterial, pathMesh);
//Debug.Log(parent.name);
List<CombineInstance> combine = new();
int id = 0;
int i = 0;
int meshVertsCount = 0;
while (i < meshFilters.Count)
{
CombineInstance combineInstance = new CombineInstance();
combineInstance.mesh = meshFilters[i].sharedMesh;
meshVertsCount += combineInstance.mesh.vertexCount;
combineInstance.transform = meshFilters[i].transform.localToWorldMatrix;
combine.Add(combineInstance);
//matrices.Add(meshFilters[i].transform.localToWorldMatrix);
i++;
if (meshVertsCount > ManagerSettings.trainglesTreesMax)
{
GameObject parent = new GameObject(terrain.name + "_Tree_Batch_" +
data.treePrototypes[tID].prefab.name + "_" + id);
parent.transform.position = terrain.transform.position;
treesBatchList.Add(parent);
MeshFilter filter = parent.AddComponent<MeshFilter>();
Mesh combinedAllMesh = new Mesh();
if (meshVertsCount > 65000)
combinedAllMesh.indexFormat = IndexFormat.UInt32;
combinedAllMesh.CombineMeshes(combine.ToArray(), true, true);
combinedAllMesh.UploadMeshData(true);
filter.sharedMesh = combinedAllMesh;
name = ManagerSettings.terrainPath + filter.name + "_" + id + ".asset";
pathMesh = "Assets/" + name;
AssetDatabase.CreateAsset(filter.sharedMesh, pathMesh);
MeshRenderer meshRenderer = parent.AddComponent<MeshRenderer>();
meshRenderer.sharedMaterial = treeMaterial;
Undo.RegisterCreatedObjectUndo(parent, "Create object trees");
id++;
combine.Clear();
meshVertsCount = 0;
}
}
if (meshVertsCount > 0)
{
GameObject parent = new GameObject(terrain.name + "_Tree_Batch_" +
data.treePrototypes[tID].prefab.name + "_" + id);
parent.transform.position = terrain.transform.position;
treesBatchList.Add(parent);
MeshFilter filter = parent.AddComponent<MeshFilter>();
Mesh combinedAllMesh = new Mesh();
if (meshVertsCount > 65000)
combinedAllMesh.indexFormat = IndexFormat.UInt32;
combinedAllMesh.CombineMeshes(combine.ToArray(), true, true);
combinedAllMesh.UploadMeshData(true);
filter.sharedMesh = combinedAllMesh;
name = ManagerSettings.terrainPath + filter.name + "_" + id + ".asset";
pathMesh = "Assets/" + name;
AssetDatabase.CreateAsset(combinedAllMesh, pathMesh);
MeshRenderer meshRenderer = parent.AddComponent<MeshRenderer>();
// Debug.Log(trees[tID].name + " " + trees[tID].GetComponent<MeshRenderer>().sharedMaterial.name);
meshRenderer.sharedMaterial = treeMaterial;
Undo.RegisterCreatedObjectUndo(parent, "Create object trees");
}
i = 0;
while (i < meshFilters.Count)
{
DestroyImmediate(meshFilters[i].gameObject);
i++;
}
}
else if (trees[tID] != null)
{
Debug.Log($"Tree {trees[tID].name} is incompatible with tree exporter");
}
}
AssetDatabase.SaveAssets();
Physics.queriesHitBackfaces = backFace;
return treesBatchList;
}
}
}