diff --git a/Assets/WL/Island/WLIslandGateFlow.cs b/Assets/WL/Island/WLIslandGateFlow.cs index 7002077ed..975d5d739 100644 --- a/Assets/WL/Island/WLIslandGateFlow.cs +++ b/Assets/WL/Island/WLIslandGateFlow.cs @@ -300,17 +300,24 @@ namespace WL.Island var rends = f.GetComponentsInChildren(true); for (int r = 0; r < rends.Length; r++) { - if (rends[r] == null || !rends[r].enabled) continue; + var rr = rends[r]; + if (rr == null) continue; + // ๐Ÿ”ด WL-816zf โ€” ๊ตฌ์—ญ ๋ฐ”์šด์ฆˆ๋Š” **์ด๋ฏธ ๊บผ ๋‘” ๋ Œ๋”๋Ÿฌ๊นŒ์ง€ ์„ผ๋‹ค**. + // 3 s ์žฌํ›‘๊ธฐ์—์„œ๋Š” ๋ฐญ ๋ Œ๋”๋Ÿฌ๊ฐ€ ์ „๋ถ€ ๊บผ์ ธ ์žˆ์–ด, ์ผœ์ง„ ๊ฒƒ๋งŒ ์„ธ๋ฉด ๋‘ ๋ฒˆ์งธ ํ›‘๊ธฐ๋ถ€ํ„ฐ + // ๊ตฌ์—ญ์„ ํ†ต์งธ๋กœ ์žƒ๋Š”๋‹ค(ZoneBoundsValid = false โ†’ ํ™ ๋ฐ์นผยท๋ฐœํŒ ์ž๋ฆฌ ์†Œ์‹ค). + // ๋ˆ ๋ Œ๋”๋Ÿฌ๋„ `bounds` ๋Š” ๊ทธ๋Œ€๋กœ ์œ ํšจํ•˜๋‹ค. if (visible) { - if (!boundsSet) { b = rends[r].bounds; boundsSet = true; } - else b.Encapsulate(rends[r].bounds); - if (!zoneSet) { zone = rends[r].bounds; zoneSet = true; } // WL-816u โ€” ๊ตฌ์—ญ ์ „์ฒด - else zone.Encapsulate(rends[r].bounds); + if (!boundsSet) { b = rr.bounds; boundsSet = true; } + else b.Encapsulate(rr.bounds); + if (!zoneSet) { zone = rr.bounds; zoneSet = true; } // WL-816u โ€” ๊ตฌ์—ญ ์ „์ฒด + else zone.Encapsulate(rr.bounds); } + if (!rr.enabled) continue; // ๐Ÿ”ด WL-816w โ‘ข โ€” ํ™์นธ(Soil)์€ ๋‚จ๊ธด๋‹ค(๋ฐญ ์ž๋ฆฌ๊ฐ€ ํ™์œผ๋กœ ๋ณด์ด๊ฒŒ). ์ฝœ๋ผ์ด๋”๋Š” ์•„๋ž˜์—์„œ ๋ˆ๋‹ค. - if (IsSoilRenderer(cfg, rends[r])) { SoilRenderersKept++; continue; } - rends[r].enabled = false; s_hiddenRends.Add(rends[r]); RenderersHidden++; + // 816zf ๋ฐ์นผ์ด ์ผœ์ ธ ์žˆ์œผ๋ฉด `IsSoilRenderer` ๊ฐ€ false ๋ผ ํ™์นธ๋„ ์—ฌ๊ธฐ์„œ ๊บผ์ง„๋‹ค(๋ฐฐํƒ€). + if (IsSoilRenderer(cfg, rr)) { SoilRenderersKept++; continue; } + rr.enabled = false; s_hiddenRends.Add(rr); RenderersHidden++; } var cols = f.GetComponentsInChildren(true); @@ -368,10 +375,15 @@ namespace WL.Island /// WL-816w ์ง„๋‹จ โ€” ๋‚จ๊ฒจ ๋‘” ํ™์นธ ๋ Œ๋”๋Ÿฌ ์ˆ˜ ยท ๋ˆ ์šธํƒ€๋ฆฌ ๋ Œ๋”๋Ÿฌ ์ˆ˜. public static int SoilRenderersKept, FenceRenderersHidden; - /// WL-816w โ‘ข โ€” ์ด ๋ Œ๋”๋Ÿฌ๊ฐ€ ํ™์นธ(FI `Soil`)์˜ ๊ฒƒ์ธ๊ฐ€(์ž๊ธฐ ์ž์‹  ๋˜๋Š” ๋ถ€๋ชจ). + /// + /// WL-816w โ‘ข โ€” ์ด ๋ Œ๋”๋Ÿฌ๊ฐ€ ํ™์นธ(FI `Soil`)์˜ ๊ฒƒ์ธ๊ฐ€(์ž๊ธฐ ์ž์‹  ๋˜๋Š” ๋ถ€๋ชจ). + /// ๐Ÿ”ด WL-816zf โ€” ๋ถ€๋“œ๋Ÿฌ์šด ํ™ ๋ฐ์นผ(`WLSoftDirt`)์ด ์ผœ์ ธ ์žˆ์œผ๋ฉด **false ๋ฅผ ๋Œ๋ ค ํ™ ํƒ€์ผ์„ ์ˆจ๊ธด๋‹ค**. + /// ๊ฐ์ง„ 1ร—1 ํƒ€์ผ๊ณผ ๋‘ฅ๊ทผ ๋ฐ์นผ์ด ๊ฒน์น˜๋ฉด ๋ฐ์นผ ๊ฐ€์žฅ์ž๋ฆฌ ๋ฐ–์œผ๋กœ ํƒ€์ผ ๋ชจ์„œ๋ฆฌ๊ฐ€ ์‚์ ธ๋‚˜์˜จ๋‹ค(๋ฐฐํƒ€). + /// static bool IsSoilRenderer(WLIslandSettings cfg, Renderer r) { if (cfg.showFarmSoil == 0 || r == null) return false; + if (global::WL.Look.Farm.WLSoftDirt.Active) return false; return r.GetComponentInParent(true) != null; } diff --git a/Assets/WL/Look/Farm/WLIslandGrass.cs b/Assets/WL/Look/Farm/WLIslandGrass.cs index 008900263..6ecbdd788 100644 --- a/Assets/WL/Look/Farm/WLIslandGrass.cs +++ b/Assets/WL/Look/Farm/WLIslandGrass.cs @@ -62,6 +62,8 @@ namespace WL.Look.Farm readonly List _blockers = new List(128); // 816za โ€” ๋ฐญ ์ž๋ฆฌ๋งŒ ๋”ฐ๋กœ. ๋ฐ˜๋“ฏํ•œ ์‚ฌ๊ฐํ˜•์ด ์•„๋‹ˆ๋ผ ใ€Œ๋ชจ์„œ๋ฆฌ ๋‘ฅ๊ธ€๊ฒŒ + ๊ฒฝ๊ณ„ ๋…ธ์ด์ฆˆใ€๋กœ ํŒ์ •ํ•œ๋‹ค. readonly List _farmRounds = new List(8); + // 816zf โ€” ํ™ ๋ฐ์นผ์ด ์‚ด์•„ ์žˆ์œผ๋ฉด ๋ฐญ ์ œ์™ธ๋Š” ์œ„ ๋‘ ๋ชฉ๋ก ๋Œ€์‹  `WLSoftDirt` ๋งˆ์Šคํฌ๊ฐ€ ํŒ์ •ํ•œ๋‹ค. + bool _softDirt; // โ”€โ”€ 816o ์บ์‹œ โ€” ํ•œ ๋ฒˆ ๋ฝ‘์€ ํ›„๋ณด๋ฅผ ๋“ค๊ณ  ์žˆ๋‹ค๊ฐ€, ๊ตฌ๋ฆ„์ด ํ๋ฅด๋ฉด ใ€Œ๊ณ ๋ฅด๊ธฐใ€๋งŒ ๋‹ค์‹œ ํ•œ๋‹ค โ”€โ”€ struct Cand { public float x, z, y; public int def; public float sc; public float yaw; } @@ -741,11 +743,13 @@ namespace WL.Look.Farm _blockers.Clear(); _farmRounds.Clear(); bool round = cfg.farmEdgeRoundEnabled != 0 && cfg.farmEdgeRadius > 0f; + // 816zf โ€” ํ™ ๋ฐ์นผ์ด ๊ฒฝ๊ณ„๋ฅผ ์ฅ๊ณ  ์žˆ์œผ๋ฉด ๋ฐญ ์ œ์™ธ๋Š” **๋ฐ์นผ ๋งˆ์Šคํฌ**๊ฐ€ ์ •ํ•œ๋‹ค(๊ฒฝ๊ณ„ ์ •์˜ 1๊ฐœ). + _softDirt = WLSoftDirt.Ready; var scene = tiles.Count > 0 ? tiles[0].tf.gameObject.scene : default(UnityEngine.SceneManagement.Scene); // โ‘  ๋†์‚ฌ โ€” Farm ์ด Awake ์—์„œ ๋งŒ๋“œ๋Š” BoxCollider(size = (length,2,width))๊ฐ€ ๋ฐญ์˜ ์ •ํ™•ํ•œ ๋„“์ด๋‹ค. var farms = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); - for (int i = 0; i < farms.Length; i++) + for (int i = 0; i < farms.Length && !_softDirt; i++) { var f = farms[i]; if (f == null || f.gameObject.scene != scene) continue; @@ -758,8 +762,9 @@ namespace WL.Look.Farm // โ‘ก ๋†์‚ฌ ํƒ€์ผ ํ•˜๋‚˜ํ•˜๋‚˜(1ร—1) โ€” Farm ์ด ๋Ÿฐํƒ€์ž„์— ๋งŒ๋“ ๋‹ค. ์ด์ค‘ ์•ˆ์ „. // ๋ผ์šด๋“œ ๋ชจ๋“œ์—์„œ๋Š” โ‘  ์•ˆ์— ๋“  ํƒ€์ผ์„ ๋นผ์•ผ ํ•œ๋‹ค. ์•ˆ ๊ทธ๋Ÿฌ๋ฉด ๋ฐ˜๋“ฏํ•œ 1ร—1 ์‚ฌ๊ฐํ˜•๋“ค์ด ๊นŽ์•„๋‚ธ ๋ชจ์„œ๋ฆฌ๋ฅผ ๋„๋กœ ๋ฉ”์šด๋‹ค. + // 816zf ๋ฐ์นผ ๋ชจ๋“œ์—์„œ๋Š” 1ร—1 ์‚ฌ๊ฐํ˜•์ด **๋‘ฅ๊ทผ ๊ฒฝ๊ณ„๋ฅผ ๋„๋กœ ๊ฐ์ง€๊ฒŒ** ๋งŒ๋“ค๋ฏ€๋กœ ์•„์˜ˆ ๋„ฃ์ง€ ์•Š๋Š”๋‹ค. var soils = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); - for (int i = 0; i < soils.Length; i++) + for (int i = 0; i < soils.Length && !_softDirt; i++) { var s = soils[i]; if (s == null || s.gameObject.scene != scene) continue; @@ -801,6 +806,9 @@ namespace WL.Look.Farm var r = _blockers[i]; if (x >= r.xMin && x <= r.xMax && z >= r.yMin && z <= r.yMax) return true; } + // 816zf โ€” ํ™ ๋ฐ์นผ๊ณผ **๊ฐ™์€ ๊ฒฝ๊ณ„**๋ฅผ ์“ด๋‹ค. ๋ฐ์นผ ๊ฐ€์žฅ์ž๋ฆฌ๋ณด๋‹ค `softDirtGrassBite` ์•ˆ์ชฝ๋งŒ ๋ง‰์œผ๋ฏ€๋กœ + // ๊ทธ ํญ๋งŒํผ ํ’€ ํ†ฑ๋‹ˆ๊ฐ€ ํ™ ์•ˆ์œผ๋กœ ํŒŒ๊ณ ๋“ ๋‹ค(๋‘ ๊ตฐ๋ฐ์„œ ๋”ฐ๋กœ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค). + if (_softDirt) return WLSoftDirt.Blocks(x, z); for (int i = 0; i < _farmRounds.Count; i++) if (InRoundedFarm(_farmRounds[i], x, z)) return true; return false; diff --git a/Assets/WL/Look/Farm/WLIslandLook.cs b/Assets/WL/Look/Farm/WLIslandLook.cs index 90cd2d758..327712dca 100644 --- a/Assets/WL/Look/Farm/WLIslandLook.cs +++ b/Assets/WL/Look/Farm/WLIslandLook.cs @@ -131,6 +131,8 @@ namespace WL.Look.Farm if (cfg.applyLook != 0 && cfg.applyReferenceLook != 0) ApplyReferenceLook(cfg, scene); if (cfg.soilToneEnabled != 0) ToneSoil(cfg, scene); + // ๐Ÿ”ด 816zf โ€” ํ™ ๋ฐ์นผ์€ **ํ’€๋ณด๋‹ค ๋จผ์ €** ๋งŒ๋“ ๋‹ค. ํ’€์˜ ๋ฐญ ์ œ์™ธ๊ฐ€ ์ด ๋งˆ์Šคํฌ๋ฅผ ์ฝ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. + if (cfg.softDirtEnabled != 0) SpawnSoftDirt(cfg, scene); if (cfg.grassEnabled != 0) SpawnGrass(cfg, scene); if (cfg.shoreFoamEnabled != 0) SpawnShoreFoam(cfg, scene); @@ -140,6 +142,7 @@ namespace WL.Look.Farm + " ยท ์กฐ๋ช… " + (cfg.applyLighting != 0 ? "๋ฐ๋ชจ" : "์›๋ณธ") + " ยท ReferenceLook " + (ReferenceLookApplied ? "on" : "off") + " ยท ํ’€ " + WLIslandGrass.LastLog + + " ยท ํ™๋ฐ์นผ " + WLSoftDirt.LastLog + " ยท ๋‘˜๋ ˆ๊ฑฐํ’ˆ " + WLShoreFoam.LastLog); // ๋Šฆ๊ฒŒ ์ƒ๊ธฐ๋Š” ์˜ค๋ธŒ์ ํŠธ(์ƒ์ธยท์ž‘๋ฌผยท์•„์ดํ…œ)๊นŒ์ง€ ๋‹ค์‹œ ํ›‘๋Š”๋‹ค @@ -158,6 +161,18 @@ namespace WL.Look.Farm // ๊ทธ๋ž˜์„œ ์กฐ๋ช…์€ ์žฌํ›‘๊ธฐ์—์„œ **๋‹ค์‹œ ๊ฑธ์ง€ ์•Š๋Š”๋‹ค** โ€” ๋‹ค์‹œ ๊ฑธ๋ฉด `WLReferenceLook` ์˜ // ๋ฌด๋“œ(Flat)๋ฅผ Skybox ๋กœ ๋˜๋Œ๋ ค ์˜คํžˆ๋ ค ๋ฃฉ์ด ๋ฐ”๋€๋‹ค(์‹ค์ธก์œผ๋กœ ํ™•์ธ). if (cfg.soilToneEnabled != 0) ToneSoil(cfg, scene); + // ๐Ÿ”ด 816zf โ€” ๋ฐญ ๊ตฌ์—ญ(`WLIslandGateFlow.ZoneBounds`)์€ ๊ฒŒ์ดํŠธ ํ”Œ๋กœ์šฐ๊ฐ€ ๋ฐญ์„ ํ›‘์€ ๋’ค์—์•ผ ์ •ํ•ด์ง„๋‹ค. + // ์žฌํ›‘๊ธฐ์—์„œ ๊ตฌ์—ญ์ด ์ฒ˜์Œ ์žกํžˆ๊ฑฐ๋‚˜ ๋ฐ”๋€Œ๋ฉด ๋ฐ์นผ์„ ๊ตฝ๊ณ , **๊ทธ๋•Œ๋งŒ** ํ’€๋„ ๋‹ค์‹œ ๊น๋‹ค(๊ฒฝ๊ณ„๊ฐ€ ๊ฐ™์•„์ง€๊ฒŒ). + if (cfg.softDirtEnabled != 0) + { + int before = WLSoftDirt.Rebuilds; + SpawnSoftDirt(cfg, scene); + if (WLSoftDirt.Rebuilds != before && cfg.grassEnabled != 0) + { + var g2 = Object.FindFirstObjectByType(FindObjectsInactive.Include); + if (g2 != null) { g2.Rebuild(); GrassRebuilds++; } + } + } HookIslands(cfg, scene); } } @@ -248,6 +263,13 @@ namespace WL.Look.Farm static void RequestGrassRebuild(WLIslandLookSettings cfg) { + // ๐Ÿ”ด 816zf โ€” ํ™ ๋ฐ์นผ์„ **๋จผ์ €** ๋‹ค์‹œ ๊ตฝ๋Š”๋‹ค. ์„ฌ ํ™•์žฅยท๋ฐญ ์žฌ๊ตฌ์„ฑ์œผ๋กœ ๊ตฌ์—ญ์ด ๋ฐ”๋€Œ๋ฉด + // ํ’€์˜ ๋ฐญ ์ œ์™ธ๋„ ๊ทธ ์ƒˆ ๊ฒฝ๊ณ„๋ฅผ ์ฝ์–ด์•ผ ํ™๊ณผ ํ’€์ด ๊ฐ™์€ ์„ ์—์„œ ๋งŒ๋‚œ๋‹ค. + if (cfg.softDirtEnabled != 0) + { + var sd = Object.FindFirstObjectByType(FindObjectsInactive.Include); + if (sd != null) { sd.cfg = cfg; sd.Rebuild(); } + } if (cfg.grassEnabled != 0) { var g = Object.FindFirstObjectByType(FindObjectsInactive.Include); @@ -263,6 +285,24 @@ namespace WL.Look.Farm public static int ShoreFoamRebuilds; public static bool ShoreFoamSpawned; + public static bool SoftDirtSpawned; + + /// 816zf โ€” ๋ฐญ ์ž๋ฆฌ ๋ถ€๋“œ๋Ÿฌ์šด ํ™ ๋ฐ์นผ. ํ’€ยท๊ฑฐํ’ˆ๊ณผ ๊ฐ™์€ ์ž๋ฆฌ์—์„œ ๋งŒ๋“ค๊ณ  ๊ฐ™์€ ํ›…์œผ๋กœ ๋‹ค์‹œ ๋งŒ๋“ ๋‹ค. + public static void SpawnSoftDirt(WLIslandLookSettings cfg, Scene scene) + { + var d = Object.FindFirstObjectByType(FindObjectsInactive.Include); + if (d == null) + { + var go = new GameObject(WLSoftDirt.ObjectName); + SceneManager.MoveGameObjectToScene(go, scene); + go.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); + d = go.AddComponent(); + d.cfg = cfg; + d.Rebuild(); // OnEnable ์ด cfg ์ „์— ๋Œ ์ˆ˜ ์žˆ๋‹ค + } + else { d.cfg = cfg; d.Rebuild(); } + SoftDirtSpawned = true; + } /// ์„ฌ ๋‘˜๋ ˆ ๊ฑฐํ’ˆ ๋  โ€” ํ’€๊ณผ ๊ฐ™์€ ์ž๋ฆฌ์—์„œ ๋งŒ๋“ค๊ณ  ๊ฐ™์€ ํ›…์œผ๋กœ ๋‹ค์‹œ ๋งŒ๋“ ๋‹ค(816j2). public static void SpawnShoreFoam(WLIslandLookSettings cfg, Scene scene) diff --git a/Assets/WL/Look/Farm/WLIslandLookSettings.cs b/Assets/WL/Look/Farm/WLIslandLookSettings.cs index f56d69a5b..903039720 100644 --- a/Assets/WL/Look/Farm/WLIslandLookSettings.cs +++ b/Assets/WL/Look/Farm/WLIslandLookSettings.cs @@ -235,6 +235,57 @@ namespace WL.Look.Farm [Tooltip("๊ฑฐํ’ˆ์ด ํ๋ฅด๋Š” ์†๋„(1 = ์ดˆ๋‹น ๋ฌด๋Šฌ 1์นธ). 0 ์ด๋ฉด ์ •์ง€.")] public float shoreFoamScroll = 0.06f; + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ยง816zf ๋ถ€๋“œ๋Ÿฌ์šด ํ™ ๋ฐ์นผ + [Header("ยง816zf โ€” ํ™ ์ž๋ฆฌ๋ฅผ ๋ฐ๋ชจ์ฒ˜๋Ÿผ ใ€Œ๋‘ฅ๊ธ€๊ฒŒ ๋ฒˆ์ง€๊ฒŒใ€ (PD 2026-09-15)")] + [Tooltip("0 = ์ง€๊ธˆ(๊ฐ์ง„ 1ร—1 ํ™ ํƒ€์ผ ยท 100 % ๋ณต๊ท€) ยท 1 = ๋ฐญ ๊ตฌ์—ญ์„ ๋ฎ๋Š” **๋ถ€๋“œ๋Ÿฌ์šด ํ™ ๋ฐ์นผ 1์žฅ**" + + "(๋“œ๋กœ์šฐ์ฝœ +1)์œผ๋กœ ๋ฐ”๊พธ๊ณ  ํ™ ํƒ€์ผ ๋ Œ๋”๋Ÿฌ๋Š” ์ˆจ๊ธด๋‹ค(`WLIslandSettings.showFarmSoil` ๊ณผ ๋ฐฐํƒ€). " + + "816za ํ’€ ์ œ์™ธ๋„ ์ด ๋ฐ์นผ๊ณผ **๊ฐ™์€ ๊ฒฝ๊ณ„**๋ฅผ ์“ฐ๊ฒŒ ๋œ๋‹ค.")] + public int softDirtEnabled = 1; + + [Tooltip("๋ฐ์นผ์— ์“ธ ๋จธํ‹ฐ๋ฆฌ์–ผ. **๋น„์›Œ ๋‘๋Š” ๊ฒƒ์ด ๊ธฐ๋ณธ** โ€” ๋น„์šฐ๋ฉด 816j2 ๊ฑฐํ’ˆ ๋ ์™€ ๊ฐ™์€ URP Unlit ์…ฐ์ด๋”๋กœ " + + "๋Ÿฐํƒ€์ž„์— ๋ฐ˜ํˆฌ๋ช… ๋จธํ‹ฐ๋ฆฌ์–ผ์„ ๋งŒ๋“ ๋‹ค(์—์…‹ 0์ค„). ์ฑ„์šฐ๋ฉด ๊ทธ ๋จธํ‹ฐ๋ฆฌ์–ผ์˜ ๋ณต์‚ฌ๋ณธ์„ ์“ด๋‹ค.")] + public Material softDirtMaterial; + + [Tooltip("ํ™ ์ƒ‰(๋ฆฌ๋‹ˆ์–ด) โ€” 816r ๋ฐ๋ชจ ํ™ ํ†ค `#A4A88B` ๋ฅผ ๋ฆฌ๋‹ˆ์–ด๋กœ ์˜ฎ๊ธด ๊ฐ’.")] + public Color softDirtColor = new Color(0.371f, 0.392f, 0.258f, 1f); + + [Tooltip("๋ฐ์นผ ์ „์ฒด์˜ ๋ถˆํˆฌ๋ช…๋„. 1 = ํ™์ƒ‰ ๊ทธ๋Œ€๋กœ ยท ๋‚ฎ์ถ”๋ฉด ๋ฐ‘์˜ ์ž”๋””๊ฐ€ ๋น„์ณ ํ™์ด ์˜…์–ด์ง„๋‹ค.")] + [Range(0f, 1f)] public float softDirtOpacity = 1f; + + [Tooltip("ํ™์นธ ์œ—๋ฉด๋ณด๋‹ค ์–ผ๋งˆ๋‚˜ ์œ„์— ๊น” ๊ฒƒ์ธ๊ฐ€(m). ๋„ˆ๋ฌด ์ž‘์œผ๋ฉด z-fighting, ๋„ˆ๋ฌด ํฌ๋ฉด ๋–  ๋ณด์ธ๋‹ค.")] + public float softDirtY = 0.02f; + + [Tooltip("๋ฐญ ๊ตฌ์—ญ ๋ฐ”์šด์ฆˆ๋ฅผ ์‚ฌ๋ฐฉ์œผ๋กœ ์ด๋งŒํผ(m) ๋„“ํ˜€ ํ™์œผ๋กœ ์นœ๋‹ค. ๋ฐ์นผ ๊ฐ€์žฅ์ž๋ฆฌ๊ฐ€ ํŽ˜์ด๋“œ๋กœ " + + "๋ฌผ๋Ÿฌ๋‚˜๋Š” ๋งŒํผ์„ ๋ฉ”์›Œ, ํ™ ์ž๋ฆฌ๊ฐ€ ์ง€๊ธˆ๋ณด๋‹ค ์ข์•„ ๋ณด์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.")] + public float softDirtExpand = 0.2f; + + [Tooltip("ํ™ ์‚ฌ๊ฐํ˜•์˜ ๋„ค ๋ชจ์„œ๋ฆฌ๋ฅผ ๊นŽ๋Š” ๋ฐ˜์ง€๋ฆ„(m). ํด์ˆ˜๋ก ๋‘ฅ๊ธ€๊ฒŒ ๋ฒˆ์ง„ ๋ชจ์–‘์œผ๋กœ ์ฝํžŒ๋‹ค.")] + public float softDirtCornerRadius = 1.4f; + + [Tooltip("ํ™ ๊ฒฝ๊ณ„๋ฅผ ํ”๋“œ๋Š” ์ง„ํญ(m). ์ด๋งŒํผ ํ™์ด ํ’€ ์ชฝ์œผ๋กœ ๋ฐ€๋ ค๋‚˜๊ฑฐ๋‚˜ ๋ฌผ๋Ÿฌ๋‚˜ ๊ฒฝ๊ณ„๊ฐ€ ๋“ค์ญ‰๋‚ ์ญ‰ํ•ด์ง„๋‹ค.")] + public float softDirtNoise = 0.45f; + + [Tooltip("๊ฒฝ๊ณ„ ๋…ธ์ด์ฆˆ์˜ ์…€ ํฌ๊ธฐ(m). ์ž‘์„์ˆ˜๋ก ์ž˜๊ฒŒ ๋“ค์ญ‰๋‚ ์ญ‰ํ•˜๊ณ , ํด์ˆ˜๋ก ํฌ๊ฒŒ ๊ตฝ์ด์นœ๋‹ค.")] + public float softDirtNoiseCell = 0.9f; + + [Tooltip("๊ฐ€์žฅ์ž๋ฆฌ๊ฐ€ ํ™ โ†’ ํ’€๋กœ ์‚ฌ๋ผ์ง€๋Š” ํญ(m). ๋ฐ๋ชจ์˜ ์Šคํ”Œ๋žซ ๋ฒˆ์ง์— ํ•ด๋‹นํ•œ๋‹ค(0.8~1.2 ๊ถŒ์žฅ).")] + public float softDirtFeather = 1.0f; + + [Tooltip("ํ’€์„ ๋ฐ์นผ ๊ฐ€์žฅ์ž๋ฆฌ๋ณด๋‹ค ์ด๋งŒํผ(m) **์•ˆ์ชฝ๊นŒ์ง€** ๋“ค์—ฌ๋ณด๋‚ธ๋‹ค โ€” ํ™ ๊ฐ€์žฅ์ž๋ฆฌ์— ํ’€ ํ†ฑ๋‹ˆ๊ฐ€ " + + "ํŒŒ๊ณ ๋“  ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ๊ฐ’. 0 ์ด๋ฉด ํ’€์ด ํ™ ๊ฒฝ๊ณ„์—์„œ ๋”ฑ ๋Š๊ธด๋‹ค.")] + public float softDirtGrassBite = 0.3f; + + [Tooltip("๋ฐ์นผ ์‚ฌ๊ฐํ˜•์„ ๊ตฌ์—ญ ๋ฐ”๊นฅ์œผ๋กœ ๋” ๋‘๋Š” ์—ฌ์œ  ํญ(m). ํŽ˜์ด๋“œ+๋…ธ์ด์ฆˆ๊ฐ€ ์ž˜๋ฆฌ์ง€ ์•Š์„ ๋งŒํผ " + + "์ž๋™์œผ๋กœ ์ปค์ง„๋‹ค โ€” ๋ณดํ†ต ์†๋Œˆ ์ผ์ด ์—†๋‹ค.")] + public float softDirtPad = 1.5f; + + [Tooltip("๋งˆ์Šคํฌ ํ…์Šค์ฒ˜ ํ•œ ๋ณ€์˜ ํ”ฝ์…€ ์ˆ˜. 256 = 256 KB ยท ๊ตฝ๋Š” ๋ฐ ํ•œ ๋ฒˆ ๋ช‡ ms. " + + "๊ฒฝ๊ณ„๊ฐ€ ๋ญ‰๊ฐœ์ ธ ๋ณด์ด๋ฉด 512 ๋กœ ์˜ฌ๋ฆฐ๋‹ค.")] + public int softDirtTexRes = 256; + + [Tooltip("ํ™ ์–ผ๋ฃฉ์˜ ์„ธ๊ธฐ(0~1). ํ•œ ๋ฉ์–ด๋ฆฌ ๋‹จ์ƒ‰์œผ๋กœ ๋ณด์ด์ง€ ์•Š๊ฒŒ ๋ฐ๊ธฐ๋ฅผ ์ด๋งŒํผ ํ”๋“ ๋‹ค.")] + [Range(0f, 1f)] public float softDirtTexNoise = 0.08f; + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ยง1 ํ’€๋ฐญ [Header("ยง1 โ€” ์„ฌ ํƒ€์ผ ์œ„ ํ’€๋ฐญ (๋ฐ๋ชจ์™€ ๊ฐ™์€ ์ธ์Šคํ„ด์‹ฑ)")] [Tooltip("0 ์ด๋ฉด ํ’€์„ ๊น”์ง€ ์•Š๋Š”๋‹ค.")] diff --git a/Assets/WL/Look/Farm/WLSoftDirt.cs b/Assets/WL/Look/Farm/WLSoftDirt.cs new file mode 100644 index 000000000..4fd49ea56 --- /dev/null +++ b/Assets/WL/Look/Farm/WLSoftDirt.cs @@ -0,0 +1,358 @@ +// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// WLSoftDirt.cs โ€” ๋ฐญ(ํ™) ์ž๋ฆฌ๋ฅผ ๋ฐ๋ชจ์ฒ˜๋Ÿผ **๋‘ฅ๊ธ€๊ฒŒ ๋ฒˆ์ง€๋Š” ํ•œ ์žฅ**์œผ๋กœ (WL-816zf ยท #816) +// +// PD ์ง€์‹œ(2026-09-15) ใ€Œ๋ฐ๋ชจ์”ฌ์„ ๋ณด๋ฉด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ™๊ณผ ์ž”๋””๊ฐ€ ๋ผ์šด๋”ฉ ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ฐฐ์น˜๋˜์–ด ์žˆ์–ด. +// ๋ฐ๋ชจ์”ฌ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๋งž์ถœ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„์„œ ์ตœ๋Œ€ํ•œ ๋งž์ถฐ.ใ€ +// +// โ–  ์™œ ๊ฐ์ ธ ๋ณด์ด๋Š”๊ฐ€ +// ๋ฐ๋ชจ๋Š” **ํ„ฐ๋ ˆ์ธ ์Šคํ”Œ๋žซ**์ด๋ผ ํ™์ด ์ž”๋”” ์†์œผ๋กœ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋ฒˆ์ง„๋‹ค. +// ์šฐ๋ฆฌ ์„ฌ์€ FI ์˜ **1ร—1 ํ™ ํƒ€์ผ(Soil)** ๋ฌถ์Œ์ด๋ผ ๊ฒฝ๊ณ„๊ฐ€ ๊ณ„๋‹จ์ฒ˜๋Ÿผ ๊ฐ์ง„๋‹ค. +// ํƒ€์ผ์„ ์•„๋ฌด๋ฆฌ ๊ณจ๋ผ ์ˆจ๊ฒจ๋„ ์ตœ์†Œ ๋‹จ์œ„๊ฐ€ 1 m ๋ผ ใ€Œ๋‘ฅ๊ธ€๊ฒŒใ€๊ฐ€ ์•ˆ ๋‚˜์˜จ๋‹ค. +// +// โ–  ์–ด๋–ป๊ฒŒ ๋ฐ”๊พธ๋Š”๊ฐ€ +// ๋ฐญ ๊ตฌ์—ญ(`WLIslandGateFlow.ZoneBounds`)์„ ๋ฎ๋Š” **ํ‰๋ฉด ๋ฉ”์‹œ 1์žฅ**(4์ •์  ยท 2์‚ผ๊ฐํ˜•)์— +// ๋Ÿฐํƒ€์ž„์— ๊ตฌ์šด **๋งˆ์Šคํฌ ํ…์Šค์ฒ˜**(๋ชจ์„œ๋ฆฌ๋ฅผ ๊นŽ์€ ์‚ฌ๊ฐํ˜• + ๊ฒฝ๊ณ„ ๋…ธ์ด์ฆˆ + ๊ฐ€์žฅ์ž๋ฆฌ ํŽ˜์ด๋“œ)๋ฅผ ์”Œ์šด๋‹ค. +// ๋ชจ์–‘์€ ์ „๋ถ€ ํ…์Šค์ฒ˜์˜ ์•ŒํŒŒ๊ฐ€ ๋งŒ๋“ค๋ฏ€๋กœ ๋ฉ”์‹œ๋Š” ์‚ฌ๊ฐํ˜• ํ•˜๋‚˜๋ฉด ๋œ๋‹ค = **๋“œ๋กœ์šฐ์ฝœ +1**. +// ์ผœ์ง€๋ฉด ๊ฐ์ง„ ํ™ ํƒ€์ผ ๋ Œ๋”๋Ÿฌ๋Š” ์ˆจ๊ธด๋‹ค(`WLIslandGateFlow.IsSoilRenderer` ๊ฐ€ ๋ฐฐํƒ€ ์ฒ˜๋ฆฌ). +// +// โ–  ํ’€๊ณผ ํ™์ด ๊ฐ™์€ ์„ ์„ ์“ด๋‹ค +// 816za ์˜ ํ’€ ์ œ์™ธ(๋ฐญ ์ž๋ฆฌ)๋Š” ์ด ํŒŒ์ผ์˜ `SignedDist` ๋ฅผ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค. +// ํ’€์€ ๋ฐ์นผ ๊ฐ€์žฅ์ž๋ฆฌ๋ณด๋‹ค `softDirtGrassBite`(m) **์•ˆ์ชฝ๊นŒ์ง€** ๋“ค์–ด์™€, ํ™ ๊ฐ€์žฅ์ž๋ฆฌ์— +// ํ’€ ํ†ฑ๋‹ˆ๊ฐ€ ํŒŒ๊ณ ๋“  ๊ฒƒ์ฒ˜๋Ÿผ ์ฝํžŒ๋‹ค โ€” ๊ฒฝ๊ณ„๋ฅผ ๋‘ ๊ตฐ๋ฐ์„œ ๋”ฐ๋กœ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค. +// +// โ–  ๋˜๋Œ๋ฆฌ๊ธฐ โ€” `softDirtEnabled = 0` ์ด๋ฉด ํ™ ํƒ€์ผ์ด ๊ทธ๋Œ€๋กœ ๋ณด์ด๊ณ  ํ’€ ์ œ์™ธ๋„ 816za ๋ฐฉ์‹์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค. +// +// ๐Ÿ”ด FI ์ฝ”๋“œ 0์ค„ ยท ์”ฌ ํŒŒ์ผ 0์ค„ ยท ์—์…‹ 0์ค„. ๋ฉ”์‹œยทํ…์Šค์ฒ˜ยท๋จธํ‹ฐ๋ฆฌ์–ผ ์ „๋ถ€ ๋Ÿฐํƒ€์ž„ ์ƒ์„ฑ์ด๋‹ค. +// ๐Ÿ”ด Debug.LogError / Debug.LogException ์„ ์“ฐ์ง€ ์•Š๋Š”๋‹ค(ErrorLogHookManager ํŒ์—…). +// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +using UnityEngine; +using FISoil = CryingSnow.FarmingIsland.Soil; + +namespace WL.Look.Farm +{ + [DisallowMultipleComponent] + public sealed class WLSoftDirt : MonoBehaviour + { + public const string ObjectName = "~WL_SoftDirt"; + + // โ”€โ”€ ์ง„๋‹จ(ํ”„๋กœ๋ธŒยท๋ณด๊ณ ๊ฐ€ ์ฝ๋Š”๋‹ค) + public static int Rebuilds; + public static string LastLog = "๊บผ์ง"; + + public WLIslandLookSettings cfg; + + MeshFilter _mf; + MeshRenderer _mr; + Mesh _mesh; + Texture2D _tex; + Material _inst; + + static readonly int IdBaseMap = Shader.PropertyToID("_BaseMap"); + static readonly int IdMainTex = Shader.PropertyToID("_MainTex"); + static readonly int IdBaseColor = Shader.PropertyToID("_BaseColor"); + static readonly int IdColor = Shader.PropertyToID("_Color"); + static readonly int IdSurface = Shader.PropertyToID("_Surface"); + static readonly int IdBlend = Shader.PropertyToID("_Blend"); + static readonly int IdSrcBlend = Shader.PropertyToID("_SrcBlend"); + static readonly int IdDstBlend = Shader.PropertyToID("_DstBlend"); + static readonly int IdZWrite = Shader.PropertyToID("_ZWrite"); + static readonly int IdCull = Shader.PropertyToID("_Cull"); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ๋งˆ์Šคํฌ โ€” ํ™ ๊ฒฝ๊ณ„์˜ **๋‹จ์ผ ์ •์˜**. ๋ฐ์นผ ํ…์Šค์ฒ˜์™€ ํ’€ ์ œ์™ธ๊ฐ€ ๊ฐ™์€ ๊ฐ’์„ ์ฝ๋Š”๋‹ค. + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + static bool s_ready; + static float s_cx, s_cz, s_hx, s_hz, s_rad, s_noise, s_cell, s_bite; + + /// ์„ค์ •์ด ๋ฐ์นผ์„ ์“ฐ๊ธฐ๋กœ ํ–ˆ๋Š”๊ฐ€ โ€” ํ™ ํƒ€์ผ ์ˆจ๊น€(๋ฐฐํƒ€)์„ ๊ฐ€๋ฅด๋Š” ๊ฐ’. ๊ตฌ์—ญ์„ ๋ชฐ๋ผ๋„ ์ฐธ์ด๋‹ค. + public static bool Active + { + get + { + var c = WLIslandLookSettings.Instance; + return c != null && c.enabled_ != 0 && c.softDirtEnabled != 0; + } + } + + /// ๋งˆ์Šคํฌ๋ฅผ ์‹ค์ œ๋กœ ๊ณ„์‚ฐํ•ด ๋‘์—ˆ๋Š”๊ฐ€(๋ฐญ ๊ตฌ์—ญ์„ ์•Œ์•„๋‚ธ ๋’ค). ํ’€ ์ œ์™ธ๋Š” ์ด๊ฒƒ์ด ์ฐธ์ผ ๋•Œ๋งŒ ๋ฐ”๋€๋‹ค. + public static bool Ready { get { return s_ready && Active; } } + + /// + /// ํ™ ๊ฒฝ๊ณ„๊นŒ์ง€์˜ ๋ถ€ํ˜ธ ์žˆ๋Š” ๊ฑฐ๋ฆฌ(m). **์Œ์ˆ˜ = ํ™ ์•ˆ์ชฝ ยท ์–‘์ˆ˜ = ๋ฐ”๊นฅ(ํ’€)**. + /// ๋ชจ์„œ๋ฆฌ๋ฅผ `softDirtCornerRadius` ๋งŒํผ ๊นŽ์€ ์‚ฌ๊ฐํ˜•์— ์…€ ๋…ธ์ด์ฆˆ๋กœ ๊ฒฝ๊ณ„๋ฅผ ํ”๋“  ๋ชจ์–‘์ด๋‹ค. + /// + public static float SignedDist(float x, float z) + { + float dx = Mathf.Abs(x - s_cx) - (s_hx - s_rad); + float dz = Mathf.Abs(z - s_cz) - (s_hz - s_rad); + float ox = Mathf.Max(dx, 0f), oz = Mathf.Max(dz, 0f); + float d = Mathf.Min(Mathf.Max(dx, dz), 0f) + Mathf.Sqrt(ox * ox + oz * oz) - s_rad; + if (s_noise > 0f && s_cell > 0.0001f) + d -= (CellNoise(x / s_cell, z / s_cell) * 2f - 1f) * s_noise; + return d; + } + + /// ์—ฌ๊ธฐ์— ํ’€์„ ๊น”์ง€ ๋ง ๊ฒƒ์ธ๊ฐ€ โ€” ๋ฐ์นผ ๊ฐ€์žฅ์ž๋ฆฌ๋ณด๋‹ค `softDirtGrassBite` ๋งŒํผ **๋” ์•ˆ์ชฝ**๋งŒ ๋ง‰๋Š”๋‹ค. + public static bool Blocks(float x, float z) + { + if (!s_ready) return false; + // ์‹ธ๊ฒŒ ๋จผ์ € ๊ฑฐ๋ฅธ๋‹ค โ€” ๊ตฌ์—ญ ๋ฐ–์ด๋ฉด ๋…ธ์ด์ฆˆ๋ฅผ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค. + if (Mathf.Abs(x - s_cx) > s_hx + s_noise || Mathf.Abs(z - s_cz) > s_hz + s_noise) return false; + return SignedDist(x, z) < -s_bite; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + void OnEnable() { Ensure(); _dirty = true; Rebuild(); } + void OnDisable() { if (_mr != null) _mr.enabled = false; s_ready = false; } + + void OnDestroy() + { + if (_inst != null) Destroy(_inst); + if (_tex != null) Destroy(_tex); + if (_mesh != null) Destroy(_mesh); + s_ready = false; + } + + void Ensure() + { + if (_mf == null) _mf = GetComponent(); + if (_mf == null) _mf = gameObject.AddComponent(); + if (_mr == null) _mr = GetComponent(); + if (_mr == null) _mr = gameObject.AddComponent(); + _mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; + _mr.receiveShadows = false; + _mr.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off; + _mr.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off; + _mr.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; + if (_mesh == null) + { + _mesh = new Mesh { name = "WL_SoftDirtMesh" }; + _mesh.MarkDynamic(); + _mesh.hideFlags = HideFlags.DontSave; + } + _mf.sharedMesh = _mesh; + } + + // ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ตฌ์šด ์กฐ๊ฑด โ€” ๊ฐ™์œผ๋ฉด ๋‹ค์‹œ ๊ตฝ์ง€ ์•Š๋Š”๋‹ค(1์ดˆ ํด๋ง์—์„œ ๊ณต์งœ๋กœ ๋Œ๊ฒŒ). + bool _dirty = true; + float _lastCx, _lastCz, _lastHx, _lastHz, _lastY, _lastSig = float.NaN; + + /// ๋‹ค์‹œ ๋งŒ๋“ ๋‹ค(์„ฌ ํ™•์žฅยท๋ฐญ ์žฌ๊ตฌ์„ฑ). ๋ฉฑ๋“ฑ โ€” ์กฐ๊ฑด์ด ๊ทธ๋Œ€๋กœ๋ฉด ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. + public void Rebuild() + { + Ensure(); + if (cfg == null) cfg = WLIslandLookSettings.Instance; + if (cfg == null || cfg.enabled_ == 0 || cfg.softDirtEnabled == 0) + { _mr.enabled = false; s_ready = false; LastLog = "๊บผ์ง"; return; } + + if (!global::WL.Island.WLIslandGateFlow.ZoneBoundsValid) + { _mr.enabled = false; s_ready = false; LastLog = "๋ฐญ ๊ตฌ์—ญ ๋ฏธํ™•์ • โ€” ๋Œ€๊ธฐ"; return; } + + var zone = global::WL.Island.WLIslandGateFlow.ZoneBounds; + float hx = zone.extents.x + cfg.softDirtExpand; + float hz = zone.extents.z + cfg.softDirtExpand; + if (hx <= 0.01f || hz <= 0.01f) + { _mr.enabled = false; s_ready = false; LastLog = "๋ฐญ ๊ตฌ์—ญ ๋„“์ด 0"; return; } + + float y = SoilTopY(zone) + cfg.softDirtY; + + // ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ๋‹ค์‹œ ๊ตฝ๋Š”๋‹ค(์ธ์ŠคํŽ™ํ„ฐ์—์„œ ๋งŒ์ ธ๋„ ๋‹ค์Œ ํด๋ง์— ๋ฐ˜์˜๋œ๋‹ค). + float sig = cfg.softDirtCornerRadius * 7.1f + cfg.softDirtNoise * 13.3f + cfg.softDirtNoiseCell * 3.7f + + cfg.softDirtFeather * 29.3f + cfg.softDirtGrassBite * 5.9f + cfg.softDirtPad * 11.9f + + cfg.softDirtTexRes * 0.017f + cfg.softDirtTexNoise * 23.1f + cfg.softDirtOpacity * 17.7f + + cfg.softDirtColor.r * 101f + cfg.softDirtColor.g * 103f + cfg.softDirtColor.b * 107f; + + bool same = !_dirty && _tex != null && _inst != null + && Mathf.Abs(_lastCx - zone.center.x) < 0.001f && Mathf.Abs(_lastCz - zone.center.z) < 0.001f + && Mathf.Abs(_lastHx - hx) < 0.001f && Mathf.Abs(_lastHz - hz) < 0.001f + && Mathf.Abs(_lastY - y) < 0.001f && Mathf.Abs(_lastSig - sig) < 0.0001f; + + // ๋งˆ์Šคํฌ ๊ฐ’์€ ๋งค๋ฒˆ ๊ฐฑ์‹ ํ•œ๋‹ค โ€” ํ’€(816za)์ด ์ฝ๋Š” ๋‹จ์ผ ์ถœ์ฒ˜๋‹ค. + s_cx = zone.center.x; s_cz = zone.center.z; + s_hx = hx; s_hz = hz; + s_rad = Mathf.Clamp(cfg.softDirtCornerRadius, 0f, Mathf.Min(hx, hz)); + s_noise = Mathf.Max(0f, cfg.softDirtNoise); + s_cell = Mathf.Max(0.05f, cfg.softDirtNoiseCell); + s_bite = Mathf.Max(0f, cfg.softDirtGrassBite); + s_ready = true; + + if (same) { _mr.enabled = true; return; } + + if (!EnsureMaterial()) { _mr.enabled = false; s_ready = false; LastLog = "๐Ÿ”ด URP Unlit ์…ฐ์ด๋”๋ฅผ ๋ชป ์ฐพ์Œ"; return; } + + float feather = Mathf.Max(0.05f, cfg.softDirtFeather); + float pad = Mathf.Max(cfg.softDirtPad, feather + s_noise + 0.25f); + int res = Mathf.Clamp(cfg.softDirtTexRes, 32, 1024); + + BakeMask(hx + pad, hz + pad, res, feather); + BuildQuad(hx + pad, hz + pad, y); + + _inst.SetTexture(IdBaseMap, _tex); + if (_inst.HasProperty(IdMainTex)) _inst.SetTexture(IdMainTex, _tex); + var col = cfg.softDirtColor; col.a = Mathf.Clamp01(cfg.softDirtOpacity); + _inst.SetColor(IdBaseColor, col); + if (_inst.HasProperty(IdColor)) _inst.SetColor(IdColor, col); + _mr.sharedMaterial = _inst; + _mr.enabled = true; + + _lastCx = s_cx; _lastCz = s_cz; _lastHx = hx; _lastHz = hz; _lastY = y; _lastSig = sig; + _dirty = false; + Rebuilds++; + + LastLog = "ํ™ ๋ฐ์นผ โ€” ๊ตฌ์—ญ " + (hx * 2f).ToString("F1") + "ร—" + (hz * 2f).ToString("F1") + + " m ยท ๋ชจ์„œ๋ฆฌ R " + s_rad.ToString("F2") + " m ยท ๋…ธ์ด์ฆˆ ยฑ" + s_noise.ToString("F2") + + " m(์…€ " + s_cell.ToString("F2") + ") ยท ํŽ˜์ด๋“œ " + feather.ToString("F2") + + " m ยท ํ’€ ํŒŒ๊ณ ๋“ฆ " + s_bite.ToString("F2") + " m ยท ํ…์Šค์ฒ˜ " + res + "ยฒ ยท ๋“œ๋กœ์šฐ์ฝœ +1"; + cfg.Log(LastLog); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + /// + /// ํ™์นธ(Soil) ์œ—๋ฉด ๋†’์ด โ€” ๋ฐ์นผ์„ ์—ฌ๊ธฐ ๋ฐ”๋กœ ์œ„์— ๊น๋‹ค(๋ฐญ ๋ฐ”๋‹ฅ์ด ๊ณง ๊ฑธ์–ด๋‹ค๋‹ˆ๋Š” ๋ฉด). + /// ๋ Œ๋”๋Ÿฌ๋ฅผ ๊บผ ๋‘์–ด๋„ `bounds` ๋Š” ์œ ํšจํ•˜๋‹ค. ๊ตฌ์—ญ ์•ˆ์— ํ™์นธ์ด ํ•˜๋‚˜๋„ ์—†์œผ๋ฉด ๊ตฌ์—ญ ๋ฐ”๋‹ฅ์„ ์“ด๋‹ค. + /// + static float SoilTopY(Bounds zone) + { + float y = float.MinValue; + var soils = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); + for (int i = 0; i < soils.Length; i++) + { + var s = soils[i]; + if (s == null) continue; + var p = s.transform.position; + if (Mathf.Abs(p.x - zone.center.x) > zone.extents.x + 1f) continue; // ๋‹ค๋ฅธ ์„ฌ์˜ ๋ฐญ์€ ๋บ€๋‹ค + if (Mathf.Abs(p.z - zone.center.z) > zone.extents.z + 1f) continue; + var r = s.GetComponentInChildren(true); + float t = r != null ? r.bounds.max.y : p.y; + if (t > y) y = t; + } + return y > -1e8f ? y : zone.min.y; + } + + /// URP Unlit ๋ฐ˜ํˆฌ๋ช… ๋Ÿฐํƒ€์ž„ ๋จธํ‹ฐ๋ฆฌ์–ผ(816j2 `Farm_ShoreFoam.mat` ๊ณผ ๊ฐ™์€ ๊ตฌ์„ฑ). + bool EnsureMaterial() + { + if (cfg.softDirtMaterial != null) + { + if (_inst == null || _inst.shader != cfg.softDirtMaterial.shader) + { + if (_inst != null) Destroy(_inst); + _inst = new Material(cfg.softDirtMaterial) { name = "WL_SoftDirt(runtime)", hideFlags = HideFlags.DontSave }; + } + return true; + } + + if (_inst != null) return true; + + // ์—์…‹์„ ์ƒˆ๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” 816j2 ๊ฑฐํ’ˆ ๋ ๊ฐ€ ์“ฐ๋Š” URP Unlit ์…ฐ์ด๋”๋ฅผ ๊ทธ๋Œ€๋กœ ๋นŒ๋ ค ์“ด๋‹ค. + Shader sh = cfg.shoreFoamMaterial != null ? cfg.shoreFoamMaterial.shader : null; + if (sh == null) sh = Shader.Find("Universal Render Pipeline/Unlit"); + if (sh == null) sh = Shader.Find("Unlit/Transparent"); + if (sh == null) return false; + + _inst = new Material(sh) { name = "WL_SoftDirt(runtime)", hideFlags = HideFlags.DontSave }; + // URP Unlit ์„ ๋ฐ˜ํˆฌ๋ช…์œผ๋กœ โ€” Farm_ShoreFoam.mat ์‹ค์ธก๊ฐ’๊ณผ ๊ฐ™๋‹ค(_Surface 1 ยท SrcAlpha/OneMinusSrcAlpha ยท ZWrite 0). + if (_inst.HasProperty(IdSurface)) _inst.SetFloat(IdSurface, 1f); + if (_inst.HasProperty(IdBlend)) _inst.SetFloat(IdBlend, 0f); + if (_inst.HasProperty(IdSrcBlend)) _inst.SetFloat(IdSrcBlend, (float)UnityEngine.Rendering.BlendMode.SrcAlpha); + if (_inst.HasProperty(IdDstBlend)) _inst.SetFloat(IdDstBlend, (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + if (_inst.HasProperty(IdZWrite)) _inst.SetFloat(IdZWrite, 0f); + if (_inst.HasProperty(IdCull)) _inst.SetFloat(IdCull, (float)UnityEngine.Rendering.CullMode.Off); + _inst.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); + _inst.SetShaderPassEnabled("ShadowCaster", false); + _inst.SetShaderPassEnabled("DepthOnly", false); + _inst.SetShaderPassEnabled("MOTIONVECTORS", false); + _inst.SetOverrideTag("RenderType", "Transparent"); + // ๊ฑฐํ’ˆ ๋ (3050)๋ณด๋‹ค ๋จผ์ € โ€” ๋•…์— ๋ถ™๋Š” ๋ฐ์นผ์ด ๋จผ์ € ๊น”๋ฆฌ๊ณ  ๊ทธ ์œ„๋กœ ๋ฌผ๊ฐ€ ๊ฑฐํ’ˆ์ด ์˜จ๋‹ค. + _inst.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; + return true; + } + + /// ๋งˆ์Šคํฌ๋ฅผ ๊ตฝ๋Š”๋‹ค โ€” ์•ŒํŒŒ = ํ™ ๋ฎ์ž„(1) โ†’ ํ’€(0) ์œผ๋กœ `feather`(m) ํญ์—์„œ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋„˜์–ด๊ฐ„๋‹ค. + void BakeMask(float halfX, float halfZ, int res, float feather) + { + // ๊ตฌ์šด ๋’ค CPU ์‚ฌ๋ณธ์„ ๋ฒ„๋ฆฌ๋ฏ€๋กœ(์•„๋ž˜ `Apply(false, true)`) ๋‹ค์‹œ ๊ตฌ์šธ ๋•Œ๋Š” ์ƒˆ๋กœ ๋งŒ๋“ ๋‹ค. + if (_tex != null) Destroy(_tex); + // linear:true โ€” RGB ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๊ณฑํ•˜๊ฒŒ ํ•œ๋‹ค(sRGB ๋ณ€ํ™˜์ด ๋ผ๋ฉด ์–ผ๋ฃฉ ์„ธ๊ธฐ๊ฐ€ ๋œป๋Œ€๋กœ ์•ˆ ๋‚˜์˜จ๋‹ค). + _tex = new Texture2D(res, res, TextureFormat.RGBA32, false, true) + { name = "WL_SoftDirtMask", hideFlags = HideFlags.DontSave, wrapMode = TextureWrapMode.Clamp, filterMode = FilterMode.Bilinear }; + + float texNoise = Mathf.Clamp01(cfg.softDirtTexNoise); + float half = feather * 0.5f; + var px = new Color32[res * res]; + float stepX = halfX * 2f / res, stepZ = halfZ * 2f / res; + + for (int j = 0; j < res; j++) + { + float wz = s_cz - halfZ + (j + 0.5f) * stepZ; + for (int i = 0; i < res; i++) + { + float wx = s_cx - halfX + (i + 0.5f) * stepX; + float d = SignedDist(wx, wz); + // 1 = ํ™ ยท 0 = ํ’€. ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ feather(m) ํญ์œผ๋กœ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์‚ฌ๋ผ์ง„๋‹ค. + float a = Mathf.Clamp01((half - d) / Mathf.Max(0.0001f, feather)); + a = a * a * (3f - 2f * a); // smoothstep โ€” ๋ ๊ฐ€ ์„ ์ฒ˜๋Ÿผ ๋ณด์ด์ง€ ์•Š๊ฒŒ + + float v = 1f; + if (texNoise > 0f) // ํ™ ์–ผ๋ฃฉ โ€” ํ•œ ๋ฉ์–ด๋ฆฌ ๋‹จ์ƒ‰์œผ๋กœ ๋ณด์ด์ง€ ์•Š๊ฒŒ + v = 1f - texNoise * CellNoise(wx * 1.7f, wz * 1.7f); + + byte c = (byte)Mathf.RoundToInt(Mathf.Clamp01(v) * 255f); + px[j * res + i] = new Color32(c, c, c, (byte)Mathf.RoundToInt(a * 255f)); + } + } + + _tex.SetPixels32(px); + _tex.Apply(false, true); // ๋ฐ‰ ์—†์Œ + CPU ์‚ฌ๋ณธ ํ•ด์ œ + } + + /// ๊ตฌ์—ญ์„ ๋ฎ๋Š” ์‚ฌ๊ฐํ˜• ํ•œ ์žฅ(4์ •์  ยท 2์‚ผ๊ฐํ˜•). ๋ชจ์–‘์€ ์ „๋ถ€ ๋งˆ์Šคํฌ๊ฐ€ ๋งŒ๋“ ๋‹ค. + void BuildQuad(float halfX, float halfZ, float y) + { + float x0 = s_cx - halfX, x1 = s_cx + halfX; + float z0 = s_cz - halfZ, z1 = s_cz + halfZ; + + _mesh.Clear(); + _mesh.SetVertices(new System.Collections.Generic.List(4) + { + new Vector3(x0, y, z0), new Vector3(x1, y, z0), + new Vector3(x0, y, z1), new Vector3(x1, y, z1) + }); + _mesh.SetUVs(0, new System.Collections.Generic.List(4) + { + new Vector2(0f, 0f), new Vector2(1f, 0f), + new Vector2(0f, 1f), new Vector2(1f, 1f) + }); + _mesh.SetTriangles(new int[] { 0, 2, 1, 1, 2, 3 }, 0); + _mesh.RecalculateBounds(); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // 816za ์™€ **๊ฐ™์€ ์‹**์˜ ์…€ ๊ฒฉ์ž ๊ฐ’ ๋…ธ์ด์ฆˆ [0,1] โ€” ๊ฒฝ๊ณ„๋ฅผ ๋“ค์ญ‰๋‚ ์ญ‰ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค. + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + static float CellNoise(float fx, float fz) + { + int x0 = Mathf.FloorToInt(fx), z0 = Mathf.FloorToInt(fz); + float tx = fx - x0, tz = fz - z0; + tx = tx * tx * (3f - 2f * tx); + tz = tz * tz * (3f - 2f * tz); + float a = CellRand(x0, z0), b = CellRand(x0 + 1, z0); + float c = CellRand(x0, z0 + 1), d = CellRand(x0 + 1, z0 + 1); + return Mathf.Lerp(Mathf.Lerp(a, b, tx), Mathf.Lerp(c, d, tx), tz); + } + + static float CellRand(int x, int z) + { + uint h = Hash(unchecked((uint)x * 73856093U) ^ unchecked((uint)z * 19349663U) ^ 0x9E3779B9U); + return (h & 0xFFFFFF) / 16777215f; + } + + static uint Hash(uint x) + { + x ^= x >> 16; x *= 0x7feb352dU; + x ^= x >> 15; x *= 0x846ca68bU; + x ^= x >> 16; + return x; + } + } +}