223 lines
11 KiB
C#
223 lines
11 KiB
C#
using System.Collections;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.Universal;
|
|
using UnityEngine.TestTools;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace BumpCombat.Tests
|
|
{
|
|
public sealed class ComparisonSpriteFlipRenderPlayModeTests
|
|
{
|
|
private const int FlipRenderTestLayer = 31;
|
|
|
|
[UnityTest]
|
|
public IEnumerator SpriteRendererFlipXMirrorsAsymmetricRenderedSprite()
|
|
{
|
|
const int width = 80;
|
|
const int height = 40;
|
|
const int textureWidth = 8;
|
|
const int textureHeight = 4;
|
|
GameObject cameraObject = null;
|
|
GameObject spriteObject = null;
|
|
Texture2D source = null;
|
|
Texture2D capture = null;
|
|
RenderTexture target = null;
|
|
Material material = null;
|
|
Sprite sprite = null;
|
|
|
|
try
|
|
{
|
|
Shader shader = Shader.Find("BumpCombat/Comparison Sprite Depth");
|
|
Assert.That(shader, Is.Not.Null);
|
|
material = new Material(shader) { enableInstancing = true };
|
|
source = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false)
|
|
{
|
|
filterMode = FilterMode.Point,
|
|
wrapMode = TextureWrapMode.Clamp,
|
|
};
|
|
Color32[] sourcePixels = new Color32[textureWidth * textureHeight];
|
|
for (int i = 0; i < sourcePixels.Length; i++) sourcePixels[i] = new Color32(0, 0, 0, 0);
|
|
sourcePixels[0] = new Color32(255, 32, 32, 255);
|
|
sourcePixels[1] = new Color32(255, 96, 32, 255);
|
|
sourcePixels[textureWidth + 2] = new Color32(32, 255, 32, 255);
|
|
sourcePixels[textureWidth * 2 + 5] = new Color32(32, 64, 255, 255);
|
|
sourcePixels[textureWidth * 3 + 7] = new Color32(255, 255, 32, 255);
|
|
source.SetPixels32(sourcePixels);
|
|
source.Apply(false, false);
|
|
sprite = Sprite.Create(source, new Rect(0, 0, textureWidth, textureHeight),
|
|
new Vector2(.5f, .5f), 2f);
|
|
|
|
target = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32)
|
|
{
|
|
filterMode = FilterMode.Point,
|
|
antiAliasing = 1,
|
|
};
|
|
target.Create();
|
|
cameraObject = new GameObject("Comparison flip render test camera");
|
|
Camera camera = cameraObject.AddComponent<Camera>();
|
|
cameraObject.AddComponent<UniversalAdditionalCameraData>().SetRenderer(1);
|
|
camera.orthographic = true;
|
|
camera.orthographicSize = 1f;
|
|
camera.aspect = (float)width / height;
|
|
camera.clearFlags = CameraClearFlags.SolidColor;
|
|
camera.backgroundColor = Color.black;
|
|
camera.targetTexture = target;
|
|
camera.cullingMask = 1 << FlipRenderTestLayer;
|
|
camera.transform.position = new Vector3(0f, 0f, -10f);
|
|
|
|
spriteObject = new GameObject("Asymmetric flip test sprite");
|
|
spriteObject.layer = FlipRenderTestLayer;
|
|
SpriteRenderer renderer = spriteObject.AddComponent<SpriteRenderer>();
|
|
renderer.sprite = sprite;
|
|
renderer.sharedMaterial = material;
|
|
renderer.flipX = false;
|
|
capture = new Texture2D(width, height, TextureFormat.RGBA32, false);
|
|
yield return null;
|
|
Color[] forward = Capture(camera, target, capture);
|
|
|
|
renderer.flipX = true;
|
|
yield return null;
|
|
Color[] flipped = Capture(camera, target, capture);
|
|
|
|
int mismatchedPixels = 0;
|
|
int forwardBrightPixels = 0;
|
|
int flippedBrightPixels = 0;
|
|
int flipChangedPixels = 0;
|
|
for (int y = 0; y < height; y++)
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
Color expected = forward[y * width + (width - 1 - x)];
|
|
Color actual = flipped[y * width + x];
|
|
Color sameCoordinateForward = forward[y * width + x];
|
|
if (IsBright(forward[y * width + x])) forwardBrightPixels++;
|
|
if (IsBright(actual)) flippedBrightPixels++;
|
|
if (ColorDistance(sameCoordinateForward, actual) > .02f) flipChangedPixels++;
|
|
if (Mathf.Abs(expected.r - actual.r) + Mathf.Abs(expected.g - actual.g)
|
|
+ Mathf.Abs(expected.b - actual.b) + Mathf.Abs(expected.a - actual.a) > .02f)
|
|
mismatchedPixels++;
|
|
}
|
|
Assert.That(forwardBrightPixels, Is.GreaterThan(0), "The forward image must contain rendered sprite pixels.");
|
|
Assert.That(flippedBrightPixels, Is.GreaterThan(0), "The flipped image must contain rendered sprite pixels.");
|
|
Assert.That(flipChangedPixels, Is.GreaterThan(0), "Flipping this asymmetric sprite must change actual pixels.");
|
|
Assert.That(mismatchedPixels, Is.Zero,
|
|
"SpriteRenderer.flipX must mirror the rendered pixels, not merely expose a flip flag.");
|
|
}
|
|
finally
|
|
{
|
|
if (spriteObject != null) Object.Destroy(spriteObject);
|
|
if (cameraObject != null) Object.Destroy(cameraObject);
|
|
if (sprite != null) Object.Destroy(sprite);
|
|
if (source != null) Object.Destroy(source);
|
|
if (capture != null) Object.Destroy(capture);
|
|
if (target != null) { target.Release(); Object.Destroy(target); }
|
|
if (material != null) Object.Destroy(material);
|
|
}
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator SharedSpriteMaterialStillRendersMeshAndLineGeometry()
|
|
{
|
|
const int width = 80;
|
|
const int height = 80;
|
|
GameObject cameraObject = null;
|
|
GameObject meshObject = null;
|
|
GameObject lineObject = null;
|
|
Texture2D capture = null;
|
|
RenderTexture target = null;
|
|
Material material = null;
|
|
Mesh mesh = null;
|
|
try
|
|
{
|
|
Shader shader = Shader.Find("BumpCombat/Comparison Sprite Depth");
|
|
Assert.That(shader, Is.Not.Null);
|
|
material = new Material(shader);
|
|
target = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32);
|
|
target.Create();
|
|
|
|
cameraObject = new GameObject("Shared material render test camera");
|
|
Camera camera = cameraObject.AddComponent<Camera>();
|
|
cameraObject.AddComponent<UniversalAdditionalCameraData>().SetRenderer(1);
|
|
camera.orthographic = true;
|
|
camera.orthographicSize = 2f;
|
|
camera.aspect = 1f;
|
|
camera.clearFlags = CameraClearFlags.SolidColor;
|
|
camera.backgroundColor = Color.black;
|
|
camera.targetTexture = target;
|
|
camera.cullingMask = 1 << FlipRenderTestLayer;
|
|
camera.transform.position = new Vector3(0f, 0f, -10f);
|
|
|
|
meshObject = new GameObject("Ground-like MeshRenderer");
|
|
meshObject.layer = FlipRenderTestLayer;
|
|
mesh = new Mesh
|
|
{
|
|
vertices = new[]
|
|
{
|
|
new Vector3(-.6f, .1f, 0f), new Vector3(.6f, .1f, 0f),
|
|
new Vector3(-.6f, 1.3f, 0f), new Vector3(.6f, 1.3f, 0f),
|
|
},
|
|
uv = new[] { Vector2.zero, Vector2.right, Vector2.up, Vector2.one },
|
|
colors = new[] { Color.white, Color.white, Color.white, Color.white },
|
|
triangles = new[] { 0, 2, 1, 2, 3, 1 },
|
|
};
|
|
mesh.RecalculateBounds();
|
|
meshObject.AddComponent<MeshFilter>().sharedMesh = mesh;
|
|
meshObject.AddComponent<MeshRenderer>().sharedMaterial = material;
|
|
|
|
lineObject = new GameObject("Ground-like LineRenderer");
|
|
lineObject.layer = FlipRenderTestLayer;
|
|
LineRenderer line = lineObject.AddComponent<LineRenderer>();
|
|
line.useWorldSpace = true;
|
|
line.positionCount = 2;
|
|
line.startWidth = line.endWidth = .12f;
|
|
line.startColor = line.endColor = Color.red;
|
|
line.sharedMaterial = material;
|
|
line.SetPosition(0, new Vector3(-1.2f, -1f, 0f));
|
|
line.SetPosition(1, new Vector3(1.2f, -1f, 0f));
|
|
|
|
capture = new Texture2D(width, height, TextureFormat.RGBA32, false);
|
|
yield return null;
|
|
Color[] pixels = Capture(camera, target, capture);
|
|
int meshPixels = 0;
|
|
int linePixels = 0;
|
|
foreach (Color pixel in pixels)
|
|
{
|
|
if (pixel.r > .8f && pixel.g > .8f && pixel.b > .8f) meshPixels++;
|
|
if (pixel.r > .8f && pixel.g < .3f && pixel.b < .3f) linePixels++;
|
|
}
|
|
Assert.That(meshPixels, Is.GreaterThan(10),
|
|
"The shared shader must keep opaque floor/mesh geometry visible with default SpriteProps.");
|
|
Assert.That(linePixels, Is.GreaterThan(3),
|
|
"LineRenderer geometry using the shared comparison material must remain visible.");
|
|
}
|
|
finally
|
|
{
|
|
if (meshObject != null) Object.Destroy(meshObject);
|
|
if (lineObject != null) Object.Destroy(lineObject);
|
|
if (cameraObject != null) Object.Destroy(cameraObject);
|
|
if (capture != null) Object.Destroy(capture);
|
|
if (mesh != null) Object.Destroy(mesh);
|
|
if (target != null) { target.Release(); Object.Destroy(target); }
|
|
if (material != null) Object.Destroy(material);
|
|
}
|
|
}
|
|
|
|
private static Color[] Capture(Camera camera, RenderTexture target, Texture2D capture)
|
|
{
|
|
RenderTexture previous = RenderTexture.active;
|
|
camera.Render();
|
|
RenderTexture.active = target;
|
|
capture.ReadPixels(new Rect(0f, 0f, target.width, target.height), 0, 0);
|
|
capture.Apply(false, false);
|
|
Color[] pixels = capture.GetPixels();
|
|
RenderTexture.active = previous;
|
|
return pixels;
|
|
}
|
|
|
|
private static bool IsBright(Color color) => color.r > .25f || color.g > .25f || color.b > .25f;
|
|
|
|
private static float ColorDistance(Color a, Color b) => Mathf.Abs(a.r - b.r) + Mathf.Abs(a.g - b.g)
|
|
+ Mathf.Abs(a.b - b.b) + Mathf.Abs(a.a - b.a);
|
|
}
|
|
}
|