Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  //
  // 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.cast;
 
 import java.util.Map;
 
 import java.awt.Point;
 
 
 
 
 
 
 
 import static com.threerings.cast.Log.log;

The character manager provides facilities for constructing sprites that are used to represent characters in a scene. It also handles the compositing and caching of composited character animations.
 
 public class CharacterManager
     implements DirectionCodes
 {
    
Sets the size of the cache used for composited animation frames. This must be called before the CharacterManager is created.
 
     public static void setCacheSize (int cacheKilobytes)
     {
          = cacheKilobytes;
     }

    
Constructs the character manager.
 
     public CharacterManager (ImageManager imgrComponentRepository crepo)
     {
         // keep these around
          = imgr;
          = crepo;
 
         // populate our actions table
         Iterator<ActionSequenceiter = crepo.enumerateActionSequences();
         while (iter.hasNext()) {
             ActionSequence action = iter.next();
             .put(action.nameaction);
         }
 
         // create a cache for our composited action frames
         .debug("Creating action cache [size=" +  + "k].");
                  * 1024, new LRUHashMap.ItemSizer<CompositedMultiFrameImage>() {
             public int computeSize (CompositedMultiFrameImage value) {
                 return (int)value.getEstimatedMemoryUsage();
             }
         });
         .setTracking(true); // TODO
     }

    
Returns the component repository being used by this manager.
 
     {
         return ;
     }

    
Instructs the character manager to construct instances of this derived class of CharacterSprite when creating new sprites.

Throws:
java.lang.IllegalArgumentException thrown if the supplied class does not derive from CharacterSprite.
    public void setCharacterClass (Class<? extends CharacterSpritecharClass)
    {
        // make a note of it
         = charClass;
    }

    
Instructs the character manager to use the provided cache for composited action animations.
    public void setActionCache (ActionCache cache)
    {
         = cache;
    }

    
Returns a CharacterSprite representing the character described by the given CharacterDescriptor, or null if an error occurs.

Parameters:
desc the character descriptor.
    {
        return getCharacter(desc);
    }

    
Returns a CharacterSprite representing the character described by the given CharacterDescriptor, or null if an error occurs.

Parameters:
desc the character descriptor.
charClass the CharacterSprite derived class that should be instantiated instead of the configured default (which is set via setCharacterClass(java.lang.Class)).
    public <T extends CharacterSprite> T getCharacter (CharacterDescriptor desc,
        Class<T> charClass)
    {
        try {
            T sprite = charClass.newInstance();
            sprite.init(descthis);
            return sprite;
        } catch (Exception e) {
            .warning("Failed to instantiate character sprite."e);
            return null;
        }
    }

    
Obtains the composited animation frames for the specified action for a character with the specified descriptor. The resulting composited animation will be cached.

Throws:
NoSuchComponentException thrown if any of the components in the supplied descriptor do not exist.
java.lang.IllegalArgumentException thrown if any of the components referenced in the descriptor do not support the specified action.
    public ActionFrames getActionFrames (
        CharacterDescriptor descripString action)
        throws NoSuchComponentException
    {
        Tuple<CharacterDescriptorStringkey = new Tuple<CharacterDescriptorString>(descripaction);
        ActionFrames frames = .get(key);
        if (frames == null) {
            // this doesn't actually composite the images, but prepares an
            // object to be able to do so
            frames = createCompositeFrames(descripaction);
            .put(keyframes);
        }
        // periodically report our frame image cache performance
        if (!.throttleOp()) {
            long size = getEstimatedCacheMemoryUsage();
            int[] eff = .getTrackedEffectiveness();
            .debug("CharacterManager LRU [mem=" + (size / 1024) + "k" +
                      ", size=" + .size() + ", hits=" + eff[0] +
                      ", misses=" + eff[1] + "].");
        }
        return frames;
    }

    
Informs the character manager that the action sequence for the given character descriptor is likely to be needed in the near future and so any efforts that can be made to load it into the action sequence cache in advance should be undertaken.

