com.bladecoder.engine.model.AtlasRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.bladecoder.engine.model.AtlasRenderer.java

Source

/*******************************************************************************
 * Copyright 2014 Rafael Garcia Moreno.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.bladecoder.engine.model;

import java.util.ArrayList;
import java.util.HashMap;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.JsonValue;
import com.bladecoder.engine.actions.ActionCallback;
import com.bladecoder.engine.actions.ActionCallbackQueue;
import com.bladecoder.engine.anim.AnimationDesc;
import com.bladecoder.engine.anim.AtlasAnimationDesc;
import com.bladecoder.engine.anim.FATween;
import com.bladecoder.engine.anim.Tween;
import com.bladecoder.engine.assets.EngineAssetManager;
import com.bladecoder.engine.util.EngineLogger;
import com.bladecoder.engine.util.RectangleRenderer;
import com.bladecoder.engine.util.SerializationHelper;
import com.bladecoder.engine.util.SerializationHelper.Mode;

public class AtlasRenderer implements ActorRenderer {
    private final static float DEFAULT_DIM = 200;

    private HashMap<String, AnimationDesc> fanims = new HashMap<String, AnimationDesc>();

    /** Starts this anim the first time that the scene is loaded */
    private String initAnimation;

    private AtlasAnimationDesc currentAnimation;

    private AtlasRegion tex;
    private boolean flipX;
    private FATween faTween;

    private int currentFrameIndex;

    private final HashMap<String, AtlasCacheEntry> sourceCache = new HashMap<String, AtlasCacheEntry>();
    private Polygon bbox;

    class AtlasCacheEntry {
        int refCounter;
    }

    public AtlasRenderer() {

    }

    @Override
    public void setInitAnimation(String fa) {
        initAnimation = fa;
    }

    @Override
    public String getInitAnimation() {
        return initAnimation;
    }

    @Override
    public String[] getInternalAnimations(AnimationDesc anim) {
        retrieveSource(anim.source);

        TextureAtlas atlas = EngineAssetManager.getInstance().getTextureAtlas(anim.source);

        Array<AtlasRegion> animations = atlas.getRegions();
        ArrayList<String> l = new ArrayList<String>();

        for (int i = 0; i < animations.size; i++) {
            AtlasRegion a = animations.get(i);
            if (!l.contains(a.name))
                l.add(a.name);
        }

        return l.toArray(new String[l.size()]);
    }

    @Override
    public void update(float delta) {
        if (faTween != null) {
            faTween.update(this, delta);
            if (faTween.isComplete()) {
                faTween = null;
                computeBbox();
            }
        }
    }

    public void setFrame(int i) {
        currentFrameIndex = i;
        tex = currentAnimation.regions.get(i);
    }

    @Override
    public void draw(SpriteBatch batch, float x, float y, float scale) {

        if (tex == null) {
            x = x - getWidth() / 2 * scale;

            RectangleRenderer.draw(batch, x, y, getWidth() * scale, getHeight() * scale, Color.RED);
            return;
        }

        x = x + tex.offsetX - tex.originalWidth / 2;
        y = y + tex.offsetY - tex.originalHeight * (1 - scale) / 2;

        if (!flipX) {
            batch.draw(tex, x, y, tex.originalWidth / 2 - tex.offsetX, tex.originalHeight / 2 - tex.offsetY,
                    tex.packedWidth, tex.packedHeight, scale, scale, 0);
        } else {
            batch.draw(tex, x, y, tex.originalWidth / 2 - tex.offsetX, tex.originalHeight / 2 - tex.offsetY,
                    tex.packedWidth, tex.packedHeight, -scale, scale, 0);
        }
    }

    @Override
    public float getWidth() {
        if (tex == null)
            return DEFAULT_DIM;

        // return tex.getRegionWidth();
        return tex.originalWidth;
    }

    @Override
    public float getHeight() {
        if (tex == null)
            return DEFAULT_DIM;

        // return tex.getRegionHeight();
        return tex.originalHeight;
    }

    @Override
    public AnimationDesc getCurrentAnimation() {
        return currentAnimation;
    }

    @Override
    public HashMap<String, AnimationDesc> getAnimations() {
        return (HashMap<String, AnimationDesc>) fanims;
    }

    @Override
    public void startAnimation(String id, Tween.Type repeatType, int count, ActionCallback cb) {

        if (id == null)
            id = initAnimation;

        AtlasAnimationDesc fa = getAnimation(id);

        if (fa == null) {
            EngineLogger.error("AnimationDesc not found: " + id);

            return;
        }

        if (currentAnimation != null && currentAnimation.disposeWhenPlayed) {
            disposeSource(currentAnimation.source);
            currentAnimation.regions = null;
        }

        currentAnimation = fa;

        // If the source is not loaded. Load it.
        if (currentAnimation != null && currentAnimation.regions == null) {

            retrieveFA(fa);

            if (currentAnimation.regions == null || currentAnimation.regions.size == 0) {
                EngineLogger.error(currentAnimation.id + " has no regions in ATLAS " + currentAnimation.source);
                fanims.remove(currentAnimation.id);
            }
        }

        if (currentAnimation == null) {

            tex = null;

            computeBbox();
            return;
        }

        if (currentAnimation.regions.size == 1 || currentAnimation.duration == 0.0) {

            setFrame(0);
            computeBbox();

            if (cb != null) {
                ActionCallbackQueue.add(cb);
            }

            return;
        }

        if (repeatType == Tween.Type.SPRITE_DEFINED) {
            repeatType = currentAnimation.animationType;
            count = currentAnimation.count;
        }

        faTween = new FATween();
        faTween.start(this, repeatType, count, currentAnimation.duration, cb);

        if (repeatType == Tween.Type.REVERSE)
            setFrame(getNumFrames() - 1);
        else
            setFrame(0);

        computeBbox();
    }

    public int getNumFrames() {
        return currentAnimation.regions.size;
    }

    @Override
    public String getCurrentAnimationId() {
        if (currentAnimation == null)
            return null;

        String id = currentAnimation.id;

        if (flipX) {
            id = AnimationDesc.getFlipId(id);
        }

        return id;

    }

    private void computeBbox() {
        if (bbox == null)
            return;

        if (bbox.getVertices() == null || bbox.getVertices().length != 8) {
            bbox.setVertices(new float[8]);
        }

        float[] verts = bbox.getVertices();

        verts[0] = -getWidth() / 2;
        verts[1] = 0f;
        verts[2] = -getWidth() / 2;
        verts[3] = getHeight();
        verts[4] = getWidth() / 2;
        verts[5] = getHeight();
        verts[6] = getWidth() / 2;
        verts[7] = 0f;
        bbox.dirty();
    }

    @Override
    public void addAnimation(AnimationDesc fa) {
        if (initAnimation == null)
            initAnimation = fa.id;

        fanims.put(fa.id, (AtlasAnimationDesc) fa);
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer(super.toString());

        sb.append("\n  Anims:");

        for (String v : fanims.keySet()) {
            sb.append(" ").append(v);
        }

        if (currentAnimation != null)
            sb.append("\n  Current Anim: ").append(currentAnimation.id);

        sb.append("\n");

        return sb.toString();
    }

    private AtlasAnimationDesc getAnimation(String id) {
        AnimationDesc fa = fanims.get(id);
        flipX = false;

        if (fa == null) {
            // Search for flipped
            String flipId = AnimationDesc.getFlipId(id);

            fa = fanims.get(flipId);

            if (fa != null)
                flipX = true;
            else {
                // search for .left if .frontleft not found and viceversa
                StringBuilder sb = new StringBuilder();

                if (id.endsWith(AnimationDesc.FRONTLEFT)) {
                    sb.append(id.substring(0, id.lastIndexOf('.') + 1));
                    sb.append(AnimationDesc.LEFT);
                } else if (id.endsWith(AnimationDesc.FRONTRIGHT)) {
                    sb.append(id.substring(0, id.lastIndexOf('.') + 1));
                    sb.append(AnimationDesc.RIGHT);
                } else if (id.endsWith(AnimationDesc.BACKLEFT) || id.endsWith(AnimationDesc.BACKRIGHT)) {
                    sb.append(id.substring(0, id.lastIndexOf('.') + 1));
                    sb.append(AnimationDesc.BACK);
                } else if (id.endsWith(AnimationDesc.LEFT)) {
                    sb.append(id.substring(0, id.lastIndexOf('.') + 1));
                    sb.append(AnimationDesc.FRONTLEFT);
                } else if (id.endsWith(AnimationDesc.RIGHT)) {
                    sb.append(id.substring(0, id.lastIndexOf('.') + 1));
                    sb.append(AnimationDesc.FRONTRIGHT);
                }

                String s = sb.toString();

                fa = fanims.get(s);

                if (fa == null) {
                    // Search for flipped
                    flipId = AnimationDesc.getFlipId(s);

                    fa = fanims.get(flipId);

                    if (fa != null)
                        flipX = true;
                }
            }
        }

        return (AtlasAnimationDesc) fa;
    }

    @Override
    public void startAnimation(String id, Tween.Type repeatType, int count, ActionCallback cb, String direction) {
        StringBuilder sb = new StringBuilder(id);

        // if dir==null gets the current animation direction
        if (direction == null) {
            int idx = getCurrentAnimationId().indexOf('.');

            if (idx != -1) {
                String dir = getCurrentAnimationId().substring(idx);
                sb.append(dir);
            }
        } else {
            sb.append('.');
            sb.append(direction);
        }

        String anim = sb.toString();

        if (getAnimation(anim) == null) {
            anim = id;
        }

        startAnimation(anim, repeatType, count, null);
    }

    @Override
    public void startAnimation(String id, Tween.Type repeatType, int count, ActionCallback cb, Vector2 p0,
            Vector2 pf) {
        startAnimation(id, repeatType, count, cb,
                AnimationDesc.getDirectionString(p0, pf, AnimationDesc.getDirs(id, fanims)));
    }

    @Override
    public void updateBboxFromRenderer(Polygon bbox) {
        this.bbox = bbox;

        computeBbox();
    }

    private void loadSource(String source) {
        AtlasCacheEntry entry = sourceCache.get(source);

        if (entry == null) {
            entry = new AtlasCacheEntry();
            sourceCache.put(source, entry);
        }

        if (entry.refCounter == 0)
            EngineAssetManager.getInstance().loadAtlas(source);

        entry.refCounter++;
    }

    private void retrieveFA(AtlasAnimationDesc fa) {
        retrieveSource(fa.source);
        fa.regions = EngineAssetManager.getInstance().getRegions(fa.source, fa.id);
    }

    private void retrieveSource(String source) {
        AtlasCacheEntry entry = sourceCache.get(source);

        if (entry == null || entry.refCounter < 1) {
            loadSource(source);
            EngineAssetManager.getInstance().finishLoading();
        }
    }

    private void disposeSource(String source) {
        AtlasCacheEntry entry = sourceCache.get(source);

        if (entry.refCounter == 1) {
            EngineAssetManager.getInstance().disposeAtlas(source);
        }

        entry.refCounter--;
    }

    @Override
    public void loadAssets() {
        for (AnimationDesc fa : fanims.values()) {
            if (fa.preload)
                loadSource(fa.source);
        }

        if (currentAnimation != null && !currentAnimation.preload) {
            loadSource(currentAnimation.source);
        } else if (currentAnimation == null && initAnimation != null) {
            String a = initAnimation;

            if (flipX) {
                a = AnimationDesc.getFlipId(a);
            }

            AtlasAnimationDesc fa = (AtlasAnimationDesc) fanims.get(a);

            if (fa != null && !fa.preload)
                loadSource(fa.source);
        }
    }

    @Override
    public void retrieveAssets() {
        for (AnimationDesc fa : fanims.values()) {
            if (fa.preload)
                retrieveFA((AtlasAnimationDesc) fa);
        }

        if (currentAnimation != null && !currentAnimation.preload) {
            retrieveFA(currentAnimation);
        } else if (currentAnimation == null && initAnimation != null) {
            String a = initAnimation;

            if (flipX) {
                a = AnimationDesc.getFlipId(a);
            }

            AtlasAnimationDesc fa = (AtlasAnimationDesc) fanims.get(a);

            if (fa != null && !fa.preload)
                retrieveFA(fa);
        }

        if (currentAnimation != null) {
            setFrame(currentFrameIndex);
        } else if (initAnimation != null) {
            startAnimation(initAnimation, Tween.Type.SPRITE_DEFINED, 1, null);
        }

        computeBbox();
    }

    @Override
    public void dispose() {
        for (String key : sourceCache.keySet()) {
            EngineAssetManager.getInstance().disposeAtlas(key);
        }

        sourceCache.clear();
    }

    @Override
    public void write(Json json) {
        if (SerializationHelper.getInstance().getMode() == Mode.MODEL) {

            json.writeValue("fanims", fanims, HashMap.class, AtlasAnimationDesc.class);
            json.writeValue("initAnimation", initAnimation);

        } else {

            String currentAnimationId = null;

            if (currentAnimation != null)
                currentAnimationId = currentAnimation.id;

            json.writeValue("currentAnimation", currentAnimationId);

            json.writeValue("flipX", flipX);
            json.writeValue("currentFrameIndex", currentFrameIndex);

            json.writeValue("faTween", faTween, faTween == null ? null : FATween.class);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void read(Json json, JsonValue jsonData) {

        if (SerializationHelper.getInstance().getMode() == Mode.MODEL) {

            fanims = json.readValue("fanims", HashMap.class, AtlasAnimationDesc.class, jsonData);
            initAnimation = json.readValue("initAnimation", String.class, jsonData);

        } else {

            String currentAnimationId = json.readValue("currentAnimation", String.class, jsonData);

            if (currentAnimationId != null)
                currentAnimation = (AtlasAnimationDesc) fanims.get(currentAnimationId);

            flipX = json.readValue("flipX", Boolean.class, jsonData);
            currentFrameIndex = json.readValue("currentFrameIndex", Integer.class, jsonData);
            faTween = json.readValue("faTween", FATween.class, jsonData);
        }
    }
}