Project_WL/Assets/WL/Look/Env/WLEnvLookSettings.cs

151 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ─────────────────────────────────────────────────────────────────────────────
// WLEnvLookSettings.cs — Critter Environment 룩(툰 램프 + 1px 픽셀 외곽선 + 구름 그림자
// + 인스턴싱 잔디 + 거품 물)의 값 SOT. PD 지시 #814 · 발주서 WL-814c (2026-09-10)
//
// 🔴 코드 상수 0(C45) — 여기 있는 값만 바꾸면 룩이 바뀐다.
// 🔴 originals / toons 는 에디터 스크립트(AgentScripts/WL814c_Convert.cs)가 채우는 스왑 표다.
// 런타임은 이 표에 **있는 머티리얼만** 갈아끼운다 → 파티클·UI·이펙트는 절대 안 바뀐다.
// 🔴 원본 .mat 파일은 어떤 경로로도 수정되지 않는다(참조만 보관).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.Look.Env
{
public sealed class WLEnvLookSettings : ScriptableObject
{
public const string kResourcePath = "WL/WLEnvLookSettings";
// ── C8 롤백 ──────────────────────────────────────────────────────────
[Tooltip("0 = 이 기능 전체 정지(러너 오브젝트 0 · 스왑 0 · 피처 0 · 잔디 0)")]
public bool enabled = true;
[Tooltip("적용/복원 1줄 로그")]
public bool verboseLog = false;
[Tooltip("🔴 WL-814e — 1 이면 814d 룩 토글 허브(LookModeHub.EnvLookOn)를 따른다: 맵 진입 자동 적용도 허브가 ON 일 때만 · [배경] /. 0 814c ( )")]
public bool followLookModeHub = true;
// ── 맵 경계 ──────────────────────────────────────────────────────────
[Tooltip("인게임 맵에서만 적용(로비/타이틀 제외)")]
public bool applyOnlyInBattleMap = true;
[Tooltip("맵 이탈·종료에서 원복")]
public bool restoreOnExit = true;
[Tooltip("맵 경계 폴링 주기(초)")]
public float pollSeconds = 0.5f;
[Tooltip("새로 스폰된 몹을 훑는 주기(초) · 0 이면 새 스폰 적용 안 함")]
public float respawnSweepSeconds = 0.5f;
// ── 스왑 대상 ────────────────────────────────────────────────────────
[Tooltip("맵 렌더러를 Toon 으로 스왑")]
public bool swapMap = true;
[Tooltip("캐릭터(PC·몹·보스) 렌더러를 Toon 으로 스왑 — PD 결정용(캡처 ⓔ/ⓕ 비교)")]
public bool swapCharacters = true;
// ── 스왑 표(에디터 스크립트가 채운다 · 손으로 만지지 말 것) ───────────
public Material[] originals;
public Material[] toons;
// ── 머티리얼 생성 값(에디터 스크립트가 읽는다) ────────────────────────
[Tooltip("캐릭터 머티리얼도 Toon 으로 만들 것인가(생성 단계)")]
public bool convertCharacters = true;
[Tooltip("툰 램프 단계 수 4~8 (데모 7 · 제안 5)")]
public int shades = 5;
public float brightness = 0.25f;
public float minimumDarkness = 0.2f;
public float ambientStrength = 0.1f;
[Tooltip("그림자 색 = 원본 색 × 이 계수(데모 머티리얼 관계 근사)")]
public Color shadowTint = new Color(0.42f, 0.40f, 0.55f, 1f);
// ── 1px 픽셀 외곽선(오브젝트 셰이더 내부 계산) ────────────────────────
public bool outlinesEnabled = true;
[Tooltip("🔴 캐릭터에도 외곽선을 줄 것인가. 실측: 스킨드 메시는 폴리곤이 촘촘해 270px 에서 노이즈가 된다 → 기본 off")]
public bool characterOutlines = false;
[Tooltip("깊이 외곽선 임계 = (외곽선을 낼 최소 단차 m) ÷ (직교 카메라 far near). 데모 0.01 은 원근 카메라 기준")]
public float depthThreshold = 0.004f;
[Tooltip("노멀 외곽선 임계. 데모 1 · 낮출수록 면 안쪽까지 선이 생겨 노이즈가 된다")]
public float normalThreshold = 1f;
public Vector4 normalBias = new Vector4(1f, 1f, 1f, 0f);
public float depthEdgeStrength = 0.5f;
public float normalEdgeStrength = 0.3f;
public Color outlineColor = new Color(0f, 0f, 0f, 0f);
// ── 구름 그림자 ──────────────────────────────────────────────────────
// 🔴 실측: Critter Toon 셰이더에 `_CLOUDSENABLED` 키워드는 **없다**(데모 .mat 의 그 키워드는 구버전 잔재).
// on/off 는 `_Cloud_Strength` 로 한다(0 = 끔).
public bool cloudShadows = true;
public float cloudDensity = 0.01f;
public Vector4 cloudMovement = new Vector4(1f, 1f, 0f, 0f);
public float cloudStrength = 1f;
public float cloudCover = 0.5f;
public float cloudChange = 0.005f;
public Vector4 cloudStep = new Vector4(13f, 17f, 0f, 0f);
// ── 렌더러 피처(깊이·노멀 텍스처) ─────────────────────────────────────
[Tooltip("PixelOutlineSetupFeature 를 런타임에 켠다")]
public bool outlineFeature = true;
[Tooltip("인게임 맵에서만 피처 활성(타이틀·로비 비용 0)")]
public bool outlineFeatureOnlyInBattleMap = true;
// ── 잔디 인스턴싱 ────────────────────────────────────────────────────
public bool grassEnabled = true;
[Tooltip("데모 대비 밀도 비율. 1 = 데모(Critter Density 2.5) · 0.5 = 인스턴스 절반")]
public float grassDensity = 0.5f;
[Tooltip("데모 실측 Density(면적/인스턴스). 만지지 말 것")]
public float demoDensity = 2.5f;
[Tooltip("모바일 상한 — 이 수를 넘으면 밀도를 자동으로 낮춘다")]
public int maxInstances = 20000;
[Tooltip("지면 후보 판정: 수평 투영 면적(m²) 하한")]
public float minGroundFootprint = 100f;
[Tooltip("지면 후보 판정: 바운드 높이(m) 상한 — 배경 산·절벽을 뺀다(WL_Nature 실측 산 58~200 m · 지면 2~16 m)")]
public float maxGroundBoundsHeight = 30f;
[Tooltip("지면 후보 판정: 평평함 = 높이 ÷ 짧은 변. 실측 지면 0.02~0.15 · 나무/바위 0.65~0.90")]
public float groundFlatness = 0.35f;
[Tooltip("지면 후보 판정: 메시 면적 ÷ 수평 투영 면적. 실측 지면 ≈1.0 · 나무 수관 ≈0.3 (성긴 면은 지면이 아니다)")]
public float groundAreaRatio = 0.5f;
[Tooltip("지면 후보 최대 개수(플레이어에 가까운 순)")]
public int maxGroundMeshes = 4;
[Tooltip("잔디 한 포기의 목표 높이(m) — 메시 크기로 Scale 을 자동 계산한다(0 이면 아래 grassScale 을 그대로 쓴다)")]
public float grassHeightMeters = 0.3f;
[Tooltip("grassHeightMeters 가 0 일 때 쓰는 원시 Scale(데모 0.35)")]
public float grassScale = 0.35f;
public float grassNormalOffset = 0.04f;
[Tooltip("잔디 : 꽃 가중치(데모 100 : 1)")]
public float grassProbability = 100f;
public float flowerProbability = 1f;
[Tooltip("지면 메시가 읽기 불가일 때 레이캐스트로 만드는 대체 격자 한 변 분할 수")]
public int groundProbeGrid = 48;
// ── 물 ───────────────────────────────────────────────────────────────
[Tooltip("물 렌더러를 Critter Water 로 스왑(반사 카메라는 붙이지 않는다)")]
public bool waterEnabled = true;
public Material waterMaterial;
// ── 에셋 참조(에디터 스크립트가 채움) ─────────────────────────────────
public Mesh grassMesh;
public Material grassMaterial;
public Mesh flowerMesh;
public Material flowerMaterial;
// ─────────────────────────────────────────────────────────────────────
static WLEnvLookSettings s_ins;
static bool s_tried;
/// <summary>프로브·C8 용 런타임 강제 정지(에셋 파일은 건드리지 않는다).</summary>
public static bool RuntimeDisabled;
public static WLEnvLookSettings Instance
{
get
{
if (s_ins == null && !s_tried) { s_tried = true; s_ins = Resources.Load<WLEnvLookSettings>(kResourcePath); }
return s_ins;
}
}
/// <summary>기능이 살아 있는가 = 에셋 존재 · enabled · RuntimeDisabled off.</summary>
public static bool Enabled { get { var c = Instance; return c != null && c.enabled && !RuntimeDisabled; } }
/// <summary>프로브 전용 — 캐시를 비운다(SO 를 코드로 바꿔치기할 때).</summary>
public static void ClearCacheForProbe() { s_ins = null; s_tried = false; }
}
}