This will eventually be revamped to spiffily load action sequences in the background.

    public void resolveActionSequence (CharacterDescriptor descString action)
    {
        try {
            if (getActionFrames(descaction) == null) {
                .warning("Failed to resolve action sequence " +
                            "[desc=" + desc + ", action=" + action + "].");
            }
        } catch (NoSuchComponentException nsce) {
            .warning("Failed to resolve action sequence " +
                        "[nsce=" + nsce + "].");
        }
    }

    
Returns the action sequence instance with the specified name or null if no such sequence exists.
    public ActionSequence getActionSequence (String action)
    {
        return .get(action);
    }

    
Returns the estimated memory usage in bytes for all images currently cached by the cached action frames.
    protected long getEstimatedCacheMemoryUsage ()
    {
        long size = 0;
        while (iter.hasNext()) {
            size += iter.next().getEstimatedMemoryUsage();
        }
        return size;
    }

    
Generates the composited animation frames for the specified action for a character with the specified descriptor.

Throws:
NoSuchComponentException thrown if any of the components in the supplied descriptor do not exist.
java.lang.IllegalArgumentException thrown if any of the components referenced in the descriptor do not support the specified action.
    protected ActionFrames createCompositeFrames (
        CharacterDescriptor descripString action)
        throws NoSuchComponentException
    {
        int[] cids = descrip.getComponentIds();
        int ccount = cids.length;
        Colorization[][] zations = descrip.getColorizations();
        Point[] xlations = descrip.getTranslations();
        .debug("Compositing action [action=" + action +
                  ", descrip=" + descrip + "].");
        // this will be used to construct any shadow layers
        HashMap<StringArrayList<TranslatedComponent>> shadows = null;
        // maps components by class name for masks
        HashMap<StringArrayList<TranslatedComponent>> ccomps =
            Maps.newHashMap();
        // create colorized versions of all of the source action frames
        ArrayList<ComponentFramessources = Lists.newArrayListWithCapacity(ccount);
        for (int ii = 0; ii < ccountii++) {
            ComponentFrames cframes = new ComponentFrames();
            sources.add(cframes);
            CharacterComponent ccomp = (cframes.ccomp = .getComponent(cids[ii]));
            // load up the main component images
            ActionFrames source = ccomp.getFrames(actionnull);
            if (source == null) {
                String errmsg = "Cannot composite action frames; no such " +
                    "action for component [action=" + action +
                    ", desc=" + descrip + ", comp=" + ccomp + "]";
                throw new RuntimeException(errmsg);
            }
            source = (zations == null || zations[ii] == null) ?
                source : source.cloneColorized(zations[ii]);
            Point xlation = (xlations == null) ? null : xlations[ii];
            cframes.frames = (xlation == null) ?
                source : source.cloneTranslated(xlation.xxlation.y);
            // store the component with its translation under its class for masking
            TranslatedComponent tcomp = new TranslatedComponent(ccompxlation);
            ArrayList<TranslatedComponenttcomps = ccomps.get(ccomp.componentClass.name);
            if (tcomps == null) {
                ccomps.put(ccomp.componentClass.nametcomps = Lists.newArrayList());
            }
            tcomps.add(tcomp);
            // if this component has a shadow, make a note of it
            if (ccomp.componentClass.isShadowed()) {
                if (shadows == null) {
                    shadows = Maps.newHashMap();
                }
                ArrayList<TranslatedComponentshadlist = shadows.get(ccomp.componentClass.shadow);
                if (shadlist == null) {
                    shadows.put(ccomp.componentClass.shadowshadlist = Lists.newArrayList());
                }
                shadlist.add(tcomp);
            }
        }
        // now create any necessary shadow layers
        if (shadows != null) {
            for (Map.Entry<StringArrayList<TranslatedComponent>> entry : shadows.entrySet()) {
                ComponentFrames scf = compositeShadow(actionentry.getKey(), entry.getValue());
                if (scf != null) {
                    sources.add(scf);
                }
            }
        }
        // add any necessary masks
        for (ComponentFrames cframes : sources) {
            ArrayList<TranslatedComponentmcomps = ccomps.get(cframes.ccomp.componentClass.mask);
            if (mcomps != null) {
                cframes.frames = compositeMask(actioncframes.ccompcframes.framesmcomps);
            }
        }
        // use those to create an entity that will lazily composite things
        // together as they are needed
        ComponentFrames[] cfvec = sources.toArray(new ComponentFrames[sources.size()]);
        return new CompositedActionFrames(actioncfvec);
    }
    protected ComponentFrames compositeShadow (
        String actionString sclassArrayList<TranslatedComponentscomps)
    {
        final ComponentClass cclass = .getComponentClass(sclass);
        if (cclass == null) {
            .warning("Components reference non-existent shadow layer " +
                        "class [sclass=" + sclass +
                        ", scomps=" + StringUtil.toString(scomps) + "].");
            return null;
        }
        ComponentFrames cframes = new ComponentFrames();
        // create a fake component for the shadow layer
        cframes.ccomp = new CharacterComponent(-1, "shadow"cclassnull);
        ArrayList<ComponentFramessources = Lists.newArrayList();
        for (TranslatedComponent scomp : scomps) {
            ComponentFrames source = new ComponentFrames();
            source.ccomp = scomp.ccomp;
            source.frames = scomp.getFrames(action.);
            if (source.frames == null) {
                // skip this shadow component
                continue;
            }
            sources.add(source);
        }
        // if we ended up with no shadow, no problem!
        if (sources.size() == 0) {
            return null;
        }
        // create custom action frames that use a special compositing
        // multi-frame image that does the necessary shadow magic
        ComponentFrames[] svec = sources.toArray(new ComponentFrames[sources.size()]);
        cframes.frames = new CompositedActionFrames(actionsvec) {
            @Override
            protected CompositedMultiFrameImage createFrames (int orient) {
                return new CompositedShadowImage(
                    orientcclass.shadowAlpha);
            }
        };
        return cframes;
    }
    protected ActionFrames compositeMask (
        String actionCharacterComponent ccompActionFrames cframes,
        ArrayList<TranslatedComponentmcomps)
    {
        ArrayList<ComponentFramessources = Lists.newArrayList();
        sources.add(new ComponentFrames(ccompcframes));
        for (TranslatedComponent mcomp : mcomps) {
            ActionFrames mframes = mcomp.getFrames(action.);
            if (mframes != null) {
                sources.add(new ComponentFrames(mcomp.ccompmframes));
            }
        }
        if (sources.size() == 1) {
            return cframes;
        }
        ComponentFrames[] mvec = sources.toArray(new ComponentFrames[sources.size()]);
        return new CompositedActionFrames(actionmvec) {
            @Override
            protected CompositedMultiFrameImage createFrames (int orient) {
                return new CompositedMaskedImage(orient);
            }
        };
    }

    
