//function to create the voxel noises and caves etc. public static byte[, , ]CreateVoxels(byte[,,] m_voxels,Vector3 m_pos,VoxelChunk chunk,NoiseModule noiseModule) { //float startTime = Time.realtimeSinceStartup; //Creates the data the mesh is created form. Fills m_voxels with values between -1 and 1 where //-1 is a soild voxel and 1 is a empty voxel. int w = m_voxels.GetLength(0); int h= m_voxels.GetLength(1); int l = m_voxels.GetLength(2); float worldX; float worldZ; float worldY; float ht; for(int x = 0; x < w; x++) { for(int z = 0; z < l; z++) { //world pos is the voxels position plus the voxel chunks position worldX = x+m_pos.x; worldZ = z+m_pos.z; ht = generator.noise.FillVoxel2d(worldX,worldZ,m_pos,SurfacePerlin,CavePerlin); for(int y = 0; y < h; y++) { worldY = y+m_pos.y; float HT=ht+worldY-h/2; if(MakeCaves&&y<h/2-50) HT -= generator.noise.FillVoxel3d(worldX,worldY,worldZ,m_pos); HT/=16; HT = Mathf.Clamp(HT , -0.5f, 0.5f); HT+=0.5f; HT*=255; m_voxels[x,y,z]=(byte)HT; m_voxels[x,y,z]=(byte)Mathf.Clamp(m_voxels[x,y,z],0,255); if(y>=h-5&&m_voxels[x,y,z]<=127)m_voxels[x,y,z]=255; if(y<=5&&m_voxels[x,y,z]>=127)m_voxels[x,y,z]=0; } } } return m_voxels; }
public static Vector3[] Createvertices(byte[,,] m_voxels, VoxelChunk chunk, int lod) { Vector3[] vertices = MarchingCubes.CreateVertices(m_voxels, chunk, 2, 2, lod); return(vertices); }
public static Vector3[] CreateverticesVoxels(byte[,,] m_voxels,VoxelChunk chunk) { Vector3[] vertices = MarchingCubesVoxels.CreateVertices(m_voxels,chunk,2,2); return vertices; }
/// <summary> /// Creates the vertices with new voxel data /// </summary> /// <returns>The vertices.</returns> /// <param name="voxels">Voxels.</param> /// <param name="start">Start.</param> /// <param name="end">End.</param> public static Vector3[] CreateVertices(byte[,,] voxels, VoxelChunk chunk, int start, int end, int lod) { List <int> index = new List <int>(); List <Vector3> verts = new List <Vector3>(); Vector3[] edgeVertex = new Vector3[12]; byte[] cube = new byte[8]; for (int z = start; z < voxels.GetLength(2) - 1 - end; z++) { for (int y = start; y < voxels.GetLength(1) - 1 - end; y++) { for (int x = start; x < voxels.GetLength(0) - 1 - end; x++) { int i, j, vert, idx; //Get the values in the 8 neighbours which make up a cube for (i = 0; i < 8; i++) { cube[i] = voxels[x + vertexOffset[i, 0], y + vertexOffset[i, 1], z + vertexOffset[i, 2]]; } //Perform algorithm int flagIndex = 0; float offset = 0.0f; //Find which vertices are inside of the surface and which are outside for (i = 0; i < 8; i++) { if (cube[i] >= target) { flagIndex |= 1 << i; } } //Find which edges are intersected by the surface int edgeFlags = cubeEdgeFlags[flagIndex]; //If the cube is entirely inside or outside of the surface, //then there will be no intersections if (edgeFlags != 0) { //Find the point of intersection of the surface with each edge for (i = 0; i < 12; i++) { //if there is an intersection on this edge if ((edgeFlags & (1 << i)) != 0) { offset = GetOffset(cube[edgeConnection[i, 0]], cube[edgeConnection[i, 1]]); edgeVertex[i].z = z + (vertexOffset[edgeConnection[i, 0], 2] + offset * edgeDirection[i, 2]); edgeVertex[i].y = y + (vertexOffset[edgeConnection[i, 0], 1] + offset * edgeDirection[i, 1]); edgeVertex[i].x = x + (vertexOffset[edgeConnection[i, 0], 0] + offset * edgeDirection[i, 0]); } } //Save the triangles that were found. There can be up to five per cube for (i = 0; i < 5; i++) { if (triangleConnectionTable[flagIndex, 3 * i] < 0) { break; } idx = verts.Count; for (j = 0; j < 3; j++) { vert = triangleConnectionTable[flagIndex, 3 * i + j]; index.Add(idx + windingOrder[j]); verts.Add(edgeVertex[vert] * VoxelTerrainEngine.TriSize[lod]); } } } } //MarchCube(new Vector3(x,y,z), cube, verts, index); } } if (verts.Count > 65000) { //If you get this error its means that the voxels array contaions to much information and //a mesh larger than 65000 verts is need to represent it. You can fix this by using a smaller arrays of voxel, //make less 'noisey' data, or manually split up the mesh into sub meshes. Debug.Log("MarchingCubes::CreateMesh - " + "Number of mesh verts greater than 65000. Can not create mesh"); return(null); } chunk.tris = index.ToArray(); return(verts.ToArray()); }
public void Update() { int maxLoadChunks = 5; int maxMeshChunks = 1; int loadedChunks = 0; while (chunkLoadQueue.Count > 0 && loadedChunks < maxLoadChunks) { // load chunk var coords = chunkLoadQueue.Dequeue(); var chunk = new VoxelChunk(coords.Item1, coords.Item2, ChunkWidth, ChunkHeight, ChunkDepth); // generate terrain for (int x = 0; x < ChunkWidth; x++) { for (int z = 0; z < ChunkDepth; z++) { int h = (int)(ChunkHeight * (0.5 + 0.5 * noise.Evaluate((coords.Item1 * ChunkWidth + x) * 0.009, (coords.Item2 * ChunkDepth + z) * 0.009))); for (int y = 0; y < h; y++) { chunk[x, y, z] = Extensions.ColorFromHSV(110, 0.94, (h / (double)ChunkHeight)*0.4+0.55).ToShortColor(); // (ushort)(ChunkCount + 1); } } } chunksToMesh.Add(coords, chunk); } List<Tuple<int, int>> meshedChunks = new List<Tuple<int, int>>(); int chunksMeshed = 0; foreach (var kvp in chunksToMesh) { var coords = kvp.Key; int x = coords.Item1; int z = coords.Item2; if (ChunkMeshedOrLoaded(x - 1, z) && ChunkMeshedOrLoaded(x + 1, z) && ChunkMeshedOrLoaded(x, z - 1) && ChunkMeshedOrLoaded(x, z + 1)) { var chunk = kvp.Value; chunk.BuildMesh(); meshedChunks.Add(coords); chunksMeshed++; if (chunksMeshed >= maxMeshChunks) break; } } foreach (var coords in meshedChunks) { var chunk = chunksToMesh[coords]; chunksToMesh.Remove(coords); chunks.Add(coords, chunk); } //Console.WriteLine("loaded chunks: {0} meshed chunks: {1}", loadedChunks, chunksMeshed); }
//main method for calling chunk creation some optimizations could be done here void GenerateTerrains() { while (CanContinue) { try { //Vector2 Refpos =new Vector2(playerGlobal.x,playerGlobal.z); //basic routine for creating chunks for (int i = 0; i < distanceToload * m_voxelWidth; i += m_voxelWidth) { //set player positon relative to chunks basically rounding to chunk size PosX = Mathf.RoundToInt(PlayerGlobal.x / m_voxelWidth) * m_voxelWidth; PosY = Mathf.RoundToInt(PlayerGlobal.y / m_voxelHeight) * m_voxelHeight; PosZ = Mathf.RoundToInt(PlayerGlobal.z / m_voxelWidth) * m_voxelWidth; for (int x = (int)PosX - i; x < (int)PosX + i; x += m_voxelWidth) { if (CanContinue) { for (int z = (int)PosZ - i; z < (int)PosZ + i; z += m_voxelWidth) { if (CanContinue) { //this is for the voxel editing at runtime //if theres any new chunks that need to be updated they are added here if (NewVoxelChunk.Count > 0) { for (int N = 0; N < NewVoxelChunk.Count; N++) { if (CanContinue) { int C = NewVoxelChunk.Count - 1; //Create the voxel data if (C < NewVoxelChunk.Count && NewVoxelChunk[C] != null) { //set various flags so the engine knows it needs to create a new mesh for terrain NewVoxelChunk[C].hascollider = false; NewVoxelChunk[C].HasVoxel = false; NewVoxelChunk[C].CreateMeshesWithVoxels(); NewVoxelChunk.Remove(NewVoxelChunk[C]); } } } } //if the terrain has been edited calling saveTerrains() method //will save the voxel data to a file //here i just set the flag of this script to cansave =true; and it saves automatically if (cansave) { SaveTerrains(); cansave = false; } //set up variables for distance checking Vector2 chunkpos = new Vector2((x), (z)); Vector2 playerpos = new Vector2(Player.x, Player.z); //distance checking Dist = Vector2.Distance(playerpos, chunkpos); if (Dist < distanceToload) { //check if the chunk already exists if not create a chunk with x *y * z of voxels if (!m_voxelChunk.ContainsKey(x, z)) { Pos = new Vector3(chunkpos.x, 0, chunkpos.y); //set variables for chunk creation Chunk = new VoxelChunk(Pos, m_voxelWidth, m_voxelHeight, m_voxelLength, m_surfaceLevel); //add chunk to double key dictionary m_voxelChunk.Add(x, z, Chunk); //set flag on chunk m_voxelChunk[x, z].hasproccessed = true; //add chunk to list of chunks that need noise added to them RequestedChunks.Add(m_voxelChunk[x, z]); } } //if the chunk already exists and its distance is greater then render distance //+ 50 then call destroy on the chunk else if (m_voxelChunk.ContainsKey(x, z) && Dist > distanceToload + 64) { Destroy(x, z); } } } } } } //catch exceptions }catch (Exception e) { Debug.LogError(e.StackTrace); } } }
void CreateChunks() { try { //basic routine for creating chunks for (int i = 0; i < distanceToLoad * m_voxelWidthLength; i += m_voxelWidthLength) { //set player positon relative to chunks basically rounding to chunk size for (int x = 0; x < i; x += m_voxelWidthLength) { if (CanGenerate) { for (int z = 0; z < i; z += m_voxelWidthLength) { if (CanGenerate) { //this is for the voxel editing at runtime //if theres any new chunks that need to be updated they are added here /*if the terrain has been edited calling saveTerrains() method * will save the voxel data to a file * here i just set the flag of this script to cansave =true; and it saves automatically*/ if (Save) { SaveTerrains(); Save = false; } //set up variables for distance checking Vector2 chunkpos = new Vector2(x, z); Vector2 playerpos = Vector2.zero; float Dist = Vector2.Distance(playerpos, chunkpos); //check if the chunk already exists if not create a chunk with x *y * z of voxels if (!m_voxelChunk.ContainsKey(new Vector3(x, 0, z), new Vector3(m_voxelWidthLength, m_voxelHeight, m_voxelWidthLength) * minLod)) { if (Dist <= distanceToLoad) { Vector3 Pos = new Vector3(x, 0, z); //set variables for chunk creation VoxelChunk Chunk = new VoxelChunk(Pos, m_voxelWidthLength, m_voxelHeight, minLod); //add chunk to double key dictionary m_voxelChunk.Add(Pos, new Vector3(m_voxelWidthLength, m_voxelHeight, m_voxelWidthLength) * minLod, Chunk); //set flag on chunk Chunk.hasproccessed = true; //add chunk to list of chunks that need noise added to them GenerateVoxels.Enqueue(Chunk); return; } } //if the chunk already exists and its distance is greater then render distance } } } } } }catch (Exception e) { Debug.LogError(e.StackTrace); } }
public void UpdateTerrain() { if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode && playMode == EditorPlay.isEditor) { Transform[] gamos = GetComponentsInChildren <Transform>(); CanGenerate = false; EndAllThreads(); for (int i = 0; i < gamos.Length; i++) { if (gamos[i] != transform) { DestroyImmediate(gamos[i].gameObject); } } SaveTerrains(); playMode = EditorPlay.isPlaying; } if (playMode == EditorPlay.isEditor) { if (UnityEditor.EditorApplication.isCompiling == false && CanGenerate == false) { Transform[] gamos = GetComponentsInChildren <Transform>(); for (int i = 0; i < gamos.Length; i++) { if (gamos[i] != transform) { DestroyImmediate(gamos[i].gameObject); } } SaveTerrains(); Initialize(); } else if (UnityEditor.EditorApplication.isCompiling && CanGenerate == true) { CanGenerate = false; Transform[] gamos = GetComponentsInChildren <Transform>(); for (int i = 0; i < gamos.Length; i++) { if (gamos[i] != transform) { DestroyImmediate(gamos[i].gameObject); for (int t = 0; t < thread.Length; t++) { if (thread[t] != null) { thread[t].Abort(); } } CanGenerate = false; } } SaveTerrains(); } } if (Camera.current != null && UnityEditor.Selection.Contains(gameObject)) { if (timer <= 0.25f) { timer += 0.03f; } Ray myray; if (playMode == EditorPlay.isEditor) { myray = UnityEditor.HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); RaycastHit hit; if (Physics.Raycast(myray, out hit, Mathf.Infinity)) { Shader.SetGlobalVector("_highlightArea", hit.point); Event E = Event.current; if (E.button == 1 && E.control && timer >= 0.25f) { SetVoxels(hit.point, value, effectArea); timer = 0; } if (E.button == 1 && E.alt && timer >= 0.25f) { setMaterial(hit.point, VoxelMaterial, effectArea); timer = 0; } Shader.SetGlobalFloat("_highlightSize", effectArea); } } } else { Shader.SetGlobalFloat("_highlightSize", 0); } //set the wind variables on the shader comment this out if you dont use my grass shader if (Zone != null) { Shader.SetGlobalFloat("WindAmount", Zone.windMain); Shader.SetGlobalFloat("WaveSize", Zone.windPulseMagnitude); Shader.SetGlobalFloat("windSpeed", Zone.windPulseFrequency); Shader.SetGlobalFloat("SqrDistance", Zone.windTurbulence); Shader.SetGlobalFloat("WindDirectionx", Zone.transform.forward.x); Shader.SetGlobalFloat("WindDirectiony", Zone.transform.forward.y); Shader.SetGlobalFloat("WindDirectionz", Zone.transform.forward.z); } //if we have meshes to destroy destroy them //need to implement a pooling system will save on memory //as well as stop the garbage collector from being called if (Trash.Count > 0) { VoxelChunk myChunk = Trash.Dequeue(); if (myChunk.mesh != null) { DestroyImmediate(myChunk.mesh); } myChunk.mesh = null; DestroyImmediate(myChunk.m_mesh); myChunk.canDraw = false; myChunk.canCreatemesh = false; } //create the mesh . this is done in update as calling it from another thread is not allowed if (MeshChunks.Count > 0) { VoxelChunk chunk = MeshChunks.Dequeue(); chunk.CreateMesh(); chunk.canCreatemesh = false; if (ActiveChunks.Contains(chunk) == false) { ActiveChunks.Add(chunk); } } if (ActiveChunks.Count > 0) { for (int i = 0; i < ActiveChunks.Count; i++) { ActiveChunks[i].RenderGrass(); } } //need to some how check for occlusion and not render things behind other objects }
//main method for calling chunk creation some optimizations could be done here void GenerateTerrains() { while (CanContinue) { try { //Vector2 Refpos =new Vector2(playerGlobal.x,playerGlobal.z); //basic routine for creating chunks for(int i =0;i<distanceToload*m_voxelWidth;i+=m_voxelWidth){ //set player positon relative to chunks basically rounding to chunk size PosX = Mathf.RoundToInt( PlayerGlobal.x/m_voxelWidth)*m_voxelWidth; PosY = Mathf.RoundToInt(PlayerGlobal.y/m_voxelHeight)*m_voxelHeight; PosZ = Mathf.RoundToInt( PlayerGlobal.z/m_voxelWidth)*m_voxelWidth; for(int x=(int)PosX -i;x <(int)PosX +i ;x+=m_voxelWidth){if(CanContinue){ for(int z=(int)PosZ -i;z <(int)PosZ +i;z+=m_voxelWidth){if(CanContinue){ //this is for the voxel editing at runtime //if theres any new chunks that need to be updated they are added here if(NewVoxelChunk.Count >0){ for(int N= 0;N < NewVoxelChunk.Count;N++){if(CanContinue){ int C = NewVoxelChunk.Count-1; //Create the voxel data if(C<NewVoxelChunk.Count&&NewVoxelChunk[C]!=null){ //set various flags so the engine knows it needs to create a new mesh for terrain NewVoxelChunk[C].hascollider=false; NewVoxelChunk[C].HasVoxel=false; NewVoxelChunk[C].CreateMeshesWithVoxels(); NewVoxelChunk.Remove(NewVoxelChunk[C]); } } } } //if the terrain has been edited calling saveTerrains() method //will save the voxel data to a file //here i just set the flag of this script to cansave =true; and it saves automatically if(cansave){ SaveTerrains(); cansave = false; } //set up variables for distance checking Vector2 chunkpos = new Vector2((x) ,(z)); Vector2 playerpos = new Vector2(Player.x,Player.z); //distance checking Dist = Vector2.Distance(playerpos , chunkpos); if(Dist < distanceToload){ //check if the chunk already exists if not create a chunk with x *y * z of voxels if(!m_voxelChunk.ContainsKey(x,z)){ Pos = new Vector3(chunkpos.x, 0 , chunkpos.y); //set variables for chunk creation Chunk = new VoxelChunk(Pos, m_voxelWidth, m_voxelHeight, m_voxelLength, m_surfaceLevel); //add chunk to double key dictionary m_voxelChunk.Add(x,z,Chunk) ; //set flag on chunk m_voxelChunk[x,z].hasproccessed=true; //add chunk to list of chunks that need noise added to them RequestedChunks.Add(m_voxelChunk[x,z]); } } //if the chunk already exists and its distance is greater then render distance //+ 50 then call destroy on the chunk else if(m_voxelChunk.ContainsKey(x,z) && Dist > distanceToload+64){ Destroy(x,z); } } } } } } //catch exceptions }catch (Exception e) {Debug.LogError(e.StackTrace); } } }
//check if voxel exists and to what chunk it belongs to //just VoxelTerrainEngine.raycastvoxels to raycast for a //voxel then check the hit.point against this //should return a chunk public bool CheckVoxels(Vector3 HitPoint,out VoxelChunk chunk) { int C = ActiveChunks.Count; chunk = null; for(int c = 0;c < C;c++){ if(ActiveChunks[c].CheckVoxels(HitPoint)){ chunk = ActiveChunks[c]; } } if(chunk!=null) return true; else return false; }