public ChunkBlock GetNeighbor(Side side) { if (chunk == null) throw new InvalidOperationException("Chunk is null."); ChunkBlock neighbor; neighbor.chunk = chunk; neighbor.position = position + side.Direction; if (!neighbor.chunk.Contains(neighbor.position)) { neighbor.chunk = chunk.GetNeighborChunk(side); neighbor.position += chunk.Size * side.Reverse().Direction; } return neighbor; }
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; }
public override void StartPaint() { lockedSide = null; base.StartPaint(); }
// まずはカメラを更新。 // ブロック生成消滅の位置を決定する以外にも、 // ブラシ描画のための描画位置の決定に必須。 public override void Update(ICamera camera) { CanPaint = false; var eyePositionWorld = camera.View.Position; var eyeDirection = camera.View.Direction; eyeDirection.Normalize(); var ray = new Ray(eyePositionWorld, eyeDirection); // グリッドに沿っていない視点方向を考慮しての float によるオフセット。 // 約 sqrt(2) / 2 で位置を増加させつつ判定。 var prevTestPosition = new IntVector3(); for (float offset = 0.7f; offset < 10; offset += 0.7f) { var basePositionWorld = eyePositionWorld + eyeDirection * offset; var testPosition = new IntVector3 { X = (int) Math.Floor(basePositionWorld.X), Y = (int) Math.Floor(basePositionWorld.Y), Z = (int) Math.Floor(basePositionWorld.Z) }; if (prevTestPosition == testPosition) continue; var blockIndex = Manager.GetBlockIndex(ref testPosition); if (blockIndex != null && blockIndex != Block.EmptyIndex) { if (ResolvePaintPosition(ref ray, ref testPosition)) { Position = testPosition; CanPaint = true; break; } } prevTestPosition = testPosition; } // 粘着ブラシの消去位置はブラシの位置。 ErasePosition = Position; // ペイント開始かつ面未固定ならば、選択された面で固定。 if (PaintStarted && lockedSide == null) { lockedSide = paintSide; } // ペイント不能の場合は自動的にメッシュ非表示となるため、 // メッシュの更新は不要。 if (!CanPaint) return; UpdateMesh(); Node.Update(true); }
bool ResolvePaintPosition(ref Ray ray, ref IntVector3 brushPosition) { var brushPositionWorld = brushPosition.ToVector3(); Matrix world; Matrix.CreateTranslation(ref brushPositionWorld, out world); var rayDirection = ray.Direction; rayDirection.Normalize(); for (int i = 0; i < triangleInfos.Length; i++) { var side = triangleInfos[i].Side; // 面固定済みならば、それ以外の面を除外。 if (PaintStarted && lockedSide != null && side != lockedSide) continue; // 背面は判定から除外。 if (IsBackFace(side, ref rayDirection)) continue; // 面と視線が交差するか否か。 if (Intersects(ref ray, triangleInfos[i], ref world)) { var testPosition = brushPosition + side.Direction; var blockIndex = Manager.GetBlockIndex(ref testPosition); if (blockIndex == Block.EmptyIndex) { PaintPosition = testPosition; paintSide = side; return true; } } } return false; }
public TriangleInfo(Side side) { Side = side; }
bool IsBackFace(Side side, ref Vector3 eyeDirection) { var normal = side.Direction.ToVector3(); float dot; Vector3.Dot(ref eyeDirection, ref normal, out dot); return (0 < dot); }
protected override void OnNeighborPassivated(Partition neighbor, Side side) { lock (neighbors) { neighbors[side] = null; } base.OnNeighborPassivated(neighbor, side); }
protected override void OnNeighborActivated(Partition neighbor, Side side) { lock (neighbors) { neighbors[side] = neighbor as Chunk; bool allNeighborsExist = true; for (int i = 0; i < neighbors.Count; i++) { if (neighbors[i] == null) { allNeighborsExist = false; break; } } if (allNeighborsExist) { // 隣接チャンクが揃ってからメッシュ構築を要求するが、 // 実際にはメッシュ構築の最中に隣接チャンクが非アクティブ化されることもある。 chunkManager.RequestUpdateMesh(this, ChunkMeshUpdatePriority.Normal); } } base.OnNeighborActivated(neighbor, side); }
public Chunk GetNeighborChunk(Side side) { lock (neighbors) { return neighbors[side]; } }
/// <summary> /// 隣接パーティションが非アクティブになった時に呼び出されます。 /// </summary> /// <param name="neighbor">非アクティブになった隣接パーティション。</param> /// <param name="side">隣接パーティションの方向。</param> protected internal virtual void OnNeighborPassivated(Partition neighbor, Side side) { }