void SetBlock(byte blockIndex) { var chunkManager = WorldManager.ChunkManager; var chunk = chunkManager.GetChunkByBlockPosition(BlockPosition); if (chunk == null) throw new InvalidOperationException("Chunk not found: BlockPosition=" + BlockPosition); var relativePosition = chunk.GetRelativeBlockPosition(BlockPosition); lastBlockIndex = chunk.GetBlockIndex(relativePosition); // 既存ブロックと同じならば処理せず、Undo 履歴にも残さない。 if (blockIndex == lastBlockIndex) return; chunk.SetBlockIndex(relativePosition, blockIndex); // メッシュ再構築。 chunkManager.RequestUpdateMesh(chunk, ChunkMeshUpdatePriority.High); // 影響を受ける隣接チャンクがあるならば、それらもメッシュ再構築。 var chunkBlock = new ChunkBlock(chunk, relativePosition); for (int i = 0; i < Side.Count; i++) { var neighborChunkBlock = chunkBlock.GetNeighbor(Side.Items[i]); if (neighborChunkBlock.Chunk != null && neighborChunkBlock.Chunk != chunkBlock.Chunk) { chunkManager.RequestUpdateMesh(neighborChunkBlock.Chunk, ChunkMeshUpdatePriority.High); } } }
float CalculateAmbientOcclusion(ref ChunkBlock chunkBlock, Side side) { const float occlusionPerFace = 1 / 5f; // 1 は一切遮蔽されていない状態を表す。 float occlustion = 1; var mySide = side.Reverse(); // 面隣接ブロックに対して面隣接ブロックが存在する場合、遮蔽と判定。 for (int i = 0; i < Side.Count; i++) { var s = Side.Items[i]; // 自身に対する方向はスキップ。 if (mySide == s) { continue; } // 遮蔽ブロック位置。 var occluderBlockLocation = chunkBlock.GetNeighbor(s); // 遮蔽ブロック インデックス。 var occluderBlockIndex = occluderBlockLocation.GetBlockIndex(); // 未定と空の場合は非遮蔽。 if (occluderBlockIndex == null || occluderBlockIndex == Block.EmptyIndex) { continue; } // ブロック情報を取得。 var occluderBlock = Chunk.Region.BlockCatalog[occluderBlockIndex.Value]; // 対象とする面が存在しない場合は非遮蔽。 if (occluderBlock.Mesh.MeshParts[s.Reverse()] == null) { continue; } // 流体ブロックは非遮蔽。 if (occluderBlock.Fluid) { continue; } // 半透明ブロックは非遮蔽。 if (occluderBlock.Translucent) { continue; } // 遮蔽度で減算。 occlustion -= occlusionPerFace; } return(occlustion); }
float CalculateAmbientOcclusion(ref ChunkBlock chunkBlock, Side side) { const float occlusionPerFace = 1 / 5f; // 1 は一切遮蔽されていない状態を表す。 float occlustion = 1; var mySide = side.Reverse(); // 面隣接ブロックに対して面隣接ブロックが存在する場合、遮蔽と判定。 for (int i = 0; i < Side.Count; i++) { var s = Side.Items[i]; // 自身に対する方向はスキップ。 if (mySide == s) continue; // 遮蔽ブロック位置。 var occluderBlockLocation = chunkBlock.GetNeighbor(s); // 遮蔽ブロック インデックス。 var occluderBlockIndex = occluderBlockLocation.GetBlockIndex(); // 未定と空の場合は非遮蔽。 if (occluderBlockIndex == null || occluderBlockIndex == Block.EmptyIndex) continue; // ブロック情報を取得。 var occluderBlock = Chunk.Region.BlockCatalog[occluderBlockIndex.Value]; // 対象とする面が存在しない場合は非遮蔽。 if (occluderBlock.Mesh.MeshParts[s.Reverse()] == null) continue; // 流体ブロックは非遮蔽。 if (occluderBlock.Fluid) continue; // 半透明ブロックは非遮蔽。 if (occluderBlock.Translucent) continue; // 遮蔽度で減算。 occlustion -= occlusionPerFace; } return occlustion; }
void BuildBlock(int segmentX, int segmentY, int segmentZ, int x, int y, int z) { // チャンク内相対ブロック位置。 var blockPosition = new IntVector3 { X = segmentX * ChunkMeshManager.MeshSize.X + x, Y = segmentY * ChunkMeshManager.MeshSize.Y + y, Z = segmentZ * ChunkMeshManager.MeshSize.Z + z }; // 現在ブロック インデックス。 var blockIndex = Chunk.GetBlockIndex(blockPosition); // 空ならば頂点なし。 if (Block.EmptyIndex == blockIndex) return; // 現在ブロック。 var block = Chunk.Region.BlockCatalog[blockIndex]; // 現在ブロック位置。 var chunkBlock = new ChunkBlock(Chunk, blockPosition); // MeshPart が必ずしも平面であるとは限らないが、 // ここでは平面を仮定して隣接状態を考える。 for (int i = 0; i < Side.Count; i++) { var side = Side.Items[i]; var meshPart = block.Mesh.MeshParts[side]; // 対象面が存在しない場合はスキップ。 if (meshPart == null) continue; // 面隣接ブロック位置。 var neighborChunkBlock = chunkBlock.GetNeighbor(side); // 面隣接ブロック インデックス。 var neighborBlockIndex = neighborChunkBlock.GetBlockIndex(); // 未定の場合は面なしとする。 // 正確な描画には面なしとすべきではないが、 // 未定の場合に面を無視することで膨大な数の頂点を節約できる。 // このように節約しない場合、メモリ不足へ容易に到達する。 if (neighborBlockIndex == null) continue; if (neighborBlockIndex != Block.EmptyIndex) { // 面隣接ブロックとの関係から対象面の要否を判定。 var neighborBlock = Chunk.Region.BlockCatalog[neighborBlockIndex.Value]; // 半透明な連続した流体ブロックを並べる際、流体ブロック間の面は不要。 // ※流体ブロックは常に半透明を仮定して処理。 if (neighborBlock.Fluid && block.Fluid) continue; // 隣接ブロックが半透明ではないならば、不可視面となるため不要。 if (!neighborBlock.Translucent) continue; } float lightIntensity = 1; // 面隣接ブロック環境光遮蔽。 lightIntensity *= CalculateAmbientOcclusion(ref neighborChunkBlock, side); // 光量に基づいた頂点色。 var vertexColor = new Color(lightIntensity, lightIntensity, lightIntensity); // メッシュ追加。 ChunkVertices vertices; if (block.Fluid || block.Translucent) { vertices = translucences[segmentX, segmentY, segmentZ]; } else { vertices = opaques[segmentX, segmentY, segmentZ]; } AddMesh(x, y, z, ref vertexColor, meshPart, vertices); } }
void BuildBlock(int segmentX, int segmentY, int segmentZ, int x, int y, int z) { // チャンク内相対ブロック位置。 var blockPosition = new IntVector3 { X = segmentX * ChunkMeshManager.MeshSize.X + x, Y = segmentY * ChunkMeshManager.MeshSize.Y + y, Z = segmentZ * ChunkMeshManager.MeshSize.Z + z }; // 現在ブロック インデックス。 var blockIndex = Chunk.GetBlockIndex(blockPosition); // 空ならば頂点なし。 if (Block.EmptyIndex == blockIndex) { return; } // 現在ブロック。 var block = Chunk.Region.BlockCatalog[blockIndex]; // 現在ブロック位置。 var chunkBlock = new ChunkBlock(Chunk, blockPosition); // MeshPart が必ずしも平面であるとは限らないが、 // ここでは平面を仮定して隣接状態を考える。 for (int i = 0; i < Side.Count; i++) { var side = Side.Items[i]; var meshPart = block.Mesh.MeshParts[side]; // 対象面が存在しない場合はスキップ。 if (meshPart == null) { continue; } // 面隣接ブロック位置。 var neighborChunkBlock = chunkBlock.GetNeighbor(side); // 面隣接ブロック インデックス。 var neighborBlockIndex = neighborChunkBlock.GetBlockIndex(); // 未定の場合は面なしとする。 // 正確な描画には面なしとすべきではないが、 // 未定の場合に面を無視することで膨大な数の頂点を節約できる。 // このように節約しない場合、メモリ不足へ容易に到達する。 if (neighborBlockIndex == null) { continue; } if (neighborBlockIndex != Block.EmptyIndex) { // 面隣接ブロックとの関係から対象面の要否を判定。 var neighborBlock = Chunk.Region.BlockCatalog[neighborBlockIndex.Value]; // 半透明な連続した流体ブロックを並べる際、流体ブロック間の面は不要。 // ※流体ブロックは常に半透明を仮定して処理。 if (neighborBlock.Fluid && block.Fluid) { continue; } // 隣接ブロックが半透明ではないならば、不可視面となるため不要。 if (!neighborBlock.Translucent) { continue; } } float lightIntensity = 1; // 面隣接ブロック環境光遮蔽。 lightIntensity *= CalculateAmbientOcclusion(ref neighborChunkBlock, side); // 光量に基づいた頂点色。 var vertexColor = new Color(lightIntensity, lightIntensity, lightIntensity); // メッシュ追加。 ChunkVertices vertices; if (block.Fluid || block.Translucent) { vertices = translucences[segmentX, segmentY, segmentZ]; } else { vertices = opaques[segmentX, segmentY, segmentZ]; } AddMesh(x, y, z, ref vertexColor, meshPart, vertices); } }