Combines a component with an optional translation for shadowing or masking.
    protected static class TranslatedComponent
    {
        public CharacterComponent ccomp;
        public Point xlation;
        public TranslatedComponent (CharacterComponent ccompPoint xlation)
        {
            this. = ccomp;
            this. = xlation;
        }
        public ActionFrames getFrames (String actionString type)
        {
            ActionFrames frames = .getFrames(actiontype);
            return (frames == null ||  == null) ?
                frames : frames.cloneTranslated(..);
        }
    }

    
The image manager with whom we interact.
    protected ImageManager _imgr;

    
The component repository.
    protected ComponentRepository _crepo;

    
A table of our action sequences.
    protected Map<StringActionSequence_actions = Maps.newHashMap();

    
A table of composited action sequences (these don't reference the actual image data directly and thus take up little memory).
        Maps.newHashMap();

    
A cache of composited animation frames.
The character class to be created.
    protected Class<? extends CharacterSprite_charClass = CharacterSprite.class;

    
The action animation cache, if we have one.
    protected ActionCache _acache;

    
Throttle our cache status logging to once every 30 seconds.
    protected Throttle _cacheStatThrottle = new Throttle(1, 30000L);

    
Register our image cache size with the runtime adjustments framework.
    protected static RuntimeAdjust.IntAdjust _cacheSize =
        new RuntimeAdjust.IntAdjust(
            "Size (in kb of memory used) of the character manager LRU " +
            "action cache [requires restart]""narya.cast.action_cache_size",
            ., 32768);

    
Cache size to be used in this run. Adjusted by setCacheSize without affecting the stored value.
    protected static int _runCacheSize = .getValue();
New to GrepCode? Check out our FAQ X