Project_WL/Assets/WL/Combat/Weapon/WLWeaponGlowFx.cs

114 lines
6.9 KiB
C#
Raw Permalink 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.

// ─────────────────────────────────────────────────────────────────────────────
// WLWeaponGlowFx.cs — 무기 흰 빛 덧그림 도우미 (#916 · 2026-10-17)
//
// 해머 각성(WLHammerQuake #912)과 같은 방식: 소켓 아래 무기 메시마다 같은 메시를 쓰는 자식을 만들어 WL/WeaponGlow 재질(가산 · 테두리)을 씌우고,
// 세기 · 색 · 몸통 덮기 · 테두리를 렌더러별 프로퍼티 블록으로 쓴다(재질 인스턴스 0). End 에서 자식을 지운다.
// 🔴 Debug.LogError 금지(원본 팝업).
// ─────────────────────────────────────────────────────────────────────────────
using System.Collections.Generic;
using UnityEngine;
namespace WL.Combat.Weapon
{
public sealed class WLWeaponGlowFx
{
static readonly Dictionary<string, Material> s_mats = new Dictionary<string, Material>();
static readonly int kGlow = Shader.PropertyToID("_Glow"), kColor = Shader.PropertyToID("_GlowColor"),
kFill = Shader.PropertyToID("_Fill"), kRim = Shader.PropertyToID("_RimPower"),
kColor2 = Shader.PropertyToID("_GlowColor2"), kAxis = Shader.PropertyToID("_GradAxis"), kLen = Shader.PropertyToID("_GradLen"); // #958 그라데이션
readonly List<GameObject> _gos = new List<GameObject>();
readonly List<Renderer> _rs = new List<Renderer>();
readonly List<Vector4> _axis = new List<Vector4>(); // #958 렌더러별 그라데이션 축(오브젝트 공간 · w = 손잡이 끝 투영)
readonly List<float> _len = new List<float>(); // 1 / (칼끝 − 손잡이 투영)
MaterialPropertyBlock _mpb;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void Reset() { s_mats.Clear(); }
public bool Active { get { return _rs.Count > 0; } }
/// <summary>socket 아래 켜진 MeshRenderer 마다 덧그림 자식을 만든다 — 만든 수.</summary>
public int Begin(Transform socket, string materialPath)
{
End();
if (socket == null || string.IsNullOrEmpty(materialPath)) return 0;
Material mat;
if (!s_mats.TryGetValue(materialPath, out mat)) { mat = Resources.Load<Material>(materialPath); s_mats[materialPath] = mat; }
if (mat == null) return 0;
foreach (var mf in socket.GetComponentsInChildren<MeshFilter>(false))
{
var mr = mf.GetComponent<MeshRenderer>();
if (mr == null || !mr.enabled || mf.sharedMesh == null || mf.name == "[WL] Glow") continue;
var go = new GameObject("[WL] Glow") { layer = mf.gameObject.layer };
go.transform.SetParent(mf.transform, false);
go.AddComponent<MeshFilter>().sharedMesh = mf.sharedMesh;
var r = go.AddComponent<MeshRenderer>();
var mats = new Material[Mathf.Max(1, mf.sharedMesh.subMeshCount)];
for (int i = 0; i < mats.Length; i++) mats[i] = mat;
r.sharedMaterials = mats; r.enabled = false;
r.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; r.receiveShadows = false;
r.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off; r.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
_gos.Add(go); _rs.Add(r);
// #958 그라데이션 축 = 메시 경계의 가장 긴 축 · 방향 = 소켓(손잡이) 쪽 끝 → 먼 끝(칼끝)
var b = mf.sharedMesh.bounds; var s = b.size;
Vector3 ax = s.x >= s.y && s.x >= s.z ? Vector3.right : (s.y >= s.z ? Vector3.up : Vector3.forward);
float lo = Vector3.Dot(b.min, ax), hi = Vector3.Dot(b.max, ax);
float sock = Vector3.Dot(mf.transform.InverseTransformPoint(socket.position), ax);
if (Mathf.Abs(sock - hi) < Mathf.Abs(sock - lo)) { ax = -ax; float t = lo; lo = -hi; hi = -t; }
_axis.Add(new Vector4(ax.x, ax.y, ax.z, lo)); _len.Add(hi - lo > 1e-4f ? 1f / (hi - lo) : 0f);
}
return _rs.Count;
}
/// <summary>#958 손잡이 color → 칼끝 color2 그라데이션(WL/WeaponGlow 의 _GlowColor2 · _GradAxis · _GradLen).</summary>
public void Set(float glow, Color color, Color color2, float fill, float rimPower)
{
if (_rs.Count == 0) return;
if (_mpb == null) _mpb = new MaterialPropertyBlock();
for (int i = 0; i < _rs.Count; i++)
{
var r = _rs[i]; if (r == null) continue;
r.enabled = glow > 0.001f;
_mpb.Clear(); _mpb.SetFloat(kGlow, glow); _mpb.SetColor(kColor, color); _mpb.SetFloat(kFill, fill); _mpb.SetFloat(kRim, rimPower);
_mpb.SetColor(kColor2, color2); _mpb.SetVector(kAxis, i < _axis.Count ? _axis[i] : Vector4.zero); _mpb.SetFloat(kLen, i < _len.Count ? _len[i] : 0f);
r.SetPropertyBlock(_mpb);
}
}
public void Set(float glow, Color color, float fill, float rimPower)
{
if (_rs.Count == 0) return;
if (_mpb == null) _mpb = new MaterialPropertyBlock();
for (int i = 0; i < _rs.Count; i++)
{
var r = _rs[i]; if (r == null) continue;
r.enabled = glow > 0.001f;
_mpb.Clear(); _mpb.SetFloat(kGlow, glow); _mpb.SetColor(kColor, color); _mpb.SetFloat(kFill, fill); _mpb.SetFloat(kRim, rimPower);
r.SetPropertyBlock(_mpb);
}
}
public void End()
{
for (int i = 0; i < _gos.Count; i++) if (_gos[i] != null) Object.Destroy(_gos[i]);
_gos.Clear(); _rs.Clear(); _axis.Clear(); _len.Clear();
}
/// <summary>socket 아래 가장 큰 메시의 경계 중심(socket 로컬) — 무기 가운데. 없으면 원점.</summary>
public static Vector3 CenterLocal(Transform socket)
{
if (socket == null) return Vector3.zero;
Vector3 best = Vector3.zero; float bestSize = -1f;
foreach (var mf in socket.GetComponentsInChildren<MeshFilter>(false))
{
var mr = mf.GetComponent<MeshRenderer>(); var mesh = mf.sharedMesh;
if (mr == null || !mr.enabled || mesh == null || mf.name == "[WL] Glow") continue;
float size = mesh.bounds.size.sqrMagnitude;
if (size > bestSize) { bestSize = size; best = socket.InverseTransformPoint(mf.transform.TransformPoint(mesh.bounds.center)); }
}
return best;
}
}
}