summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/SceneDetailRender.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-28 20:26:42 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-28 20:26:42 +0800
commit08925d4863d8aaf93a974b8fa21a91e931a0928e (patch)
treef18055c232fc1a6022eeab552402185c6948ec02 /src/LivingVillage.Desktop/SceneDetailRender.fs
parent2ab4b8a22aaec3482d62433cfa4b73a657f2a165 (diff)
downloadliving-village-08925d4863d8aaf93a974b8fa21a91e931a0928e.tar.gz
p47: 场景细节升级 — 河道倒影(waterFrameTickAt微抖)+岸边芦苇加密度+鸭/叶漂浮元素+近景屋顶统一光源阴影
Diffstat (limited to 'src/LivingVillage.Desktop/SceneDetailRender.fs')
-rw-r--r--src/LivingVillage.Desktop/SceneDetailRender.fs97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/SceneDetailRender.fs b/src/LivingVillage.Desktop/SceneDetailRender.fs
new file mode 100644
index 0000000..2136d25
--- /dev/null
+++ b/src/LivingVillage.Desktop/SceneDetailRender.fs
@@ -0,0 +1,97 @@
+namespace LivingVillage.Desktop
+
+open System
+open Microsoft.Xna.Framework
+open Microsoft.Xna.Framework.Graphics
+open LivingVillage.Kernel
+
+/// P47 场景细节渲染层:把 `SceneDetail` 的纯函数结果落成画面。
+/// 反射用「光源精灵垂直镜像 + 相位微抖」,岸边芦苇/漂浮元素用既有瓦片精灵与原创点阵图集,
+/// 近景屋顶用既有屋瓦精灵 + 统一方向阴影。全部只读,不改瓦片/可达性。
+module SceneDetailRender =
+
+ let private tileX (camera: Vector2) (x: int) = x * Sim.tilePixels - int camera.X
+ let private tileY (camera: Vector2) (y: int) = y * Sim.tilePixels - int camera.Y
+
+ let private drawFlipVertical
+ (spriteBatch: SpriteBatch)
+ (atlas: Texture2D)
+ (source: Rectangle)
+ (x: int)
+ (y: int)
+ (color: Color) =
+ spriteBatch.Draw(
+ atlas,
+ Rectangle(x, y, Sim.tilePixels, Sim.tilePixels),
+ Nullable(source),
+ color,
+ 0.0f,
+ Vector2.Zero,
+ SpriteEffects.FlipVertically,
+ 0.0f)
+
+ /// 河道倒影:岸上每个光源在其正下方水面格画一份垂直镜像,随 waterFrameTickAt 相位水平微抖。
+ let private drawReflections (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (camera: Vector2) (map: MapGen.Result) (tick: int64) =
+ let source, atlas =
+ match textures.Cc0TileAtlas with
+ | Some cc0 -> VillageArt.cc0TileRect VillageArt.cc0TileIndexStoneLantern, cc0
+ | None -> VillageArt.atlasTileRectangle 20, textures.TileAtlas
+ for (x, y, alpha) in SceneDetail.reflectionStreaks map do
+ let wobble = SceneDetail.reflectionWobbleAt tick x y
+ let color = VillageArt.premultiply 255 214 168 alpha
+ drawFlipVertical spriteBatch atlas source (tileX camera x + wobble) (tileY camera y) color
+
+ /// 岸边芦苇加密度:用既有芦苇精灵补绘一层贴水芦苇。
+ let private drawBankReeds (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (camera: Vector2) (map: MapGen.Result) =
+ match textures.Cc0TileAtlas with
+ | Some cc0 ->
+ for (x, y) in SceneDetail.bankReedTiles map do
+ spriteBatch.Draw(cc0, Rectangle(tileX camera x, tileY camera y, Sim.tilePixels, Sim.tilePixels), Nullable(VillageArt.cc0TileRect VillageArt.cc0TileIndexReeds), Color.White)
+ | None ->
+ let source = VillageArt.atlasTileRectangle 18
+ for (x, y) in SceneDetail.bankReedTiles map do
+ spriteBatch.Draw(textures.TileAtlas, Rectangle(tileX camera x, tileY camera y, Sim.tilePixels, Sim.tilePixels), Nullable(source), Color.White)
+
+ /// 漂浮元素:原创点阵鸭/叶,按 tick 慢漂移。
+ let private drawFloaters (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (camera: Vector2) (map: MapGen.Result) (tick: int64) =
+ for (x, y, kind) in SceneDetail.floatingProps map do
+ let seed = x * 31 + y * 7
+ let dx, dy = SceneDetail.floatDrift tick seed
+ let frame = SceneDetail.floatFrame tick seed
+ let kindIndex =
+ match kind with
+ | SceneDetail.DuckFloat -> FloaterArt.duckIndex
+ | SceneDetail.FallenLeaf -> FloaterArt.leafIndex
+ let source = FloaterArt.sourceRectangle kindIndex frame
+ spriteBatch.Draw(
+ textures.FloaterAtlas,
+ Rectangle(tileX camera x + dx + (Sim.tilePixels - FloaterArt.cellWidth) / 2, tileY camera y + dy + (Sim.tilePixels - FloaterArt.cellHeight) / 2, FloaterArt.cellWidth, FloaterArt.cellHeight),
+ Nullable(source),
+ Color.White)
+
+ /// 近景楼层层错落:屏幕空间前景屋顶带,所有阴影统一投向右下(SceneDetail.shadowOffsetPx)。
+ let private drawForeground (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (viewportWidth: int) (viewportHeight: int) (tick: int64) =
+ let ridgeSource = VillageArt.atlasTileRectangle 8
+ let eaveSource = VillageArt.atlasTileRectangle 9
+ let shadowColor = VillageArt.premultiply 8 12 18 120
+ let dx, dy = SceneDetail.shadowOffsetPx
+ for (x, y, w) in SceneDetail.foregroundRooftops viewportWidth viewportHeight tick do
+ // 统一方向阴影:先把 eave 的暗色镜像平移一份到右下,再画本体。
+ spriteBatch.Draw(textures.TileAtlas, Rectangle(x + dx, y + dy, w, Sim.tilePixels), Nullable(eaveSource), shadowColor)
+ spriteBatch.Draw(textures.TileAtlas, Rectangle(x, y - Sim.tilePixels, w, Sim.tilePixels), Nullable(ridgeSource), Color.White)
+ spriteBatch.Draw(textures.TileAtlas, Rectangle(x, y, w, Sim.tilePixels), Nullable(eaveSource), Color.White)
+
+ /// 一次性绘制全部 P47 场景细节(倒影 → 岸边芦苇 → 漂浮元素 → 近景前景层)。
+ let draw
+ (spriteBatch: SpriteBatch)
+ (textures: VillageArt.ArtTextures)
+ (pixel: Texture2D)
+ (camera: Vector2)
+ (viewportWidth: int)
+ (viewportHeight: int)
+ (map: MapGen.Result)
+ (tick: int64) =
+ drawReflections spriteBatch textures camera map tick
+ drawBankReeds spriteBatch textures camera map
+ drawFloaters spriteBatch textures camera map tick
+ drawForeground spriteBatch textures viewportWidth viewportHeight tick