2026-10-18 15:09:05 +00:00
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
// 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"),
|
2026-10-21 04:05:18 +00:00
|
|
|
|
kFill = Shader.PropertyToID("_Fill"), kRim = Shader.PropertyToID("_RimPower"),
|
|
|
|
|
|
kColor2 = Shader.PropertyToID("_GlowColor2"), kAxis = Shader.PropertyToID("_GradAxis"), kLen = Shader.PropertyToID("_GradLen"); // #958 그라데이션
|
2026-10-18 15:09:05 +00:00
|
|
|
|
|
|
|
|
|
|
readonly List<GameObject> _gos = new List<GameObject>();
|
|
|
|
|
|
readonly List<Renderer> _rs = new List<Renderer>();
|
2026-10-21 04:05:18 +00:00
|
|
|
|
readonly List<Vector4> _axis = new List<Vector4>(); // #958 렌더러별 그라데이션 축(오브젝트 공간 · w = 손잡이 끝 투영)
|
|
|
|
|
|
readonly List<float> _len = new List<float>(); // 1 / (칼끝 − 손잡이 투영)
|
2026-10-18 15:09:05 +00:00
|
|
|
|
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);
|
2026-10-21 04:05:18 +00:00
|
|
|
|
// #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);
|
2026-10-18 15:09:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
return _rs.Count;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-21 04:05:18 +00:00
|
|
|
|
/// <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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-18 15:09:05 +00:00
|
|
|
|
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]);
|
2026-10-21 04:05:18 +00:00
|
|
|
|
_gos.Clear(); _rs.Clear(); _axis.Clear(); _len.Clear();
|
2026-10-18 15:09:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|