1/* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33package com.jme3.asset; 34 35import com.jme3.asset.AssetCache.SmartAssetInfo; 36import com.jme3.audio.AudioData; 37import com.jme3.audio.AudioKey; 38import com.jme3.font.BitmapFont; 39import com.jme3.material.Material; 40import com.jme3.scene.Spatial; 41import com.jme3.shader.Shader; 42import com.jme3.shader.ShaderKey; 43import com.jme3.texture.Texture; 44import java.io.IOException; 45import java.io.InputStream; 46import java.net.URL; 47import java.util.ArrayList; 48import java.util.Arrays; 49import java.util.Collections; 50import java.util.List; 51import java.util.logging.Level; 52import java.util.logging.Logger; 53 54/** 55 * <code>AssetManager</code> is the primary method for managing and loading 56 * assets inside jME. 57 * 58 * @author Kirill Vainer 59 */ 60public class DesktopAssetManager implements AssetManager { 61 62 private static final Logger logger = Logger.getLogger(AssetManager.class.getName()); 63 64 private final AssetCache cache = new AssetCache(); 65 private final ImplHandler handler = new ImplHandler(this); 66 67 private AssetEventListener eventListener = null; 68 private List<ClassLoader> classLoaders; 69 70// private final ThreadingManager threadingMan = new ThreadingManager(this); 71// private final Set<AssetKey> alreadyLoadingSet = new HashSet<AssetKey>(); 72 73 public DesktopAssetManager(){ 74 this(null); 75 } 76 77 @Deprecated 78 public DesktopAssetManager(boolean loadDefaults){ 79 this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Desktop.cfg")); 80 } 81 82 public DesktopAssetManager(URL configFile){ 83 if (configFile != null){ 84 InputStream stream = null; 85 try{ 86 AssetConfig cfg = new AssetConfig(this); 87 stream = configFile.openStream(); 88 cfg.loadText(stream); 89 }catch (IOException ex){ 90 logger.log(Level.SEVERE, "Failed to load asset config", ex); 91 }finally{ 92 if (stream != null) 93 try{ 94 stream.close(); 95 }catch (IOException ex){ 96 } 97 } 98 } 99 logger.info("DesktopAssetManager created."); 100 } 101 102 public void addClassLoader(ClassLoader loader){ 103 if(classLoaders == null) 104 classLoaders = Collections.synchronizedList(new ArrayList<ClassLoader>()); 105 synchronized(classLoaders) { 106 classLoaders.add(loader); 107 } 108 } 109 110 public void removeClassLoader(ClassLoader loader){ 111 if(classLoaders != null) synchronized(classLoaders) { 112 classLoaders.remove(loader); 113 } 114 } 115 116 public List<ClassLoader> getClassLoaders(){ 117 return classLoaders; 118 } 119 120 public void setAssetEventListener(AssetEventListener listener){ 121 eventListener = listener; 122 } 123 124 public void registerLoader(Class<? extends AssetLoader> loader, String ... extensions){ 125 handler.addLoader(loader, extensions); 126 if (logger.isLoggable(Level.FINER)){ 127 logger.log(Level.FINER, "Registered loader: {0} for extensions {1}", 128 new Object[]{loader.getSimpleName(), Arrays.toString(extensions)}); 129 } 130 } 131 132 public void registerLoader(String clsName, String ... extensions){ 133 Class<? extends AssetLoader> clazz = null; 134 try{ 135 clazz = (Class<? extends AssetLoader>) Class.forName(clsName); 136 }catch (ClassNotFoundException ex){ 137 logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex); 138 }catch (NoClassDefFoundError ex){ 139 logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex); 140 } 141 if (clazz != null){ 142 registerLoader(clazz, extensions); 143 } 144 } 145 146 public void registerLocator(String rootPath, Class<? extends AssetLocator> locatorClass){ 147 handler.addLocator(locatorClass, rootPath); 148 if (logger.isLoggable(Level.FINER)){ 149 logger.log(Level.FINER, "Registered locator: {0}", 150 locatorClass.getSimpleName()); 151 } 152 } 153 154 public void registerLocator(String rootPath, String clsName){ 155 Class<? extends AssetLocator> clazz = null; 156 try{ 157 clazz = (Class<? extends AssetLocator>) Class.forName(clsName); 158 }catch (ClassNotFoundException ex){ 159 logger.log(Level.WARNING, "Failed to find locator: "+clsName, ex); 160 }catch (NoClassDefFoundError ex){ 161 logger.log(Level.WARNING, "Failed to find loader: "+clsName, ex); 162 } 163 if (clazz != null){ 164 registerLocator(rootPath, clazz); 165 } 166 } 167 168 public void unregisterLocator(String rootPath, Class<? extends AssetLocator> clazz){ 169 handler.removeLocator(clazz, rootPath); 170 if (logger.isLoggable(Level.FINER)){ 171 logger.log(Level.FINER, "Unregistered locator: {0}", 172 clazz.getSimpleName()); 173 } 174 } 175 176 public void clearCache(){ 177 cache.deleteAllAssets(); 178 } 179 180 /** 181 * Delete an asset from the cache, returns true if it was deleted 182 * successfully. 183 * <br/><br/> 184 * <font color="red">Thread-safe.</font> 185 */ 186 public boolean deleteFromCache(AssetKey key){ 187 return cache.deleteFromCache(key); 188 } 189 190 /** 191 * Adds a resource to the cache. 192 * <br/><br/> 193 * <font color="red">Thread-safe.</font> 194 */ 195 public void addToCache(AssetKey key, Object asset){ 196 cache.addToCache(key, asset); 197 } 198 199 public AssetInfo locateAsset(AssetKey<?> key){ 200 if (handler.getLocatorCount() == 0){ 201 logger.warning("There are no locators currently"+ 202 " registered. Use AssetManager."+ 203 "registerLocator() to register a"+ 204 " locator."); 205 return null; 206 } 207 208 AssetInfo info = handler.tryLocate(key); 209 if (info == null){ 210 logger.log(Level.WARNING, "Cannot locate resource: {0}", key); 211 } 212 213 return info; 214 } 215 216 /** 217 * <font color="red">Thread-safe.</font> 218 * 219 * @param <T> 220 * @param key 221 * @return 222 */ 223 public <T> T loadAsset(AssetKey<T> key){ 224 if (key == null) 225 throw new IllegalArgumentException("key cannot be null"); 226 227 if (eventListener != null) 228 eventListener.assetRequested(key); 229 230 AssetKey smartKey = null; 231 Object o = null; 232 if (key.shouldCache()){ 233 if (key.useSmartCache()){ 234 SmartAssetInfo smartInfo = cache.getFromSmartCache(key); 235 if (smartInfo != null){ 236 smartKey = smartInfo.smartKey.get(); 237 if (smartKey != null){ 238 o = smartInfo.asset; 239 } 240 } 241 }else{ 242 o = cache.getFromCache(key); 243 } 244 } 245 if (o == null){ 246 AssetLoader loader = handler.aquireLoader(key); 247 if (loader == null){ 248 throw new IllegalStateException("No loader registered for type \"" + 249 key.getExtension() + "\""); 250 } 251 252 if (handler.getLocatorCount() == 0){ 253 throw new IllegalStateException("There are no locators currently"+ 254 " registered. Use AssetManager."+ 255 "registerLocator() to register a"+ 256 " locator."); 257 } 258 259 AssetInfo info = handler.tryLocate(key); 260 if (info == null){ 261 if (handler.getParentKey() != null && eventListener != null){ 262 // Inform event listener that an asset has failed to load. 263 // If the parent AssetLoader chooses not to propagate 264 // the exception, this is the only means of finding 265 // that something went wrong. 266 eventListener.assetDependencyNotFound(handler.getParentKey(), key); 267 } 268 throw new AssetNotFoundException(key.toString()); 269 } 270 271 try { 272 handler.establishParentKey(key); 273 o = loader.load(info); 274 } catch (IOException ex) { 275 throw new AssetLoadException("An exception has occured while loading asset: " + key, ex); 276 } finally { 277 handler.releaseParentKey(key); 278 } 279 if (o == null){ 280 throw new AssetLoadException("Error occured while loading asset \"" + key + "\" using" + loader.getClass().getSimpleName()); 281 }else{ 282 if (logger.isLoggable(Level.FINER)){ 283 logger.log(Level.FINER, "Loaded {0} with {1}", 284 new Object[]{key, loader.getClass().getSimpleName()}); 285 } 286 287 // do processing on asset before caching 288 o = key.postProcess(o); 289 290 if (key.shouldCache()) 291 cache.addToCache(key, o); 292 293 if (eventListener != null) 294 eventListener.assetLoaded(key); 295 } 296 } 297 298 // object o is the asset 299 // create an instance for user 300 T clone = (T) key.createClonedInstance(o); 301 302 if (key.useSmartCache()){ 303 if (smartKey != null){ 304 // smart asset was already cached, use original key 305 ((Asset)clone).setKey(smartKey); 306 }else{ 307 // smart asset was cached on this call, use our key 308 ((Asset)clone).setKey(key); 309 } 310 } 311 312 return clone; 313 } 314 315 public Object loadAsset(String name){ 316 return loadAsset(new AssetKey(name)); 317 } 318 319 /** 320 * Loads a texture. 321 * 322 * @return 323 */ 324 public Texture loadTexture(TextureKey key){ 325 return (Texture) loadAsset(key); 326 } 327 328 public Material loadMaterial(String name){ 329 return (Material) loadAsset(new MaterialKey(name)); 330 } 331 332 /** 333 * Loads a texture. 334 * 335 * @param name 336 * @param generateMipmaps Enable if applying texture to 3D objects, disable 337 * for GUI/HUD elements. 338 * @return 339 */ 340 public Texture loadTexture(String name, boolean generateMipmaps){ 341 TextureKey key = new TextureKey(name, true); 342 key.setGenerateMips(generateMipmaps); 343 key.setAsCube(false); 344 return loadTexture(key); 345 } 346 347 public Texture loadTexture(String name, boolean generateMipmaps, boolean flipY, boolean asCube, int aniso){ 348 TextureKey key = new TextureKey(name, flipY); 349 key.setGenerateMips(generateMipmaps); 350 key.setAsCube(asCube); 351 key.setAnisotropy(aniso); 352 return loadTexture(key); 353 } 354 355 public Texture loadTexture(String name){ 356 return loadTexture(name, true); 357 } 358 359 public AudioData loadAudio(AudioKey key){ 360 return (AudioData) loadAsset(key); 361 } 362 363 public AudioData loadAudio(String name){ 364 return loadAudio(new AudioKey(name, false)); 365 } 366 367 /** 368 * Loads a bitmap font with the given name. 369 * 370 * @param name 371 * @return 372 */ 373 public BitmapFont loadFont(String name){ 374 return (BitmapFont) loadAsset(new AssetKey(name)); 375 } 376 377 public InputStream loadGLSLLibrary(AssetKey key){ 378 return (InputStream) loadAsset(key); 379 } 380 381 /** 382 * Load a vertex/fragment shader combo. 383 * 384 * @param key 385 * @return 386 */ 387 public Shader loadShader(ShaderKey key){ 388 // cache abuse in method 389 // that doesn't use loaders/locators 390 Shader s = (Shader) cache.getFromCache(key); 391 if (s == null){ 392 String vertName = key.getVertName(); 393 String fragName = key.getFragName(); 394 395 String vertSource = (String) loadAsset(new AssetKey(vertName)); 396 String fragSource = (String) loadAsset(new AssetKey(fragName)); 397 398 s = new Shader(key.getLanguage()); 399 s.addSource(Shader.ShaderType.Vertex, vertName, vertSource, key.getDefines().getCompiled()); 400 s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled()); 401 402 cache.addToCache(key, s); 403 } 404 return s; 405 } 406 407 public Spatial loadModel(ModelKey key){ 408 return (Spatial) loadAsset(key); 409 } 410 411 /** 412 * Load a model. 413 * 414 * @param name 415 * @return 416 */ 417 public Spatial loadModel(String name){ 418 return loadModel(new ModelKey(name)); 419 } 420 421} 422