diff --git a/src/com/blarg/gdx/graphics/atlas/AnimationSequence.java b/src/com/blarg/gdx/graphics/atlas/AnimationSequence.java new file mode 100644 index 0000000..b6317dc --- /dev/null +++ b/src/com/blarg/gdx/graphics/atlas/AnimationSequence.java @@ -0,0 +1,33 @@ +package com.blarg.gdx.graphics.atlas; + +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.utils.Disposable; + +class AnimationSequence implements Disposable { + public TextureAtlas atlas; + public int animatingIndex; + public int start; + public int stop; + public int current; + public float delay; + public float currentFrameTime; + public boolean isAnimating; + public boolean loop; + public Pixmap originalAnimatingTile; + public Pixmap[] frames; + + public int getNumFrames() { + return stop - start + 1; + } + + public boolean isAnimationFinished() { + return (isAnimating && !loop && current == stop); + } + + @Override + public void dispose() { + originalAnimatingTile.dispose(); + for (int i = 0; i < frames.length; ++i) + frames[i].dispose(); + } +} \ No newline at end of file diff --git a/src/com/blarg/gdx/graphics/atlas/TextureAtlasAnimator.java b/src/com/blarg/gdx/graphics/atlas/TextureAtlasAnimator.java new file mode 100644 index 0000000..3c951b5 --- /dev/null +++ b/src/com/blarg/gdx/graphics/atlas/TextureAtlasAnimator.java @@ -0,0 +1,149 @@ +package com.blarg.gdx.graphics.atlas; + +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.TextureData; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.utils.Disposable; +import com.badlogic.gdx.utils.ObjectMap; +import com.blarg.gdx.graphics.GraphicsHelpers; + +public class TextureAtlasAnimator implements Disposable { + ObjectMap animations; + + public TextureAtlasAnimator() { + animations = new ObjectMap(); + } + + public void addSequence(String name, TextureAtlas atlas, int tileToBeAnimated, int start, int stop, float delay, boolean loop) { + if (animations.containsKey(name)) + throw new UnsupportedOperationException("Duplicate animation sequence name."); + if (atlas.texture.getTextureData().getType() != TextureData.TextureDataType.Pixmap) + throw new UnsupportedOperationException("This only works with Textures backed by Pixmap texture data."); + + AnimationSequence sequence = new AnimationSequence(); + sequence.atlas = atlas; + sequence.animatingIndex = tileToBeAnimated; + sequence.start = start; + sequence.stop = stop; + sequence.delay = delay; + sequence.isAnimating = true; + sequence.loop = loop; + sequence.frames = new Pixmap[sequence.getNumFrames()]; + sequence.current = sequence.start; + sequence.currentFrameTime = 0.0f; + + // build up some CPU-side cache's of image data from the original TextureAtlas texture. we grab the image data + // for the tile we're placing animation frames to so we can restore the original texture when/if needed. and we + // grab each of the tiles that appear in the animation sequence so we can quickly upload it to the texture when + // we're at that frame + + // grab the TextureAtlas texture's image data + TextureData textureData = atlas.texture.getTextureData(); + if (!textureData.isPrepared()) + textureData.prepare(); + Pixmap textureImage = textureData.consumePixmap(); + + // copy the image data for the destination animation tile region + TextureRegion tileRegion = atlas.get(tileToBeAnimated); + sequence.originalAnimatingTile = new Pixmap(tileRegion.getRegionWidth(), tileRegion.getRegionHeight(), textureData.getFormat()); + sequence.originalAnimatingTile.drawPixmap( + textureImage, 0, 0, + tileRegion.getRegionX(), tileRegion.getRegionY(), tileRegion.getRegionWidth(), tileRegion.getRegionHeight() + ); + + // copy image data for each of the animation sequence tiles (from "start" to and including "stop") + for (int i = 0; i < sequence.getNumFrames(); ++i) { + TextureRegion tile = atlas.get(i + sequence.start); + sequence.frames[i] = new Pixmap(tileRegion.getRegionWidth(), tileRegion.getRegionHeight(), textureData.getFormat()); + sequence.frames[i].drawPixmap( + textureImage, 0, 0, + tile.getRegionX(), tile.getRegionY(), tile.getRegionWidth(), tile.getRegionHeight() + ); + } + + // all good! + animations.put(name, sequence); + } + + public void resetSequence(String name) { + AnimationSequence sequence = animations.get(name); + if (sequence == null) + throw new RuntimeException("No sequence with that name."); + + sequence.isAnimating = true; + sequence.current = sequence.start; + sequence.currentFrameTime = 0.0f; + + updateTextureWithCurrentTileFrame(sequence); + } + + public void stopSequence(String name, boolean restoreOriginalTile) { + AnimationSequence sequence = animations.get(name); + if (sequence == null) + throw new RuntimeException("No sequence with that name."); + + sequence.isAnimating = false; + sequence.current = sequence.stop; + sequence.currentFrameTime = 0.0f; + + if (restoreOriginalTile) + restoreTextureWithOriginalTile(sequence); + else + updateTextureWithCurrentTileFrame(sequence); + } + + public void enableSequence(String name, boolean enable) { + AnimationSequence sequence = animations.get(name); + if (sequence == null) + throw new RuntimeException("No sequence with that name."); + + sequence.isAnimating = enable; + sequence.currentFrameTime = 0.0f; + + updateTextureWithCurrentTileFrame(sequence); + } + + public void onUpdate(float delta) { + for (ObjectMap.Entry i : animations.entries()) { + AnimationSequence sequence = i.value; + if (!sequence.isAnimationFinished() && sequence.isAnimating) { + sequence.currentFrameTime += delta; + if (sequence.currentFrameTime >= sequence.delay) { + // move to the next frame + sequence.currentFrameTime = 0.0f; + ++sequence.current; + if (sequence.current > sequence.stop) + sequence.current = sequence.start; + + updateTextureWithCurrentTileFrame(sequence); + } + } + } + } + + public void onResume() { + for (ObjectMap.Entry i : animations.entries()) + updateTextureWithCurrentTileFrame(i.value); + } + + private void updateTextureWithCurrentTileFrame(AnimationSequence sequence) { + int frameIndex = sequence.current - sequence.start; + Pixmap frameImage = sequence.frames[frameIndex]; + TextureRegion tile = sequence.atlas.get(sequence.animatingIndex); + GraphicsHelpers.drawToTexture(sequence.atlas.texture, frameImage, tile.getRegionX(), tile.getRegionY()); + } + + private void restoreTextureWithOriginalTile(AnimationSequence sequence) { + TextureRegion tile = sequence.atlas.get(sequence.animatingIndex); + GraphicsHelpers.drawToTexture(sequence.atlas.texture, sequence.originalAnimatingTile, tile.getRegionX(), tile.getRegionY()); + } + + @Override + public void dispose() { + for (ObjectMap.Entry i : animations.entries()) { + restoreTextureWithOriginalTile(i.value); + i.value.dispose(); + } + animations.clear(); + } +}