/* | |
* 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<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>(); | |
/** A map containing the file block headers. The key is the block code. */ | |
private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>(); | |
/** | |
* 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<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>(); | |
/** | |
* 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<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>(); | |
/** A stack that hold the parent structure of currently loaded feature. */ | |
private Stack<Structure> parentStack = new Stack<Structure>(); | |
/** | |
* A map storing loaded ipos. The key is the ipo's owner old memory address | |
* and the value is the ipo. | |
*/ | |
private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>(); | |
/** A list of modifiers for the specified object. */ | |
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>(); | |
/** A list of constraints for the specified object. */ | |
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>(); | |
/** Anim data loaded for features. */ | |
private Map<Long, AnimData> animData = new HashMap<Long, AnimData>(); | |
/** Loaded skeletons. */ | |
private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>(); | |
/** A map of mesh contexts. */ | |
protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>(); | |
/** A map of bone contexts. */ | |
protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>(); | |
/** A map of material contexts. */ | |
protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>(); | |
/** A map og helpers that perform loading. */ | |
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>(); | |
/** | |
* 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<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); | |
if (headers == null) { | |
headers = new ArrayList<FileBlockHeader>(); | |
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<FileBlockHeader> 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 <T> | |
* the type of the helper | |
* @param clazz | |
* helper's class definition | |
* @param helper | |
* the helper instance | |
*/ | |
public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) { | |
helpers.put(clazz.getSimpleName(), helper); | |
} | |
@SuppressWarnings("unchecked") | |
public <T> 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<Modifier> objectModifiers = this.modifiers.get(ownerOMA); | |
if (objectModifiers == null) { | |
objectModifiers = new ArrayList<Modifier>(); | |
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<Modifier> getModifiers(Long objectOMA, String type) { | |
List<Modifier> result = new ArrayList<Modifier>(); | |
List<Modifier> 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<Constraint> constraints) { | |
List<Constraint> objectConstraints = this.constraints.get(ownerOMA); | |
if (objectConstraints == null) { | |
objectConstraints = new ArrayList<Constraint>(); | |
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 - <b>null</b> is returned. | |
* | |
* @param objectOMA | |
* object's old memory address | |
* @return the list of object's modifiers or null | |
*/ | |
public List<Constraint> 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 <b>null</b> 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 <b>null</b> 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 <b>null</b> 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; | |
} | |
} | |
} |