Package tripleplay.flump

Source Code of tripleplay.flump.Movie$LayerAnimator

//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2014, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.flump;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import react.Signal;

import pythagoras.f.FloatMath;

import playn.core.GroupLayer;
import playn.core.Layer;
import playn.core.util.Clock;
import static playn.core.PlayN.*;

public class Movie
    implements Instance
{
    public static class Symbol
        implements tripleplay.flump.Symbol
    {
        /** The number of frames in this movie. */
        public final int frames;

        /** The layers in this movie. */
        public final List<LayerData> layers;

        /** The duration of this movie, in milliseconds. */
        public final float duration;

        protected Symbol (float frameRate, String name, List<LayerData> layers) {
            _name = name;
            this.layers = Collections.unmodifiableList(layers);

            int frames = 0;
            for (LayerData layer : layers) {
                frames = Math.max(layer.frames(), frames);
            }
            this.frames = frames;

            _framesPerMs = frameRate/1000;
            this.duration = frames/_framesPerMs;
        }

        @Override public String name () {
            return _name;
        }

        @Override public Movie createInstance () {
            return new Movie(this);
        }

        protected String _name;
        protected float _framesPerMs;
    }

    public final Signal<String> labelPassed = Signal.create();

    protected Movie (Symbol symbol) {
        _symbol = symbol;
        _animators = new LayerAnimator[symbol.layers.size()];
        for (int ii = 0, ll = _animators.length; ii < ll; ++ii) {
            LayerAnimator animator = new LayerAnimator(symbol.layers.get(ii));
            _animators[ii] = animator;
            _root.add(animator.content);
        }
        setFrame(1, 0);
    }

    @Override public GroupLayer layer () {
        return _root;
    }

    @Override public void paint (Clock clock) {
        paint(clock.dt());
    }

    @Override public void paint (float dt) {
        dt *= _speed;

        _position += dt;
        if (_position > _symbol.duration) {
            _position = _position % _symbol.duration;
        } else if (_position < 0) {
            // Normally we shouldn't be negative, but if we're setPositioning, submovies may
            // have completely different durations, so stepping backwards wraps around the
            // other way
            _position = _symbol.duration + (_position % _symbol.duration);
        }

        float nextFrame = _position*_symbol._framesPerMs;
        setFrame(nextFrame, dt);
    }

    @Override public void destroy () {
        _root.destroy();
    }

    /** The playback position, in milliseconds. */
    public float position () {
        return _position;
    }

    /** Changes the playback position. */
    public void setPosition (float position) {
        if (position < 0) position = 0;
        paint(position - _position);
    }

    public Symbol symbol () {
        return _symbol;
    }

    /** The playback speed multiplier, defaults to 1. Larger values will play faster. */
    public float speed () {
        return _speed;
    }

    /** Changes the playback speed multiplier. */
    public void setSpeed (float speed) {
        _speed = speed;
    }

    /** Retrieves a named layer. It should generally not be modified. */
    public Layer getNamedLayer (String name) {
        LayerAnimator animator = getNamedAnimator(name);
        return animator != null ? animator.content : null;
    }

    /** Returns all of the {@code Instance}s on a named layer. */
    public List<Instance> getInstances (String name) {
        LayerAnimator animator = getNamedAnimator(name);
        if (animator == null) return Collections.<Instance>emptyList();
        if (animator._instances == null) return Collections.singletonList(animator._current);
        return Collections.unmodifiableList(Arrays.asList(animator._instances));
    }

    /**
     * Replaces the instance on a named layer. The layer should already be empty or contain a single
     * Movie.
     */
    public void setNamedLayer (String name, Instance instance) {
        LayerAnimator animator = getNamedAnimator(name);
        if (animator != null) {
            assert animator.content instanceof GroupLayer :
                "Layer not a container[name=" + name + "]";
            animator.setCurrent(instance);
        }
    }

    /** Retrieves all named layers. They generally should not be modified. */
    public Map<String,Layer> namedLayers () {
        Map<String,Layer> namedLayers = new HashMap<String,Layer>();
        for (LayerAnimator animator : _animators) {
            namedLayers.put(animator.data.name, animator.content);
        }
        return Collections.unmodifiableMap(namedLayers);
    }

    protected LayerAnimator getNamedAnimator (String name) {
        for (LayerAnimator animator : _animators) {
            if (animator.data.name.equals(name)) {
                return animator;
            }
        }
        return null; // Not found
    }

    protected void setFrame (float frame, float dt) {
        if (frame == _frame) {
            return;
        }

        if (frame < _frame) {
            // Wrap back to the beginning
            for (int ii = 0, ll = _animators.length; ii < ll; ++ii) {
                LayerAnimator animator = _animators[ii];
                animator.changedKeyframe = true;
                animator.keyframeIdx = 0;
            }
        }
        for (int ii = 0, ll = _animators.length; ii < ll; ++ii) {
            LayerAnimator animator = _animators[ii];
            animator.setFrame(frame, dt);
        }
        _frame = frame;
    }

    // Controls a single Flash layer
    protected class LayerAnimator {
        public final LayerData data;
        public final Layer content;

        public int keyframeIdx = 0;
        public boolean changedKeyframe = false;

        public LayerAnimator (LayerData data) {
            this.data = data;
            if (data._multipleSymbols) {
                _instances = new Instance[data.keyframes.size()];
                for (int ii = 0, ll = _instances.length; ii < ll; ++ii) {
                    tripleplay.flump.Symbol sym = data.keyframes.get(ii).symbol();
                    if (sym == null) {
                        throw new IllegalArgumentException("Keyframe missing symbol layer=" +
                            data.name + " frame=" + ii);
                    }
                    _instances[ii] = sym.createInstance();
                }
                content = graphics().createGroupLayer();
                setCurrent(_instances[0]);

            } else if (data._lastSymbol != null) {
                _current = data._lastSymbol.createInstance();
                content = _current.layer();

            } else {
                content = graphics().createGroupLayer();
            }
        }

        public void setFrame (float frame, float dt) {
            List<KeyframeData> keyframes = data.keyframes;
            int finalFrame = keyframes.size()-1;

            int startFrame = keyframeIdx + 1;

            while (keyframeIdx < finalFrame && keyframes.get(keyframeIdx+1).index <= frame) {
                ++keyframeIdx;
                changedKeyframe = true;
            }

            if (changedKeyframe && _instances != null) {
                // Switch to the next instance if this is a multi-symbol layer
                setCurrent(_instances[keyframeIdx]);
                changedKeyframe = false;
            }

            KeyframeData kf = keyframes.get(keyframeIdx);
            tripleplay.flump.Symbol currSymbol = kf.symbol();
            boolean visible = currSymbol != null && kf.visible;
            content.setVisible(visible);

            // NOTE: This has some exciting limitations regarding setPosition. If you jump to a
            //  position, note that like flash itself, we're not smart enough to start anywhere but
            //  at the very beginning
            if (currSymbol != _prevFrameSymbol && _current instanceof Movie) {
                ((Movie)_current).setPosition(0);
            }
            _prevFrameSymbol = currSymbol;

            if (!visible) {
                emitLabelSignals(startFrame, keyframeIdx);
                return; // Don't bother animating invisible layers
            }

            float locX = kf.loc.x();
            float locY = kf.loc.y();
            float scaleX = kf.scale.x();
            float scaleY = kf.scale.y();
            float skewX = kf.skew.x();
            float skewY = kf.skew.y();
            float alpha = kf.alpha;

            if (kf.tweened && keyframeIdx < finalFrame) {
                // Interpolate with the next keyframe, if there's something on the next keyframe
                KeyframeData nextKf = keyframes.get(keyframeIdx+1);
                if (nextKf.symbol() != null) {
                    float interp = (frame-kf.index) / kf.duration;
                    float ease = kf.ease;
                    if (ease != 0) {
                        float t;
                        if (ease < 0) {
                            // Ease in
                            float inv = 1 - interp;
                            t = 1 - inv*inv;
                            ease = -ease;
                        } else {
                            // Ease out
                            t = interp*interp;
                        }
                        interp = ease*t + (1-ease)*interp;
                    }
                    locX += (nextKf.loc.x()-locX) * interp;
                    locY += (nextKf.loc.y()-locY) * interp;
                    scaleX += (nextKf.scale.x()-scaleX) * interp;
                    scaleY += (nextKf.scale.y()-scaleY) * interp;
                    skewX += (nextKf.skew.x()-skewX) * interp;
                    skewY += (nextKf.skew.y()-skewY) * interp;
                    alpha += (nextKf.alpha-alpha) * interp;
                }
            }

            float sinX = FloatMath.sin(skewX), cosX = FloatMath.cos(skewX);
            float sinY = FloatMath.sin(skewY), cosY = FloatMath.cos(skewY);

            // Create a transformation matrix that translates to locX/Y, skews, then scales
            float m00 = cosY * scaleX;
            float m01 = sinY * scaleX;
            float m10 = -sinX * scaleY;
            float m11 = cosX * scaleY;
            content.transform().setTransform(m00, m01, m10, m11, locX, locY);
            content.setOrigin(kf.pivot.x(), kf.pivot.y());

            content.setAlpha(alpha);

            if (_current != null) {
                _current.paint(dt);
            }

            emitLabelSignals(startFrame, keyframeIdx);
        }

        protected void emitLabelSignals (int startIdx, int endIdx) {
            for (int ii = startIdx; ii <= endIdx; ii++) {
                String label = data.keyframes.get(ii).label;
                if (label != null) {
                    labelPassed.emit(label);
                }
            }
        }

        protected void setCurrent (Instance current) {
            if (_current != current) {
                _current = current;
                GroupLayer group = (GroupLayer)content;
                group.removeAll();
                group.add(current.layer());
            }
        }

        protected Instance _current; // The instance currently visible
        protected Instance[] _instances; // Null if only 0-1 instance on this layer

        /** We track this to know where we've added a new symbol and thus should reset position. */
        protected tripleplay.flump.Symbol _prevFrameSymbol = null;
    }

    protected Symbol _symbol;
    protected GroupLayer _root = graphics().createGroupLayer();
    protected LayerAnimator[] _animators;

    protected float _frame = 0;
    protected float _position = 0;
    protected float _speed = 1;
}
TOP

Related Classes of tripleplay.flump.Movie$LayerAnimator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.