
com.threerings.media.MetaMediaManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nenya Show documentation
Show all versions of nenya Show documentation
Facilities for making networked multiplayer games.
The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/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.util.Iterator;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import com.google.common.collect.Iterators;
import com.samskivert.util.IntListUtil;
import com.samskivert.util.StringUtil;
import com.samskivert.swing.Label;
import com.threerings.media.animation.Animation;
import com.threerings.media.animation.AnimationManager;
import com.threerings.media.sprite.Sprite;
import com.threerings.media.sprite.SpriteManager;
import com.threerings.media.timer.MediaTimer;
import static com.threerings.media.Log.log;
/**
* 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, Iterable
{
public MetaMediaManager (FrameManager framemgr, MediaHost host)
{
// keep these for later
_framemgr = framemgr;
_host = host;
// initialize our managers
_animmgr.init(host, _remgr);
_spritemgr.init(host, _remgr);
}
/**
* 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 the region manager used to coordinate our dirty regions.
*/
public RegionManager getRegionManager ()
{
return _remgr;
}
/**
* 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;
}
_paused = paused;
if (_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. Note: 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();
}
/**
* 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 method 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 method 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);
}
public Iterator iterator ()
{
return Iterators.concat(_spritemgr.enumerateSprites(), _animmgr.iterator());
}
/** The frame manager with whom we register. */
protected FrameManager _framemgr;
/** Our media host, so gracious and accomodating. */
protected MediaHost _host;
/** Used to accumulate and merge dirty regions on each tick. */
protected RegionManager _remgr = new RegionManager();
/** The animation manager in use by this panel. */
protected AnimationManager _animmgr = new AnimationManager();
/** The sprite manager in use by this panel. */
protected SpriteManager _spritemgr = new SpriteManager();
/** 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;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy