/* | |
* Copyright (c) 2009-2010 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 com.jme3.asset.AssetCache.SmartAssetInfo; | |
import com.jme3.audio.AudioData; | |
import com.jme3.audio.AudioKey; | |
import com.jme3.font.BitmapFont; | |
import com.jme3.material.Material; | |
import com.jme3.scene.Spatial; | |
import com.jme3.shader.Shader; | |
import com.jme3.shader.ShaderKey; | |
import com.jme3.texture.Texture; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
/** | |
* <code>AssetManager</code> is the primary method for managing and loading | |
* assets inside jME. | |
* | |
* @author Kirill Vainer | |
*/ | |
public class DesktopAssetManager implements AssetManager { | |
private static final Logger logger = Logger.getLogger(AssetManager.class.getName()); | |
private final AssetCache cache = new AssetCache(); | |
private final ImplHandler handler = new ImplHandler(this); | |
private AssetEventListener eventListener = null; | |
private List<ClassLoader> classLoaders; | |
// private final ThreadingManager threadingMan = new ThreadingManager(this); | |
// private final Set<AssetKey> alreadyLoadingSet = new HashSet<AssetKey>(); | |
public DesktopAssetManager(){ | |
this(null); | |
} | |
@Deprecated | |
public DesktopAssetManager(boolean loadDefaults){ | |
this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Desktop.cfg")); | |
} | |
public DesktopAssetManager(URL configFile){ | |
if (configFile != null){ | |
InputStream stream = null; | |
try{ | |
AssetConfig cfg = new AssetConfig(this); | |
stream = configFile.openStream(); | |
cfg.loadText(stream); | |
}catch (IOException ex){ | |
logger.log(Level.SEVERE, "Failed to load asset config", ex); | |
}finally{ | |
if (stream != null) | |
try{ | |
stream.close(); | |
}catch (IOException ex){ | |
} | |
} | |
} | |
logger.info("DesktopAssetManager created."); | |
} | |
public void addClassLoader(ClassLoader loader){ | |
if(classLoaders == null) | |
classLoaders = Collections.synchronizedList(new ArrayList<ClassLoader>()); | |
synchronized(classLoaders) { | |
classLoaders.add(loader); | |
} | |
} | |
public void removeClassLoader(ClassLoader loader){ | |
if(classLoaders != null) synchronized(classLoaders) { | |
classLoaders.remove(loader); | |
} | |
} | |
public List<ClassLoader> getClassLoaders(){ | |
return classLoaders; | |
} | |
public void setAssetEventListener(AssetEventListener listener){ | |
eventListener = listener; | |
} | |
public void registerLoader(Class<? extends AssetLoader> loader, String ... extensions){ | |
handler.addLoader(loader, extensions); | |
if (logger.isLoggable(Level.FINER)){ | |
logger.log(Level.FINER, "Registered loader: {0} for extensions {1}", | |
new Object[]{loader.getSimpleName(), Arrays.toString(extensions)}); | |
} | |
} | |
public void registerLoader(String clsName, String ... extensions){ | |
Class<? extends AssetLoader> clazz = null; | |
try{ | |
clazz = (Class<? extends AssetLoader>) Class.forName(clsName); | |
}catch (ClassNotFoundException ex){ | |
logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex); | |
}catch (NoClassDefFoundError ex){ | |
logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex); | |
} | |
if (clazz != null){ | |
registerLoader(clazz, extensions); | |
} | |
} | |
public void registerLocator(String rootPath, Class<? extends AssetLocator> locatorClass){ | |
handler.addLocator(locatorClass, rootPath); | |
if (logger.isLoggable(Level.FINER)){ | |
logger.log(Level.FINER, "Registered locator: {0}", | |
locatorClass.getSimpleName()); | |
} | |
} | |
public void registerLocator(String rootPath, String clsName){ | |
Class<? extends AssetLocator> clazz = null; | |
try{ | |
clazz = (Class<? extends AssetLocator>) Class.forName(clsName); | |
}catch (ClassNotFoundException ex){ | |
logger.log(Level.WARNING, "Failed to find locator: "+clsName, ex); | |
}catch (NoClassDefFoundError ex){ | |
logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex); | |
} | |
if (clazz != null){ | |
registerLocator(rootPath, clazz); | |
} | |
} | |
public void unregisterLocator(String rootPath, Class<? extends AssetLocator> clazz){ | |
handler.removeLocator(clazz, rootPath); | |
if (logger.isLoggable(Level.FINER)){ | |
logger.log(Level.FINER, "Unregistered locator: {0}", | |
clazz.getSimpleName()); | |
} | |
} | |
public void clearCache(){ | |
cache.deleteAllAssets(); | |
} | |
/** | |
* Delete an asset from the cache, returns true if it was deleted | |
* successfully. | |
* <br/><br/> | |
* <font color="red">Thread-safe.</font> | |
*/ | |
public boolean deleteFromCache(AssetKey key){ | |
return cache.deleteFromCache(key); | |
} | |
/** | |
* Adds a resource to the cache. | |
* <br/><br/> | |
* <font color="red">Thread-safe.</font> | |
*/ | |
public void addToCache(AssetKey key, Object asset){ | |
cache.addToCache(key, asset); | |
} | |
public AssetInfo locateAsset(AssetKey<?> key){ | |
if (handler.getLocatorCount() == 0){ | |
logger.warning("There are no locators currently"+ | |
" registered. Use AssetManager."+ | |
"registerLocator() to register a"+ | |
" locator."); | |
return null; | |
} | |
AssetInfo info = handler.tryLocate(key); | |
if (info == null){ | |
logger.log(Level.WARNING, "Cannot locate resource: {0}", key); | |
} | |
return info; | |
} | |
/** | |
* <font color="red">Thread-safe.</font> | |
* | |
* @param <T> | |
* @param key | |
* @return | |
*/ | |
public <T> T loadAsset(AssetKey<T> key){ | |
if (key == null) | |
throw new IllegalArgumentException("key cannot be null"); | |
if (eventListener != null) | |
eventListener.assetRequested(key); | |
AssetKey smartKey = null; | |
Object o = null; | |
if (key.shouldCache()){ | |
if (key.useSmartCache()){ | |
SmartAssetInfo smartInfo = cache.getFromSmartCache(key); | |
if (smartInfo != null){ | |
smartKey = smartInfo.smartKey.get(); | |
if (smartKey != null){ | |
o = smartInfo.asset; | |
} | |
} | |
}else{ | |
o = cache.getFromCache(key); | |
} | |
} | |
if (o == null){ | |
AssetLoader loader = handler.aquireLoader(key); | |
if (loader == null){ | |
throw new IllegalStateException("No loader registered for type \"" + | |
key.getExtension() + "\""); | |
} | |
if (handler.getLocatorCount() == 0){ | |
throw new IllegalStateException("There are no locators currently"+ | |
" registered. Use AssetManager."+ | |
"registerLocator() to register a"+ | |
" locator."); | |
} | |
AssetInfo info = handler.tryLocate(key); | |
if (info == null){ | |
if (handler.getParentKey() != null && eventListener != null){ | |
// Inform event listener that an asset has failed to load. | |
// If the parent AssetLoader chooses not to propagate | |
// the exception, this is the only means of finding | |
// that something went wrong. | |
eventListener.assetDependencyNotFound(handler.getParentKey(), key); | |
} | |
throw new AssetNotFoundException(key.toString()); | |
} | |
try { | |
handler.establishParentKey(key); | |
o = loader.load(info); | |
} catch (IOException ex) { | |
throw new AssetLoadException("An exception has occured while loading asset: " + key, ex); | |
} finally { | |
handler.releaseParentKey(key); | |
} | |
if (o == 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()}); | |
} | |
// do processing on asset before caching | |
o = key.postProcess(o); | |
if (key.shouldCache()) | |
cache.addToCache(key, o); | |
if (eventListener != null) | |
eventListener.assetLoaded(key); | |
} | |
} | |
// object o is the asset | |
// create an instance for user | |
T clone = (T) key.createClonedInstance(o); | |
if (key.useSmartCache()){ | |
if (smartKey != null){ | |
// smart asset was already cached, use original key | |
((Asset)clone).setKey(smartKey); | |
}else{ | |
// smart asset was cached on this call, use our key | |
((Asset)clone).setKey(key); | |
} | |
} | |
return clone; | |
} | |
public Object loadAsset(String name){ | |
return loadAsset(new AssetKey(name)); | |
} | |
/** | |
* Loads a texture. | |
* | |
* @return | |
*/ | |
public Texture loadTexture(TextureKey key){ | |
return (Texture) loadAsset(key); | |
} | |
public Material loadMaterial(String name){ | |
return (Material) loadAsset(new MaterialKey(name)); | |
} | |
/** | |
* Loads a texture. | |
* | |
* @param name | |
* @param generateMipmaps Enable if applying texture to 3D objects, disable | |
* for GUI/HUD elements. | |
* @return | |
*/ | |
public Texture loadTexture(String name, boolean generateMipmaps){ | |
TextureKey key = new TextureKey(name, true); | |
key.setGenerateMips(generateMipmaps); | |
key.setAsCube(false); | |
return loadTexture(key); | |
} | |
public Texture loadTexture(String name, boolean generateMipmaps, boolean flipY, boolean asCube, int aniso){ | |
TextureKey key = new TextureKey(name, flipY); | |
key.setGenerateMips(generateMipmaps); | |
key.setAsCube(asCube); | |
key.setAnisotropy(aniso); | |
return loadTexture(key); | |
} | |
public Texture loadTexture(String name){ | |
return loadTexture(name, true); | |
} | |
public AudioData loadAudio(AudioKey key){ | |
return (AudioData) loadAsset(key); | |
} | |
public AudioData loadAudio(String name){ | |
return loadAudio(new AudioKey(name, false)); | |
} | |
/** | |
* Loads a bitmap font with the given name. | |
* | |
* @param name | |
* @return | |
*/ | |
public BitmapFont loadFont(String name){ | |
return (BitmapFont) loadAsset(new AssetKey(name)); | |
} | |
public InputStream loadGLSLLibrary(AssetKey key){ | |
return (InputStream) loadAsset(key); | |
} | |
/** | |
* Load a vertex/fragment shader combo. | |
* | |
* @param key | |
* @return | |
*/ | |
public Shader loadShader(ShaderKey key){ | |
// cache abuse in method | |
// that doesn't use loaders/locators | |
Shader s = (Shader) cache.getFromCache(key); | |
if (s == null){ | |
String vertName = key.getVertName(); | |
String fragName = key.getFragName(); | |
String vertSource = (String) loadAsset(new AssetKey(vertName)); | |
String fragSource = (String) loadAsset(new AssetKey(fragName)); | |
s = new Shader(key.getLanguage()); | |
s.addSource(Shader.ShaderType.Vertex, vertName, vertSource, key.getDefines().getCompiled()); | |
s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled()); | |
cache.addToCache(key, s); | |
} | |
return s; | |
} | |
public Spatial loadModel(ModelKey key){ | |
return (Spatial) loadAsset(key); | |
} | |
/** | |
* Load a model. | |
* | |
* @param name | |
* @return | |
*/ | |
public Spatial loadModel(String name){ | |
return loadModel(new ModelKey(name)); | |
} | |
} |