a8de5bb789
render sprites and animations anywhere in the frame for those times when you just need to draw outside the lines. The actual MediaOverlay isn't done yet, but all the refactoring to support it is done, so the rest should be easy. In theory this shouldn't break anything (like the Yohoho build), but we're about to find out. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@112 ed5b42cb-e716-0410-a449-f6a68f950b19
371 lines
11 KiB
Java
371 lines
11 KiB
Java
//
|
|
// $Id$
|
|
//
|
|
// Nenya library - tools for developing networked games
|
|
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
|
// http://www.threerings.net/code/nenya/
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License as published
|
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
package com.threerings.media;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.awt.Color;
|
|
import java.awt.Dimension;
|
|
import java.awt.Font;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.Rectangle;
|
|
|
|
import com.samskivert.swing.Label;
|
|
import com.samskivert.util.IntListUtil;
|
|
import com.samskivert.util.StringUtil;
|
|
|
|
import com.threerings.media.sprite.Sprite;
|
|
import com.threerings.media.sprite.SpriteManager;
|
|
import com.threerings.media.animation.Animation;
|
|
import com.threerings.media.animation.AnimationManager;
|
|
|
|
/**
|
|
* Coordinates interaction between a sprite and animation manager and the media host that hosts and
|
|
* renders them. This class is a little fiddly because {@link MediaPanel} has been around a long
|
|
* time and is thoroughly out in the wild and now that we need to abstract out a bunch of its
|
|
* functionality, we're constrained by all the extant usages and derivations.
|
|
*/
|
|
public class MetaMediaManager
|
|
implements MediaConstants
|
|
{
|
|
public MetaMediaManager (FrameManager framemgr, MediaHost host)
|
|
{
|
|
// keep these for later
|
|
_framemgr = framemgr;
|
|
_host = host;
|
|
|
|
// create our region manager
|
|
_remgr = new RegionManager();
|
|
|
|
// create our animation and sprite managers
|
|
_animmgr = new AnimationManager(host);
|
|
_spritemgr = new SpriteManager(host);
|
|
}
|
|
|
|
/**
|
|
* Returns the frame manager with which we are coordinating.
|
|
*/
|
|
public FrameManager getFrameManager ()
|
|
{
|
|
return _framemgr;
|
|
}
|
|
|
|
/**
|
|
* Returns a reference to the animation manager used by this media panel.
|
|
*/
|
|
public AnimationManager getAnimationManager ()
|
|
{
|
|
return _animmgr;
|
|
}
|
|
|
|
/**
|
|
* Returns a reference to the sprite manager used by this media panel.
|
|
*/
|
|
public SpriteManager getSpriteManager ()
|
|
{
|
|
return _spritemgr;
|
|
}
|
|
|
|
/**
|
|
* Returns true if we are paused, false if we are running normally.
|
|
*/
|
|
public boolean isPaused ()
|
|
{
|
|
return _paused;
|
|
}
|
|
|
|
/**
|
|
* Pauses the sprites and animations that are currently active on this media panel. Also stops
|
|
* listening to the frame tick while paused.
|
|
*/
|
|
public void setPaused (boolean paused)
|
|
{
|
|
// sanity check
|
|
if ((paused && (_pauseTime != 0)) || (!paused && (_pauseTime == 0))) {
|
|
Log.warning("Requested to pause when paused or vice-versa [paused=" + paused + "].");
|
|
return;
|
|
}
|
|
|
|
if (_paused = paused) {
|
|
// make a note of our pause time
|
|
_pauseTime = _framemgr.getTimeStamp();
|
|
|
|
} else {
|
|
// let the animation and sprite managers know that we just warped into the future
|
|
long delta = _framemgr.getTimeStamp() - _pauseTime;
|
|
_animmgr.fastForward(delta);
|
|
_spritemgr.fastForward(delta);
|
|
|
|
// clear out our pause time
|
|
_pauseTime = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a timestamp from the {@link MediaTimer} used to track time intervals for this media
|
|
* panel. <em>Note:</em> this should only be called from the AWT thread.
|
|
*/
|
|
public long getTimeStamp ()
|
|
{
|
|
return _framemgr.getTimeStamp();
|
|
}
|
|
|
|
/**
|
|
* Adds a sprite to this panel.
|
|
*/
|
|
public void addSprite (Sprite sprite)
|
|
{
|
|
_spritemgr.addSprite(sprite);
|
|
}
|
|
|
|
/**
|
|
* @return true if the sprite is already added to this panel.
|
|
*/
|
|
public boolean isManaged (Sprite sprite)
|
|
{
|
|
return _spritemgr.isManaged(sprite);
|
|
}
|
|
|
|
/**
|
|
* Removes a sprite from this panel.
|
|
*/
|
|
public void removeSprite (Sprite sprite)
|
|
{
|
|
_spritemgr.removeSprite(sprite);
|
|
}
|
|
|
|
/**
|
|
* Removes all sprites from this panel.
|
|
*/
|
|
public void clearSprites ()
|
|
{
|
|
_spritemgr.clearMedia();
|
|
}
|
|
|
|
/**
|
|
* Adds an animation to this panel. Animations are automatically removed when they finish.
|
|
*/
|
|
public void addAnimation (Animation anim)
|
|
{
|
|
_animmgr.registerAnimation(anim);
|
|
}
|
|
|
|
/**
|
|
* @return true if the animation is already added to this panel.
|
|
*/
|
|
public boolean isManaged (Animation anim)
|
|
{
|
|
return _animmgr.isManaged(anim);
|
|
}
|
|
|
|
/**
|
|
* Aborts a currently running animation and removes it from this panel. Animations are normally
|
|
* automatically removed when they finish.
|
|
*/
|
|
public void abortAnimation (Animation anim)
|
|
{
|
|
_animmgr.unregisterAnimation(anim);
|
|
}
|
|
|
|
/**
|
|
* Removes all animations from this panel.
|
|
*/
|
|
public void clearAnimations ()
|
|
{
|
|
_animmgr.clearMedia();
|
|
}
|
|
|
|
/**
|
|
* Returns the region manager used to coordiante our dirty regions.
|
|
*/
|
|
public RegionManager getRegionManager ()
|
|
{
|
|
return _remgr;
|
|
}
|
|
|
|
/**
|
|
* Called by the host to coordinate dirty region tracking. This should be supplied with the
|
|
* number of dirty regions being painted on this tick and called just before painting them.
|
|
*/
|
|
public void noteDirty (int regions)
|
|
{
|
|
_dirty[_tick] = regions;
|
|
}
|
|
|
|
/**
|
|
* Our media front end should implement {@link FrameParticipant} and call this methed in their
|
|
* {@link FrameParticipant#tick} method. They must also first check {@link #isPaused} and not
|
|
* call this method if we are paused. As they will probably want to have willTick() and
|
|
* didTick() calldown methods, we cannot handle pausedness for them.
|
|
*/
|
|
public void tick (long tickStamp)
|
|
{
|
|
// now tick our animations and sprites
|
|
_animmgr.tick(tickStamp);
|
|
_spritemgr.tick(tickStamp);
|
|
|
|
// if performance debugging is enabled,
|
|
if (FrameManager._perfDebug.getValue()) {
|
|
if (_perfLabel == null) {
|
|
_perfLabel = new Label("", Label.OUTLINE, Color.white, Color.black,
|
|
new Font("Arial", Font.PLAIN, 10));
|
|
}
|
|
if (_perfRect == null) {
|
|
_perfRect = new Rectangle(5, 5, 0, 0);
|
|
}
|
|
|
|
StringBuilder perf = new StringBuilder();
|
|
perf.append("[FPS: ");
|
|
perf.append(_framemgr.getPerfTicks()).append("/");
|
|
perf.append(_framemgr.getPerfTries());
|
|
perf.append(" PM:");
|
|
StringUtil.toString(perf, _framemgr.getPerfMetrics());
|
|
// perf.append(" MP:").append(_dirtyPerTick);
|
|
perf.append("]");
|
|
|
|
String perfStatus = perf.toString();
|
|
if (!_perfStatus.equals(perfStatus)) {
|
|
_perfStatus = perfStatus;
|
|
_perfLabel.setText(perfStatus);
|
|
|
|
Graphics2D gfx = _host.createGraphics();
|
|
if (gfx != null) {
|
|
_perfLabel.layout(gfx);
|
|
gfx.dispose();
|
|
|
|
// make sure the region we dirty contains the old and the new text (which we
|
|
// ensure by never letting the rect shrink)
|
|
Dimension psize = _perfLabel.getSize();
|
|
_perfRect.width = Math.max(_perfRect.width, psize.width);
|
|
_perfRect.height = Math.max(_perfRect.height, psize.height);
|
|
_remgr.addDirtyRegion(new Rectangle(_perfRect));
|
|
}
|
|
}
|
|
} else {
|
|
_perfRect = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Our media front end should implement {@link FrameParticipant} and call this methed in their
|
|
* {@link FrameParticipant#needsPaint} method.
|
|
*/
|
|
public boolean needsPaint ()
|
|
{
|
|
// compute our average dirty regions per tick
|
|
if (_tick++ == 99) {
|
|
_tick = 0;
|
|
int dirty = IntListUtil.sum(_dirty);
|
|
Arrays.fill(_dirty, 0);
|
|
_dirtyPerTick = (float)dirty/100;
|
|
}
|
|
|
|
// regardless of whether or not we paint, we need to let our abstract media managers know
|
|
// that we've gotten to the point of painting because they need to remain prepared to deal
|
|
// with media changes that happen any time between the tick() and the paint() and thus need
|
|
// to know when paint() happens
|
|
_animmgr.willPaint();
|
|
_spritemgr.willPaint();
|
|
|
|
// if we have dirty regions, we need painting
|
|
return _remgr.haveDirtyRegions();
|
|
}
|
|
|
|
/**
|
|
* Renders the sprites and animations that intersect the supplied dirty region in the specified
|
|
* layer.
|
|
*/
|
|
public void paintMedia (Graphics2D gfx, int layer, Rectangle dirty)
|
|
{
|
|
if (layer == FRONT) {
|
|
_spritemgr.paint(gfx, layer, dirty);
|
|
_animmgr.paint(gfx, layer, dirty);
|
|
} else {
|
|
_animmgr.paint(gfx, layer, dirty);
|
|
_spritemgr.paint(gfx, layer, dirty);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders our performance debugging information if enabled.
|
|
*/
|
|
public void paintPerf (Graphics2D gfx)
|
|
{
|
|
if (_perfRect != null && FrameManager._perfDebug.getValue()) {
|
|
gfx.setClip(null);
|
|
_perfLabel.render(gfx, _perfRect.x, _perfRect.y);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If our host supports scrolling around in a virtual view, it should call this method when the
|
|
* view origin changes.
|
|
*/
|
|
public void viewLocationDidChange (int dx, int dy)
|
|
{
|
|
if (_perfRect != null) {
|
|
Rectangle sdirty = new Rectangle(_perfRect);
|
|
sdirty.translate(-dx, -dy);
|
|
_remgr.addDirtyRegion(sdirty);
|
|
}
|
|
|
|
// let our sprites and animations know what's up
|
|
_animmgr.viewLocationDidChange(dx, dy);
|
|
_spritemgr.viewLocationDidChange(dx, dy);
|
|
}
|
|
|
|
/** The frame manager with whom we register. */
|
|
protected FrameManager _framemgr;
|
|
|
|
/** Our media host, so gracious and accomodating. */
|
|
protected MediaHost _host;
|
|
|
|
/** The animation manager in use by this panel. */
|
|
protected AnimationManager _animmgr;
|
|
|
|
/** The sprite manager in use by this panel. */
|
|
protected SpriteManager _spritemgr;
|
|
|
|
/** Used to accumulate and merge dirty regions on each tick. */
|
|
protected RegionManager _remgr;
|
|
|
|
/** Whether we're currently paused. */
|
|
protected boolean _paused;
|
|
|
|
/** Used to track the clock time at which we were paused. */
|
|
protected long _pauseTime;
|
|
|
|
/** Used to keep metrics. */
|
|
protected int[] _dirty = new int[200];
|
|
|
|
/** Used to keep metrics. */
|
|
protected int _tick;
|
|
|
|
/** Used to keep metrics. */
|
|
protected float _dirtyPerTick;
|
|
|
|
// used to render performance metrics
|
|
protected String _perfStatus = "";
|
|
protected Label _perfLabel;
|
|
protected Rectangle _perfRect;
|
|
}
|