/// <summary> /// This is highly dependent on TerrainChunk internal details. (See "GetDirectionsToLook" method.) /// A TerrainChunk stores some blocks of adjacent chunks in order to simplify mesh generation. /// Therefore, when we change a block type, we have to update the block type in adjacent chunks as well. /// This method gathers the adjacent chunks that need to be updated. /// </summary> private IEnumerable <TerrainChunk> GetAdjacentChunksThatShareBlock(TerrainChunk chunk, Index3D blockLocalIndex) { foreach (var d in GetDirectionsToLook(chunk, blockLocalIndex)) { yield return(chunkGenerator.GetOrGenerateEmpty(chunk.Index.Step(d), chunksParent)); } }
private void SetTypeOfSameBlockInAdjacentChunks(TerrainChunk chunk, Index3D blockLocalIndex, Vector3 pointOnTerrainMesh, TerrainBlock.Type blockNewType) { foreach (var adjacentChunk in GetAdjacentChunksThatShareBlock(chunk, blockLocalIndex)) { SetBlockTypeAt(adjacentChunk, adjacentChunk.GetBlockLocalIndexAt(pointOnTerrainMesh), blockNewType); } }
public Mesh BuildMesh(TerrainChunk chunk) { chunk.ForEachGroundBlock(block => { if (block.LocalIndex.Y < config.WaterLevelInBlocks) { var x = block.LocalIndex.X * config.BlockSize - config.HalfBlockSize; var z = block.LocalIndex.Z * config.BlockSize - config.HalfBlockSize; triangles.Add(vertices.Count); triangles.Add(vertices.Count + 1); triangles.Add(vertices.Count + 2); triangles.Add(vertices.Count); triangles.Add(vertices.Count + 2); triangles.Add(vertices.Count + 3); vertices.Add(new Vector3(x, 0, z)); vertices.Add(new Vector3(x, 0, z + config.BlockSize)); vertices.Add(new Vector3(x + config.BlockSize, 0, z + config.BlockSize)); vertices.Add(new Vector3(x + config.BlockSize, 0, z)); uvs.AddRange(new Vector2[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0) }); } }); return(BuildMesh()); }
public TerrainTreesGenerator(TerrainConfig config, TerrainChunk chunk) { this.config = config; this.chunk = chunk; rand = new System.Random(chunk.Index.X * 10000 + chunk.Index.Z); }
private TerrainBlock.Type SetBlockTypeAt(TerrainChunk chunk, Index3D blockLocalIndex, TerrainBlock.Type blockNewType) { var blockType = chunk.GetBlock(blockLocalIndex).BlockType; chunk.SetBlockType(blockLocalIndex, blockNewType); chunkGenerator.BuildMeshFor(chunk); return(blockType); }
private TerrainBlock GetBlockAt(Vector3 pointInWorld, out TerrainChunk chunk) { var chunkIndex = config.GetChunkIndexAt(pointInWorld); chunk = chunkGenerator.GetOrGenerateEmpty(chunkIndex, chunksParent); var blockLocalIndex = chunk.GetBlockLocalIndexAt(pointInWorld); return(chunk.GetBlock(blockLocalIndex)); }
public void GenerateEmptyBlocksFor(TerrainChunk chunk, float blocksSize) { chunk.ForEachBlockIndex( (localIndex, globalIndex) => { var block = new TerrainBlock(TerrainBlock.Type.None, localIndex, globalIndex, blocksSize); chunk.SetBlock(localIndex, block); } ); }
public void GenerateBlocksFor(TerrainChunk chunk, float blocksSize) { chunk.ForEachBlockIndex( (localIndex, globalIndex) => { var blockType = CalculateBlockType(globalIndex); var block = new TerrainBlock(blockType, localIndex, globalIndex, blocksSize); chunk.SetBlock(localIndex, block); } ); new TerrainTreesGenerator(config, chunk).Generate(noise); }
public Mesh BuildMesh(TerrainChunk chunk) { chunk.ForEachNonEmptyVisibleBlock(block => { var blockLocalIndex = block.LocalIndex; var blockMinVertex = new Vector3( blockLocalIndex.X * config.BlockSize - config.HalfBlockSize, blockLocalIndex.Y * config.BlockSize - config.HalfBlockSize, blockLocalIndex.Z * config.BlockSize - config.HalfBlockSize ); var blockUVs = TerrainBlockUVs.Create(block.BlockType); if (chunk.IsBlockEmpty(blockLocalIndex.StepUp())) { AddBlockFace_up(blockMinVertex, blockUVs); } if (chunk.IsBlockEmpty(blockLocalIndex.StepDown())) { AddBlockFace_down(blockMinVertex, blockUVs); } if (chunk.IsBlockEmpty(blockLocalIndex.StepForward())) { AddBlockFace_forward(blockMinVertex, blockUVs); } if (chunk.IsBlockEmpty(blockLocalIndex.StepRight())) { AddBlockFace_right(blockMinVertex, blockUVs); } if (chunk.IsBlockEmpty(blockLocalIndex.StepBack())) { AddBlockFace_back(blockMinVertex, blockUVs); } if (chunk.IsBlockEmpty(blockLocalIndex.StepLeft())) { AddBlockFace_left(blockMinVertex, blockUVs); } }); return(BuildMesh()); }
private IEnumerable <Vector3> GetDirectionsToLook(TerrainChunk chunk, Index3D blockLocalIndex) { var directionsToUpdate = new List <Vector3>(); if (blockLocalIndex.X <= chunk.MinBlockIndex.X) { directionsToUpdate.Add(Vector3.left); } else if (blockLocalIndex.X >= chunk.MaxBlockIndex.X) { directionsToUpdate.Add(Vector3.right); } if (blockLocalIndex.Y <= chunk.MinBlockIndex.Y) { directionsToUpdate.Add(Vector3.down); } else if (blockLocalIndex.Y >= chunk.MaxBlockIndex.Y) { directionsToUpdate.Add(Vector3.up); } if (blockLocalIndex.Z <= chunk.MinBlockIndex.Z) { directionsToUpdate.Add(Vector3.back); } else if (blockLocalIndex.Z >= chunk.MaxBlockIndex.Z) { directionsToUpdate.Add(Vector3.forward); } var numberOfDirections = directionsToUpdate.Count; for (var i = 0; i < Mathf.CeilToInt(numberOfDirections / 2.0f); ++i) { for (var j = i + 1; j < numberOfDirections; ++j) { directionsToUpdate.Add(directionsToUpdate[i] + directionsToUpdate[j]); } } return(directionsToUpdate); }
public void BuildMeshFor(TerrainChunk chunk) { var meshBuilder = new TerrainChunkMeshBuilder(config); var mesh = meshBuilder.BuildMesh(chunk); var chunkObject = chunk.gameObject; chunkObject.GetComponent <MeshFilter>().mesh = mesh; chunkObject.GetComponent <MeshCollider>().sharedMesh = mesh; chunkObject.transform.position = chunk.FirstVisibleBlockGlobalIndex.AsVector3() * config.BlockSize; if (chunk.Index.Y == 0) // generates water only for chunks at the ground level { var waterMesh = new WaterMeshBuilder(config).BuildMesh(chunk); if (waterMesh != null) { var waterObject = chunkObject.transform.GetChild(0); waterObject.transform.localPosition = new Vector3(0, config.WaterLevelInBlocks * config.BlockSize - config.HalfBlockSize * 1.3f, 0); waterObject.GetComponent <MeshFilter>().mesh = waterMesh; } } }