Forráskód Böngészése

AssetManager: javadoc, cleanup, new method
* Move *cache* methods up into the AssetManager interface from DesktopAssetManager
* Fix various incorrect javadoc
* Add new method to load an asset from an InputStream
* Restructure DesktopAssetManager.loadAsset() to make it less monolithic

shadowislord 10 éve
szülő
commit
90925e3968

+ 99 - 63
jme3-core/src/main/java/com/jme3/asset/AssetManager.java

@@ -46,6 +46,8 @@ import com.jme3.shader.ShaderGenerator;
 import com.jme3.shader.ShaderKey;
 import com.jme3.texture.Texture;
 import com.jme3.texture.plugins.TGALoader;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.EnumSet;
 import java.util.List;
 
@@ -75,7 +77,15 @@ import java.util.List;
  * <li>{@link TGALoader} - Used to load Targa image files</li>
  * </ul>
  * <p>
- * Once the asset has been loaded, 
+ * Once the asset has been loaded, it will be 
+ * {@link AssetProcessor#postProcess(com.jme3.asset.AssetKey, java.lang.Object) 
+ * post-processed} by the {@link AssetKey#getProcessorType() key's processor}.
+ * If the key specifies a {@link AssetKey#getCacheType() cache type}, the asset
+ * will be cached in the specified cache. Next, the {@link AssetProcessor}
+ * will be requested to {@link AssetProcessor#createClone(java.lang.Object) } 
+ * generate a clone for the asset. Some assets do not require cloning,
+ * such as immutable or shared assets. Others, like models, must be cloned
+ * so that modifications to one instance do not leak onto others.
  */
 public interface AssetManager {
 
@@ -100,46 +110,13 @@ public interface AssetManager {
      */
     public List<ClassLoader> getClassLoaders();
     
-    /**
-     * Registers a loader for the given extensions.
-     * 
-     * @param loaderClassName
-     * @param extensions
-     * 
-     * @deprecated Please use {@link #registerLoader(java.lang.Class, java.lang.String[]) }
-     * together with {@link Class#forName(java.lang.String) } to find a class
-     * and then register it.
-     * 
-     * @deprecated Please use {@link #registerLoader(java.lang.Class, java.lang.String[]) }
-     * with {@link Class#forName(java.lang.String) } instead.
-     */
-    @Deprecated
-    public void registerLoader(String loaderClassName, String ... extensions);
-
-    /**
-     * Registers an {@link AssetLocator} by using a class name. 
-     * See the {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
-     * method for more information.
-     *
-     * @param rootPath The root path from which to locate assets, this 
-     * depends on the implementation of the asset locator. 
-     * A URL based locator will expect a url folder such as "http://www.example.com/"
-     * while a File based locator will expect a file path (OS dependent).
-     * @param locatorClassName The full class name of the {@link AssetLocator}
-     * implementation.
-     * 
-     * @deprecated Please use {@link #registerLocator(java.lang.String, java.lang.Class)  }
-     * together with {@link Class#forName(java.lang.String) } to find a class
-     * and then register it.
-     */
-    @Deprecated
-    public void registerLocator(String rootPath, String locatorClassName);
-
     /**
      * Register an {@link AssetLoader} by using a class object.
      * 
-     * @param loaderClass
-     * @param extensions
+     * @param loaderClass The loader class to register.
+     * @param extensions Which extensions this loader is responsible for loading,
+     * if there are already other loaders registered for that extension, they
+     * will be overridden - there should only be one loader for each extension.
      */
     public void registerLoader(Class<? extends AssetLoader> loaderClass, String ... extensions);
     
@@ -161,7 +138,7 @@ public interface AssetManager {
      * to the {@link AssetLoader} to load the asset.
      * Once a locator is registered, it can be removed via
      * {@link #unregisterLocator(java.lang.String, java.lang.Class) }.
-     *
+     * 
      * @param rootPath Specifies the root path from which to locate assets
      * for the given {@link AssetLocator}. The purpose of this parameter
      * depends on the type of the {@link AssetLocator}.
@@ -206,18 +183,6 @@ public interface AssetManager {
      */
     public void clearAssetEventListeners();
     
-    /**
-     * Set an {@link AssetEventListener} to receive events from this
-     * <code>AssetManager</code>. Any currently added listeners are
-     * cleared and then the given listener is added.
-     * 
-     * @param listener The listener to set
-     * @deprecated Please use {@link #addAssetEventListener(com.jme3.asset.AssetEventListener) }
-     * to listen for asset events.
-     */
-    @Deprecated
-    public void setAssetEventListener(AssetEventListener listener);
-
     /**
      * Manually locates an asset with the given {@link AssetKey}. 
      * This method should be used for debugging or internal uses.
@@ -233,6 +198,23 @@ public interface AssetManager {
      */
     public AssetInfo locateAsset(AssetKey<?> key);
 
+    /**
+     * Load an asset from an {@link InputStream}. 
+     * In some cases it may be required to load an asset from memory 
+     * or arbitrary streams so that registering a custom locator and key
+     * type is not necessary. 
+     * 
+     * @param <T> The object type that will be loaded from the AssetKey instance.
+     * @param key The AssetKey. Note that the asset will not be cached - 
+     * following the same behavior as if {@link AssetKey#getCacheType()} returned null.
+     * @param inputStream The input stream from which the asset shall be loaded.
+     * @return The loaded asset.
+     * 
+     * @throws AssetLoadException If the {@link AssetLoader} has failed
+     * to load the asset due to an {@link IOException} or another error.
+     */
+    public <T> T loadAssetFromStream(AssetKey<T> key, InputStream inputStream);
+    
     /**
      * Load an asset from a key, the asset will be located
      * by one of the {@link AssetLocator} implementations provided in the
@@ -244,17 +226,18 @@ public interface AssetManager {
      *
      * @param <T> The object type that will be loaded from the AssetKey instance.
      * @param key The AssetKey
-     * @return The loaded asset, or null if it was failed to be located
-     * or loaded.
+     * @return The loaded asset.
+     * 
+     * @throws AssetNotFoundException If all registered locators have failed 
+     * to locate the asset.
+     * @throws AssetLoadException If the {@link AssetLoader} has failed
+     * to load the asset due to an {@link IOException} or another error.
      */
     public <T> T loadAsset(AssetKey<T> key);
 
     /**
-     * Load an asset by name, calling this method
-     * is the same as calling
-     * <code>
-     * loadAsset(new AssetKey(name)).
-     * </code>
+     * Load an asset by name, calling this method is the same as calling
+     * <code>loadAsset(new AssetKey(name))</code>.
      *
      * @param name The name of the asset to load.
      * @return The loaded asset, or null if failed to be loaded.
@@ -265,7 +248,7 @@ public interface AssetManager {
 
     /**
      * Loads texture file, supported types are BMP, JPG, PNG, GIF,
-     * TGA and DDS.
+     * TGA, DDS, PFM, and HDR.
      *
      * @param key The {@link TextureKey} to use for loading.
      * @return The loaded texture, or null if failed to be loaded.
@@ -276,8 +259,10 @@ public interface AssetManager {
 
     /**
      * Loads texture file, supported types are BMP, JPG, PNG, GIF,
-     * TGA and DDS.
+     * TGA, DDS, PFM, and HDR.
      *
+     * The texture will be loaded with mip-mapping enabled. 
+     * 
      * @param name The name of the texture to load.
      * @return The texture that was loaded
      *
@@ -306,7 +291,8 @@ public interface AssetManager {
 
     /**
      * Loads a 3D model with a ModelKey. 
-     * Models can be jME3 object files (J3O) or OgreXML/OBJ files.
+     * Models can be jME3 object files (J3O), OgreXML (mesh.xml), BLEND, FBX 
+     * and OBJ files.
      * @param key Asset key of the model to load
      * @return The model that was loaded
      *
@@ -315,8 +301,9 @@ public interface AssetManager {
     public Spatial loadModel(ModelKey key);
 
     /**
-     * Loads a 3D model. Models can be jME3 object files (J3O) or
-     * OgreXML/OBJ files.
+     * Loads a 3D model. Models can be jME3 object files (J3O),
+     * OgreXML (mesh.xml), BLEND, FBX and OBJ files.
+     * 
      * @param name Asset name of the model to load
      * @return The model that was loaded
      *
@@ -381,4 +368,53 @@ public interface AssetManager {
      */
     public ShaderGenerator getShaderGenerator(EnumSet<Caps> caps);
     
+    /**
+     * Retrieve an asset from the asset cache.
+     * 
+     * <b>NOTE:</b> Do <em>not</em> modify the returned asset! 
+     * It is the same reference as what is stored in the cache, therefore any 
+     * modifications to it will leak onto assets loaded from the same key in the future.
+     * 
+     * @param <T> The object type that will be retrieved from the AssetKey instance.
+     * @param key The AssetKey to get from the cache.
+     * @return The cached asset, if found. Otherwise, <code>null</code>.
+     * 
+     * @throws IllegalArgumentException If {@link AssetKey#getCacheType() caching}
+     * is disabled for the key.
+     */
+    public <T> T getFromCache(AssetKey<T> key);
+    
+    /**
+     * Inject an asset into the asset cache.
+     * 
+     * <b>NOTE:</b> Do <em>not</em> modify the cached asset after storing!
+     * It is the same reference as what is stored in the cache, therefore any 
+     * modifications to it will leak onto assets loaded from the same key in the future.
+     * 
+     * @param <T> The object type of the asset.
+     * @param key The key where the asset shall be stored.
+     * @param asset The asset to inject into the cache.
+     * 
+     * @throws IllegalArgumentException If {@link AssetKey#getCacheType() caching}
+     * is disabled for the key.
+     */
+    public <T> void addToCache(AssetKey<T> key, T asset);
+    
+    /**
+     * Delete an asset from the asset cache.
+     * 
+     * @param <T> The object type of the AssetKey instance.
+     * @param key The asset key to remove from the cache.
+     * @return True if the asset key was found in the cache and was removed
+     * successfully. False if the asset key was not present in the cache.
+     * 
+     * @throws IllegalArgumentException If {@link AssetKey#getCacheType() caching}
+     * is disabled for the key.
+     */
+    public <T> boolean deleteFromCache(AssetKey<T> key);
+    
+    /**
+     * Clears the asset cache.
+     */
+    public void clearCache();
 }

+ 113 - 56
jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java

@@ -207,6 +207,7 @@ public class DesktopAssetManager implements AssetManager {
         return info;
     }
     
+    @Override
     public <T> T getFromCache(AssetKey<T> key) {
         AssetCache cache = handler.getCache(key.getCacheType());
         if (cache != null) {
@@ -221,6 +222,7 @@ public class DesktopAssetManager implements AssetManager {
         }
     }
     
+    @Override
     public <T> void addToCache(AssetKey<T> key, T asset) {
         AssetCache cache = handler.getCache(key.getCacheType());
         if (cache != null) {
@@ -231,6 +233,7 @@ public class DesktopAssetManager implements AssetManager {
         }
     }
     
+    @Override
     public <T> boolean deleteFromCache(AssetKey<T> key) {
         AssetCache cache = handler.getCache(key.getCacheType());
         if (cache != null) {
@@ -240,6 +243,7 @@ public class DesktopAssetManager implements AssetManager {
         }
     }
     
+    @Override
     public void clearCache(){
         handler.clearCache();
         if (logger.isLoggable(Level.FINER)){
@@ -248,13 +252,110 @@ public class DesktopAssetManager implements AssetManager {
     }
 
     /**
-     * <font color="red">Thread-safe.</font>
-     *
-     * @param <T>
-     * @param key
-     * @return the loaded asset
+     * Loads an asset that has already been located.
+     * @param <T> The asset type
+     * @param key The asset key
+     * @param info The AssetInfo from the locator
+     * @param proc AssetProcessor to use, or null to disable processing
+     * @param cache The cache to store the asset in, or null to disable caching
+     * @return The loaded asset
+     * 
+     * @throws AssetLoadException If failed to load asset due to exception or
+     * other error.
+     */
+    protected <T> T loadLocatedAsset(AssetKey<T> key, AssetInfo info, AssetProcessor proc, AssetCache cache) {
+        AssetLoader loader = handler.aquireLoader(key);
+        Object obj;
+        try {
+            handler.establishParentKey(key);
+            obj = loader.load(info);
+        } catch (IOException ex) {
+            throw new AssetLoadException("An exception has occured while loading asset: " + key, ex);
+        } finally {
+            handler.releaseParentKey(key);
+        }
+        if (obj == null) {
+            throw new AssetLoadException("Error occured while loading asset \""
+                    + key + "\" using " + loader.getClass().getSimpleName());
+        } else {
+            if (logger.isLoggable(Level.FINER)) {
+                logger.log(Level.FINER, "Loaded {0} with {1}",
+                        new Object[]{key, loader.getClass().getSimpleName()});
+            }
+
+            if (proc != null) {
+                // do processing on asset before caching
+                obj = proc.postProcess(key, obj);
+            }
+
+            if (cache != null) {
+                // At this point, obj should be of type T
+                cache.addToCache(key, (T) obj);
+            }
+
+            for (AssetEventListener listener : eventListeners) {
+                listener.assetLoaded(key);
+            }
+
+            return (T) obj;
+        }
+    }
+    
+    /**
+     * Clones the asset using the given processor and registers the clone
+     * with the cache.
+     * 
+     * @param <T> The asset type
+     * @param key The asset key
+     * @param obj The asset to clone / register, must implement 
+     * {@link CloneableSmartAsset}.
+     * @param proc The processor which will generate the clone, cannot be null
+     * @param cache The cache to register the clone with, cannot be null.
+     * @return The cloned asset, cannot be the same as the given asset since
+     * it is a clone.
+     * 
+     * @throws IllegalStateException If asset does not implement 
+     * {@link CloneableSmartAsset}, if the cache is null, or if the 
+     * processor did not clone the asset.
      */
-      public <T> T loadAsset(AssetKey<T> key){
+    protected <T> T registerAndCloneSmartAsset(AssetKey<T> key, T obj, AssetProcessor proc, AssetCache cache) {
+        // object obj is the original asset
+        // create an instance for user
+        T clone = (T) obj;
+        if (proc == null) {
+            throw new IllegalStateException("Asset implements "
+                    + "CloneableSmartAsset but doesn't "
+                    + "have processor to handle cloning");
+        } else {
+            clone = (T) proc.createClone(obj);
+            if (cache != null && clone != obj) {
+                cache.registerAssetClone(key, clone);
+            } else {
+                throw new IllegalStateException("Asset implements "
+                        + "CloneableSmartAsset but doesn't have cache or "
+                        + "was not cloned");
+            }
+        }
+        return clone;
+    }
+    
+    @Override
+    public <T> T loadAssetFromStream(AssetKey<T> key, InputStream inputStream) {
+        if (key == null) {
+            throw new IllegalArgumentException("key cannot be null");
+        }
+        
+        for (AssetEventListener listener : eventListeners){
+            listener.assetRequested(key);
+        }
+        
+        AssetProcessor proc = handler.getProcessor(key.getProcessorType());
+        StreamAssetInfo info = new StreamAssetInfo(this, key, inputStream);
+        return loadLocatedAsset(key, info, proc, null);
+    }
+    
+    @Override
+    public <T> T loadAsset(AssetKey<T> key){
         if (key == null)
             throw new IllegalArgumentException("key cannot be null");
         
@@ -268,7 +369,6 @@ public class DesktopAssetManager implements AssetManager {
         Object obj = cache != null ? cache.getFromCache(key) : null;
         if (obj == null){
             // Asset not in cache, load it from file system.
-            AssetLoader loader = handler.aquireLoader(key);
             AssetInfo info = handler.tryLocate(key);
             if (info == null){
                 if (handler.getParentKey() != null){
@@ -282,59 +382,16 @@ public class DesktopAssetManager implements AssetManager {
                 }
                 throw new AssetNotFoundException(key.toString());
             }
-
-            try {
-                handler.establishParentKey(key);
-                obj = loader.load(info);
-            } catch (IOException ex) {
-                throw new AssetLoadException("An exception has occured while loading asset: " + key, ex);
-            } finally {
-                handler.releaseParentKey(key);
-            }
-            if (obj == null){
-                throw new AssetLoadException("Error occured while loading asset \"" + key + "\" using " + loader.getClass().getSimpleName());
-            }else{
-                if (logger.isLoggable(Level.FINER)){
-                    logger.log(Level.FINER, "Loaded {0} with {1}",
-                            new Object[]{key, loader.getClass().getSimpleName()});
-                }
-                
-                if (proc != null){
-                    // do processing on asset before caching
-                    obj = proc.postProcess(key, obj);
-                }
-                
-                if (cache != null){
-                    // At this point, obj should be of type T
-                    cache.addToCache(key, (T) obj);
-                }
-                
-                for (AssetEventListener listener : eventListeners){
-                    listener.assetLoaded(key);
-                }
-            }
+            
+            obj = loadLocatedAsset(key, info, proc, cache);
         }
 
-        // object obj is the original asset
-        // create an instance for user
         T clone = (T) obj;
-        if (clone instanceof CloneableSmartAsset){
-            if (proc == null){
-                throw new IllegalStateException("Asset implements "
-                        + "CloneableSmartAsset but doesn't "
-                        + "have processor to handle cloning");
-            }else{
-                clone = (T) proc.createClone(obj);
-                if (cache != null && clone != obj){
-                    cache.registerAssetClone(key, clone);
-                } else{
-                    throw new IllegalStateException("Asset implements "
-                        + "CloneableSmartAsset but doesn't have cache or "
-                        + "was not cloned");
-                }
-            }
+        
+        if (obj instanceof CloneableSmartAsset) {
+            clone = registerAndCloneSmartAsset(key, clone, proc, cache);
         }
-       
+        
         return clone;
     }
 

+ 60 - 0
jme3-core/src/main/java/com/jme3/asset/StreamAssetInfo.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009-2015 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.asset;
+
+import java.io.InputStream;
+
+/**
+ * An {@link AssetInfo} wrapper for {@link InputStream InputStreams}.
+ * 
+ * @author Kirill Vainer
+ */
+public class StreamAssetInfo extends AssetInfo {
+
+    private boolean alreadyOpened;
+    private final InputStream inputStream;
+    
+    public StreamAssetInfo(AssetManager assetManager, AssetKey<?> assetKey, InputStream inputStream) {
+        super(assetManager, assetKey);
+        this.inputStream = inputStream;
+    }
+    
+    @Override
+    public InputStream openStream() {
+        if (alreadyOpened) {
+            throw new IllegalStateException("Stream already opened");
+        }
+        alreadyOpened = true;
+        return inputStream;
+    }
+    
+}