2021-04-04 18:31:51 +02:00
|
|
|
|
using System;
|
2023-05-15 16:13:18 +02:00
|
|
|
|
using System.Collections.Generic;
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using System.IO;
|
2021-04-04 18:31:51 +02:00
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
|
using MLEM.Data;
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using MLEM.Graphics;
|
|
|
|
|
using MLEM.Maths;
|
2021-04-04 18:31:51 +02:00
|
|
|
|
using MLEM.Textures;
|
|
|
|
|
using NUnit.Framework;
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using Color = Microsoft.Xna.Framework.Color;
|
2021-04-04 18:31:51 +02:00
|
|
|
|
|
2022-12-13 13:11:36 +01:00
|
|
|
|
namespace Tests;
|
2021-04-04 18:31:51 +02:00
|
|
|
|
|
2024-10-26 14:41:36 +02:00
|
|
|
|
public class TexturePackerTests : GameTestFixture {
|
2021-04-04 18:31:51 +02:00
|
|
|
|
|
2024-11-07 16:18:51 +01:00
|
|
|
|
private readonly List<TextureRegion> generatedTextures = [];
|
2021-11-01 16:00:13 +01:00
|
|
|
|
|
2022-10-27 10:22:25 +02:00
|
|
|
|
[TearDown]
|
|
|
|
|
public void TearDown() {
|
2024-11-07 16:18:51 +01:00
|
|
|
|
foreach (var tex in this.generatedTextures)
|
|
|
|
|
tex.Texture.Dispose();
|
|
|
|
|
this.generatedTextures.Clear();
|
2022-10-27 10:22:25 +02:00
|
|
|
|
}
|
2021-04-04 18:31:51 +02:00
|
|
|
|
|
2022-10-27 10:22:25 +02:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestPacking() {
|
|
|
|
|
using var packer = new RuntimeTexturePacker();
|
|
|
|
|
for (var i = 0; i < 5; i++) {
|
|
|
|
|
var width = 16 * (i + 1);
|
2024-11-07 16:18:51 +01:00
|
|
|
|
packer.Add(this.MakeTextureRegion(width, 64), r => {
|
2022-10-27 10:22:25 +02:00
|
|
|
|
Assert.AreEqual(r.Width, width);
|
|
|
|
|
Assert.AreEqual(r.Height, 64);
|
2021-04-04 18:31:51 +02:00
|
|
|
|
});
|
|
|
|
|
}
|
2024-10-26 14:41:36 +02:00
|
|
|
|
packer.Pack(this.Game.GraphicsDevice);
|
2024-11-07 16:18:51 +01:00
|
|
|
|
TexturePackerTests.SaveTexture(packer);
|
|
|
|
|
Assert.AreEqual(packer.PackedTexture.Width, 128);
|
|
|
|
|
Assert.AreEqual(packer.PackedTexture.Height, 128);
|
2022-10-27 10:22:25 +02:00
|
|
|
|
}
|
2021-04-04 18:31:51 +02:00
|
|
|
|
|
2023-05-15 16:13:18 +02:00
|
|
|
|
[Test]
|
2024-11-07 16:18:51 +01:00
|
|
|
|
public void TestOverlap([Values(0, 1, 5, 10)] int padding) {
|
2023-05-15 16:13:18 +02:00
|
|
|
|
var packed = new List<TextureRegion>();
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using var packer = new RuntimeTexturePacker();
|
|
|
|
|
for (var i = 1; i <= 1000; i++)
|
|
|
|
|
packer.Add(this.MakeTextureRegion(i % 239, i % 673), packed.Add, padding);
|
|
|
|
|
packer.Pack(this.Game.GraphicsDevice);
|
|
|
|
|
|
|
|
|
|
TexturePackerTests.SaveTexture(packer, padding.ToString());
|
2023-05-15 16:13:18 +02:00
|
|
|
|
|
|
|
|
|
foreach (var r1 in packed) {
|
2024-11-07 16:18:51 +01:00
|
|
|
|
var r1Padded = r1.Area;
|
|
|
|
|
r1Padded.Inflate(padding, padding);
|
|
|
|
|
|
2023-05-15 16:13:18 +02:00
|
|
|
|
foreach (var r2 in packed) {
|
|
|
|
|
if (r1 == r2)
|
|
|
|
|
continue;
|
2024-11-07 16:18:51 +01:00
|
|
|
|
|
|
|
|
|
Assert.False(r1.Area.Intersects(r2.Area), $"Regions {r1.Area} and {r2.Area} intersect");
|
|
|
|
|
|
|
|
|
|
var r2Padded = r2.Area;
|
|
|
|
|
r2Padded.Inflate(padding, padding);
|
|
|
|
|
Assert.False(r1Padded.Intersects(r2Padded), $"Padded regions {r1Padded} and {r2Padded} intersect");
|
2023-05-15 16:13:18 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 10:22:25 +02:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestDisposal() {
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using var packer = new RuntimeTexturePacker(disposeTextures: true);
|
|
|
|
|
var disposeLater = this.MakeTextureRegion(16, 16);
|
|
|
|
|
packer.Add(disposeLater, TexturePackerTests.StubResult);
|
|
|
|
|
packer.Add(new TextureRegion(disposeLater, 0, 0, 8, 8), TexturePackerTests.StubResult);
|
2024-10-26 14:41:36 +02:00
|
|
|
|
packer.Pack(this.Game.GraphicsDevice);
|
2024-11-07 16:18:51 +01:00
|
|
|
|
Assert.True(disposeLater.Texture.IsDisposed);
|
2022-10-27 10:22:25 +02:00
|
|
|
|
Assert.False(packer.PackedTexture.IsDisposed);
|
|
|
|
|
}
|
2022-10-20 23:59:42 +02:00
|
|
|
|
|
2022-10-27 10:22:25 +02:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestBounds() {
|
|
|
|
|
// test power of two forcing
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using var packer3 = new RuntimeTexturePacker(true);
|
|
|
|
|
packer3.Add(this.MakeTextureRegion(37, 170), TexturePackerTests.StubResult);
|
2024-10-26 14:41:36 +02:00
|
|
|
|
packer3.Pack(this.Game.GraphicsDevice);
|
2022-10-27 10:22:25 +02:00
|
|
|
|
Assert.AreEqual(64, packer3.PackedTexture.Width);
|
|
|
|
|
Assert.AreEqual(256, packer3.PackedTexture.Height);
|
|
|
|
|
|
|
|
|
|
// test square forcing
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using var packer4 = new RuntimeTexturePacker(forceSquare: true);
|
|
|
|
|
packer4.Add(this.MakeTextureRegion(37, 170), TexturePackerTests.StubResult);
|
2024-10-26 14:41:36 +02:00
|
|
|
|
packer4.Pack(this.Game.GraphicsDevice);
|
2022-10-27 10:22:25 +02:00
|
|
|
|
Assert.AreEqual(170, packer4.PackedTexture.Width);
|
|
|
|
|
Assert.AreEqual(170, packer4.PackedTexture.Height);
|
|
|
|
|
}
|
2022-10-20 23:59:42 +02:00
|
|
|
|
|
2022-10-27 10:22:25 +02:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestPackMultipleTimes() {
|
2024-11-07 16:18:51 +01:00
|
|
|
|
using var packer = new RuntimeTexturePacker();
|
2022-10-20 23:59:42 +02:00
|
|
|
|
|
2022-10-27 10:22:25 +02:00
|
|
|
|
// pack the first time
|
|
|
|
|
var results = 0;
|
|
|
|
|
for (var i = 0; i < 10; i++)
|
2024-11-07 16:18:51 +01:00
|
|
|
|
packer.Add(this.MakeTextureRegion(64, 64), _ => results++);
|
2024-10-26 14:41:36 +02:00
|
|
|
|
packer.Pack(this.Game.GraphicsDevice);
|
2022-10-27 10:22:25 +02:00
|
|
|
|
Assert.AreEqual(10, results);
|
|
|
|
|
|
2024-11-07 16:18:51 +01:00
|
|
|
|
// pack again
|
|
|
|
|
packer.Add(this.MakeTextureRegion(64, 64), _ => results++);
|
2024-10-26 14:41:36 +02:00
|
|
|
|
packer.Pack(this.Game.GraphicsDevice);
|
2024-11-07 16:18:51 +01:00
|
|
|
|
// all callbacks are called again, so we add 10 again, as well as the callback we just added
|
|
|
|
|
Assert.AreEqual(10 + 10 + 1, results);
|
2022-10-27 10:22:25 +02:00
|
|
|
|
}
|
2022-10-20 23:59:42 +02:00
|
|
|
|
|
2023-05-15 16:13:18 +02:00
|
|
|
|
[Test]
|
2024-11-07 16:18:51 +01:00
|
|
|
|
public void TestPackTimes([Values(1, 100, 1000, 5000, 10000)] int total) {
|
|
|
|
|
var random = new Random(1238492384);
|
|
|
|
|
using var sameSizePacker = new RuntimeTexturePacker();
|
|
|
|
|
using var diffSizePacker = new RuntimeTexturePacker();
|
|
|
|
|
for (var i = 0; i < total; i++) {
|
|
|
|
|
sameSizePacker.Add(this.MakeTextureRegion(10, 10), TexturePackerTests.StubResult);
|
|
|
|
|
diffSizePacker.Add(this.MakeTextureRegion(random.Next(10, 200), random.Next(10, 200)), TexturePackerTests.StubResult);
|
|
|
|
|
}
|
|
|
|
|
sameSizePacker.Pack(this.Game.GraphicsDevice);
|
|
|
|
|
diffSizePacker.Pack(this.Game.GraphicsDevice);
|
|
|
|
|
|
|
|
|
|
TexturePackerTests.SaveTexture(sameSizePacker, $"SameSize{total}");
|
|
|
|
|
TexturePackerTests.SaveTexture(diffSizePacker, $"DiffSize{total}");
|
|
|
|
|
|
|
|
|
|
TestContext.WriteLine($"""
|
|
|
|
|
{total} regions,
|
|
|
|
|
same-size {sameSizePacker.LastCalculationTime.TotalMilliseconds}ms calc, {sameSizePacker.LastPackTime.TotalMilliseconds}ms pack, {sameSizePacker.LastTotalTime.TotalMilliseconds}ms total,
|
|
|
|
|
diff-size {diffSizePacker.LastCalculationTime.TotalMilliseconds}ms calc, {diffSizePacker.LastPackTime.TotalMilliseconds}ms pack, {diffSizePacker.LastTotalTime.TotalMilliseconds}ms total
|
|
|
|
|
""");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TextureRegion MakeTextureRegion(int width, int height) {
|
|
|
|
|
var color = ColorHelper.FromHexRgb(SingleRandom.Int(this.generatedTextures.Count));
|
|
|
|
|
var texture = new Texture2D(this.Game.GraphicsDevice, Math.Max(width, 1), Math.Max(height, 1));
|
|
|
|
|
using (var data = texture.GetTextureData()) {
|
|
|
|
|
for (var x = 0; x < texture.Width; x++) {
|
|
|
|
|
for (var y = 0; y < texture.Height; y++)
|
|
|
|
|
data[x, y] = color;
|
2023-05-15 16:13:18 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-07 16:18:51 +01:00
|
|
|
|
var region = new TextureRegion(texture, 0, 0, width, height);
|
|
|
|
|
this.generatedTextures.Add(region);
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void SaveTexture(RuntimeTexturePacker packer, string append = "") {
|
|
|
|
|
var caller = new System.Diagnostics.StackTrace(1).GetFrame(0).GetMethod().Name + append;
|
|
|
|
|
var file = Path.GetFullPath(Path.Combine(TestContext.CurrentContext.WorkDirectory, "PackedTextures", caller + ".png"));
|
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(file)!);
|
|
|
|
|
using (var stream = File.Create(file))
|
|
|
|
|
packer.PackedTexture.SaveAsPng(stream, packer.PackedTexture.Width, packer.PackedTexture.Height);
|
|
|
|
|
TestContext.WriteLine($"Saving texture generated by {caller} to {file}");
|
|
|
|
|
TestContext.AddTestAttachment(file);
|
2023-05-15 16:13:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 10:22:25 +02:00
|
|
|
|
private static void StubResult(TextureRegion region) {}
|
2021-04-04 18:31:51 +02:00
|
|
|
|
|
2022-06-17 18:23:47 +02:00
|
|
|
|
}
|