diff --git a/HarmonySrc/CinerariumGenie.cs b/HarmonySrc/CinerariumGenie.cs
index 83124f7..1648402 100644
--- a/HarmonySrc/CinerariumGenie.cs
+++ b/HarmonySrc/CinerariumGenie.cs
@@ -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 - после тела.
///
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)
+ /// Холодный бледный оттенок духа (умножается на серую текстуру).
+ public static Color Tint = new Color(0.55f, 0.75f, 1f, 1f);
+ /// Своё свечение тела, доля от Tint.
+ 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 _stuckMats = new Dictionary();
+
+ /// Почерневший дух (класть некуда): свечением чёрное не нарисовать, поэтому обычная Fade-копия, тёмная.
+ 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;
+ }
+
+ /// Исходный материал тела по его светящейся копии.
+ public static Material Source(Material _ghost)
+ {
+ foreach (KeyValuePair p in _ghostMats)
+ {
+ if (p.Value == _ghost)
+ {
+ return p.Key;
+ }
+ }
+ return _ghost;
+ }
}
/// Покачивание и поворот джинна над урной. Только вид - никакой логики.
@@ -150,8 +227,8 @@ namespace NecromancerTome
public const float VisitSeconds = 2.2f;
/// Почернел и замер (готовое класть некуда, решение 06.10).
bool _stuck;
- static readonly MaterialPropertyBlock _mpb = new MaterialPropertyBlock();
- static readonly int ColorId = Shader.PropertyToID("_Color");
+ /// Светящиеся материалы рендереров - вернуть после почернения.
+ readonly Dictionary _glowing = new Dictionary();
/// _worldUnity - точка в координатах сцены (без Origin): низ блока, где дух работал.
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 - shift along the spirit's own forward (keeps the curled tail inside the neck)\n" +
"necrogenie turn - idle turning speed\n" +
"necrogenie bob - 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 - body colour";
}
public override void Execute(List _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);
}
}
}