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);
                }
            }
        }
Example #2
0
        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);
            }
        }
Example #5
0
        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);
            }
        }