Урна: дух - светящаяся дымка (сложение One/One) вместо Fade с пятнами; necrogenie glow/tint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-16 19:49:20 +03:00
co-authored by Claude Opus 5.5
parent 3ba6572bef
commit 49c7afe273
+121 -20
View File
@@ -14,8 +14,8 @@ namespace NecromancerTome
/// (TEFeatureCinerarium.HasSkull) и пропадает без него. Сдвиги Origin и выгрузку чанка переживает с моделью урны.
///
/// Модель - бандл necrotechnologist (NecroTechnologistSetup.cs): своя сетка на своём скелете NT_rig в позе покоя,
/// глаза - отдельные рендереры (в имени «eye»). Тело - полупрозрачное, как у торговцев-призраков (alpha 0.3, Standard
/// в режиме Fade, как у крысы при проходе); текстура уже серая (сделана под обесцвечивание). Глаза - свой
/// глаза - отдельные рендереры (в имени «eye»). Тело - светящаяся дымка (16.10: Fade давал пятна; сложение One/One,
/// холодный оттенок Tint, своё слабое свечение Glow); текстура уже серая (сделана под обесцвечивание). Глаза - свой
/// непрозрачный светящийся материал из бандла, очередь отрисовки 3100 - после тела.
/// </summary>
public static class CinerariumGenie
@@ -105,27 +105,55 @@ namespace NecromancerTome
{
m.shader = std;
}
// 16.10: Fade (SrcAlpha/OneMinusSrcAlpha) давал «прозрачный, но какими-то пятнами» и «не выглядит бесплотным
// духом»: без записи глубины треугольники одной сетки ложатся в случайном порядке. Свечение (One/One) от порядка
// не зависит: дух только добавляет свет к тому, что за ним, - дымка. Альфа здесь не нужна, яркость - цветом.
m.SetFloat("_Mode", 2f);
m.SetOverrideTag("RenderType", "Transparent");
m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One);
m.SetInt("_ZWrite", 0);
m.DisableKeyword("_ALPHATEST_ON");
m.EnableKeyword("_ALPHABLEND_ON");
m.DisableKeyword("_ALPHABLEND_ON");
m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
if (m.HasProperty("_Glossiness"))
{
m.SetFloat("_Glossiness", 0f); // без бликов: блик при сложении - белое пятно
}
if (m.HasProperty("_Metallic"))
{
m.SetFloat("_Metallic", 0f);
}
if (m.HasProperty("_EmissionMap") && m.HasProperty("_MainTex"))
{
m.SetTexture("_EmissionMap", m.GetTexture("_MainTex")); // своё свечение - чтобы ночью не пропадал
m.EnableKeyword("_EMISSION");
m.globalIlluminationFlags = MaterialGlobalIlluminationFlags.None;
}
m.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
SetAlpha(m, Alpha);
SetLook(m);
_ghostMats[_src] = m;
return m;
}
static void SetAlpha(Material _m, float _a)
/// <summary>Холодный бледный оттенок духа (умножается на серую текстуру).</summary>
public static Color Tint = new Color(0.55f, 0.75f, 1f, 1f);
/// <summary>Своё свечение тела, доля от Tint.</summary>
public static float Glow = 0.35f;
static void SetLook(Material _m)
{
if (_m != null && _m.HasProperty("_Color"))
if (_m == null)
{
Color c = _m.color;
c.a = _a;
_m.color = c;
return;
}
if (_m.HasProperty("_Color"))
{
_m.color = new Color(Tint.r * Alpha, Tint.g * Alpha, Tint.b * Alpha, 1f);
}
if (_m.HasProperty("_EmissionColor"))
{
_m.SetColor("_EmissionColor", new Color(Tint.r * Glow, Tint.g * Glow, Tint.b * Glow, 1f));
}
}
@@ -133,9 +161,58 @@ namespace NecromancerTome
{
foreach (Material m in _ghostMats.Values)
{
SetAlpha(m, Alpha);
SetLook(m);
}
}
static readonly Dictionary<Material, Material> _stuckMats = new Dictionary<Material, Material>();
/// <summary>Почерневший дух (класть некуда): свечением чёрное не нарисовать, поэтому обычная Fade-копия, тёмная.</summary>
public static Material Stuck(Material _src)
{
if (_src == null)
{
return null;
}
if (_stuckMats.TryGetValue(_src, out Material m) && m != null)
{
return m;
}
m = new Material(_src) { name = _src.name + "_stuck" };
Shader std = Shader.Find("Standard");
if (std != null && m.shader != std)
{
m.shader = std;
}
m.SetFloat("_Mode", 2f);
m.SetOverrideTag("RenderType", "Transparent");
m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
m.SetInt("_ZWrite", 1); // с глубиной: пятен нет, тёмный и почти плотный
m.DisableKeyword("_ALPHATEST_ON");
m.EnableKeyword("_ALPHABLEND_ON");
m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
m.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
if (m.HasProperty("_Color"))
{
m.color = new Color(0.04f, 0.035f, 0.045f, 0.8f);
}
_stuckMats[_src] = m;
return m;
}
/// <summary>Исходный материал тела по его светящейся копии.</summary>
public static Material Source(Material _ghost)
{
foreach (KeyValuePair<Material, Material> p in _ghostMats)
{
if (p.Value == _ghost)
{
return p.Key;
}
}
return _ghost;
}
}
/// <summary>Покачивание и поворот джинна над урной. Только вид - никакой логики.</summary>
@@ -150,8 +227,8 @@ namespace NecromancerTome
public const float VisitSeconds = 2.2f;
/// <summary>Почернел и замер (готовое класть некуда, решение 06.10).</summary>
bool _stuck;
static readonly MaterialPropertyBlock _mpb = new MaterialPropertyBlock();
static readonly int ColorId = Shader.PropertyToID("_Color");
/// <summary>Светящиеся материалы рендереров - вернуть после почернения.</summary>
readonly Dictionary<Renderer, Material[]> _glowing = new Dictionary<Renderer, Material[]>();
/// <summary>_worldUnity - точка в координатах сцены (без Origin): низ блока, где дух работал.</summary>
public void Visit(Vector3 _worldUnity)
@@ -177,15 +254,23 @@ namespace NecromancerTome
{
continue;
}
if (!_glowing.ContainsKey(r))
{
_glowing[r] = r.sharedMaterials;
}
Material[] glow = _glowing[r];
if (_on)
{
r.GetPropertyBlock(_mpb);
_mpb.SetColor(ColorId, new Color(0.04f, 0.035f, 0.045f, Mathf.Max(CinerariumGenie.Alpha, 0.6f)));
r.SetPropertyBlock(_mpb);
Material[] dark = new Material[glow.Length];
for (int i = 0; i < glow.Length; i++)
{
dark[i] = CinerariumGenie.Stuck(CinerariumGenie.Source(glow[i]));
}
r.sharedMaterials = dark;
}
else
{
r.SetPropertyBlock(null);
r.sharedMaterials = glow;
}
}
}
@@ -261,7 +346,9 @@ namespace NecromancerTome
"necrogenie fwd <m> - shift along the spirit's own forward (keeps the curled tail inside the neck)\n" +
"necrogenie turn <deg/s> - idle turning speed\n" +
"necrogenie bob <m> <s> - bobbing height and period\n" +
"necrogenie alpha <0-1> - body opacity";
"necrogenie alpha <0-1> - body brightness (the body adds light, it is not alpha-blended)\n" +
"necrogenie glow <0-1> - body's own glow (visible at night)\n" +
"necrogenie tint <r> <g> <b> - body colour";
}
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
@@ -291,6 +378,19 @@ namespace NecromancerTome
CinerariumGenie.Alpha = Mathf.Clamp01(v);
CinerariumGenie.ApplyAlpha();
break;
case "glow":
CinerariumGenie.Glow = Mathf.Clamp01(v);
CinerariumGenie.ApplyAlpha();
break;
case "tint":
if (_params.Count >= 4 &&
float.TryParse(_params[2], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float g) &&
float.TryParse(_params[3], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float b))
{
CinerariumGenie.Tint = new Color(v, g, b, 1f);
CinerariumGenie.ApplyAlpha();
}
break;
default:
con.Output("necrogenie: unknown '" + _params[0] + "' - see 'help necrogenie'");
return;
@@ -298,7 +398,8 @@ namespace NecromancerTome
}
con.Output("necrogenie: y " + CinerariumGenie.OffsetY.ToString("F2") + ", fwd " + CinerariumGenie.Forward.ToString("F2") + ", turn " + CinerariumGenie.TurnSpeed.ToString("F1") +
", bob " + CinerariumGenie.BobHeight.ToString("F2") + " / " + CinerariumGenie.BobPeriod.ToString("F1") + " s, alpha " +
CinerariumGenie.Alpha.ToString("F2") + "; spirits on screen: " + CinerariumGenie.Live.Count);
CinerariumGenie.Alpha.ToString("F2") + ", glow " + CinerariumGenie.Glow.ToString("F2") + ", tint " +
CinerariumGenie.Tint.r.ToString("F2") + " " + CinerariumGenie.Tint.g.ToString("F2") + " " + CinerariumGenie.Tint.b.ToString("F2") + "; spirits on screen: " + CinerariumGenie.Live.Count);
}
}
}