ChunkTerrainGenerator.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using KairoEngine.Core;
  5. using KairoEngine.Chunks;
  6. using KairoEngine.NoiseUtilities;
  7. using KairoEngine.UniverseGenerator;
  8. using Utilities = KairoEngine.Utility.Utilities;
  9. using Sirenix.OdinInspector;
  10. using UniRx;
  11. namespace KairoEngine.TerrainEngine
  12. {
  13. [HideMonoScript]
  14. public class ChunkTerrainGenerator : MonoBehaviour
  15. {
  16. public MarchingCubes4 marchingCubes3;
  17. public MarchingCubes2 marchingCubes2;
  18. public Vector3 voxelSize = new Vector3(1f, 1f, 1f);
  19. public Vector3Int chunkSize = new Vector3Int(16, 16, 16);
  20. public Vector3Int chunkLimit = new Vector3Int(2, 1, 2);
  21. public int dataProcessLimit = 4;
  22. public int parallelMeshJobs = 4;
  23. public Dictionary<Vector3, GameObject> terrainMeshes = new Dictionary<Vector3, GameObject>();
  24. //public float noiseMultiplier = 1.5f;
  25. [InlineEditor(InlineEditorObjectFieldModes.Foldout)] public PlanetTemplate planetTemplate;
  26. public ChunkSystem<BlockBase> chunkSystem;
  27. private List<Vector3> chunkDataBuffer = new List<Vector3>(); // Store list of chunks for populating with data
  28. private List<Vector3> chunkMeshBuffer = new List<Vector3>(); // Store list of chunks for generating the mesh
  29. private bool initialized = false;
  30. public class VoxelUpdateData
  31. {
  32. public Vector3 position;
  33. public BlockBase block;
  34. public VoxelUpdateData(Vector3 position, BlockBase block)
  35. {
  36. this.position = position;
  37. this.block = block;
  38. }
  39. }
  40. private Dictionary<Vector3,List<VoxelUpdateData>> chunkUpdates = new Dictionary<Vector3, List<VoxelUpdateData>>();
  41. public void Initialize ()
  42. {
  43. chunkSystem = new ChunkSystem<BlockBase>(chunkSize,voxelSize, true);
  44. initialized = true;
  45. }
  46. public bool IsInitialized() => initialized;
  47. [Button("Generate World"), ButtonGroup("Buttons")]
  48. void GenerateWorld()
  49. {
  50. if(planetTemplate == null)
  51. {
  52. Debug.LogError("Missing planet template", this.gameObject);
  53. return;
  54. }
  55. DestroyTerrain();
  56. Debug.Log("Starting chunk generation");
  57. Initialize();
  58. for (int x = 0; x < chunkLimit.x; x++) {
  59. for (int z = 0; z < chunkLimit.z; z++) {
  60. for (int y = 0; y < chunkLimit.y; y++) {
  61. Vector3 chunkGlobalPos = new Vector3(x * chunkSize.x * voxelSize.x, y * chunkSize.y * voxelSize.y, z * chunkSize.z * voxelSize.z);
  62. Vector3Int chunkPos = new Vector3Int(x * chunkSize.x, y * chunkSize.y, z * chunkSize.z);
  63. chunkSystem.CreateChunk(chunkGlobalPos, chunkPos, (Chunk<BlockBase> c, Vector3Int pos) => new BlockBase(0, 0));
  64. PopulateChunkData(chunkGlobalPos);
  65. }
  66. }
  67. }
  68. for (int x = 0; x < chunkLimit.x; x++) {
  69. for (int z = 0; z < chunkLimit.z; z++) {
  70. for (int y = 0; y < chunkLimit.y; y++) {
  71. //Vector3Int chunkPos = new Vector3Int(x * chunkSize.x, y * chunkSize.y, z * chunkSize.z);
  72. Vector3 chunkPos = new Vector3(x * chunkSize.x * voxelSize.x, y * chunkSize.y * voxelSize.y, z * chunkSize.z * voxelSize.z);
  73. GenerateTerrainMesh(chunkPos);
  74. //else if(marchingCubes2 != null) marchingCubes2.Generate(chunkSystem, chunkPos);
  75. }
  76. }
  77. }
  78. #if UNITY_EDITOR
  79. UpdateEditor ();
  80. #endif
  81. }
  82. public void GenerateChunk(Vector3 position)
  83. {
  84. position = chunkSystem.GetChunkGlobalPositionFromGlobalPosition(position);
  85. bool buffered = false;
  86. for (int i = 0; i < chunkDataBuffer.Count; i++)
  87. {
  88. if(chunkDataBuffer[i] == position) buffered = true;
  89. }
  90. for (int i = 0; i < chunkMeshBuffer.Count; i++)
  91. {
  92. if(chunkMeshBuffer[i] == position) buffered = true;
  93. }
  94. //if(chunkSystem.ChunkExists(position)) buffered = true;
  95. //bool meshGenerated = terrainMeshes.TryGetValue(position, out GameObject obj);
  96. //if(meshGenerated) buffered = true;
  97. if(buffered) return;
  98. chunkDataBuffer.Add(position);
  99. }
  100. void PopulateChunkData(Vector3 initialPos)
  101. {
  102. float width = (float)(chunkLimit.x * chunkSize.x * voxelSize.x);
  103. float height = (float)(chunkLimit.y * chunkSize.y * voxelSize.y);
  104. float length = (float)(chunkLimit.z * chunkSize.z * voxelSize.z);
  105. for (int x = 0; x < chunkSize.x; x++) {
  106. for (int z = 0; z < chunkSize.z; z++) {
  107. for (int y = 0; y < chunkSize.y; y++) {
  108. // Get a terrain height using regular old Perlin noise.
  109. Vector3 pos = initialPos + new Vector3(x * voxelSize.x, y * voxelSize.y, z * voxelSize.z);
  110. // Old example noise:
  111. //float thisHeight = height * Mathf.PerlinNoise((float)pos.x / width * noiseMultiplier + 0.001f, (float)pos.z / length * noiseMultiplier + 0.001f);
  112. float elevation = planetTemplate.SamplePoint((float)pos.x / width, (float)pos.z / length);
  113. float thisHeight = height * elevation;
  114. uint code = planetTemplate.SamplePointCode((float)pos.x / width, (float)pos.z / length, elevation);
  115. BlockBase block = new BlockBase(code, (uint)Mathf.FloorToInt(thisHeight * 1000));
  116. chunkSystem.SetBlock(pos, block);
  117. }
  118. }
  119. }
  120. chunkUpdates.TryGetValue(initialPos, out List<VoxelUpdateData> updates);
  121. if(updates == null) return;
  122. for (int i = 0; i < updates.Count; i++)
  123. {
  124. chunkSystem.SetBlock(updates[i].position, updates[i].block);
  125. }
  126. }
  127. public void GenerateChunkData(Vector3 position)
  128. {
  129. position = chunkSystem.GetChunkGlobalPositionFromGlobalPosition(position);
  130. Vector3Int chunkPos = chunkSystem.GetChunkPositionFromGlobalPosition(position);
  131. if(!chunkSystem.ChunkExists(position))
  132. {
  133. //Debug.Log($"Generating Terrain Data ({position} | {chunkPos})");
  134. chunkSystem.CreateChunk(position, chunkPos, (Chunk<BlockBase> c, Vector3Int pos) => new BlockBase(0, 0));
  135. PopulateChunkData(position);
  136. }
  137. }
  138. public void GenerateTerrainMesh(Vector3 position, bool overwrite = false)
  139. {
  140. bool meshExists = terrainMeshes.TryGetValue(position, out GameObject obj);
  141. if(meshExists && !overwrite) return;
  142. if(!overwrite) terrainMeshes.Add(position, null);
  143. if(marchingCubes3 != null) marchingCubes3.Generate(chunkSystem, this, position);
  144. }
  145. public void UpdateGenerator()
  146. {
  147. if(marchingCubes3.GetJobCount() == 0) ProcessChunkDataBuffer();
  148. ProcessChunkMeshBuffer();
  149. }
  150. public void UpdateEditor ()
  151. {
  152. Timer.ExecuteRealTime(50, () => {
  153. if(marchingCubes3 != null)
  154. {
  155. UpdateGenerator();
  156. if(!marchingCubes3.IsGeneratorDone()) UpdateEditor();
  157. }
  158. });
  159. }
  160. private void ProcessChunkDataBuffer()
  161. {
  162. int limit = chunkDataBuffer.Count < dataProcessLimit ? chunkDataBuffer.Count : dataProcessLimit;
  163. for (int i = 0; i < limit; i++)
  164. {
  165. Vector3 position = chunkDataBuffer[i];
  166. List<Vector3> positions = new List<Vector3>{
  167. new Vector3( 0, 0, 0),
  168. new Vector3( 1, 0, 0),
  169. new Vector3( 1, 0, -1),
  170. new Vector3( 0, 0, -1),
  171. new Vector3(-1, 0, -1),
  172. new Vector3(-1, 0, 0),
  173. new Vector3(-1, 0, 1),
  174. new Vector3( 0, 0, 1),
  175. new Vector3( 1, 0, 1),
  176. new Vector3( 0, 1, 0),
  177. new Vector3( 1, 1, 0),
  178. new Vector3( 1, 1, -1),
  179. new Vector3( 0, 1, -1),
  180. new Vector3(-1, 1, -1),
  181. new Vector3(-1, 1, 0),
  182. new Vector3(-1, 1, 1),
  183. new Vector3( 0, 1, 1),
  184. new Vector3( 1, 1, 1),
  185. new Vector3( 0, -1, 0),
  186. new Vector3( 1, -1, 0),
  187. new Vector3( 1, -1, -1),
  188. new Vector3( 0, -1, -1),
  189. new Vector3(-1, -1, -1),
  190. new Vector3(-1, -1, 0),
  191. new Vector3(-1, -1, 1),
  192. new Vector3( 0, -1, 1),
  193. new Vector3( 1, -1, 1),
  194. };
  195. for (int a = 0; a < positions.Count; a++)
  196. {
  197. //position = new Vector3(position.x + chunkSize.x * voxelSize.x, position.y, position.z);
  198. Vector3 chunkPos = new Vector3(
  199. position.x + (positions[a].x * chunkSize.x * voxelSize.x),
  200. position.y + (positions[a].y * chunkSize.y * voxelSize.y),
  201. position.z + (positions[a].z * chunkSize.z * voxelSize.z)
  202. );
  203. GenerateChunkData(chunkPos);
  204. }
  205. chunkMeshBuffer.Add(chunkDataBuffer[i]);
  206. }
  207. chunkDataBuffer.RemoveRange(0, limit);
  208. }
  209. private void ProcessChunkMeshBuffer()
  210. {
  211. marchingCubes3.UpdateFinishedJobs();
  212. int limit = marchingCubes3.GetJobCount() < parallelMeshJobs ? parallelMeshJobs - marchingCubes3.GetJobCount() : 0;
  213. limit = limit < chunkMeshBuffer.Count ? limit : chunkMeshBuffer.Count;
  214. for (int i = 0; i < limit; i++)
  215. {
  216. GenerateTerrainMesh(chunkMeshBuffer[i]);
  217. }
  218. chunkMeshBuffer.RemoveRange(0, limit);
  219. }
  220. public void DestroyChunk(Vector3 position)
  221. {
  222. DestroyChunkData(position);
  223. DestroyChunkMesh(position);
  224. }
  225. public void DestroyChunkData(Vector3 position)
  226. {
  227. chunkSystem.DestroyChunk(position);
  228. }
  229. public void DestroyChunkMesh(Vector3 position)
  230. {
  231. terrainMeshes.TryGetValue(position, out GameObject obj);
  232. if(obj != null)
  233. {
  234. Destroy(obj);
  235. #if UNITY_EDITOR
  236. DestroyImmediate(obj);
  237. #endif
  238. }
  239. terrainMeshes.Remove(position);
  240. }
  241. public void ClearChunkDataBuffer() => chunkDataBuffer.Clear();
  242. [Button("Destroy"), ButtonGroup("Buttons")]
  243. public void DestroyTerrain()
  244. {
  245. var childTransforms = this.gameObject.transform.GetComponentsInChildren<Transform>();
  246. if(childTransforms.Length > 0) Debug.Log($"Destroying {childTransforms.Length - 1} terrain objects");
  247. for (int i = 0; i < childTransforms.Length; i++)
  248. {
  249. if(childTransforms[i] == this.transform) continue;
  250. #if UNITY_EDITOR
  251. DestroyImmediate(childTransforms[i].gameObject);
  252. #else
  253. Destroy(childTransforms[i].gameObject);
  254. #endif
  255. }
  256. terrainMeshes = new Dictionary<Vector3, GameObject>();
  257. chunkDataBuffer = new List<Vector3>();
  258. chunkMeshBuffer = new List<Vector3>();
  259. chunkSystem.chunks = new Dictionary<Vector3Int, Chunk<BlockBase>>();
  260. chunkSystem.chunkList = new List<Vector3>();
  261. }
  262. public enum VoxelOperation
  263. {
  264. Add,
  265. Remove,
  266. Level
  267. }
  268. public void ChangeVoxels(Vector3 pos, uint code, VoxelOperation operation, int radius = 3)
  269. {
  270. int startHeigth = 0, endHeigth = 0, targetHeigth = 0, updatedVoxels = 0;
  271. switch (operation)
  272. {
  273. case VoxelOperation.Add:
  274. startHeigth = -1;
  275. endHeigth = radius;
  276. targetHeigth = (int)(((chunkSystem.GetBlock(pos).value/1000) + (radius * voxelSize.y)) * 1000);
  277. break;
  278. case VoxelOperation.Remove:
  279. startHeigth = -1 * (radius + 1);
  280. endHeigth = radius + 1;
  281. targetHeigth = (int)(Mathf.Clamp((chunkSystem.GetBlock(pos).value/1000) - (radius * voxelSize.y), 0, 10000) * 1000);
  282. break;
  283. case VoxelOperation.Level:
  284. startHeigth = -1 * radius;
  285. endHeigth = radius;
  286. targetHeigth = (int)chunkSystem.GetBlock(pos).value;
  287. break;
  288. default:
  289. return;
  290. }
  291. Vector3 chunkPosition = chunkSystem.GetChunkGlobalPositionFromGlobalPosition(pos);
  292. List<Vector2Int> points = Utilities.GetPointsInRadius(radius, true);
  293. List<Vector3> chunkPositions = new List<Vector3>();
  294. for (int j = 0; j < points.Count; j++)
  295. {
  296. for (int y = startHeigth; y < endHeigth + 1; y++)
  297. {
  298. updatedVoxels += 1;
  299. Vector3 voxelPos = new Vector3(pos.x + points[j].x * voxelSize.x, pos.y + y * voxelSize.y, pos.z + points[j].y * voxelSize.z);
  300. BlockBase block = chunkSystem.GetBlock(voxelPos);
  301. block.code = code;
  302. block.value = (uint)targetHeigth;
  303. chunkSystem.SetBlock(voxelPos, block);
  304. // Get positions
  305. Vector3Int localVoxelPos = chunkSystem.GetVoxelFromGlobalPosition(voxelPos);
  306. Vector3 chunkPos = chunkSystem.GetChunkGlobalPositionFromGlobalPosition(voxelPos);
  307. // Save data update
  308. VoxelUpdateData updateData = new VoxelUpdateData(voxelPos, block);
  309. chunkUpdates.TryGetValue(chunkPos, out List<VoxelUpdateData> chunkUpdate);
  310. if(chunkUpdate != null)
  311. {
  312. bool exists = false;
  313. for (int h = 0; h < chunkUpdate.Count; h++)
  314. {
  315. if(chunkUpdate[h].position.Equals(updateData.position))
  316. {
  317. exists = true;
  318. chunkUpdate[h].block = updateData.block;
  319. }
  320. }
  321. if(!exists) chunkUpdate.Add(updateData);
  322. }
  323. else chunkUpdates.Add(chunkPos, new List<VoxelUpdateData>{updateData});
  324. // Check if changed voxels are in the edge of the chunk and if true add the neightboring chunk to the update list
  325. List<Vector3> chunks = new List<Vector3>();
  326. chunks.Add(chunkPos);
  327. if(localVoxelPos.x == 0) chunks.Add(new Vector3(chunkPos.x - chunkSize.x * voxelSize.x, chunkPos.y, chunkPos.z));
  328. if(localVoxelPos.x == 16) chunks.Add(new Vector3(chunkPos.x + chunkSize.x * voxelSize.x, chunkPos.y, chunkPos.z));
  329. if(localVoxelPos.y == 0) chunks.Add(new Vector3(chunkPos.x, chunkPos.y - chunkSize.y * voxelSize.y, chunkPos.z));
  330. if(localVoxelPos.y == 16) chunks.Add(new Vector3(chunkPos.x, chunkPos.y + chunkSize.y * voxelSize.y, chunkPos.z));
  331. if(localVoxelPos.z == 0) chunks.Add(new Vector3(chunkPos.x, chunkPos.y, chunkPos.z - chunkSize.z * voxelSize.z));
  332. if(localVoxelPos.z == 16) chunks.Add(new Vector3(chunkPos.x , chunkPos.y, chunkPos.z + chunkSize.z * voxelSize.z));
  333. for (int i = 0; i < chunks.Count; i++)
  334. {
  335. bool duplicate = false;
  336. for (int k = 0; k < chunkPositions.Count; k++)
  337. {
  338. if(chunkPositions[k].Equals(chunks[i]))
  339. {
  340. duplicate = true;
  341. break;
  342. }
  343. }
  344. if(!duplicate) chunkPositions.Add(chunks[i]);
  345. }
  346. }
  347. }
  348. for (int c = 0; c < chunkPositions.Count; c++) GenerateTerrainMesh(chunkPositions[c], true);
  349. Debug.Log($"Changing voxel at {pos}, updated {updatedVoxels} voxels and {chunkPositions.Count} chunks");
  350. }
  351. public void AddGeneratedTerrainMesh(Vector3 chunkPosition, GameObject meshObj)
  352. {
  353. bool meshExists = terrainMeshes.TryGetValue(chunkPosition, out GameObject currentMesh);
  354. if(currentMesh != null)
  355. {
  356. //Debug.Log($"Updating chunk mesh at {chunkPosition}");
  357. DestroyChunkMesh(chunkPosition);
  358. terrainMeshes[chunkPosition] = meshObj;
  359. }
  360. else
  361. {
  362. //Debug.Log($"Adding chunk mesh at {chunkPosition}");
  363. //terrainMeshes.Add(chunkPosition, meshObj);
  364. terrainMeshes[chunkPosition] = meshObj;
  365. }
  366. }
  367. [Button("Save"), ButtonGroup("Buttons")]
  368. public void SaveChunkUpdateData()
  369. {
  370. }
  371. }
  372. }