Fix honey badger red stripes: remove shader replacement for 3D models (#6434)

* Remove ClipRectMeshUnlit shader replacement for 3D beast models

The shader replacement multiplied texture color by vertex colors, turning
honey badger white stripes red. RendererViewportClipper already handles
viewport clipping for all 3D models generically, making the shader
approach redundant and harmful.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Set render queue on beast models to render above province texture

The old ClipRectMeshUnlit shader used Queue=Transparent+100 and
ZTest Always to draw above the province colour layer. Now that models
keep their original shaders, set mat.renderQueue = 3100 in
RendererViewportClipper.Awake() to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix spelling: colour -> color

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 14:08:31 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent dad8c9ebe7
commit 57703ffbda
39 changed files with 16 additions and 177 deletions
@@ -18,9 +18,6 @@ namespace eagle {
public float moveSpeed = 15f;
public float animalScale = 15f;
[Header("Shader")]
public Shader clipRectMeshShader;
[Header("Animator Override")]
[Tooltip(
"If set, replaces the prefab's animator controller at spawn time (applies to all prefabs)")]
@@ -72,18 +69,10 @@ namespace eagle {
var startPos = RandomWanderPoint();
instance.transform.localPosition = startPos;
var needsRendererClipper = false;
foreach (var renderer in instance.GetComponentsInChildren<Renderer>()) {
renderer.sortingOrder = 103;
if (clipRectMeshShader != null) {
foreach (var mat in renderer.materials) {
if (!ShaderUtils.ReplaceWithClipRect(mat, clipRectMeshShader)) {
needsRendererClipper = true;
}
}
}
}
if (needsRendererClipper) { RendererViewportClipper.Attach(instance); }
RendererViewportClipper.Attach(instance);
var animator = instance.GetComponent<Animator>();
if (animator != null) {
@@ -13,9 +13,6 @@ namespace eagle {
[Header("Animator")]
public RuntimeAnimatorController animatorController;
[Header("Shader")]
public Shader clipRectMeshShader;
[Header("Scale & Motion")]
public float dragonScale = 10f;
public float circleRadius = 20f;
@@ -82,18 +79,10 @@ namespace eagle {
_dragonInstance.transform.localScale =
new Vector3(dragonScale, dragonScale, dragonScale);
var needsRendererClipper = false;
foreach (var renderer in _dragonInstance.GetComponentsInChildren<Renderer>()) {
renderer.sortingOrder = 103;
if (clipRectMeshShader != null) {
foreach (var mat in renderer.materials) {
if (!ShaderUtils.ReplaceWithClipRect(mat, clipRectMeshShader)) {
needsRendererClipper = true;
}
}
}
}
if (needsRendererClipper) { RendererViewportClipper.Attach(_dragonInstance); }
RendererViewportClipper.Attach(_dragonInstance);
_animator = _dragonInstance.GetComponent<Animator>();
if (animatorController != null) {
@@ -57,7 +57,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 008c8f0c0e074544b52b2db1598bbd5a, type: 2}
minIdleDuration: 1
maxIdleDuration: 3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 0}
animatorControllers:
- {fileID: 9100000, guid: c1047a2b3c4d5e6f7890ab1c2d3e4f50, type: 2}
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 50
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -51,7 +51,6 @@ MonoBehaviour:
dragonPrefab: {fileID: -927199367670048503, guid: ad3b61469eab69b4a8bb87c38a499466,
type: 3}
animatorController: {fileID: 9100000, guid: f8a2d6c4b1e3970548d2a6f0c8e4b2d7, type: 2}
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
dragonScale: 10
circleRadius: 20
circleSpeed: 0.6
@@ -56,7 +56,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 50
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 20
moveSpeed: 10
animalScale: 45
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 008c8f0c0e074544b52b2db1598bbd5a, type: 2}
minIdleDuration: 1
maxIdleDuration: 3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 92380771f04954666b597fc23a05714c, type: 2}
animatorControllers: []
minIdleDuration: 1
@@ -57,7 +57,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 008c8f0c0e074544b52b2db1598bbd5a, type: 2}
minIdleDuration: 1
maxIdleDuration: 3
@@ -57,7 +57,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 008c8f0c0e074544b52b2db1598bbd5a, type: 2}
minIdleDuration: 1
maxIdleDuration: 3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: a1b2c3d4e5f6789012345678abcdef01, type: 2}
minIdleDuration: 1
maxIdleDuration: 3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 008c8f0c0e074544b52b2db1598bbd5a, type: 2}
minIdleDuration: 1
maxIdleDuration: 3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 20
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 5
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 96365e099542d42af8c90e2b61a6a256, type: 2}
animatorControllers: []
minIdleDuration: 1
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 50
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 50
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 0}
animatorControllers: []
minIdleDuration: 1
@@ -56,7 +56,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -57,7 +57,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
animatorController: {fileID: 9100000, guid: 008c8f0c0e074544b52b2db1598bbd5a, type: 2}
minIdleDuration: 1
maxIdleDuration: 3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 20
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 20
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 20
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -54,7 +54,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
animalScale: 15
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -55,7 +55,6 @@ MonoBehaviour:
wanderRadius: 25
moveSpeed: 15
monsterScale: 2
clipRectMeshShader: {fileID: 4800000, guid: 5e8a5e5aaae834c70b5296c46c5916f4, type: 3}
minIdleDuration: 1
maxIdleDuration: 3
actionChance: 0.3
@@ -18,9 +18,6 @@ namespace eagle {
public float moveSpeed = 15f;
public float monsterScale = 2f;
[Header("Shader")]
public Shader clipRectMeshShader;
[Header("Behavior Timing")]
public float minIdleDuration = 1f;
public float maxIdleDuration = 3f;
@@ -61,18 +58,10 @@ namespace eagle {
var startPos = RandomWanderPoint();
instance.transform.localPosition = startPos;
var needsRendererClipper = false;
foreach (var renderer in instance.GetComponentsInChildren<Renderer>()) {
renderer.sortingOrder = 103;
if (clipRectMeshShader != null) {
foreach (var mat in renderer.materials) {
if (!ShaderUtils.ReplaceWithClipRect(mat, clipRectMeshShader)) {
needsRendererClipper = true;
}
}
}
}
if (needsRendererClipper) { RendererViewportClipper.Attach(instance); }
RendererViewportClipper.Attach(instance);
var animator = instance.GetComponent<Animator>();
if (animator != null) { animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; }
@@ -3,10 +3,9 @@ using UnityEngine;
namespace eagle {
/// <summary>
/// Clips a 3D model to the map viewport by toggling renderer visibility each frame.
/// Used for models whose original shaders are too complex to replace with
/// ClipRectMeshUnlit (e.g. Polytope Studio characters). Reads the global
/// _ViewportClipRect and _PopupClipRect properties already broadcast by
/// ViewportClipper and PopupClipper.
/// Used for beast/animal/dragon/monster models to avoid shader replacement, which
/// can break models with vertex colors. Reads the global _ViewportClipRect and
/// _PopupClipRect properties already broadcast by ViewportClipper and PopupClipper.
/// </summary>
public class RendererViewportClipper : MonoBehaviour {
private static readonly int ViewportClipRectId = Shader.PropertyToID("_ViewportClipRect");
@@ -20,7 +19,16 @@ namespace eagle {
}
}
void Awake() { _renderers = GetComponentsInChildren<Renderer>(); }
// Transparent queue (3000) + 100, matching the old ClipRectMeshUnlit shader so
// beast models render above the province color texture.
private const int RenderQueue = 3100;
void Awake() {
_renderers = GetComponentsInChildren<Renderer>();
foreach (var r in _renderers) {
foreach (var mat in r.materials) { mat.renderQueue = RenderQueue; }
}
}
void LateUpdate() {
if (_renderers == null || _renderers.Length == 0) return;
@@ -1,20 +0,0 @@
using UnityEngine;
namespace eagle {
public static class ShaderUtils {
private static readonly int MainTexId = Shader.PropertyToID("_MainTex");
/// <summary>
/// Replaces the material's shader with the clip-rect shader, preserving the
/// main texture. Materials whose original shader doesn't use _MainTex (e.g.
/// Polytope Studio characters) are left unchanged — call
/// RendererViewportClipper.Attach() instead for those models.
/// Returns true if the shader was replaced, false if the material was skipped.
/// </summary>
public static bool ReplaceWithClipRect(Material mat, Shader clipRectShader) {
if (!mat.HasProperty(MainTexId) || mat.GetTexture(MainTexId) == null) { return false; }
mat.shader = clipRectShader;
return true;
}
}
}
@@ -1,75 +0,0 @@
Shader "Eagle/ClipRectMeshUnlit" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Color ("Tint Color", Color) = (1,1,1,1)
}
SubShader {
Tags {
"Queue"="Transparent+100"
"RenderType"="Transparent"
}
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest Always
Cull Off
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 _Color;
float4 _ViewportClipRect; // xMin, yMin, xMax, yMax in screen pixels
float4 _PopupClipRect; // xMin, yMin, xMax, yMax (zero when no popup)
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 screenPos : TEXCOORD1;
fixed4 color : COLOR;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = fixed4(v.color.rgb, 1) * _Color;
o.screenPos = ComputeScreenPos(o.pos);
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Viewport clip rect check
float2 screenPixel = (i.screenPos.xy / i.screenPos.w) * _ScreenParams.xy;
if (screenPixel.x < _ViewportClipRect.x || screenPixel.x > _ViewportClipRect.z ||
screenPixel.y < _ViewportClipRect.y || screenPixel.y > _ViewportClipRect.w) {
discard;
}
// Discard fragments inside popup area
if (_PopupClipRect.z > 0 &&
screenPixel.x >= _PopupClipRect.x && screenPixel.x <= _PopupClipRect.z &&
screenPixel.y >= _PopupClipRect.y && screenPixel.y <= _PopupClipRect.w) {
discard;
}
fixed4 col = tex2D(_MainTex, i.uv) * i.color;
return col;
}
ENDCG
}
}
Fallback "Unlit/Transparent"
}
@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 5e8a5e5aaae834c70b5296c46c5916f4
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant: