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

89 lines
4.9 KiB
C#

// ─────────────────────────────────────────────────────────────────────────────
// 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");
readonly List<GameObject> _gos = new List<GameObject>();
readonly List<Renderer> _rs = new List<Renderer>();
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);
}
return _rs.Count;
}
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();
}
/// <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;
}
}
}