mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-25 22:18:34 +01:00
Compare commits
No commits in common. "61770d59b1b9db6547ca6eb629fbee2bc71506fb" and "951f4babd5e5d9e499f831488250b985c18c5efb" have entirely different histories.
61770d59b1
...
951f4babd5
3 changed files with 22 additions and 54 deletions
|
@ -58,7 +58,6 @@ Improvements
|
||||||
- Premultiply textures when using RawContentManager
|
- Premultiply textures when using RawContentManager
|
||||||
- Allow enumerating all region names of a DataTextureAtlas
|
- Allow enumerating all region names of a DataTextureAtlas
|
||||||
- Cache RuntimeTexturePacker texture data while packing to improve performance
|
- Cache RuntimeTexturePacker texture data while packing to improve performance
|
||||||
- Improve RuntimeTexturePacker performance by checking against packed textures in the same order as packing
|
|
||||||
|
|
||||||
Fixes
|
Fixes
|
||||||
- Fixed SoundEffectReader incorrectly claiming it could read ogg and mp3 files
|
- Fixed SoundEffectReader incorrectly claiming it could read ogg and mp3 files
|
||||||
|
|
|
@ -34,8 +34,7 @@ namespace MLEM.Data {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan LastTotalTime => this.LastCalculationTime + this.LastPackTime;
|
public TimeSpan LastTotalTime => this.LastCalculationTime + this.LastPackTime;
|
||||||
|
|
||||||
private readonly List<Request> texturesToPack = new List<Request>();
|
private readonly List<Request> textures = new List<Request>();
|
||||||
private readonly List<Request> alreadyPackedTextures = new List<Request>();
|
|
||||||
private readonly Dictionary<Texture2D, TextureData> dataCache = new Dictionary<Texture2D, TextureData>();
|
private readonly Dictionary<Texture2D, TextureData> dataCache = new Dictionary<Texture2D, TextureData>();
|
||||||
private readonly bool autoIncreaseMaxWidth;
|
private readonly bool autoIncreaseMaxWidth;
|
||||||
private readonly bool forcePowerOfTwo;
|
private readonly bool forcePowerOfTwo;
|
||||||
|
@ -69,27 +68,17 @@ namespace MLEM.Data {
|
||||||
/// <param name="result">The result callback which will receive the resulting texture regions.</param>
|
/// <param name="result">The result callback which will receive the resulting texture regions.</param>
|
||||||
/// <param name="padding">The padding that the texture should have around itself. This can be useful if texture bleeding issues occur due to texture coordinate rounding.</param>
|
/// <param name="padding">The padding that the texture should have around itself. This can be useful if texture bleeding issues occur due to texture coordinate rounding.</param>
|
||||||
/// <param name="padWithPixels">Whether the texture's padding should be filled with a copy of the texture's border, rather than transparent pixels. This value only has an effect if <paramref name="padding"/> is greater than 0.</param>
|
/// <param name="padWithPixels">Whether the texture's padding should be filled with a copy of the texture's border, rather than transparent pixels. This value only has an effect if <paramref name="padding"/> is greater than 0.</param>
|
||||||
/// <param name="ignoreTransparent">Whether completely transparent texture regions in the <paramref name="atlas"/> should be ignored. If this is true, they will not be part of the <paramref name="result"/> collection either.</param>
|
|
||||||
/// <exception cref="InvalidOperationException">Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width.</exception>
|
/// <exception cref="InvalidOperationException">Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width.</exception>
|
||||||
public void Add(UniformTextureAtlas atlas, Action<Dictionary<Point, TextureRegion>> result, int padding = 0, bool padWithPixels = false, bool ignoreTransparent = false) {
|
public void Add(UniformTextureAtlas atlas, Action<Dictionary<Point, TextureRegion>> result, int padding = 0, bool padWithPixels = false) {
|
||||||
var addedRegions = new List<TextureRegion>();
|
|
||||||
var resultRegions = new Dictionary<Point, TextureRegion>();
|
var resultRegions = new Dictionary<Point, TextureRegion>();
|
||||||
for (var x = 0; x < atlas.RegionAmountX; x++) {
|
for (var x = 0; x < atlas.RegionAmountX; x++) {
|
||||||
for (var y = 0; y < atlas.RegionAmountY; y++) {
|
for (var y = 0; y < atlas.RegionAmountY; y++) {
|
||||||
var pos = new Point(x, y);
|
var pos = new Point(x, y);
|
||||||
var region = atlas[pos];
|
this.Add(atlas[pos], r => {
|
||||||
|
|
||||||
if (ignoreTransparent) {
|
|
||||||
if (IsTransparent(region))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Add(region, r => {
|
|
||||||
resultRegions.Add(pos, r);
|
resultRegions.Add(pos, r);
|
||||||
if (resultRegions.Count >= addedRegions.Count)
|
if (resultRegions.Count >= atlas.RegionAmountX * atlas.RegionAmountY)
|
||||||
result.Invoke(resultRegions);
|
result.Invoke(resultRegions);
|
||||||
}, padding, padWithPixels);
|
}, padding, padWithPixels);
|
||||||
addedRegions.Add(region);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +138,7 @@ namespace MLEM.Data {
|
||||||
throw new InvalidOperationException($"Cannot add texture with width {texture.Width} to a texture packer with max width {this.maxWidth}");
|
throw new InvalidOperationException($"Cannot add texture with width {texture.Width} to a texture packer with max width {this.maxWidth}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.texturesToPack.Add(new Request(texture, result, padding, padWithPixels));
|
this.textures.Add(new Request(texture, result, padding, padWithPixels));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -164,18 +153,15 @@ namespace MLEM.Data {
|
||||||
throw new InvalidOperationException("Cannot pack a texture packer that is already packed");
|
throw new InvalidOperationException("Cannot pack a texture packer that is already packed");
|
||||||
|
|
||||||
// set pack areas for each request
|
// set pack areas for each request
|
||||||
// we pack larger textures first, so that smaller textures can fit in the gaps that larger ones leave
|
|
||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
foreach (var request in this.texturesToPack.OrderByDescending(t => t.Texture.Width * t.Texture.Height)) {
|
foreach (var request in this.textures.OrderByDescending(t => t.Texture.Width * t.Texture.Height))
|
||||||
request.PackedArea = this.FindFreeArea(request);
|
request.PackedArea = this.FindFreeArea(request);
|
||||||
this.alreadyPackedTextures.Add(request);
|
|
||||||
}
|
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
this.LastCalculationTime = stopwatch.Elapsed;
|
this.LastCalculationTime = stopwatch.Elapsed;
|
||||||
|
|
||||||
// figure out texture size and generate texture
|
// figure out texture size and generate texture
|
||||||
var width = this.alreadyPackedTextures.Max(t => t.PackedArea.Right);
|
var width = this.textures.Max(t => t.PackedArea.Right);
|
||||||
var height = this.alreadyPackedTextures.Max(t => t.PackedArea.Bottom);
|
var height = this.textures.Max(t => t.PackedArea.Bottom);
|
||||||
if (this.forcePowerOfTwo) {
|
if (this.forcePowerOfTwo) {
|
||||||
width = ToPowerOfTwo(width);
|
width = ToPowerOfTwo(width);
|
||||||
height = ToPowerOfTwo(height);
|
height = ToPowerOfTwo(height);
|
||||||
|
@ -187,22 +173,21 @@ namespace MLEM.Data {
|
||||||
// copy texture data onto the packed texture
|
// copy texture data onto the packed texture
|
||||||
stopwatch.Restart();
|
stopwatch.Restart();
|
||||||
using (var data = this.PackedTexture.GetTextureData()) {
|
using (var data = this.PackedTexture.GetTextureData()) {
|
||||||
foreach (var request in this.alreadyPackedTextures)
|
foreach (var request in this.textures)
|
||||||
this.CopyRegion(data, request);
|
this.CopyRegion(data, request);
|
||||||
}
|
}
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
this.LastPackTime = stopwatch.Elapsed;
|
this.LastPackTime = stopwatch.Elapsed;
|
||||||
|
|
||||||
// invoke callbacks
|
// invoke callbacks
|
||||||
foreach (var request in this.alreadyPackedTextures) {
|
foreach (var request in this.textures) {
|
||||||
var packedArea = request.PackedArea.Shrink(new Point(request.Padding));
|
var packedArea = request.PackedArea.Shrink(new Point(request.Padding));
|
||||||
request.Result.Invoke(new TextureRegion(this.PackedTexture, packedArea));
|
request.Result.Invoke(new TextureRegion(this.PackedTexture, packedArea));
|
||||||
if (this.disposeTextures)
|
if (this.disposeTextures)
|
||||||
request.Texture.Texture.Dispose();
|
request.Texture.Texture.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.texturesToPack.Clear();
|
this.textures.Clear();
|
||||||
this.alreadyPackedTextures.Clear();
|
|
||||||
this.dataCache.Clear();
|
this.dataCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,8 +197,7 @@ namespace MLEM.Data {
|
||||||
public void Reset() {
|
public void Reset() {
|
||||||
this.PackedTexture?.Dispose();
|
this.PackedTexture?.Dispose();
|
||||||
this.PackedTexture = null;
|
this.PackedTexture = null;
|
||||||
this.texturesToPack.Clear();
|
this.textures.Clear();
|
||||||
this.alreadyPackedTextures.Clear();
|
|
||||||
this.dataCache.Clear();
|
this.dataCache.Clear();
|
||||||
this.LastCalculationTime = TimeSpan.Zero;
|
this.LastCalculationTime = TimeSpan.Zero;
|
||||||
this.LastPackTime = TimeSpan.Zero;
|
this.LastPackTime = TimeSpan.Zero;
|
||||||
|
@ -234,7 +218,7 @@ namespace MLEM.Data {
|
||||||
while (true) {
|
while (true) {
|
||||||
var intersected = false;
|
var intersected = false;
|
||||||
var area = new Rectangle(pos, size);
|
var area = new Rectangle(pos, size);
|
||||||
foreach (var tex in this.alreadyPackedTextures) {
|
foreach (var tex in this.textures) {
|
||||||
if (tex.PackedArea.Intersects(area)) {
|
if (tex.PackedArea.Intersects(area)) {
|
||||||
pos.X = tex.PackedArea.Right;
|
pos.X = tex.PackedArea.Right;
|
||||||
// when we move down, we want to move down by the smallest intersecting texture's height
|
// when we move down, we want to move down by the smallest intersecting texture's height
|
||||||
|
@ -255,7 +239,13 @@ namespace MLEM.Data {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyRegion(TextureData destination, Request request) {
|
private void CopyRegion(TextureData destination, Request request) {
|
||||||
var data = this.GetCachedTextureData(request.Texture.Texture);
|
// we cache texture data in case multiple requests use the same underlying texture
|
||||||
|
// this collection doesn't need to be disposed since we don't actually edit these textures
|
||||||
|
if (!this.dataCache.TryGetValue(request.Texture.Texture, out var data)) {
|
||||||
|
data = request.Texture.Texture.GetTextureData();
|
||||||
|
this.dataCache.Add(request.Texture.Texture, data);
|
||||||
|
}
|
||||||
|
|
||||||
var location = request.PackedArea.Location + new Point(request.Padding);
|
var location = request.PackedArea.Location + new Point(request.Padding);
|
||||||
for (var x = -request.Padding; x < request.Texture.Width + request.Padding; x++) {
|
for (var x = -request.Padding; x < request.Texture.Width + request.Padding; x++) {
|
||||||
for (var y = -request.Padding; y < request.Texture.Height + request.Padding; y++) {
|
for (var y = -request.Padding; y < request.Texture.Height + request.Padding; y++) {
|
||||||
|
@ -273,27 +263,6 @@ namespace MLEM.Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextureData GetCachedTextureData(Texture2D texture) {
|
|
||||||
// we cache texture data in case multiple requests use the same underlying texture
|
|
||||||
// this collection doesn't need to be disposed since we don't actually edit these textures
|
|
||||||
if (!this.dataCache.TryGetValue(texture, out var data)) {
|
|
||||||
data = texture.GetTextureData();
|
|
||||||
this.dataCache.Add(texture, data);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTransparent(TextureRegion region) {
|
|
||||||
var data = this.GetCachedTextureData(region.Texture);
|
|
||||||
for (var rX = 0; rX < region.Width; rX++) {
|
|
||||||
for (var rY = 0; rY < region.Height; rY++) {
|
|
||||||
if (data[region.U + rX, region.V + rY] != Color.Transparent)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ToPowerOfTwo(int value) {
|
private static int ToPowerOfTwo(int value) {
|
||||||
var ret = 1;
|
var ret = 1;
|
||||||
while (ret < value)
|
while (ret < value)
|
||||||
|
|
|
@ -323,7 +323,7 @@ namespace Sandbox {
|
||||||
packer.Add(new UniformTextureAtlas(tex, 16, 16), r => {
|
packer.Add(new UniformTextureAtlas(tex, 16, 16), r => {
|
||||||
regions.AddRange(r.Values);
|
regions.AddRange(r.Values);
|
||||||
Console.WriteLine($"Returned {r.Count} regions: {string.Join(", ", r.Select(kv => kv.Key + ": " + kv.Value.Area))}");
|
Console.WriteLine($"Returned {r.Count} regions: {string.Join(", ", r.Select(kv => kv.Key + ": " + kv.Value.Area))}");
|
||||||
}, 1, true, true);
|
}, 1, true);
|
||||||
packer.Add(this.Content.LoadTextureAtlas("Textures/Furniture"), r => {
|
packer.Add(this.Content.LoadTextureAtlas("Textures/Furniture"), r => {
|
||||||
regions.AddRange(r.Values);
|
regions.AddRange(r.Values);
|
||||||
Console.WriteLine($"Returned {r.Count} regions: {string.Join(", ", r.Select(kv => kv.Key + ": " + kv.Value.Area))}");
|
Console.WriteLine($"Returned {r.Count} regions: {string.Join(", ", r.Select(kv => kv.Key + ": " + kv.Value.Area))}");
|
||||||
|
@ -342,7 +342,7 @@ namespace Sandbox {
|
||||||
x += r.Width * sc + 1;
|
x += r.Width * sc + 1;
|
||||||
if (x >= 1000) {
|
if (x >= 1000) {
|
||||||
x = 0;
|
x = 0;
|
||||||
y += 250;
|
y += 50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.SpriteBatch.End();
|
this.SpriteBatch.End();
|
||||||
|
|
Loading…
Reference in a new issue