/* * Copyright (c) 2009-2012 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.scene.plugins.blender; import java.io.IOException; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.animation.Skeleton; import com.jme3.asset.AssetManager; import com.jme3.asset.BlenderKey; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.scene.plugins.blender.animations.BoneContext; import com.jme3.scene.plugins.blender.animations.Ipo; import com.jme3.scene.plugins.blender.constraints.Constraint; import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.DnaBlockData; import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.materials.MaterialContext; import com.jme3.scene.plugins.blender.meshes.MeshContext; import com.jme3.scene.plugins.blender.modifiers.Modifier; import com.jme3.scene.plugins.ogre.AnimData; /** * The class that stores temporary data and manages it during loading the belnd * file. This class is intended to be used in a single loading thread. It holds * the state of loading operations. * * @author Marcin Roguski (Kaelthas) */ public class BlenderContext { private static final Logger LOGGER = Logger.getLogger(BlenderContext.class.getName()); /** The blender file version. */ private int blenderVersion; /** The blender key. */ private BlenderKey blenderKey; /** The header of the file block. */ private DnaBlockData dnaBlockData; /** The input stream of the blend file. */ private BlenderInputStream inputStream; /** The asset manager. */ private AssetManager assetManager; /** * A map containing the file block headers. The key is the old pointer * address. */ private Map fileBlockHeadersByOma = new HashMap(); /** A map containing the file block headers. The key is the block code. */ private Map> fileBlockHeadersByCode = new HashMap>(); /** * This map stores the loaded features by their old memory address. The * first object in the value table is the loaded structure and the second - * the structure already converted into proper data. */ private Map loadedFeatures = new HashMap(); /** * This map stores the loaded features by their name. Only features with ID * structure can be stored here. The first object in the value table is the * loaded structure and the second - the structure already converted into * proper data. */ private Map loadedFeaturesByName = new HashMap(); /** A stack that hold the parent structure of currently loaded feature. */ private Stack parentStack = new Stack(); /** * A map storing loaded ipos. The key is the ipo's owner old memory address * and the value is the ipo. */ private Map loadedIpos = new HashMap(); /** A list of modifiers for the specified object. */ protected Map> modifiers = new HashMap>(); /** A list of constraints for the specified object. */ protected Map> constraints = new HashMap>(); /** Anim data loaded for features. */ private Map animData = new HashMap(); /** Loaded skeletons. */ private Map skeletons = new HashMap(); /** A map of mesh contexts. */ protected Map meshContexts = new HashMap(); /** A map of bone contexts. */ protected Map boneContexts = new HashMap(); /** A map of material contexts. */ protected Map materialContexts = new HashMap(); /** A map og helpers that perform loading. */ private Map helpers = new HashMap(); /** * This method sets the blender file version. * * @param blenderVersion * the blender file version */ public void setBlenderVersion(String blenderVersion) { this.blenderVersion = Integer.parseInt(blenderVersion); } /** * @return the blender file version */ public int getBlenderVersion() { return blenderVersion; } /** * This method sets the blender key. * * @param blenderKey * the blender key */ public void setBlenderKey(BlenderKey blenderKey) { this.blenderKey = blenderKey; } /** * This method returns the blender key. * * @return the blender key */ public BlenderKey getBlenderKey() { return blenderKey; } /** * This method sets the dna block data. * * @param dnaBlockData * the dna block data */ public void setBlockData(DnaBlockData dnaBlockData) { this.dnaBlockData = dnaBlockData; } /** * This method returns the dna block data. * * @return the dna block data */ public DnaBlockData getDnaBlockData() { return dnaBlockData; } /** * This method returns the asset manager. * * @return the asset manager */ public AssetManager getAssetManager() { return assetManager; } /** * This method sets the asset manager. * * @param assetManager * the asset manager */ public void setAssetManager(AssetManager assetManager) { this.assetManager = assetManager; } /** * This method returns the input stream of the blend file. * * @return the input stream of the blend file */ public BlenderInputStream getInputStream() { return inputStream; } /** * This method sets the input stream of the blend file. * * @param inputStream * the input stream of the blend file */ public void setInputStream(BlenderInputStream inputStream) { this.inputStream = inputStream; } /** * This method adds a file block header to the map. Its old memory address * is the key. * * @param oldMemoryAddress * the address of the block header * @param fileBlockHeader * the block header to store */ public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); List headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); if (headers == null) { headers = new ArrayList(); fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); } headers.add(fileBlockHeader); } /** * This method returns the block header of a given memory address. If the * header is not present then null is returned. * * @param oldMemoryAddress * the address of the block header * @return loaded header or null if it was not yet loaded */ public FileBlockHeader getFileBlock(Long oldMemoryAddress) { return fileBlockHeadersByOma.get(oldMemoryAddress); } /** * This method returns a list of file blocks' headers of a specified code. * * @param code * the code of file blocks * @return a list of file blocks' headers of a specified code */ public List getFileBlocks(Integer code) { return fileBlockHeadersByCode.get(code); } /** * This method clears the saved block headers stored in the features map. */ public void clearFileBlocks() { fileBlockHeadersByOma.clear(); fileBlockHeadersByCode.clear(); } /** * This method adds a helper instance to the helpers' map. * * @param * the type of the helper * @param clazz * helper's class definition * @param helper * the helper instance */ public void putHelper(Class clazz, AbstractBlenderHelper helper) { helpers.put(clazz.getSimpleName(), helper); } @SuppressWarnings("unchecked") public T getHelper(Class clazz) { return (T) helpers.get(clazz.getSimpleName()); } /** * This method adds a loaded feature to the map. The key is its unique old * memory address. * * @param oldMemoryAddress * the address of the feature * @param featureName * the name of the feature * @param structure * the filled structure of the feature * @param feature * the feature we want to store */ public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) { if (oldMemoryAddress == null || structure == null || feature == null) { throw new IllegalArgumentException("One of the given arguments is null!"); } Object[] storedData = new Object[] { structure, feature }; loadedFeatures.put(oldMemoryAddress, storedData); if (featureName != null) { loadedFeaturesByName.put(featureName, storedData); } } /** * This method returns the feature of a given memory address. If the feature * is not yet loaded then null is returned. * * @param oldMemoryAddress * the address of the feature * @param loadedFeatureDataType * the type of data we want to retreive it can be either filled * structure or already converted feature * @return loaded feature or null if it was not yet loaded */ public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) { Object[] result = loadedFeatures.get(oldMemoryAddress); if (result != null) { return result[loadedFeatureDataType.getIndex()]; } return null; } /** * This method returns the feature of a given name. If the feature is not * yet loaded then null is returned. * * @param featureName * the name of the feature * @param loadedFeatureDataType * the type of data we want to retreive it can be either filled * structure or already converted feature * @return loaded feature or null if it was not yet loaded */ public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) { Object[] result = loadedFeaturesByName.get(featureName); if (result != null) { return result[loadedFeatureDataType.getIndex()]; } return null; } /** * This method clears the saved features stored in the features map. */ public void clearLoadedFeatures() { loadedFeatures.clear(); } /** * This method adds the structure to the parent stack. * * @param parent * the structure to be added to the stack */ public void pushParent(Structure parent) { parentStack.push(parent); } /** * This method removes the structure from the top of the parent's stack. * * @return the structure that was removed from the stack */ public Structure popParent() { try { return parentStack.pop(); } catch (EmptyStackException e) { return null; } } /** * This method retreives the structure at the top of the parent's stack but * does not remove it. * * @return the structure from the top of the stack */ public Structure peekParent() { try { return parentStack.peek(); } catch (EmptyStackException e) { return null; } } /** * This method adds new ipo curve for the feature. * * @param ownerOMA * the OMA of blender feature that owns the ipo * @param ipo * the ipo to be added */ public void addIpo(Long ownerOMA, Ipo ipo) { loadedIpos.put(ownerOMA, ipo); } /** * This method removes the ipo curve from the feature. * * @param ownerOma * the OMA of blender feature that owns the ipo */ public Ipo removeIpo(Long ownerOma) { return loadedIpos.remove(ownerOma); } /** * This method returns the ipo curve of the feature. * * @param ownerOMA * the OMA of blender feature that owns the ipo */ public Ipo getIpo(Long ownerOMA) { return loadedIpos.get(ownerOMA); } /** * This method adds a new modifier to the list. * * @param ownerOMA * the owner's old memory address * @param modifier * the object's modifier */ public void addModifier(Long ownerOMA, Modifier modifier) { List objectModifiers = this.modifiers.get(ownerOMA); if (objectModifiers == null) { objectModifiers = new ArrayList(); this.modifiers.put(ownerOMA, objectModifiers); } objectModifiers.add(modifier); } /** * This method returns modifiers for the object specified by its old memory * address and the modifier type. If no modifiers are found - empty list is * returned. If the type is null - all modifiers for the object are * returned. * * @param objectOMA * object's old memory address * @param type * the type of the modifier * @return the list of object's modifiers */ public List getModifiers(Long objectOMA, String type) { List result = new ArrayList(); List readModifiers = modifiers.get(objectOMA); if (readModifiers != null && readModifiers.size() > 0) { for (Modifier modifier : readModifiers) { if (type == null || type.isEmpty() || modifier.getType().equals(type)) { result.add(modifier); } } } return result; } /** * This method adds a new modifier to the list. * * @param ownerOMA * the owner's old memory address * @param constraints * the object's constraints */ public void addConstraints(Long ownerOMA, List constraints) { List objectConstraints = this.constraints.get(ownerOMA); if (objectConstraints == null) { objectConstraints = new ArrayList(); this.constraints.put(ownerOMA, objectConstraints); } objectConstraints.addAll(constraints); } /** * This method returns constraints for the object specified by its old * memory address. If no modifiers are found - null is returned. * * @param objectOMA * object's old memory address * @return the list of object's modifiers or null */ public List getConstraints(Long objectOMA) { return objectOMA == null ? null : constraints.get(objectOMA); } /** * This method sets the anim data for the specified OMA of its owner. * * @param ownerOMA * the owner's old memory address * @param animData * the animation data for the feature specified by ownerOMA */ public void setAnimData(Long ownerOMA, AnimData animData) { this.animData.put(ownerOMA, animData); } /** * This method returns the animation data for the specified owner. * * @param ownerOMA * the old memory address of the animation data owner * @return the animation data or null if none exists */ public AnimData getAnimData(Long ownerOMA) { return this.animData.get(ownerOMA); } /** * This method sets the skeleton for the specified OMA of its owner. * * @param skeletonOMA * the skeleton's old memory address * @param skeleton * the skeleton specified by the given OMA */ public void setSkeleton(Long skeletonOMA, Skeleton skeleton) { this.skeletons.put(skeletonOMA, skeleton); } /** * This method returns the skeleton for the specified OMA of its owner. * * @param skeletonOMA * the skeleton's old memory address * @return the skeleton specified by the given OMA */ public Skeleton getSkeleton(Long skeletonOMA) { return this.skeletons.get(skeletonOMA); } /** * This method sets the mesh context for the given mesh old memory address. * If the context is already set it will be replaced. * * @param meshOMA * the mesh's old memory address * @param meshContext * the mesh's context */ public void setMeshContext(Long meshOMA, MeshContext meshContext) { this.meshContexts.put(meshOMA, meshContext); } /** * This method returns the mesh context for the given mesh old memory * address. If no context exists then null is returned. * * @param meshOMA * the mesh's old memory address * @return mesh's context */ public MeshContext getMeshContext(Long meshOMA) { return this.meshContexts.get(meshOMA); } /** * This method sets the bone context for the given bone old memory address. * If the context is already set it will be replaced. * * @param boneOMA * the bone's old memory address * @param boneContext * the bones's context */ public void setBoneContext(Long boneOMA, BoneContext boneContext) { this.boneContexts.put(boneOMA, boneContext); } /** * This method returns the bone context for the given bone old memory * address. If no context exists then null is returned. * * @param boneOMA * the bone's old memory address * @return bone's context */ public BoneContext getBoneContext(Long boneOMA) { return boneContexts.get(boneOMA); } /** * This method sets the material context for the given material. If the * context is already set it will be replaced. * * @param material * the material * @param materialContext * the material's context */ public void setMaterialContext(Material material, MaterialContext materialContext) { this.materialContexts.put(material, materialContext); } /** * This method returns the material context for the given material. If no * context exists then null is returned. * * @param material * the material * @return material's context */ public MaterialContext getMaterialContext(Material material) { return materialContexts.get(material); } /** * This metod returns the default material. * * @return the default material */ public synchronized Material getDefaultMaterial() { if (blenderKey.getDefaultMaterial() == null) { Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); defaultMaterial.setColor("Color", ColorRGBA.DarkGray); blenderKey.setDefaultMaterial(defaultMaterial); } return blenderKey.getDefaultMaterial(); } public void dispose() { try { inputStream.close(); } catch (IOException e) { LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); } loadedFeatures.clear(); loadedFeaturesByName.clear(); } /** * This enum defines what loaded data type user wants to retreive. It can be * either filled structure or already converted data. * * @author Marcin Roguski */ public static enum LoadedFeatureDataType { LOADED_STRUCTURE(0), LOADED_FEATURE(1); private int index; private LoadedFeatureDataType(int index) { this.index = index; } public int getIndex() { return index; } } }