Skip to content

Commit

Permalink
Refactor texture packing (LostArtefacts#664)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahm86 authored May 6, 2024
1 parent 7137d0b commit 4f36d39
Show file tree
Hide file tree
Showing 66 changed files with 2,453 additions and 1,878 deletions.
Binary file modified Deps/RectanglePacker.dll
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Drawing;
using TRImageControl;
using TRImageControl;
using TRImageControl.Packing;
using TRLevelControl.Model;

Expand Down Expand Up @@ -45,53 +44,42 @@ public override void ApplyToLevel(TR3Level level)
}.ApplyToLevel(level));
}

private List<EMTextureMap> BuildAndPackTextures<E, L>(TRTexturePacker<E, L> packer, List<TRObjectTexture> textures)
where L : class
where E : Enum
private List<EMTextureMap> BuildAndPackTextures(TRTexturePacker packer, List<TRObjectTexture> textures)
{
List<EMTextureMap> mappings = new();

foreach (EMTextureData data in Data)
{
IndexedTRObjectTexture indexedTexture = new()
TRTextileSegment indexedSegment = new()
{
Index = data.Background,
Texture = textures[data.Background]
};
TRImage tile = packer.Tiles[indexedTexture.Atlas].Image;
TRImage clip = tile.Export(indexedTexture.Bounds);
TRImage tile = packer.Tiles[indexedSegment.Atlas].Image;
TRImage clip = tile.Export(indexedSegment.Bounds);
clip.Overlay(new(data.Overlay));

IndexedTRObjectTexture texture = CreateTexture(clip.Size);
TRTextileRegion segment = new(texture, clip);
packer.AddRectangle(segment);
TRTextileSegment segment = new()
{
Texture = new TRObjectTexture(new(0, 0, clip.Size.Width, clip.Size.Height))
{
BlendingMode = data.RetainInWireframe ? TRBlendingMode.Unused01 : TRBlendingMode.Opaque,
}
};

packer.AddRectangle(new(segment, clip));

mappings.Add(new()
{
[(ushort)textures.Count] = data.GeometryMap
});
textures.Add(texture.Texture);

// Use a flag that's unused throughout the games to indicate this texture
// must remain as-is.
if (data.RetainInWireframe)
{
texture.Texture.BlendingMode = TRBlendingMode.Unused01;
}
textures.Add(segment.Texture as TRObjectTexture);
}

packer.Pack(true);

return mappings;
}

private static IndexedTRObjectTexture CreateTexture(Size size)
{
return new()
{
Texture = new(0, 0, size.Width, size.Height)
};
}
}

public class EMTextureData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public override void ApplyToLevel(TR1Level level)
TR1TexturePacker packer = new(level);
ApplyOverwrites(texture =>
{
return packer.GetObjectTextureSegments(new List<int> { texture })
return packer.GetObjectRegions(new List<int> { texture })
.Select(k => new Tuple<TRTextile, TRTextileRegion>(k.Key, k.Value[0]))
.First();
});
Expand All @@ -27,7 +27,7 @@ public override void ApplyToLevel(TR2Level level)
TR2TexturePacker packer = new(level);
ApplyOverwrites(texture =>
{
return packer.GetObjectTextureSegments(new List<int> { texture })
return packer.GetObjectRegions(new List<int> { texture })
.Select(k => new Tuple<TRTextile, TRTextileRegion>(k.Key, k.Value[0]))
.First();
});
Expand All @@ -40,7 +40,7 @@ public override void ApplyToLevel(TR3Level level)
TR3TexturePacker packer = new(level);
ApplyOverwrites(texture =>
{
return packer.GetObjectTextureSegments(new List<int> { texture })
return packer.GetObjectRegions(new List<int> { texture })
.Select(k => new Tuple<TRTextile, TRTextileRegion>(k.Key, k.Value[0]))
.First();
});
Expand Down Expand Up @@ -68,22 +68,22 @@ public void RemapTextures(Dictionary<ushort, ushort> indexMap)
}
}

