Skip to content

Commit

Permalink
add pot option (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
elringus authored Dec 26, 2022
1 parent e52e074 commit 46aa78b
Show file tree
Hide file tree
Showing 27 changed files with 375 additions and 53 deletions.
1 change: 1 addition & 0 deletions Assets/Samples/Atlases/Animation.asset
Original file line number Diff line number Diff line change
Expand Up @@ -5747,6 +5747,7 @@ MonoBehaviour:
- {fileID: 2800000, guid: bff6f499ccce2804aa491f583fb0fec9, type: 3}
atlasSizeLimit: 4096
forceSquare: 0
forcePot: 0
pixelsPerUnit: 100
diceUnitSize: 128
padding: 2
Expand Down
1 change: 1 addition & 0 deletions Assets/Samples/Atlases/Flat.asset
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ MonoBehaviour:
- {fileID: 2800000, guid: 45be0e6a958e2ce49a8b33cc25c8376f, type: 3}
atlasSizeLimit: 2048
forceSquare: 0
forcePot: 0
pixelsPerUnit: 100
diceUnitSize: 16
padding: 2
Expand Down
1 change: 1 addition & 0 deletions Assets/Samples/Atlases/Kohaku.asset
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ MonoBehaviour:
- {fileID: 2800000, guid: 129f337fbba715e4083dd83ac545d3f1, type: 3}
atlasSizeLimit: 2048
forceSquare: 0
forcePot: 0
pixelsPerUnit: 100
diceUnitSize: 128
padding: 2
Expand Down
2 changes: 1 addition & 1 deletion Assets/SpriteDicing/Editor/Editors/AtlasBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private List<AtlasTexture> PackTextures (IReadOnlyList<DicedTexture> dicedTextur
DeleteExistingAtlasTextures();
var basePath = atlasPath.Substring(0, atlasPath.LastIndexOf(".", StringComparison.Ordinal));
var serializer = new TextureSerializer(basePath, textureSettings);
var packer = new TexturePacker(serializer, UVInset, ForceSquare, AtlasSizeLimit, UnitSize, Padding);
var packer = new TexturePacker(serializer, UVInset, ForceSquare, ForcePot, AtlasSizeLimit, UnitSize, Padding);
var atlasTextures = packer.Pack(dicedTextures);
SaveAtlasTextures(atlasTextures);
return atlasTextures;
Expand Down
11 changes: 9 additions & 2 deletions Assets/SpriteDicing/Editor/Editors/DicedSpriteAtlasEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public class DicedSpriteAtlasEditor : Editor
private static readonly GUIContent decoupleSpriteDataContent = new GUIContent("Decouple Sprite Data", "Whether to save sprite assets in a separate folder instead of adding them as children of the atlas asset.\nWARNING: When rebuilding after changing this option the asset references to previously generated sprites will be lost.");
private static readonly GUIContent trimTransparentContent = new GUIContent("Trim Transparent", "Improves compression ratio by discarding fully-transparent diced units, but may also change sprite dimensions. Disable to preserve original texture dimensions.");
private static readonly GUIContent atlasSizeLimitContent = new GUIContent("Atlas Size Limit", "Maximum size of a single generated atlas texture; will generate multiple textures when the limit is reached.");
private static readonly GUIContent forceSquareContent = new GUIContent("Force Square", "The generated atlas textures will always be square. Less efficient, but required for PVRTC compression.");
private static readonly GUIContent forceSquareContent = new GUIContent("Square", "The generated atlas textures will always be square. Less efficient, but required for PVRTC compression.");
private static readonly GUIContent forcePotContent = new GUIContent("POT", "The generated atlas textures will always have width and height of power of two. Extremely inefficient, but may be required by some older GPUs.");
private static readonly GUIContent pixelsPerUnitContent = new GUIContent("Pixels Per Unit", "How many pixels in the sprite correspond to the unit in the world.");
private static readonly GUIContent diceUnitSizeContent = new GUIContent("Dice Unit Size", "The size of a single diced unit.");
private static readonly GUIContent paddingContent = new GUIContent("Padding", "The size of a pixel border to add between adjacent diced units inside atlas. Increase to prevent texture bleeding artifacts (usually appear as thin gaps between diced units). Larger values will consume more texture space, but yield better anti-bleeding results. Minimum value of 2 is recommended in most cases. When 2 is not enough to prevent bleeding, consider adding a bit of `UV Inset` before increasing the padding.");
Expand Down Expand Up @@ -74,7 +75,7 @@ private void DrawPaddingSlider ()
private GUIContent GetBuildButtonContent ()
{
var targetAtlas = target as DicedSpriteAtlas;
if (!targetAtlas) return GUIContent.none;
if (targetAtlas == null) return GUIContent.none;
var name = targetAtlas.Sprites.Count > 0 ? "Rebuild Atlas" : "Build Atlas";
var tooltip = InputFolder ? "" : "Select input directory to build atlas.";
return new GUIContent(name, tooltip);
Expand Down Expand Up @@ -103,7 +104,13 @@ private void DrawSizeGUI ()
var popupLabels = atlasLimitValues.Select(pair => new GUIContent(pair.ToString())).ToArray();
EditorGUI.IntPopup(rect, AtlasSizeLimitProperty, popupLabels, atlasLimitValues, GUIContent.none);
rect.x += rect.width + 5;
rect.width = 60;
EditorGUI.BeginDisabledGroup(ForcePot);
ToggleLeftGUI(rect, ForceSquareProperty, forceSquareContent);
EditorGUI.EndDisabledGroup();
rect.x += rect.width + 5;
rect.width = 60;
ToggleLeftGUI(rect, ForcePotProperty, forcePotContent);
EditorGUILayout.EndHorizontal();
}

Expand Down
3 changes: 3 additions & 0 deletions Assets/SpriteDicing/Editor/Editors/EditorProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static class EditorProperties
public static float UVInset => UVInsetProperty.floatValue;
public static float PPU => PPUProperty.floatValue;
public static bool ForceSquare => ForceSquareProperty.boolValue;
public static bool ForcePot => ForcePotProperty.boolValue;
public static int AtlasSizeLimit => AtlasSizeLimitProperty.intValue;
public static Object InputFolder => InputFolderProperty.objectReferenceValue;
public static bool IncludeSubfolders => IncludeSubfoldersProperty.boolValue;
Expand All @@ -29,6 +30,7 @@ public static class EditorProperties
public static SerializedProperty TrimTransparentProperty { get; private set; }
public static SerializedProperty AtlasSizeLimitProperty { get; private set; }
public static SerializedProperty ForceSquareProperty { get; private set; }
public static SerializedProperty ForcePotProperty { get; private set; }
public static SerializedProperty PPUProperty { get; private set; }
public static SerializedProperty UnitSizeProperty { get; private set; }
public static SerializedProperty PaddingProperty { get; private set; }
Expand All @@ -49,6 +51,7 @@ public static void InitializeProperties (SerializedObject serializedObject)
TrimTransparentProperty = serializedObject.FindProperty("trimTransparent");
AtlasSizeLimitProperty = serializedObject.FindProperty("atlasSizeLimit");
ForceSquareProperty = serializedObject.FindProperty("forceSquare");
ForcePotProperty = serializedObject.FindProperty("forcePot");
PPUProperty = serializedObject.FindProperty("pixelsPerUnit");
UnitSizeProperty = serializedObject.FindProperty("diceUnitSize");
PaddingProperty = serializedObject.FindProperty("padding");
Expand Down
5 changes: 4 additions & 1 deletion Assets/SpriteDicing/Editor/Processors/TexturePacker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ public class TexturePacker
private readonly ITextureSerializer serializer;
private readonly float uvInset;
private readonly bool square;
private readonly bool pot;
private readonly int sizeLimit;
private readonly int unitSize;
private readonly int padding;
private readonly int paddedUnitSize;
private readonly int unitsPerAtlasLimit;

public TexturePacker (ITextureSerializer serializer, float uvInset, bool square, int sizeLimit, int unitSize, int padding)
public TexturePacker (ITextureSerializer serializer, float uvInset, bool square, bool pot, int sizeLimit, int unitSize, int padding)
{
if (serializer is null) throw new ArgumentNullException(nameof(serializer));
if (uvInset < 0 || uvInset > .5f) throw new ArgumentException("UV inset should be in 0 to 0.5 range.");
Expand All @@ -30,6 +31,7 @@ public TexturePacker (ITextureSerializer serializer, float uvInset, bool square,
this.serializer = serializer;
this.uvInset = uvInset;
this.square = square;
this.pot = pot;
this.sizeLimit = sizeLimit;
this.unitSize = unitSize;
this.padding = padding;
Expand Down Expand Up @@ -83,6 +85,7 @@ private DicedTexture FindTextureToPack (IEnumerable<DicedTexture> texturesToPack
private Vector2Int EvaluateAtlasSize (int unitsCount)
{
var size = Vector2Int.one * Mathf.CeilToInt(Mathf.Sqrt(unitsCount));
if (pot) return Vector2Int.one * Mathf.NextPowerOfTwo(size.x * paddedUnitSize);
if (square) return size * paddedUnitSize;
for (var width = size.x; width > 0; width--)
{
Expand Down
1 change: 1 addition & 0 deletions Assets/SpriteDicing/Runtime/DicedSpriteAtlas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class DicedSpriteAtlas : ScriptableObject
#pragma warning disable 0169, 0414, 1635, IDE0052
[SerializeField] private int atlasSizeLimit = 2048;
[SerializeField] private bool forceSquare;
[SerializeField] private bool forcePot;
[SerializeField] private float pixelsPerUnit = 100f;
[SerializeField] private int diceUnitSize = 64;
[SerializeField] private int padding = 2;
Expand Down
18 changes: 9 additions & 9 deletions Assets/SpriteDicing/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "com.elringus.spritedicing",
"version": "1.8.0",
"displayName": "SpriteDicing",
"description": "Compress sprite textures by eliminating identical areas.",
"unity": "2019.4",
"author": {
"name": "Elringus",
"url": "https://github.com/Elringus"
}
"name": "com.elringus.spritedicing",
"version": "1.9.0",
"displayName": "SpriteDicing",
"description": "Compress sprite textures by eliminating identical areas.",
"unity": "2019.4",
"author": {
"name": "Elringus",
"url": "https://github.com/Elringus"
}
}
4 changes: 3 additions & 1 deletion Assets/Tests/Editor/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ public static class Paths
public static readonly string RGB1x3 = BuildTexturePath("RGB1x3");
public static readonly string RGB3x1 = BuildTexturePath("RGB3x1");
public static readonly string RGB4x4 = BuildTexturePath("RGB4x4");
public static readonly string UIC4x4 = BuildTexturePath("UIC4x4");
public static readonly IReadOnlyList<string> OneByOne = new[] { B, R };
public static readonly IReadOnlyList<string> TwoByTwo = new[] { BGRT, BTGR, BTGT, TTTT };
public static readonly IReadOnlyList<string> TopLevel = new[] { RGB1x3, RGB3x1, RGB4x4 };
public static readonly IReadOnlyList<string> TopLevel = new[] { RGB1x3, RGB3x1, RGB4x4, UIC4x4 };
public static readonly IReadOnlyList<string> All = TopLevel.Concat(TwoByTwo).Concat(OneByOne).ToArray();
}

Expand All @@ -37,6 +38,7 @@ public static class Textures
public static Texture2D RGB1x3 => LoadTexture(Paths.RGB1x3);
public static Texture2D RGB3x1 => LoadTexture(Paths.RGB3x1);
public static Texture2D RGB4x4 => LoadTexture(Paths.RGB4x4);
public static Texture2D UIC4x4 => LoadTexture(Paths.UIC4x4);
}

public static class Colors
Expand Down
4 changes: 2 additions & 2 deletions Assets/Tests/Editor/SpriteBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void WhenEmptyTextureDefaultSpriteIsGenerated ()
AreEqual(Vector2.one, sprite.rect.size);
}

private static List<Sprite> Build (string[] texturePaths, float uvInset = 0, bool square = false, int sizeLimit = 8,
private static List<Sprite> Build (string[] texturePaths, float uvInset = 0, bool square = false, bool pot = false, int sizeLimit = 8,
int unitSize = 1, int padding = 0, float ppu = 1, Vector2 pivot = default, bool keepOriginalPivot = false)
{
// TODO: Don't use loader, dicer and packer here; create mock atlas textures instead.
Expand All @@ -69,7 +69,7 @@ private static List<Sprite> Build (string[] texturePaths, float uvInset = 0, boo
var dicer = new TextureDicer(unitSize, padding, true);
var dicedTextures = sourceTextures.Select(dicer.Dice);
var serializer = new MockTextureSerializer();
var packer = new TexturePacker(serializer, uvInset, square, sizeLimit, unitSize, padding);
var packer = new TexturePacker(serializer, uvInset, square, pot, sizeLimit, unitSize, padding);
var atlasTextures = packer.Pack(dicedTextures);
var builder = new SpriteBuilder(ppu, pivot, keepOriginalPivot);
var sprites = new List<Sprite>();
Expand Down
22 changes: 15 additions & 7 deletions Assets/Tests/Editor/TexturePackerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public void WhenInvalidArgumentExceptionIsThrown ()
Throws<ArgumentException>(() => Pack(Array.Empty<Texture2D>(), padding: -1));
Throws<ArgumentException>(() => Pack(Array.Empty<Texture2D>(), unitSize: 1, padding: 2));
// ReSharper disable ObjectCreationAsStatement
Throws<ArgumentNullException>(() => new TexturePacker(null, 0, false, 1, 1, 0));
Throws<ArgumentNullException>(() => new TexturePacker(new MockTextureSerializer(), 0, false, 1, 1, 0).Pack(null));
Throws<ArgumentNullException>(() => new TexturePacker(null, 0, false, false, 1, 1, 0));
Throws<ArgumentNullException>(() => new TexturePacker(new MockTextureSerializer(), 0, false, false, 1, 1, 0).Pack(null));
Throws<ArgumentNullException>(() => new AtlasTexture(null, new Dictionary<Hash128, Rect>(), Array.Empty<DicedTexture>()));
Throws<ArgumentNullException>(() => new AtlasTexture(Texture2D.redTexture, null, Array.Empty<DicedTexture>()));
Throws<ArgumentNullException>(() => new AtlasTexture(Texture2D.redTexture, new Dictionary<Hash128, Rect>(), null));
Expand All @@ -39,7 +39,7 @@ public void WhenNoTexturesToPackEmptyAtlasCollectionIsReturned ()
public void DicedTexturesArePreserved ()
{
var dicedTexture = new DicedTexture(new SourceTexture("B", B), Array.Empty<DicedUnit>());
var atlasTexture = new TexturePacker(new MockTextureSerializer(), 0, false, 1, 1, 0).Pack(new[] { dicedTexture })[0];
var atlasTexture = new TexturePacker(new MockTextureSerializer(), 0, false, false, 1, 1, 0).Pack(new[] { dicedTexture })[0];
AreEqual(dicedTexture, atlasTexture.DicedTextures[0]);
}

Expand All @@ -58,7 +58,7 @@ public void WhenContentFromSingleTextureDoesntFitExceptionIsThrown ()
[Test]
public void WhenForcingSquareAtlasTextureIsSquare ()
{
var atlas = Pack(new[] { RGB4x4, B }, sizeLimit: 4, square: true)[0];
var atlas = Pack(new[] { RGB4x4, B }, square: true)[0];
IsTrue(atlas.Texture.width == atlas.Texture.height);
}

Expand All @@ -76,6 +76,14 @@ public void WhenNotForcingSquareButSquareIsOptimalAtlasTextureIsSquare ()
IsTrue(atlas.Texture.width == atlas.Texture.height);
}

[Test]
public void WhenForcingPowerOfTwoAtlasTextureIsPowerOfTwo ()
{
var atlas = Pack(new[] { UIC4x4, RGB4x4 }, pot: true)[0];
AreEqual(8, atlas.Texture.width);
AreEqual(8, atlas.Texture.height);
}

[Test]
public void BorderUVsAreCropped ()
{
Expand Down Expand Up @@ -104,14 +112,14 @@ public void SlackPixelsAreClear ()
AreEqual(Color.clear, atlas.Texture.GetPixel(4, 3));
}

private static List<AtlasTexture> Pack (Texture2D[] textures,
float uvInset = 0, bool square = false, int sizeLimit = 8, int unitSize = 1, int padding = 0)
private static List<AtlasTexture> Pack (Texture2D[] textures, float uvInset = 0, bool square = false,
bool pot = false, int sizeLimit = 1024, int unitSize = 1, int padding = 0)
{
// TODO: Don't use dicer here; create mock diced textures instead.
var dicer = new TextureDicer(unitSize, padding, true);
var dicedTextures = textures.Select(t => new SourceTexture(t.name, t)).Select(dicer.Dice);
var serializer = new MockTextureSerializer();
var packer = new TexturePacker(serializer, uvInset, square, sizeLimit, unitSize, padding);
var packer = new TexturePacker(serializer, uvInset, square, pot, sizeLimit, unitSize, padding);
return packer.Pack(dicedTextures);
}
}
Expand Down
18 changes: 15 additions & 3 deletions Assets/Tests/Textures/1x1/B.png.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions Assets/Tests/Textures/1x1/R.png.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions Assets/Tests/Textures/2x2/BGRT.png.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions Assets/Tests/Textures/2x2/BTGR.png.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 46aa78b

Please sign in to comment.