From: nathan.sweet Date: Thu, 18 Nov 2010 10:43:39 +0000 (+0000) Subject: [added] Sprite#rotate90. X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=1a45313e2a9a5942bfa2689cccd89ee3d4fc88f2;p=mikumikustudio%2Flibgdx-mikumikustudio.git [added] Sprite#rotate90. [changed] Added checks to avoid math in Sprite#getVertices. [added] Ability for SpriteSheetPacker/SpriteSheet to rotate images to fit. Added rotated image to SpriteSheetTest. [changed] SpriteSheetPacker file format. [fixed] PartileEmitter image loading. --- diff --git a/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidFileHandle.java b/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidFileHandle.java index a87e66f8a..56eadc009 100644 --- a/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidFileHandle.java +++ b/backends/gdx-backend-android/src/com/badlogic/gdx/backends/android/AndroidFileHandle.java @@ -137,12 +137,13 @@ public class AndroidFileHandle extends FileHandle { try { assets.open(fileName).close(); // Check if file exists. } catch (Exception ex) { + int count; try { - if (assets.list(fileName).length == 0) // Try as directory. - throw new GdxRuntimeException("File not found: " + fileName + " (" + type + ")", ex); + count = assets.list(fileName).length; // Try as directory. } catch (Exception ex2) { throw new GdxRuntimeException("Error locating file: " + fileName + " (" + type + ")", ex2); } + if (count == 0) throw new GdxRuntimeException("File not found: " + fileName + " (" + type + ")", ex); } } } diff --git a/extensions/image-packer/src/com/badlogic/gdx/imagepacker/SpriteSheetPacker.java b/extensions/image-packer/src/com/badlogic/gdx/imagepacker/SpriteSheetPacker.java index 28b1cd13e..d6adf67ae 100644 --- a/extensions/image-packer/src/com/badlogic/gdx/imagepacker/SpriteSheetPacker.java +++ b/extensions/image-packer/src/com/badlogic/gdx/imagepacker/SpriteSheetPacker.java @@ -3,6 +3,7 @@ package com.badlogic.gdx.imagepacker; import java.awt.Color; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.File; @@ -19,6 +20,7 @@ import java.util.regex.Pattern; import javax.imageio.ImageIO; +import com.badlogic.gdx.scenes.scene2d.actions.RotateBy; import com.badlogic.gdx.utils.MathUtils; public class SpriteSheetPacker { @@ -37,6 +39,9 @@ public class SpriteSheetPacker { private boolean pot = true; private int padding = 0; private boolean debug = false; + private boolean rotate = true; + private int maxWidth = 1024; + private int maxHeight = 1024; public SpriteSheetPacker (File inputDir, Filter filter, Direction direction, File outputDir, File packFile) throws IOException { this.inputDir = inputDir; @@ -46,6 +51,7 @@ public class SpriteSheetPacker { ArrayList files = getFiles(inputDir, filter, direction); if (files == null) return; + // Collect and squeeze images. for (File file : files) { if (file.isDirectory()) continue; Image image = squeeze(file); @@ -53,24 +59,16 @@ public class SpriteSheetPacker { } if (images.isEmpty()) return; + // Print image names. System.out.println(inputDir); if (filter != rgba8888) System.out.println("Format: " + filter.name); if (direction != null) System.out.println("Direction: " + direction); - for (Image image : images) - System.out.println("Packing... " + image.file.getName()); - - Collections.sort(images, new Comparator() { - public int compare (Image image1, Image image2) { - return image1.getWidth() * image1.getHeight() - image2.getWidth() * image2.getHeight(); - } - }); xPadding = images.size() > 1 && direction != Direction.x && direction != Direction.xy ? padding : 0; yPadding = images.size() > 1 && direction != Direction.y && direction != Direction.xy ? padding : 0; outputDir.mkdirs(); - String prefix = inputDir.getParentFile().getName(); - + String prefix = inputDir.getName(); writer = new FileWriter(packFile, true); try { while (!images.isEmpty()) @@ -85,18 +83,19 @@ public class SpriteSheetPacker { } private void writePage (String prefix, File outputDir) throws IOException { + // Remove existing image pages in output dir. int imageNumber = 1; File outputFile = new File(outputDir, prefix + imageNumber + ".png"); while (outputFile.exists()) outputFile = new File(outputDir, prefix + ++imageNumber + ".png"); writer.write("\n" + prefix + imageNumber + ".png\n"); - writer.write(direction + "\n"); + writer.write("repeat: " + direction + "\n"); + writer.write("filter: Linear,Linear\n"); // BOZO - // Try reasonably hard to find the smallest size that is also the smallest POT. + // Try reasonably hard to pack images into the smallest POT size. Comparator bestComparator = null; Comparator secondBestComparator = imageComparators.get(0); - int maxWidth = 1024, maxHeight = 1024; int bestWidth = 99999, bestHeight = 99999; int secondBestWidth = 99999, secondBestHeight = 99999; int bestUsedPixels = 0; @@ -106,14 +105,16 @@ public class SpriteSheetPacker { while (true) { if (width > maxWidth && height > maxHeight) break; for (Comparator comparator : imageComparators) { + // Pack as many images as possible, sorting the images different ways. Collections.sort(images, comparator); - int usedPixels = insert(null, new ArrayList(images), width, height); + // Store the best pack, in case not all images fit on the max texture size. if (usedPixels > bestUsedPixels) { secondBestComparator = comparator; secondBestWidth = width; secondBestHeight = height; } + // If all images fit and this sort is the best so far, take note. if (usedPixels == -1) { if (width * height < bestWidth * bestHeight) { bestComparator = comparator; @@ -124,6 +125,7 @@ public class SpriteSheetPacker { } if (bestComparator != null) break; if (pot) { + // 64,64 then 64,128 then 128,64 then 128,128 then 128,256 etc. if (i % 3 == 0) { width *= 2; i++; @@ -136,6 +138,7 @@ public class SpriteSheetPacker { i++; } } else { + // 64-127,64 then 64,64-127 then 128-255,128 then 128,128-255 etc. if (i % 3 == 0) { width++; grownPixels++; @@ -171,7 +174,6 @@ public class SpriteSheetPacker { } width = bestWidth; height = bestHeight; - if (pot) { width = MathUtils.nextPowerOfTwo(width); height = MathUtils.nextPowerOfTwo(height); @@ -197,16 +199,37 @@ public class SpriteSheetPacker { int usedPixels = 0; for (int i = images.size() - 1; i >= 0; i--) { Image image = images.get(i); - Node node = root.insert(image, canvas); - if (node == null) continue; + Node node = root.insert(image, canvas, false); + if (node == null) { + if (rotate) node = root.insert(image, canvas, true); + if (node == null) continue; + } usedPixels += image.getWidth() * image.getHeight(); images.remove(i); if (canvas != null) { - Graphics g = canvas.getGraphics(); + System.out.println("Packing... " + image.file.getName()); + Graphics2D g = (Graphics2D)canvas.getGraphics(); + if (image.rotate) { + g.translate(node.left, node.top); + g.rotate(-90 * MathUtils.degreesToRadians); + g.translate(-node.left, -node.top); + g.translate(-image.getWidth(), 0); + } g.drawImage(image, node.left, node.top, null); + if (image.rotate) { + g.translate(image.getWidth(), 0); + g.translate(node.left, node.top); + g.rotate(90 * MathUtils.degreesToRadians); + g.translate(-node.left, -node.top); + } if (debug) { g.setColor(Color.magenta); - g.drawRect(node.left, node.top, image.getWidth() - 1, image.getHeight() - 1); + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + if (image.rotate) + g.drawRect(node.left, node.top, imageHeight - 1, imageWidth - 1); + else + g.drawRect(node.left, node.top, imageWidth - 1, imageHeight - 1); } } } @@ -285,18 +308,26 @@ public class SpriteSheetPacker { this.height = height; } - public Node insert (Image image, BufferedImage canvas) throws IOException { + public Node insert (Image image, BufferedImage canvas, boolean rotate) throws IOException { if (this.image != null) return null; if (child1 != null) { - Node newNode = child1.insert(image, canvas); + Node newNode = child1.insert(image, canvas, rotate); if (newNode != null) return newNode; - return child2.insert(image, canvas); + return child2.insert(image, canvas, rotate); + } + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + if (rotate) { + int temp = imageWidth; + imageWidth = imageHeight; + imageHeight = temp; } - int neededWidth = image.getWidth() + xPadding; - int neededHeight = image.getHeight() + yPadding; + int neededWidth = imageWidth + xPadding; + int neededHeight = imageHeight + yPadding; if (neededWidth > width || neededHeight > height) return null; if (neededWidth == width && neededHeight == height) { this.image = image; + image.rotate = rotate; write(canvas); return this; } @@ -309,7 +340,7 @@ public class SpriteSheetPacker { child1 = new Node(left, top, width, neededHeight); child2 = new Node(left, top + neededHeight, width, height - neededHeight); } - return child1.insert(image, canvas); + return child1.insert(image, canvas, rotate); } private void write (BufferedImage canvas) throws IOException { @@ -325,20 +356,17 @@ public class SpriteSheetPacker { if (imageName.endsWith("_pre")) imageName = imageName.substring(0, imageName.length() - 2); writer.write(imageName.replace("\\", "/") + "\n"); - writer.write(left + "\n"); - writer.write(top + "\n"); - writer.write(image.getWidth() + "\n"); - writer.write(image.getHeight() + "\n"); - writer.write(image.offsetX + "\n"); - writer.write(image.offsetY + "\n"); - writer.write(image.originalWidth + "\n"); - writer.write(image.originalHeight + "\n"); + writer.write(" rotate: " + image.rotate + "\n"); + writer.write(" xy: " + left + ", " + top + "\n"); + writer.write(" size: " + image.getWidth() + ", " + image.getHeight() + "\n"); + writer.write(" orig: " + image.originalWidth + ", " + image.originalHeight + "\n"); + writer.write(" offset: " + image.offsetX + ", " + image.offsetY + "\n"); Matcher matcher = numberedImagePattern.matcher(imageName); if (matcher.matches()) - writer.write(Integer.parseInt(matcher.group(1)) + "\n"); + writer.write("offset: " + Integer.parseInt(matcher.group(1)) + "\n"); else - writer.write("0\n"); + writer.write("offset: 0\n"); } } @@ -346,6 +374,7 @@ public class SpriteSheetPacker { final File file; final int offsetX, offsetY; final int originalWidth, originalHeight; + boolean rotate; public Image (File file, BufferedImage src, int left, int top, int newWidth, int newHeight) { super(src.getColorModel(), src.getRaster().createWritableChild(left, top, newWidth, newHeight, 0, 0, null), src @@ -444,7 +473,7 @@ public class SpriteSheetPacker { static private void process (File inputDir, File outputDir, File packFile) throws Exception { if (outputDir.exists()) { - String prefix = inputDir.getParentFile().getName(); + String prefix = inputDir.getName(); for (File file : outputDir.listFiles()) if (file.getName().startsWith(prefix)) file.delete(); } @@ -475,6 +504,6 @@ public class SpriteSheetPacker { } public static void main (String[] args) throws Exception { - process("C:/Dev/libgdx/tests/gdx-tests-lwjgl/data/New folder", "c:/temp/pack-out"); + process("c:/temp/pack-in", "c:/temp/pack-out"); } } diff --git a/gdx/src/com/badlogic/gdx/graphics/Sprite.java b/gdx/src/com/badlogic/gdx/graphics/Sprite.java index ef616a4d0..245658a4a 100644 --- a/gdx/src/com/badlogic/gdx/graphics/Sprite.java +++ b/gdx/src/com/badlogic/gdx/graphics/Sprite.java @@ -349,8 +349,46 @@ public class Sprite { dirty = true; } + /** + * Rotates this sprite 90 degrees. This rotation is unaffected by {@link #setRotation(float)} and {@link #rotate(float)}. + */ + public void rotate90 (boolean clockwise) { + float[] vertices = this.vertices; + + float temp = width; + width = height; + height = temp; + + temp = vertices[X1]; + vertices[X1] = vertices[X4]; + vertices[X4] = vertices[X3]; + vertices[X3] = vertices[X2]; + vertices[X2] = temp; + + temp = vertices[Y1]; + vertices[Y1] = vertices[Y4]; + vertices[Y4] = vertices[Y3]; + vertices[Y3] = vertices[Y2]; + vertices[Y2] = temp; + + temp = vertices[V1]; + vertices[V1] = vertices[V4]; + vertices[V4] = vertices[V3]; + vertices[V3] = vertices[V2]; + vertices[V2] = temp; + + temp = vertices[U1]; + vertices[U1] = vertices[U4]; + vertices[U4] = vertices[U3]; + vertices[U3] = vertices[U2]; + vertices[U2] = temp; + + if (rotation != 0 || scaleX != 1 || scaleY != 1) dirty = true; + } + public void setScale(float scaleXY) { - this.scaleX = this.scaleY = scaleXY; + this.scaleX = scaleXY; + this.scaleY = scaleXY; dirty = true; } @@ -381,28 +419,53 @@ public class Sprite { float localY = -originY * scaleY; float localX2 = (-originX + width) * scaleX; float localY2 = (-originY + height) * scaleY; - float cos = MathUtils.cosDeg(rotation); - float sin = MathUtils.sinDeg(rotation); + if (scaleX != 1 || scaleY != 1) { + localX *= scaleX; + localY *= scaleY; + localX2 *= scaleX; + localY2 *= scaleY; + } float worldOriginX = this.x + originX; float worldOriginY = this.y + originY; - - float x1 = localX * cos - localY * sin + worldOriginX; - float y1 = localY * cos + localX * sin + worldOriginY; - vertices[X1] = x1; - vertices[Y1] = y1; - - float x2 = localX * cos - localY2 * sin + worldOriginX; - float y2 = localY2 * cos + localX * sin + worldOriginY; - vertices[X2] = x2; - vertices[Y2] = y2; - - float x3 = localX2 * cos - localY2 * sin + worldOriginX; - float y3 = localY2 * cos + localX2 * sin + worldOriginY; - vertices[X3] = x3; - vertices[Y3] = y3; - - vertices[X4] = x1 + (x3 - x2); - vertices[Y4] = y3 - (y2 - y1); + if (rotation != 0) { + float cos = MathUtils.cosDeg(rotation); + float sin = MathUtils.sinDeg(rotation); + + float x1 = localX * cos - localY * sin + worldOriginX; + float y1 = localY * cos + localX * sin + worldOriginY; + vertices[X1] = x1; + vertices[Y1] = y1; + + float x2 = localX * cos - localY2 * sin + worldOriginX; + float y2 = localY2 * cos + localX * sin + worldOriginY; + vertices[X2] = x2; + vertices[Y2] = y2; + + float x3 = localX2 * cos - localY2 * sin + worldOriginX; + float y3 = localY2 * cos + localX2 * sin + worldOriginY; + vertices[X3] = x3; + vertices[Y3] = y3; + + vertices[X4] = x1 + (x3 - x2); + vertices[Y4] = y3 - (y2 - y1); + } else { + float x1 = localX + worldOriginX; + float y1 = localY + worldOriginY; + float x2 = localX2 + worldOriginX; + float y2 = localY2 + worldOriginY; + + vertices[X1] = x1; + vertices[Y1] = y1; + + vertices[X2] = x1; + vertices[Y2] = y2; + + vertices[X3] = x2; + vertices[Y3] = y2; + + vertices[X4] = x2; + vertices[Y4] = y1; + } } return vertices; } diff --git a/gdx/src/com/badlogic/gdx/graphics/SpriteSheet.java b/gdx/src/com/badlogic/gdx/graphics/SpriteSheet.java index c5163bcbe..447ebad28 100644 --- a/gdx/src/com/badlogic/gdx/graphics/SpriteSheet.java +++ b/gdx/src/com/badlogic/gdx/graphics/SpriteSheet.java @@ -9,14 +9,14 @@ import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; -import com.badlogic.gdx.Files.FileType; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.Texture.TextureWrap; +import com.badlogic.gdx.utils.GdxRuntimeException; -import static com.badlogic.gdx.graphics.Texture.TextureWrap.*; import static com.badlogic.gdx.graphics.Texture.TextureFilter.*; -import com.badlogic.gdx.utils.GdxRuntimeException; +import static com.badlogic.gdx.graphics.Texture.TextureWrap.*; /** * Loads images from texture atlases created by SpriteSheetPacker.
@@ -24,6 +24,8 @@ import com.badlogic.gdx.utils.GdxRuntimeException; * A SpriteSheet must be disposed to free up the resources consumed by the backing textures. */ public class SpriteSheet { + static private final String[] tuple = new String[2]; + private final ArrayList textures = new ArrayList(4); private final PackedSprite[] images; @@ -41,42 +43,57 @@ public class SpriteSheet { else if (pageImage == null) { FileHandle file = imagesDir.child(line); - // BOZO - Get filter from file? - - String direction = reader.readLine(); - TextureWrap wrapX = ClampToEdge; - TextureWrap wrapY = ClampToEdge; + String direction = readValue(reader); + TextureWrap repeatX = ClampToEdge; + TextureWrap repeatY = ClampToEdge; if (direction.equals("x")) - wrapX = Repeat; + repeatX = Repeat; else if (direction.equals("y")) - wrapY = Repeat; + repeatY = Repeat; else if (direction.equals("xy")) { - wrapX = Repeat; - wrapY = Repeat; + repeatX = Repeat; + repeatY = Repeat; } - Texture texture = Gdx.graphics.newTexture(file, Linear, Linear, ClampToEdge, ClampToEdge); + readTuple(reader); + TextureFilter min = TextureFilter.valueOf(tuple[0]); + TextureFilter max = TextureFilter.valueOf(tuple[1]); + + Texture texture = Gdx.graphics.newTexture(file, min, max, repeatX, repeatY); textures.add(texture); pageImage = new Sprite(texture); } else { - int left = Integer.parseInt(reader.readLine()); - int top = Integer.parseInt(reader.readLine()); - int width = Integer.parseInt(reader.readLine()); - int height = Integer.parseInt(reader.readLine()); - int offsetX = Integer.parseInt(reader.readLine()); - int offsetY = Integer.parseInt(reader.readLine()); - int originalWidth = Integer.parseInt(reader.readLine()); - int originalHeight = Integer.parseInt(reader.readLine()); - PackedSprite image = new PackedSprite(pageImage, left, top, width, height); - image.setPosition(offsetX, offsetY); + boolean rotate = Boolean.valueOf(readValue(reader)); + + readTuple(reader); + int left = Integer.parseInt(tuple[0]); + int top = Integer.parseInt(tuple[1]); + + readTuple(reader); + int width = Integer.parseInt(tuple[0]); + int height = Integer.parseInt(tuple[1]); + + PackedSprite image; + if (rotate) { + image = new PackedSprite(pageImage, left, top, height, width); + image.rotate90(true); + } else + image = new PackedSprite(pageImage, left, top, width, height); image.name = line; - image.offsetX = offsetX; - image.offsetY = offsetY; - image.originalWidth = originalWidth; - image.originalHeight = originalHeight; - image.index = Integer.parseInt(reader.readLine()); + + readTuple(reader); + image.originalWidth = Integer.parseInt(tuple[0]); + image.originalHeight = Integer.parseInt(tuple[1]); + + readTuple(reader); + image.offsetX = Integer.parseInt(tuple[0]); + image.offsetY = Integer.parseInt(tuple[1]); + image.setPosition(image.offsetX, image.offsetY); + + image.index = Integer.parseInt(readValue(reader)); if (image.index == -1) image.index = Integer.MAX_VALUE; + sortedSprites.add(image); } } @@ -135,6 +152,22 @@ public class SpriteSheet { } }; + static private String readValue (BufferedReader reader) throws IOException { + String line = reader.readLine(); + int colon = line.indexOf(':'); + if (colon == -1) throw new GdxRuntimeException("Invalid line: " + line); + return line.substring(colon + 1).trim(); + } + + static private void readTuple (BufferedReader reader) throws IOException { + String line = reader.readLine(); + int colon = line.indexOf(':'); + int comma = line.indexOf(','); + if (colon == -1 || comma == -1 || comma < colon + 1) throw new GdxRuntimeException("Invalid line: " + line); + tuple[0] = line.substring(colon + 1, comma).trim(); + tuple[1] = line.substring(comma + 1).trim(); + } + /** * A sprite that provides additional information about the packed image it represents. A PackedSprite's position is relative to * the bottom left of the original image, before whitespace was removed for packing. diff --git a/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEffect.java b/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEffect.java index b5071ec83..dc4bc181f 100644 --- a/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEffect.java +++ b/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEffect.java @@ -123,7 +123,7 @@ public class ParticleEffect { ParticleEmitter emitter = emitters.get(i); String imagePath = emitter.getImagePath(); if (imagePath == null) continue; - String imageName = new File(imagePath).getName(); + String imageName = new File(imagePath.replace('\\', '/')).getName(); emitter.setTexture(loadTexture(imagesDir.child(imageName))); } } diff --git a/tests/gdx-tests-lwjgl/data/data1.png b/tests/gdx-tests-lwjgl/data/data1.png deleted file mode 100644 index 4392e0a1c..000000000 Binary files a/tests/gdx-tests-lwjgl/data/data1.png and /dev/null differ diff --git a/tests/gdx-tests-lwjgl/data/pack b/tests/gdx-tests-lwjgl/data/pack index cf40babba..d9c717ac0 100644 --- a/tests/gdx-tests-lwjgl/data/pack +++ b/tests/gdx-tests-lwjgl/data/pack @@ -1,43 +1,25 @@ -data1.png -none -badlogic -0 -0 -256 -256 -0 -0 -256 -256 -0 -particle-fire -256 -0 -127 -122 -0 -0 -128 -128 -0 +pack1.png + repeat: none + filter: Linear,Linear +badlogicslice + rotate: true + xy: 0, 0 + size: 41, 300 + orig: 41, 300 + offset: 0, 0 + offset: 0 particle-star -383 -0 -63 -63 -0 -0 -64 -64 -0 + rotate: false + xy: 0, 41 + size: 63, 63 + orig: 64, 64 + offset: 0, 0 + offset: 0 badlogicsmall -383 -63 -32 -32 -0 -0 -32 -32 -0 + rotate: false + xy: 300, 0 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + offset: 0 diff --git a/tests/gdx-tests-lwjgl/data/pack1.png b/tests/gdx-tests-lwjgl/data/pack1.png new file mode 100644 index 000000000..9bc8a4b53 Binary files /dev/null and b/tests/gdx-tests-lwjgl/data/pack1.png differ diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteSheetTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteSheetTest.java index d939da9b0..ad4e911ee 100644 --- a/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteSheetTest.java +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteSheetTest.java @@ -17,7 +17,7 @@ public class SpriteSheetTest extends GdxTest { batch = new SpriteBatch(); spriteSheet = new SpriteSheet(Gdx.files.internal("data/pack"), Gdx.files.internal("data")); - badlogic = spriteSheet.get("badlogic"); + badlogic = spriteSheet.get("badlogicslice"); badlogicSmall = spriteSheet.get("badlogicsmall"); star = spriteSheet.get("particle-star");