private void ApplyOverwrites(Func<ushort, Tuple<TRTextile, TRTextileRegion>> segmentAction)
private void ApplyOverwrites(Func<ushort, Tuple<TRTextile, TRTextileRegion>> regionAction)
{
foreach (TextureOverwrite overwrite in Overwrites)
{
Tuple<TRTextile, TRTextileRegion> segment = segmentAction(overwrite.Texture);
TRImage clippedImage = segment.Item2.Image.Export(overwrite.Clip);
Tuple<TRTextile, TRTextileRegion> region = regionAction(overwrite.Texture);
TRImage clippedImage = region.Item2.Image.Export(overwrite.Clip);

foreach (ushort targetTexture in overwrite.Targets.Keys)
{
Tuple<TRTextile, TRTextileRegion> targetSegment = segmentAction(targetTexture);
Tuple<TRTextile, TRTextileRegion> targetRegion = regionAction(targetTexture);
foreach (Point point in overwrite.Targets[targetTexture])
{
targetSegment.Item1.Image.Import(clippedImage, new
targetRegion.Item1.Image.Import(clippedImage, new
(
targetSegment.Item2.Bounds.X + point.X,
targetSegment.Item2.Bounds.Y + point.Y
targetRegion.Item2.Bounds.X + point.X,
targetRegion.Item2.Bounds.Y + point.Y
), overwrite.RetainBackground);
}
}
Expand Down
228 changes: 112 additions & 116 deletions TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using RectanglePacker.Organisation;
using System.Drawing;
using TRImageControl.Packing;
using TRImageControl.Packing;
using TRLevelControl.Model;
using TRModelTransporter.Events;
using TRModelTransporter.Model;
Expand All @@ -10,142 +8,140 @@ namespace TRModelTransporter.Handlers;

public abstract class AbstractTextureExportHandler<E, L, D>
where E : Enum
where L : class
where L : TRLevelBase
where D : TRBlobBase<E>
{
protected const int _exportBitmapWidth = 320;
protected const int _exportBitmapHeight = 640;

protected L _level;
protected D _definition;
protected ITextureClassifier _classifier;
protected IEnumerable<E> _spriteDependencies;
protected IEnumerable<int> _ignoreableTextureIndices;

protected TRTexturePacker<E, L> _packer;
protected TRTexturePacker _packer;

protected List<TRTextileRegion> _allSegments;
public event EventHandler<SegmentEventArgs> SegmentExported;

Check warning on line 25 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x86

The event 'AbstractTextureExportHandler<E, L, D>.SegmentExported' is never used

Check warning on line 25 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x86

The event 'AbstractTextureExportHandler<E, L, D>.SegmentExported' is never used

Check warning on line 25 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x86

The event 'AbstractTextureExportHandler<E, L, D>.SegmentExported' is never used

Check warning on line 25 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x64

The event 'AbstractTextureExportHandler<E, L, D>.SegmentExported' is never used

Check warning on line 25 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x64

The event 'AbstractTextureExportHandler<E, L, D>.SegmentExported' is never used

Check warning on line 25 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x64

The event 'AbstractTextureExportHandler<E, L, D>.SegmentExported' is never used
public event EventHandler<TRTextureRemapEventArgs> SegmentRemapped;

Check warning on line 26 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x86

The event 'AbstractTextureExportHandler<E, L, D>.SegmentRemapped' is never used

Check warning on line 26 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x86

The event 'AbstractTextureExportHandler<E, L, D>.SegmentRemapped' is never used

Check warning on line 26 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x86

The event 'AbstractTextureExportHandler<E, L, D>.SegmentRemapped' is never used

Check warning on line 26 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x64

The event 'AbstractTextureExportHandler<E, L, D>.SegmentRemapped' is never used

Check warning on line 26 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x64

The event 'AbstractTextureExportHandler<E, L, D>.SegmentRemapped' is never used

Check warning on line 26 in TRDataControl/Handlers/Textures/AbstractTextureExportHandler.cs

View workflow job for this annotation

GitHub Actions / Build x64

The event 'AbstractTextureExportHandler<E, L, D>.SegmentRemapped' is never used

public void Export(L level, D definition, ITextureClassifier classifier, IEnumerable<E> spriteDependencies, IEnumerable<int> ignoreableTextureIndices)
public void Export(L level, D definition, IEnumerable<E> spriteDependencies, IEnumerable<int> ignoreableTextureIndices)
{
_level = level;
_definition = definition;
_classifier = classifier;
_spriteDependencies = spriteDependencies;
_ignoreableTextureIndices = ignoreableTextureIndices;

_allSegments = new List<TRTextileRegion>();

_packer = CreatePacker();
CollateSegments();
ExportSegments();
//_packer = CreatePacker();
//CollateSegments();
//ExportSegments();
}

protected abstract TRTexturePacker<E, L> CreatePacker();

protected abstract TRSpriteSequence GetSprite(E entity);

protected virtual void CollateSegments()
{
Dictionary<TRTextile, List<TRTextileRegion>> textureSegments = _packer.GetModelSegments(_definition.Entity);

TRTextureDeduplicator<E> deduplicator = new()
{
SegmentMap = textureSegments,
UpdateGraphics = false,
SegmentRemapped = SegmentRemapped
};
deduplicator.Deduplicate();

_definition.ObjectTextures = new Dictionary<int, List<IndexedTRObjectTexture>>();
_definition.SpriteSequences = new Dictionary<E, TRSpriteSequence>();
_definition.SpriteTextures = new Dictionary<E, Dictionary<int, List<IndexedTRSpriteTexture>>>();

int bitmapIndex = 0;
foreach (List<TRTextileRegion> segments in textureSegments.Values)
{
for (int i = 0; i < segments.Count; i++)
{
TRTextileRegion segment = segments[i];
if (!deduplicator.ShouldIgnoreSegment(_ignoreableTextureIndices, segment))
{
_allSegments.Add(segment);
_definition.ObjectTextures[bitmapIndex++] = new List<IndexedTRObjectTexture>(segment.Textures.Cast<IndexedTRObjectTexture>().ToArray());
}
}
}

foreach (E spriteEntity in _spriteDependencies)
{
TRSpriteSequence sequence = GetSprite(spriteEntity);
if (sequence != null)
{
_definition.SpriteSequences[spriteEntity] = sequence;
}

Dictionary<TRTextile, List<TRTextileRegion>> spriteSegments = _packer.GetSpriteSegments(spriteEntity);
_definition.SpriteTextures[spriteEntity] = new Dictionary<int, List<IndexedTRSpriteTexture>>();
foreach (List<TRTextileRegion> segments in spriteSegments.Values)
{
for (int i = 0; i < segments.Count; i++)
{
TRTextileRegion segment = segments[i];
_allSegments.Add(segment);
_definition.SpriteTextures[spriteEntity][bitmapIndex++] = new List<IndexedTRSpriteTexture>(segment.Textures.Cast<IndexedTRSpriteTexture>().ToArray());
}
}
}
}

protected virtual void ExportSegments()
{
if (_allSegments.Count == 0)
{
return;
}

DefaultTexturePacker segmentPacker = new();
segmentPacker.AddRectangles(_allSegments);

segmentPacker.Options = new PackingOptions
{
FillMode = PackingFillMode.Horizontal,
OrderMode = PackingOrderMode.Area,
Order = PackingOrder.Descending,
GroupMode = PackingGroupMode.Squares
};
segmentPacker.TileWidth = _exportBitmapWidth;
segmentPacker.TileHeight = _exportBitmapHeight;
segmentPacker.MaximumTiles = 1;

segmentPacker.Pack();

if (segmentPacker.OrphanedRectangles.Count > 0)
{
throw new PackingException(string.Format("Failed to export textures for {0}.", _definition.Entity));
}

TRTextile tile = segmentPacker.Tiles[0];
List<Rectangle> rects = new();
foreach (TRTextileRegion segment in _allSegments)
{
rects.Add(segment.MappedBounds);
}

_definition.TextureSegments = rects.ToArray();

Rectangle region = tile.GetOccupiedRegion();
_definition.Image = tile.Image.Export(region);

foreach (TRTextileRegion segment in _allSegments)
{
SegmentExported?.Invoke(this, new SegmentEventArgs
{
SegmentIndex = segment.FirstTextureIndex,
Image = segment.Image
});
}
}
//protected abstract TRTexturePacker CreatePacker();

//protected abstract TRSpriteSequence GetSprite(E entity);

//protected virtual void CollateSegments()
//{
// Dictionary<TRTextile, List<TRTextileRegion>> textureSegments = _packer.GetModelSegments(_definition.Entity);

// TRTextureDeduplicator<E> deduplicator = new()
// {
// SegmentMap = textureSegments,
// UpdateGraphics = false,
// SegmentRemapped = SegmentRemapped
// };
// deduplicator.Deduplicate();

// _definition.ObjectTextures = new();
// _definition.SpriteSequences = new();
// _definition.SpriteTextures = new();

// int bitmapIndex = 0;
// foreach (List<TRTextileRegion> segments in textureSegments.Values)
// {
// for (int i = 0; i < segments.Count; i++)
// {
// TRTextileRegion segment = segments[i];
// if (!deduplicator.ShouldIgnoreSegment(_ignoreableTextureIndices, segment))
// {
// _allSegments.Add(segment);
// _definition.ObjectTextures[bitmapIndex++] = segment.Textures.Cast<IndexedTRObjectTexture>().ToArray();
// }
// }
// }

// foreach (E spriteEntity in _spriteDependencies)
// {
// TRSpriteSequence sequence = GetSprite(spriteEntity);
// if (sequence != null)
// {
// _definition.SpriteSequences[spriteEntity] = sequence;
// }

// Dictionary<TRTextile, List<TRTextileRegion>> spriteSegments = _packer.GetSpriteSegments(spriteEntity);
// _definition.SpriteTextures[spriteEntity] = new Dictionary<int, List<IndexedTRSpriteTexture>>();
// foreach (List<TRTextileRegion> segments in spriteSegments.Values)
// {
// for (int i = 0; i < segments.Count; i++)
// {
// TRTextileRegion segment = segments[i];
// _allSegments.Add(segment);
// _definition.SpriteTextures[spriteEntity][bitmapIndex++] = new List<IndexedTRSpriteTexture>(segment.Textures.Cast<IndexedTRSpriteTexture>().ToArray());
// }
// }
// }
//}

//protected virtual void ExportSegments()
//{
// if (_allSegments.Count == 0)
// {
// return;
// }

// DefaultTexturePacker segmentPacker = new();
// segmentPacker.AddRectangles(_allSegments);

// segmentPacker.Options = new PackingOptions
// {
// FillMode = PackingFillMode.Horizontal,
// OrderMode = PackingOrderMode.Area,
// Order = PackingOrder.Descending,
// GroupMode = PackingGroupMode.Squares
// };
// segmentPacker.TileWidth = _exportBitmapWidth;
// segmentPacker.TileHeight = _exportBitmapHeight;
// segmentPacker.MaximumTiles = 1;

// segmentPacker.Pack();

// if (segmentPacker.OrphanedRectangles.Count > 0)
// {
// throw new PackingException(string.Format("Failed to export textures for {0}.", _definition.Entity));
// }

// TRTextile tile = segmentPacker.Tiles[0];
// List<Rectangle> rects = new();
// foreach (TRTextileRegion segment in _allSegments)
// {
// rects.Add(segment.MappedBounds);
// }

// _definition.TextureSegments = rects.ToArray();

// Rectangle region = tile.GetOccupiedRegion();
// _definition.Image = tile.Image.Export(region);

// foreach (TRTextileRegion segment in _allSegments)
// {
// SegmentExported?.Invoke(this, new SegmentEventArgs
// {
// SegmentIndex = segment.FirstTextureIndex,
// Image = segment.Image
// });
// }
//}
}
Loading

0 comments on commit 4f36d39

Please sign in to comment.