using System; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Rendering; namespace Artngame.SKYMASTER.PlanetCreator { public class PlanetCreator : MonoBehaviour { public Transform Sun; public int ChunkSegments = 32; public float SphereRadius = 10f; public float ChunkSize = 128f; public Material mat; public float TerrainFreq = 0.006f; public float TerrainGain = 0.5f; public float Terrainlacunarity = 2f; public float TerrainScale = 20f; public float TerrainBumpScale = 20f; public Camera MainCamera; private List rootChunks; public int MaxLodLevel = 7; public int LodColliderStart = 5; public float MaxError = 1f; public float MaxSplitsPerSecond = 20f; private Mesh BasicPlane; private Stack MeshPool; private Queue SplitPool; public int PoolStartPopulation; public ComputeBuffer VertexComputeBuffer; public ComputeBuffer NormalComputeBuffer; public ComputeBuffer PlanetMapCreatorBuffer; public ComputeShader VertexComputeShader; public ComputeShader PlanetMapGeneratorShader; private ImprovedPerlinNoise m_perlin; private readonly List uvsTmp = new List(); private readonly List vertexTmp = new List(); private readonly List normalTmp = new List(); private float lastSplitTime; private static readonly int Frequency = Shader.PropertyToID("_Frequency"); private static readonly int Lacunarity = Shader.PropertyToID("_Lacunarity"); private static readonly int Gain = Shader.PropertyToID("_Gain"); private static readonly int PlanetRadius = Shader.PropertyToID("_PlanetRadius"); private static readonly int PermTable2D = Shader.PropertyToID("_PermTable2D"); private static readonly int Gradient3D = Shader.PropertyToID("_Gradient3D"); public float K { get; set; } public int MeshPoolSize => MeshPool.Count; public int SplitPoolSize => SplitPool.Count; public void Start() { Precompute(); MeshPool = new Stack(); SplitPool = new Queue(); BasicPlane = GeneratePremadePlane(ChunkSegments); rootChunks = new List(); CreateRoot(Vector3.left, 0f); CreateRoot(Vector3.left, 90f); CreateRoot(Vector3.left, 180f); CreateRoot(Vector3.left, 270f); CreateRoot(Vector3.forward, 90f); CreateRoot(Vector3.forward, 270f); for (int i = 0; i < PoolStartPopulation; i++) { AddToChunkPool(CreateBasicChunkObject()); } } private void CreateTemperatureMap() { for (int i = 0; i < 4; i++) { Texture2D texture2D = new Texture2D(1024, 1024, TextureFormat.RGBA32, mipChain: false); Vector3[] array = new Vector3[1048576]; Color32[] array2 = new Color32[1048576]; int num = 0; for (int j = 0; j < 1024; j++) { for (int k = 0; k < 1024; k++) { array[num++] = new Vector3(j, k, 0f); } } PlanetMapCreatorBuffer.SetData(array); PlanetMapGeneratorShader.Dispatch(0, 1048576, 1, 1); PlanetMapCreatorBuffer.GetData(array); for (int l = 0; l < 1048576; l++) { array2[l] = new Color32((byte)(array[l].x * 255f), 0, 0, byte.MaxValue); } texture2D.SetPixels32(array2); using FileStream output = new FileStream("D:/Test/" + i + ".png", FileMode.OpenOrCreate); new BinaryWriter(output).Write(texture2D.EncodeToPNG()); } } public void Precompute() { K = (float)Screen.width / (2f * Mathf.Tan(MathF.PI * 13f / 72f)); m_perlin = new ImprovedPerlinNoise(0); m_perlin.LoadResourcesFor3DNoise(); Texture2D permutationTable2D = m_perlin.GetPermutationTable2D(); Texture2D gradient3D = m_perlin.GetGradient3D(); VertexComputeShader.SetTexture(0, "_PermTable2D", permutationTable2D); VertexComputeShader.SetTexture(0, "_Gradient3D", gradient3D); mat.SetTexture(PermTable2D, permutationTable2D); mat.SetTexture(Gradient3D, gradient3D); VertexComputeBuffer = new ComputeBuffer((ChunkSegments + 2) * (ChunkSegments + 2), 12); NormalComputeBuffer = new ComputeBuffer((ChunkSegments + 2) * (ChunkSegments + 2), 12); PlanetMapCreatorBuffer = new ComputeBuffer(1048576, 32); VertexComputeShader.SetBuffer(0, "vertexBuffer", VertexComputeBuffer); VertexComputeShader.SetBuffer(0, "normalBuffer", NormalComputeBuffer); PlanetMapGeneratorShader.SetBuffer(0, "Output", PlanetMapCreatorBuffer); PlanetMapGeneratorShader.SetTexture(0, "_PermTable2D", permutationTable2D); PlanetMapGeneratorShader.SetTexture(0, "_Gradient3D", gradient3D); UpdateNoise(); } public void CreateRoot(Vector3 rotationDir, float angle) { PlanetChunkProperties planetChunkProperties = CreateChunkProperties(null, Quaternion.AngleAxis(angle, rotationDir), 1f, 0, Vector2.zero); GetFreeChunkObject(planetChunkProperties); rootChunks.Add(planetChunkProperties); } public void CreateChunk(PlanetChunkProperties parent, Vector2 min, int index) { parent.Chunks[index] = CreateChunkProperties(parent, parent.Rotation, parent.Size / 2f, parent.LODLevel + 1, min); parent.Chunks[index].ChunkObject = GetFreeChunkObject(parent.Chunks[index]); parent.Chunks[index].Bounds = parent.Chunks[index].ChunkObject.GetComponent().bounds; } private PlanetChunkObject CreateBasicChunkObject() { PlanetChunkObject component = new GameObject("Chunk", typeof(MeshFilter), typeof(MeshCollider), typeof(MeshRenderer), typeof(PlanetChunkObject)).GetComponent(); component.Filter = component.GetComponent(); component.Collider = component.GetComponent(); component.Renderer = component.GetComponent(); component.Renderer.sharedMaterial = mat; return component; } public PlanetChunkObject UpdateChunkObject(PlanetChunkProperties chunkProperties, PlanetChunkObject chunk) { chunk.transform.localPosition = Vector3.zero; chunk.Properties = chunkProperties; chunkProperties.ChunkObject = chunk; UpdateChunkMesh(chunk); return chunk; } public PlanetChunkProperties GetNearestChunkProperties(Vector3 point) { if (rootChunks == null) { return null; } float num = float.PositiveInfinity; PlanetChunkProperties parent = null; foreach (PlanetChunkProperties rootChunk in rootChunks) { float sqrMagnitude = (rootChunk.Bounds.center - point).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; parent = rootChunk; } } return GetNearestChunkProperties(parent, point); } private PlanetChunkProperties GetNearestChunkProperties(PlanetChunkProperties parent, Vector3 point) { if (parent.Chunks == null) { return parent; } float num = float.PositiveInfinity; PlanetChunkProperties parent2 = null; PlanetChunkProperties[] chunks = parent.Chunks; foreach (PlanetChunkProperties planetChunkProperties in chunks) { float sqrMagnitude = (planetChunkProperties.Bounds.center - point).sqrMagnitude; if (sqrMagnitude < num) { parent2 = planetChunkProperties; num = sqrMagnitude; } } return GetNearestChunkProperties(parent2, point); } public PlanetChunkObject GetChunkObjectFromPool() { if (MeshPool.Count > 0) { return MeshPool.Pop(); } return null; } public PlanetChunkObject GetFreeChunkObject(PlanetChunkProperties chunkProperties) { PlanetChunkObject planetChunkObject = GetChunkObjectFromPool(); if (planetChunkObject == null) { planetChunkObject = CreateBasicChunkObject(); } UpdateChunkObject(chunkProperties, planetChunkObject); return planetChunkObject; } public void UpdateChunkMesh(PlanetChunkObject chunk) { if (chunk.Filter.sharedMesh == null) { chunk.Filter.sharedMesh = CopyMesh(BasicPlane); } CaluclateVertex(chunk); } private void UpdateNoise() { VertexComputeShader.SetFloat(Frequency, TerrainFreq); VertexComputeShader.SetFloat(Lacunarity, Terrainlacunarity); VertexComputeShader.SetFloat(Gain, TerrainGain); mat.SetFloat(Frequency, TerrainFreq); mat.SetFloat(Lacunarity, Terrainlacunarity); mat.SetFloat(Gain, TerrainGain); mat.SetFloat(PlanetRadius, SphereRadius); } public void AddToChunkPool(PlanetChunkObject chunk) { MeshPool.Push(chunk); } public PlanetChunkProperties CreateChunkProperties(PlanetChunkProperties parent, Quaternion rotation, float Size, int LodLevel, Vector2 min) { PlanetChunkProperties planetChunkProperties = new PlanetChunkProperties(); planetChunkProperties.Rotation = rotation; planetChunkProperties.Parent = parent; planetChunkProperties.LODLevel = LodLevel; planetChunkProperties.Size = Size; planetChunkProperties.min = min; planetChunkProperties.Center = (planetChunkProperties.Rotation * new Vector3(planetChunkProperties.Middle.x - 0.5f, 1f, planetChunkProperties.Middle.y - 0.5f)).normalized * SphereRadius; planetChunkProperties.maxGeoError = Mathf.Pow(2f, MaxLodLevel - planetChunkProperties.LODLevel); return planetChunkProperties; } public void CaluclateVertex(PlanetChunkObject chunk) { int num = ChunkSegments + 2; int num2 = ChunkSegments + 2; int num3 = num * num2; vertexTmp.Clear(); normalTmp.Clear(); uvsTmp.Clear(); float num4 = chunk.Properties.Size / (float)ChunkSegments; for (float num5 = 0f; num5 < (float)num2; num5 += 1f) { for (float num6 = 0f; num6 < (float)num; num6 += 1f) { float x = chunk.Properties.BottomLeft.x + num6 * num4 - 0.5f; float y = chunk.Properties.BottomLeft.y + num5 * num4 - 0.5f; vertexTmp.Add(GeVertex(chunk.Properties.Rotation, SphereRadius, x, y)); uvsTmp.Add(chunk.Properties.BottomLeft + new Vector2(num6 * num4, num5 * num4)); } } VertexComputeBuffer.SetData(vertexTmp); NormalComputeBuffer.SetData(normalTmp); VertexComputeShader.SetFloat("Scale", num4); VertexComputeShader.SetFloat("TerrainScale", TerrainScale); VertexComputeShader.SetFloat("TerrainBumpScale", TerrainBumpScale); VertexComputeShader.Dispatch(0, num3 / 16, 1, 1); AsyncGPUReadback.Request(VertexComputeBuffer, chunk.ApplyVertexData); AsyncGPUReadback.Request(NormalComputeBuffer, chunk.ApplyNormalData); chunk.MarkCalculatingVertexData(); chunk.name = "Recycled Mesh"; chunk.Filter.sharedMesh.SetUVs(0, uvsTmp); chunk.SetVisible(visible: true); if (chunk.Properties != null) { if (chunk.Properties.LODLevel >= LodColliderStart) { chunk.Collider.sharedMesh = null; chunk.Collider.sharedMesh = chunk.Filter.sharedMesh; chunk.Collider.enabled = true; } else { chunk.Collider.enabled = false; } chunk.Properties.Active = true; } } public Vector3 GeVertex(Quaternion rotation, float SphereRadius, float X, float Y) { Vector3 vector = rotation * new Vector3(X, 0.5f, Y); return ToSphere(vector) * SphereRadius; } public static void calculateMeshTangents(Mesh mesh) { int[] triangles = mesh.triangles; Vector3[] vertices = mesh.vertices; Vector2[] uv = mesh.uv; Vector3[] normals = mesh.normals; int num = triangles.Length; int num2 = vertices.Length; Vector3[] array = new Vector3[num2]; Vector3[] array2 = new Vector3[num2]; Vector4[] array3 = new Vector4[num2]; for (long num3 = 0L; num3 < num; num3 += 3) { long num4 = triangles[num3]; long num5 = triangles[num3 + 1]; long num6 = triangles[num3 + 2]; Vector3 vector = vertices[num4]; Vector3 vector2 = vertices[num5]; Vector3 vector3 = vertices[num6]; Vector2 vector4 = uv[num4]; Vector2 vector5 = uv[num5]; Vector2 vector6 = uv[num6]; float num7 = vector2.x - vector.x; float num8 = vector3.x - vector.x; float num9 = vector2.y - vector.y; float num10 = vector3.y - vector.y; float num11 = vector2.z - vector.z; float num12 = vector3.z - vector.z; float num13 = vector5.x - vector4.x; float num14 = vector6.x - vector4.x; float num15 = vector5.y - vector4.y; float num16 = vector6.y - vector4.y; float num17 = 1f / (num13 * num16 - num14 * num15); Vector3 vector7 = new Vector3((num16 * num7 - num15 * num8) * num17, (num16 * num9 - num15 * num10) * num17, (num16 * num11 - num15 * num12) * num17); Vector3 vector8 = new Vector3((num13 * num8 - num14 * num7) * num17, (num13 * num10 - num14 * num9) * num17, (num13 * num12 - num14 * num11) * num17); array[num4] += vector7; array[num5] += vector7; array[num6] += vector7; array2[num4] += vector8; array2[num5] += vector8; array2[num6] += vector8; } for (long num18 = 0L; num18 < num2; num18++) { Vector3 normal = normals[num18]; Vector3 tangent = array[num18]; Vector3.OrthoNormalize(ref normal, ref tangent); array3[num18].x = tangent.x; array3[num18].y = tangent.y; array3[num18].z = tangent.z; array3[num18].w = ((Vector3.Dot(Vector3.Cross(normal, tangent), array2[num18]) < 0f) ? (-1f) : 1f); } mesh.tangents = array3; } public static Mesh CopyMesh(Mesh mesh) { return UnityEngine.Object.Instantiate(mesh); } public Mesh GeneratePremadePlane(int Segments) { Mesh mesh = new Mesh(); int num = Segments + 2; int num2 = Segments * Segments * 6; int num3 = num * num; int num4 = 0; float num5 = 1f / (float)Segments; float num6 = 1f / (float)Segments; float num7 = 1f / (float)Segments; float num8 = 1f / (float)Segments; int[] array = new int[num2]; Vector2[] array2 = new Vector2[num3]; Vector3[] array3 = new Vector3[num3]; for (float num9 = 0f; num9 < (float)num; num9 += 1f) { for (float num10 = 0f; num10 < (float)num; num10 += 1f) { float x = num10 * num7 - 0.5f; float z = num9 * num8 - 0.5f; array2[num4] = new Vector2(num10 * num5, num9 * num6); array3[num4++] = new Vector3(x, 1f, z); } } num4 = 0; for (int i = 0; i < Segments; i++) { for (int j = 0; j < Segments; j++) { array[num4] = i * num + j; array[num4 + 1] = (i + 1) * num + j; array[num4 + 2] = i * num + j + 1; array[num4 + 3] = (i + 1) * num + j; array[num4 + 4] = (i + 1) * num + j + 1; array[num4 + 5] = i * num + j + 1; num4 += 6; } } mesh.vertices = array3; mesh.triangles = array; mesh.uv = array2; mesh.RecalculateNormals(); mesh.RecalculateBounds(); calculateMeshTangents(mesh); calculateMeshTangents(mesh); return mesh; } public static Bounds CopyBounds(Bounds bounds) { return new Bounds { min = bounds.min, max = bounds.max, size = bounds.size, center = bounds.center }; } private Vector3 ToSphere(Vector3 vector) { return vector.normalized; } private Vector3 SphericalPos(Vector3 pos, float radius) { return pos.normalized * radius; } private void Update() { ManageChunks(); UpdateNoise(); if (SplitPool.Count > 0 && lastSplitTime - Time.time <= 0f) { PlanetChunkProperties chunk = SplitPool.Dequeue(); Split(chunk); lastSplitTime = Time.time + 1f / MaxSplitsPerSecond; } } public bool NeedsSplit(PlanetChunkProperties chunk) { return chunk.maxGeoError / Mathf.Sqrt(chunk.ChunkObject.Renderer.bounds.SqrDistance(MainCamera.transform.position)) * K > MaxError; } public void AddToSplitPool(PlanetChunkProperties chunk) { if (!SplitPool.Contains(chunk)) { SplitPool.Enqueue(chunk); } } private void ManageChunks() { foreach (PlanetChunkProperties rootChunk in rootChunks) { ManageRecursive(rootChunk); } } public void ManageRecursive(PlanetChunkProperties chunk) { if (NeedsSplit(chunk) && chunk.LODLevel < MaxLodLevel) { if (chunk.isSplit) { bool flag = false; PlanetChunkProperties[] children = chunk.Children; foreach (PlanetChunkProperties planetChunkProperties in children) { flag |= planetChunkProperties.ChunkObject.IsCalculating; ManageRecursive(planetChunkProperties); } if (!flag) { HideChunk(chunk); } chunk.isSpliting = false; } else { AddToSplitPool(chunk); } } else { Merge(chunk); } } public void Split(PlanetChunkProperties chunk) { if (!(chunk.ChunkObject != null) || !NeedsSplit(chunk)) { return; } if (chunk.Children == null) { chunk.Children = new PlanetChunkProperties[4]; CreateChunk(chunk, chunk.min, 0); CreateChunk(chunk, chunk.MiddleLeft, 1); CreateChunk(chunk, chunk.BottomMiddle, 2); CreateChunk(chunk, chunk.Middle, 3); } else { PlanetChunkProperties[] children = chunk.Children; foreach (PlanetChunkProperties chunk2 in children) { EnableChunk(chunk2); } } chunk.isMerged = false; } public void Merge(PlanetChunkProperties chunk) { if (chunk.isMerged) { return; } if (chunk.Children != null) { PlanetChunkProperties[] children = chunk.Children; foreach (PlanetChunkProperties child in children) { MergeChildren(chunk, child); } } EnableChunk(chunk); chunk.isMerged = true; } private void MergeChildren(PlanetChunkProperties parent, PlanetChunkProperties child) { if (!child.isMerged && child.Children != null) { PlanetChunkProperties[] children = child.Children; foreach (PlanetChunkProperties child2 in children) { MergeChildren(parent, child2); } } DisableChunk(child); parent.isMerged = true; } public void EnableChunk(PlanetChunkProperties chunk) { if (chunk.ChunkObject == null) { chunk.ChunkObject = GetFreeChunkObject(chunk); } chunk.ChunkObject.SetVisible(visible: true); } public void HideChunk(PlanetChunkProperties chunk) { if (chunk.ChunkObject != null && chunk.ChunkObject.IsVisible) { chunk.ChunkObject.SetVisible(visible: false); } } public void DisableChunk(PlanetChunkProperties chunk) { if (chunk.ChunkObject.Collider != null) { chunk.ChunkObject.Collider.enabled = false; } HideChunk(chunk); AddToChunkPool(chunk.ChunkObject); chunk.ChunkObject = null; chunk.Active = false; } private void OnDisable() { VertexComputeBuffer.Dispose(); NormalComputeBuffer.Dispose(); PlanetMapCreatorBuffer.Dispose(); } } }