Feature: added support for loading assets linked from external blender

files.
experimental
jmekaelthas 10 years ago
parent a37f38a412
commit eb767e7580
  1. 312
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  2. 138
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  3. 91
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  4. 263
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  5. 71
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java
  6. 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java
  7. 15
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
  8. 87
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java
  9. 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java
  10. 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java
  11. 25
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  12. 7
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
  13. 13
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  14. 70
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
  15. 90
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  16. 18
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
  17. 7
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
  18. 221
      jme3-core/src/main/java/com/jme3/scene/UserData.java

@ -32,36 +32,19 @@
package com.jme3.asset; package com.jme3.asset;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import com.jme3.animation.Animation;
import com.jme3.bounding.BoundingVolume;
import com.jme3.collision.Collidable;
import com.jme3.collision.CollisionResults;
import com.jme3.collision.UnsupportedCollisionException;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode; import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.scene.CameraNode;
import com.jme3.scene.LightNode;
import com.jme3.scene.Node;
import com.jme3.scene.SceneGraphVisitor;
import com.jme3.scene.Spatial;
import com.jme3.texture.Texture;
/** /**
* Blender key. Contains path of the blender file and its loading properties. * Blender key. Contains path of the blender file and its loading properties.
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class BlenderKey extends ModelKey { public class BlenderKey extends ModelKey {
protected static final int DEFAULT_FPS = 25; protected static final int DEFAULT_FPS = 25;
/** /**
* FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
@ -72,7 +55,7 @@ public class BlenderKey extends ModelKey {
* This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded. * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
*/ */
protected int featuresToLoad = FeaturesToLoad.ALL; protected int featuresToLoad = FeaturesToLoad.ALL;
/** This variable determines if assets that are not linked to the objects should be loaded. */ /** The variable that tells if content of the file (along with data unlinked to any feature on the scene) should be stored as 'user data' in the result spatial. */
protected boolean loadUnlinkedAssets; protected boolean loadUnlinkedAssets;
/** The root path for all the assets. */ /** The root path for all the assets. */
protected String assetRootPath; protected String assetRootPath;
@ -268,6 +251,7 @@ public class BlenderKey extends ModelKey {
* @param featuresToLoad * @param featuresToLoad
* bitwise flag of FeaturesToLoad interface values * bitwise flag of FeaturesToLoad interface values
*/ */
@Deprecated
public void includeInLoading(int featuresToLoad) { public void includeInLoading(int featuresToLoad) {
this.featuresToLoad |= featuresToLoad; this.featuresToLoad |= featuresToLoad;
} }
@ -277,10 +261,12 @@ public class BlenderKey extends ModelKey {
* @param featuresNotToLoad * @param featuresNotToLoad
* bitwise flag of FeaturesToLoad interface values * bitwise flag of FeaturesToLoad interface values
*/ */
@Deprecated
public void excludeFromLoading(int featuresNotToLoad) { public void excludeFromLoading(int featuresNotToLoad) {
featuresToLoad &= ~featuresNotToLoad; featuresToLoad &= ~featuresNotToLoad;
} }
@Deprecated
public boolean shouldLoad(int featureToLoad) { public boolean shouldLoad(int featureToLoad) {
return (featuresToLoad & featureToLoad) != 0; return (featuresToLoad & featureToLoad) != 0;
} }
@ -290,6 +276,7 @@ public class BlenderKey extends ModelKey {
* the blender file loader. * the blender file loader.
* @return features that will be loaded by the blender file loader * @return features that will be loaded by the blender file loader
*/ */
@Deprecated
public int getFeaturesToLoad() { public int getFeaturesToLoad() {
return featuresToLoad; return featuresToLoad;
} }
@ -317,15 +304,6 @@ public class BlenderKey extends ModelKey {
this.loadUnlinkedAssets = loadUnlinkedAssets; this.loadUnlinkedAssets = loadUnlinkedAssets;
} }
/**
* This method creates an object where loading results will be stores. Only those features will be allowed to store
* that were specified by features-to-load flag.
* @return an object to store loading results
*/
public LoadingResults prepareLoadingResults() {
return new LoadingResults(featuresToLoad);
}
/** /**
* This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
* is up axis. * is up axis.
@ -699,8 +677,11 @@ public class BlenderKey extends ModelKey {
/** /**
* This interface describes the features of the scene that are to be loaded. * This interface describes the features of the scene that are to be loaded.
* @deprecated this interface is deprecated and is not used anymore; to ensure the loading models consistency
* everything must be loaded because in blender one feature might depend on another
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
@Deprecated
public static interface FeaturesToLoad { public static interface FeaturesToLoad {
int SCENES = 0x0000FFFF; int SCENES = 0x0000FFFF;
@ -745,281 +726,4 @@ public class BlenderKey extends ModelKey {
*/ */
ALL_NAMES_MATCH; ALL_NAMES_MATCH;
} }
/**
* This class holds the loading results according to the given loading flag.
* @author Marcin Roguski (Kaelthas)
*/
public static class LoadingResults extends Spatial {
/** Bitwise mask of features that are to be loaded. */
private final int featuresToLoad;
/** The scenes from the file. */
private List<Node> scenes;
/** Objects from all scenes. */
private List<Node> objects;
/** Materials from all objects. */
private List<Material> materials;
/** Textures from all objects. */
private List<Texture> textures;
/** Animations of all objects. */
private List<Animation> animations;
/** All cameras from the file. */
private List<CameraNode> cameras;
/** All lights from the file. */
private List<LightNode> lights;
/** Loaded sky. */
private Spatial sky;
/** Scene filters (ie. FOG). */
private List<Filter> filters;
/**
* The background color of the render loaded from the horizon color of the world. If no world is used than the gray color
* is set to default (as in blender editor.
*/
private ColorRGBA backgroundColor = ColorRGBA.Gray;
/**
* Private constructor prevents users to create an instance of this class from outside the
* @param featuresToLoad
* bitwise mask of features that are to be loaded
* @see FeaturesToLoad FeaturesToLoad
*/
private LoadingResults(int featuresToLoad) {
this.featuresToLoad = featuresToLoad;
if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) {
scenes = new ArrayList<Node>();
}
if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
objects = new ArrayList<Node>();
if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
materials = new ArrayList<Material>();
if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
textures = new ArrayList<Texture>();
}
}
if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
animations = new ArrayList<Animation>();
}
}
if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
cameras = new ArrayList<CameraNode>();
}
if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
lights = new ArrayList<LightNode>();
}
}
/**
* This method returns a bitwise flag describing what features of the blend file will be included in the result.
* @return bitwise mask of features that are to be loaded
* @see FeaturesToLoad FeaturesToLoad
*/
public int getLoadedFeatures() {
return featuresToLoad;
}
/**
* This method adds a scene to the result set.
* @param scene
* scene to be added to the result set
*/
public void addScene(Node scene) {
if (scenes != null) {
scenes.add(scene);
}
}
/**
* This method adds an object to the result set.
* @param object
* object to be added to the result set
*/
public void addObject(Node object) {
if (objects != null) {
objects.add(object);
}
}
/**
* This method adds a material to the result set.
* @param material
* material to be added to the result set
*/
public void addMaterial(Material material) {
if (materials != null) {
materials.add(material);
}
}
/**
* This method adds a texture to the result set.
* @param texture
* texture to be added to the result set
*/
public void addTexture(Texture texture) {
if (textures != null) {
textures.add(texture);
}
}
/**
* This method adds a camera to the result set.
* @param camera
* camera to be added to the result set
*/
public void addCamera(CameraNode camera) {
if (cameras != null) {
cameras.add(camera);
}
}
/**
* This method adds a light to the result set.
* @param light
* light to be added to the result set
*/
public void addLight(LightNode light) {
if (lights != null) {
lights.add(light);
}
}
/**
* This method sets the sky of the scene. Only one sky can be set.
* @param sky
* the sky to be set
*/
public void setSky(Spatial sky) {
this.sky = sky;
}
/**
* This method adds a scene filter. Filters are used to load FOG or other
* scene effects that blender can define.
* @param filter
* the filter to be added
*/
public void addFilter(Filter filter) {
if (filter != null) {
if (filters == null) {
filters = new ArrayList<Filter>(5);
}
filters.add(filter);
}
}
/**
* @param backgroundColor
* the background color
*/
public void setBackgroundColor(ColorRGBA backgroundColor) {
this.backgroundColor = backgroundColor;
}
/**
* @return all loaded scenes
*/
public List<Node> getScenes() {
return scenes;
}
/**
* @return all loaded objects
*/
public List<Node> getObjects() {
return objects;
}
/**
* @return all loaded materials
*/
public List<Material> getMaterials() {
return materials;
}
/**
* @return all loaded textures
*/
public List<Texture> getTextures() {
return textures;
}
/**
* @return all loaded animations
*/
public List<Animation> getAnimations() {
return animations;
}
/**
* @return all loaded cameras
*/
public List<CameraNode> getCameras() {
return cameras;
}
/**
* @return all loaded lights
*/
public List<LightNode> getLights() {
return lights;
}
/**
* @return the scene's sky
*/
public Spatial getSky() {
return sky;
}
/**
* @return scene filters
*/
public List<Filter> getFilters() {
return filters;
}
/**
* @return the background color
*/
public ColorRGBA getBackgroundColor() {
return backgroundColor;
}
@Override
public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
return 0;
}
@Override
public void updateModelBound() {
}
@Override
public void setModelBound(BoundingVolume modelBound) {
}
@Override
public int getVertexCount() {
return 0;
}
@Override
public int getTriangleCount() {
return 0;
}
@Override
public Spatial deepClone() {
return null;
}
@Override
public void depthFirstTraversal(SceneGraphVisitor visitor) {
}
@Override
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
}
}
} }

@ -31,17 +31,34 @@
*/ */
package com.jme3.scene.plugins.blender; package com.jme3.scene.plugins.blender;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetNotFoundException;
import com.jme3.asset.BlenderKey;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.light.Light;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.scene.plugins.blender.objects.Properties; import com.jme3.scene.plugins.blender.objects.Properties;
import com.jme3.texture.Texture;
/** /**
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can * A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
@ -49,14 +66,16 @@ import com.jme3.scene.plugins.blender.objects.Properties;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public abstract class AbstractBlenderHelper { public abstract class AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName());
/** The blender context. */ /** The blender context. */
protected BlenderContext blenderContext; protected BlenderContext blenderContext;
/** The version of the blend file. */ /** The version of the blend file. */
protected final int blenderVersion; protected final int blenderVersion;
/** This variable indicates if the Y asxis is the UP axis or not. */ /** This variable indicates if the Y asxis is the UP axis or not. */
protected boolean fixUpAxis; protected boolean fixUpAxis;
/** Quaternion used to rotate data when Y is up axis. */ /** Quaternion used to rotate data when Y is up axis. */
protected Quaternion upAxisRotationQuaternion; protected Quaternion upAxisRotationQuaternion;
/** /**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
@ -129,4 +148,115 @@ public abstract class AbstractBlenderHelper {
} }
} }
} }
/**
* The method loads library of a given ID from linked blender file.
* @param id
* the ID of the linked feature (it contains its name and blender path)
* @return loaded feature or null if none was found
* @throws BlenderFileException
* and exception is throw when problems with reading a blend file occur
*/
@SuppressWarnings("unchecked")
protected Object loadLibrary(Structure id) throws BlenderFileException {
Pointer pLib = (Pointer) id.getFieldValue("lib");
if (pLib.isNotNull()) {
String fullName = id.getFieldValue("name").toString();// we need full name with the prefix
String nameOfFeatureToLoad = id.getName();
Structure library = pLib.fetchData().get(0);
String path = library.getFieldValue("filepath").toString();
if (!blenderContext.getLinkedFeatures().keySet().contains(path)) {
File file = new File(path);
List<String> pathsToCheck = new ArrayList<String>();
String currentPath = file.getName();
do {
pathsToCheck.add(currentPath);
file = file.getParentFile();
if (file != null) {
currentPath = file.getName() + '/' + currentPath;
}
} while (file != null);
Spatial loadedAsset = null;
BlenderKey blenderKey = null;
for (String p : pathsToCheck) {
blenderKey = new BlenderKey(p);
blenderKey.setLoadUnlinkedAssets(true);
try {
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
break;// break if no exception was thrown
} catch (AssetNotFoundException e) {
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p);
}
}
if (loadedAsset != null) {
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
List<Node> scenes = (List<Node>) entry.getValue().get("scenes");
for (Node scene : scenes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene);
}
List<Node> objects = (List<Node>) entry.getValue().get("objects");
for (Node object : objects) {
blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object);
}
List<TemporalMesh> meshes = (List<TemporalMesh>) entry.getValue().get("meshes");
for (TemporalMesh mesh : meshes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh);
}
List<MaterialContext> materials = (List<MaterialContext>) entry.getValue().get("materials");
for (MaterialContext materialContext : materials) {
blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext);
}
List<Texture> textures = (List<Texture>) entry.getValue().get("textures");
for (Texture texture : textures) {
blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture);
}
List<Texture> images = (List<Texture>) entry.getValue().get("images");
for (Texture image : images) {
blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image);
}
List<Animation> animations = (List<Animation>) entry.getValue().get("animations");
for (Animation animation : animations) {
blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation);
}
List<Camera> cameras = (List<Camera>) entry.getValue().get("cameras");
for (Camera camera : cameras) {
blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera);
}
List<Light> lights = (List<Light>) entry.getValue().get("lights");
for (Light light : lights) {
blenderContext.addLinkedFeature(linkedDataFilePath, "LA" + light.getName(), light);
}
Spatial sky = (Spatial) entry.getValue().get("sky");
if (sky != null) {
blenderContext.addLinkedFeature(linkedDataFilePath, sky.getName(), sky);
}
List<Filter> filters = (List<Filter>) entry.getValue().get("filters");
for (Filter filter : filters) {
blenderContext.addLinkedFeature(linkedDataFilePath, filter.getName(), filter);
}
}
} else {
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);
}
}
Object result = blenderContext.getLinkedFeature(path, fullName);
if (result == null) {
LOGGER.log(Level.WARNING, "Could NOT find asset named {0} in the library of path: {1}.", new Object[] { nameOfFeatureToLoad, path });
} else {
blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.STRUCTURE, id);
blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.FEATURE, result);
}
return result;
} else {
LOGGER.warning("Library link points to nothing!");
}
return null;
}
} }

@ -53,6 +53,7 @@ import com.jme3.scene.plugins.blender.constraints.Constraint;
import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.DnaBlockData; import com.jme3.scene.plugins.blender.file.DnaBlockData;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
/** /**
@ -64,49 +65,51 @@ import com.jme3.scene.plugins.blender.file.Structure;
*/ */
public class BlenderContext { public class BlenderContext {
/** The blender file version. */ /** The blender file version. */
private int blenderVersion; private int blenderVersion;
/** The blender key. */ /** The blender key. */
private BlenderKey blenderKey; private BlenderKey blenderKey;
/** The header of the file block. */ /** The header of the file block. */
private DnaBlockData dnaBlockData; private DnaBlockData dnaBlockData;
/** The scene structure. */ /** The scene structure. */
private Structure sceneStructure; private Structure sceneStructure;
/** The input stream of the blend file. */ /** The input stream of the blend file. */
private BlenderInputStream inputStream; private BlenderInputStream inputStream;
/** The asset manager. */ /** The asset manager. */
private AssetManager assetManager; private AssetManager assetManager;
/** The blocks read from the file. */ /** The blocks read from the file. */
protected List<FileBlockHeader> blocks; protected List<FileBlockHeader> blocks;
/** /**
* A map containing the file block headers. The key is the old memory address. * A map containing the file block headers. The key is the old memory address.
*/ */
private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>(); private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
/** A map containing the file block headers. The key is the block code. */ /** A map containing the file block headers. The key is the block code. */
private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>(); private Map<BlockCode, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<BlockCode, List<FileBlockHeader>>();
/** /**
* This map stores the loaded features by their old memory address. The * 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 - * first object in the value table is the loaded structure and the second -
* the structure already converted into proper data. * the structure already converted into proper data.
*/ */
private Map<Long, Map<LoadedDataType, Object>> loadedFeatures = new HashMap<Long, Map<LoadedDataType, Object>>(); private Map<Long, Map<LoadedDataType, Object>> loadedFeatures = new HashMap<Long, Map<LoadedDataType, Object>>();
/** Features loaded from external blender files. The key is the file path and the value is a map between feature name and loaded feature. */
private Map<String, Map<String, Object>> linkedFeatures = new HashMap<String, Map<String, Object>>();
/** A stack that hold the parent structure of currently loaded feature. */ /** A stack that hold the parent structure of currently loaded feature. */
private Stack<Structure> parentStack = new Stack<Structure>(); private Stack<Structure> parentStack = new Stack<Structure>();
/** A list of constraints for the specified object. */ /** A list of constraints for the specified object. */
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>(); protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
/** Animations loaded for features. */ /** Animations loaded for features. */
private Map<Long, List<Animation>> animations = new HashMap<Long, List<Animation>>(); private Map<Long, List<Animation>> animations = new HashMap<Long, List<Animation>>();
/** Loaded skeletons. */ /** Loaded skeletons. */
private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>(); private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>();
/** A map between skeleton and node it modifies. */ /** A map between skeleton and node it modifies. */
private Map<Skeleton, Node> nodesWithSkeletons = new HashMap<Skeleton, Node>(); private Map<Skeleton, Node> nodesWithSkeletons = new HashMap<Skeleton, Node>();
/** A map of bone contexts. */ /** A map of bone contexts. */
protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>(); protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>();
/** A map og helpers that perform loading. */ /** A map og helpers that perform loading. */
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>(); private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
/** Markers used by loading classes to store some custom data. This is made to avoid putting this data into user properties. */ /** Markers used by loading classes to store some custom data. This is made to avoid putting this data into user properties. */
private Map<String, Map<Object, Object>> markers = new HashMap<String, Map<Object, Object>>(); private Map<String, Map<Object, Object>> markers = new HashMap<String, Map<Object, Object>>();
/** A map of blender actions. The key is the action name and the value is the action itself. */ /** A map of blender actions. The key is the action name and the value is the action itself. */
private Map<String, BlenderAction> actions = new HashMap<String, BlenderAction>(); private Map<String, BlenderAction> actions = new HashMap<String, BlenderAction>();
/** /**
* This method sets the blender file version. * This method sets the blender file version.
@ -231,10 +234,10 @@ public class BlenderContext {
*/ */
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) { if (headers == null) {
headers = new ArrayList<FileBlockHeader>(); headers = new ArrayList<FileBlockHeader>();
fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers);
} }
headers.add(fileBlockHeader); headers.add(fileBlockHeader);
} }
@ -258,7 +261,7 @@ public class BlenderContext {
* the code of file blocks * the code of file blocks
* @return a list of file blocks' headers of a specified code * @return a list of file blocks' headers of a specified code
*/ */
public List<FileBlockHeader> getFileBlocks(Integer code) { public List<FileBlockHeader> getFileBlocks(BlockCode code) {
return fileBlockHeadersByCode.get(code); return fileBlockHeadersByCode.get(code);
} }
@ -299,7 +302,7 @@ public class BlenderContext {
throw new IllegalArgumentException("One of the given arguments is null!"); throw new IllegalArgumentException("One of the given arguments is null!");
} }
Map<LoadedDataType, Object> map = loadedFeatures.get(oldMemoryAddress); Map<LoadedDataType, Object> map = loadedFeatures.get(oldMemoryAddress);
if(map == null) { if (map == null) {
map = new HashMap<BlenderContext.LoadedDataType, Object>(); map = new HashMap<BlenderContext.LoadedDataType, Object>();
loadedFeatures.put(oldMemoryAddress, map); loadedFeatures.put(oldMemoryAddress, map);
} }
@ -325,6 +328,48 @@ public class BlenderContext {
return null; return null;
} }
/**
* The method adds linked content to the blender context.
* @param blenderFilePath
* the path of linked blender file
* @param featureName
* the linked feature name
* @param feature
* the linked feature
*/
public void addLinkedFeature(String blenderFilePath, String featureName, Object feature) {
if (feature != null) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
if (linkedFeatures == null) {
linkedFeatures = new HashMap<String, Object>();
this.linkedFeatures.put(blenderFilePath, linkedFeatures);
}
if (!linkedFeatures.containsKey(featureName)) {
linkedFeatures.put(featureName, feature);
}
}
}
/**
* The method returns linked feature of a given name from the specified blender path.
* @param blenderFilePath
* the blender file path
* @param featureName
* the feature name we want to get
* @return linked feature or null if none was found
*/
public Object getLinkedFeature(String blenderFilePath, String featureName) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
return linkedFeatures != null ? linkedFeatures.get(featureName) : null;
}
/**
* @return all linked features for the current blend file
*/
public Map<String, Map<String, Object>> getLinkedFeatures() {
return linkedFeatures;
}
/** /**
* This method adds the structure to the parent stack. * This method adds the structure to the parent stack.
* *

@ -33,17 +33,21 @@ package com.jme3.scene.plugins.blender;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader; import com.jme3.asset.AssetLoader;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.asset.BlenderKey.FeaturesToLoad;
import com.jme3.asset.BlenderKey.LoadingResults;
import com.jme3.asset.ModelKey; import com.jme3.asset.ModelKey;
import com.jme3.light.Light; import com.jme3.light.Light;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode; import com.jme3.scene.CameraNode;
import com.jme3.scene.LightNode; import com.jme3.scene.LightNode;
import com.jme3.scene.Node; import com.jme3.scene.Node;
@ -55,16 +59,20 @@ import com.jme3.scene.plugins.blender.curves.CurvesHelper;
import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.landscape.LandscapeHelper; import com.jme3.scene.plugins.blender.landscape.LandscapeHelper;
import com.jme3.scene.plugins.blender.lights.LightHelper; import com.jme3.scene.plugins.blender.lights.LightHelper;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.scene.plugins.blender.meshes.MeshHelper; import com.jme3.scene.plugins.blender.meshes.MeshHelper;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.scene.plugins.blender.modifiers.ModifierHelper; import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
import com.jme3.scene.plugins.blender.objects.ObjectHelper; import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.scene.plugins.blender.particles.ParticlesHelper; import com.jme3.scene.plugins.blender.particles.ParticlesHelper;
import com.jme3.scene.plugins.blender.textures.TextureHelper; import com.jme3.scene.plugins.blender.textures.TextureHelper;
import com.jme3.texture.Texture;
/** /**
* This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures. * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures.
@ -83,72 +91,130 @@ public class BlenderLoader implements AssetLoader {
try { try {
this.setup(assetInfo); this.setup(assetInfo);
List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadingResults loadingResults = blenderKey.prepareLoadingResults();
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations(); animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blocks) { for (FileBlockHeader block : blocks) {
switch (block.getCode()) { switch (block.getCode()) {
case FileBlockHeader.BLOCK_OB00:// Object case BLOCK_OB00:
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Object object = objectHelper.toObject(block.getStructure(blenderContext), blenderContext); Node object = (Node) objectHelper.toObject(block.getStructure(blenderContext), blenderContext);
if (object instanceof LightNode) { if (LOGGER.isLoggable(Level.FINE)) {
loadingResults.addLight((LightNode) object); LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { object.getName(), object.getLocalTranslation().toString(), object.getParent() == null ? "null" : object.getParent().getName() });
} else if (object instanceof CameraNode) {
loadingResults.addCamera((CameraNode) object);
} else if (object instanceof Node) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
}
if (this.isRootObject(loadingResults, (Node) object)) {
loadingResults.addObject((Node) object);
}
} }
if (object.getParent() == null) {
loadedFeatures.objects.add(object);
}
if (object instanceof LightNode && ((LightNode) object).getLight() != null) {
loadedFeatures.lights.add(((LightNode) object).getLight());
} else if (object instanceof CameraNode && ((CameraNode) object).getCamera() != null) {
loadedFeatures.cameras.add(((CameraNode) object).getCamera());
}
break;
case BLOCK_SC00:// Scene
loadedFeatures.sceneBlocks.add(block);
break; break;
// case FileBlockHeader.BLOCK_MA00:// Material case BLOCK_MA00:// Material
// MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
// MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext); MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
// if (blenderKey.isLoadUnlinkedAssets() && blenderKey.shouldLoad(FeaturesToLoad.MATERIALS)) { loadedFeatures.materials.add(materialContext);
// loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext))); break;
// } case BLOCK_ME00:// Mesh
// break; MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
case FileBlockHeader.BLOCK_SC00:// Scene TemporalMesh temporalMesh = meshHelper.toTemporalMesh(block.getStructure(blenderContext), blenderContext);
if (blenderKey.shouldLoad(FeaturesToLoad.SCENES)) { loadedFeatures.meshes.add(temporalMesh);
sceneBlocks.add(block); break;
case BLOCK_IM00:// Image
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
Texture image = textureHelper.loadImageAsTexture(block.getStructure(blenderContext), 0, blenderContext);
if (image != null && image.getImage() != null) {// render results are stored as images but are not being loaded
loadedFeatures.images.add(image);
} }
break; break;
case FileBlockHeader.BLOCK_WO00:// World case BLOCK_TE00:
if (blenderKey.shouldLoad(FeaturesToLoad.WORLD)) { Structure textureStructure = block.getStructure(blenderContext);
Structure worldStructure = block.getStructure(blenderContext); int type = ((Number) textureStructure.getFieldValue("type")).intValue();
String worldName = worldStructure.getName(); if (type == TextureHelper.TEX_IMAGE) {
if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { TextureHelper texHelper = blenderContext.getHelper(TextureHelper.class);
LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class); Texture texture = texHelper.getTexture(textureStructure, null, blenderContext);
Light ambientLight = landscapeHelper.toAmbientLight(worldStructure); if (texture != null) {// null is returned when texture has no image
if(ambientLight != null) { loadedFeatures.textures.add(texture);
loadingResults.addLight(new LightNode(null, ambientLight));
}
loadingResults.setSky(landscapeHelper.toSky(worldStructure));
loadingResults.addFilter(landscapeHelper.toFog(worldStructure));
loadingResults.setBackgroundColor(landscapeHelper.toBackgroundColor(worldStructure));
} }
} else {
LOGGER.fine("Only image textures can be loaded as unlinked assets. Generated textures will be applied to an existing object.");
} }
break; break;
case BLOCK_WO00:// World
LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class);
Structure worldStructure = block.getStructure(blenderContext);
String worldName = worldStructure.getName();
if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
Light ambientLight = landscapeHelper.toAmbientLight(worldStructure);
if (ambientLight != null) {
loadedFeatures.objects.add(new LightNode(null, ambientLight));
loadedFeatures.lights.add(ambientLight);
}
loadedFeatures.sky = landscapeHelper.toSky(worldStructure);
loadedFeatures.backgroundColor = landscapeHelper.toBackgroundColor(worldStructure);
Filter fogFilter = landscapeHelper.toFog(worldStructure);
if (fogFilter != null) {
loadedFeatures.filters.add(landscapeHelper.toFog(worldStructure));
}
}
break;
case BLOCK_AC00:
LOGGER.fine("Loading unlinked animations is not yet supported!");
break;
default:
LOGGER.log(Level.FINEST, "Ommiting the block: {0}.", block.getCode());
} }
} }
// bake constraints after everything is loaded LOGGER.fine("Baking constraints after every feature is loaded.");
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.bakeConstraints(blenderContext); constraintHelper.bakeConstraints(blenderContext);
// load the scene at the very end so that the root nodes have no parent during loading or constraints applying LOGGER.fine("Loading scenes and attaching them to the root object.");
for (FileBlockHeader sceneBlock : sceneBlocks) { for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext))); loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext)));
}
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
Node modelRoot = new Node(blenderKey.getName());
for (Node scene : loadedFeatures.scenes) {
modelRoot.attachChild(scene);
} }
return loadingResults; if (blenderKey.isLoadUnlinkedAssets()) {
LOGGER.fine("Setting loaded content as user data in resulting sptaial.");
Map<String, Map<String, Object>> linkedData = new HashMap<String, Map<String, Object>>();
Map<String, Object> thisFileData = new HashMap<String, Object>();
thisFileData.put("scenes", loadedFeatures.scenes == null ? new ArrayList<Object>() : loadedFeatures.scenes);
thisFileData.put("objects", loadedFeatures.objects == null ? new ArrayList<Object>() : loadedFeatures.objects);
thisFileData.put("meshes", loadedFeatures.meshes == null ? new ArrayList<Object>() : loadedFeatures.meshes);
thisFileData.put("materials", loadedFeatures.materials == null ? new ArrayList<Object>() : loadedFeatures.materials);
thisFileData.put("textures", loadedFeatures.textures == null ? new ArrayList<Object>() : loadedFeatures.textures);
thisFileData.put("images", loadedFeatures.images == null ? new ArrayList<Object>() : loadedFeatures.images);
thisFileData.put("animations", loadedFeatures.animations == null ? new ArrayList<Object>() : loadedFeatures.animations);
thisFileData.put("cameras", loadedFeatures.cameras == null ? new ArrayList<Object>() : loadedFeatures.cameras);
thisFileData.put("lights", loadedFeatures.lights == null ? new ArrayList<Object>() : loadedFeatures.lights);
thisFileData.put("filters", loadedFeatures.filters == null ? new ArrayList<Object>() : loadedFeatures.filters);
thisFileData.put("backgroundColor", loadedFeatures.backgroundColor);
thisFileData.put("sky", loadedFeatures.sky);
linkedData.put("this", thisFileData);
linkedData.putAll(blenderContext.getLinkedFeatures());
modelRoot.setUserData("linkedData", linkedData);
}
return modelRoot;
} catch (BlenderFileException e) { } catch (BlenderFileException e) {
throw new IOException(e.getLocalizedMessage(), e); throw new IOException(e.getLocalizedMessage(), e);
} catch (Exception e) { } catch (Exception e) {
@ -158,62 +224,36 @@ public class BlenderLoader implements AssetLoader {
} }
} }
/**
* This method indicates if the given spatial is a root object. It means it
* has no parent or is directly attached to one of the already loaded scene
* nodes.
*
* @param loadingResults
* loading results containing the scene nodes
* @param spatial
* spatial object
* @return <b>true</b> if the given spatial is a root object and
* <b>false</b> otherwise
*/
protected boolean isRootObject(LoadingResults loadingResults, Spatial spatial) {
if (spatial.getParent() == null) {
return true;
}
for (Node scene : loadingResults.getScenes()) {
if (spatial.getParent().equals(scene)) {
return true;
}
}
return false;
}
/** /**
* This method converts the given structure to a scene node. * This method converts the given structure to a scene node.
* @param structure * @param structure
* structure of a scene * structure of a scene
* @return scene's node * @return scene's node
* @throws BlenderFileException
* an exception throw when problems with blender file occur
*/ */
private Node toScene(Structure structure) { private Node toScene(Structure structure) throws BlenderFileException {
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Node result = new Node(structure.getName()); Node result = new Node(structure.getName());
try { List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase(); for (Structure b : base) {
for (Structure b : base) { Pointer pObject = (Pointer) b.getFieldValue("object");
Pointer pObject = (Pointer) b.getFieldValue("object"); if (pObject.isNotNull()) {
if (pObject.isNotNull()) { Structure objectStructure = pObject.fetchData().get(0);
Structure objectStructure = pObject.fetchData().get(0);
Object object = objectHelper.toObject(objectStructure, blenderContext); Object object = objectHelper.toObject(objectStructure, blenderContext);
if (object instanceof LightNode) { if (object instanceof LightNode) {
result.addLight(((LightNode) object).getLight()); result.addLight(((LightNode) object).getLight());// FIXME: check if this is needed !!!
result.attachChild((LightNode) object); result.attachChild((LightNode) object);
} else if (object instanceof Node) { } else if (object instanceof Node) {
if (LOGGER.isLoggable(Level.FINE)) { if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() }); LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
} }
if (((Node) object).getParent() == null) { if (((Node) object).getParent() == null) {
result.attachChild((Spatial) object); result.attachChild((Spatial) object);
}
} }
} }
} }
} catch (BlenderFileException e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
} }
return result; return result;
} }
@ -261,7 +301,7 @@ public class BlenderLoader implements AssetLoader {
blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext)); blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderContext)); blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(LandscapeHelper.class, new LandscapeHelper(inputStream.getVersionNumber(), blenderContext)); blenderContext.putHelper(LandscapeHelper.class, new LandscapeHelper(inputStream.getVersionNumber(), blenderContext));
// reading the blocks (dna block is automatically saved in the blender context when found) // reading the blocks (dna block is automatically saved in the blender context when found)
FileBlockHeader sceneFileBlock = null; FileBlockHeader sceneFileBlock = null;
do { do {
@ -269,7 +309,7 @@ public class BlenderLoader implements AssetLoader {
if (!fileBlock.isDnaBlock()) { if (!fileBlock.isDnaBlock()) {
blocks.add(fileBlock); blocks.add(fileBlock);
// save the scene's file block // save the scene's file block
if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) { if (fileBlock.getCode() == BlockCode.BLOCK_SC00) {
sceneFileBlock = fileBlock; sceneFileBlock = fileBlock;
} }
} }
@ -287,4 +327,39 @@ public class BlenderLoader implements AssetLoader {
blenderContext = null; blenderContext = null;
blocks = null; blocks = null;
} }
/**
* This class holds the loading results according to the given loading flag.
* @author Marcin Roguski (Kaelthas)
*/
private static class LoadedFeatures {
private List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
/** The scenes from the file. */
private List<Node> scenes = new ArrayList<Node>();
/** Objects from all scenes. */
private List<Node> objects = new ArrayList<Node>();
/** All meshes. */
private List<TemporalMesh> meshes = new ArrayList<TemporalMesh>();
/** Materials from all objects. */
private List<MaterialContext> materials = new ArrayList<MaterialContext>();
/** Textures from all objects. */
private List<Texture> textures = new ArrayList<Texture>();
/** The images stored in the blender file. */
private List<Texture> images = new ArrayList<Texture>();
/** Animations of all objects. */
private List<Animation> animations = new ArrayList<Animation>();
/** All cameras from the file. */
private List<Camera> cameras = new ArrayList<Camera>();
/** All lights from the file. */
private List<Light> lights = new ArrayList<Light>();
/** Loaded sky. */
private Spatial sky;
/** Scene filters (ie. FOG). */
private List<Filter> filters = new ArrayList<Filter>();
/**
* The background color of the render loaded from the horizon color of the world. If no world is used than the gray color
* is set to default (as in blender editor.
*/
private ColorRGBA backgroundColor = ColorRGBA.Gray;
}
} }

@ -31,79 +31,10 @@
*/ */
package com.jme3.scene.plugins.blender; package com.jme3.scene.plugins.blender;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.BlenderKey;
import com.jme3.asset.BlenderKey.FeaturesToLoad;
import com.jme3.scene.LightNode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.animations.AnimationHelper;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
/** /**
* This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures. * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures.
* * @deprecated this class is deprecated; use BlenderLoader instead
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class BlenderModelLoader extends BlenderLoader { public class BlenderModelLoader extends BlenderLoader {
private static final Logger LOGGER = Logger.getLogger(BlenderModelLoader.class.getName());
@Override
public Spatial load(AssetInfo assetInfo) throws IOException {
try {
this.setup(assetInfo);
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey();
List<Node> rootObjects = new ArrayList<Node>();
for (FileBlockHeader block : blocks) {
if (block.getCode() == FileBlockHeader.BLOCK_OB00) {
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Object object = objectHelper.toObject(block.getStructure(blenderContext), blenderContext);
if (object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) {
rootObjects.add((LightNode) object);
} else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) {
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
if (((Node) object).getParent() == null) {
rootObjects.add((Node) object);
}
}
}
}
// bake constraints after everything is loaded
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.bakeConstraints(blenderContext);
// attach the nodes to the root node at the very end so that the root objects have no parents during constraint applying
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene to it.");
Node modelRoot = new Node(blenderKey.getName());
for (Node node : rootObjects) {
if (node instanceof LightNode) {
modelRoot.addLight(((LightNode) node).getLight());
}
modelRoot.attachChild(node);
}
return modelRoot;
} catch (BlenderFileException e) {
throw new IOException(e.getLocalizedMessage(), e);
} catch (Exception e) {
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
} finally {
this.clear();
}
}
} }

@ -25,6 +25,7 @@ import com.jme3.scene.plugins.blender.curves.BezierCurve;
import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper; import com.jme3.scene.plugins.blender.objects.ObjectHelper;
@ -48,7 +49,7 @@ public class AnimationHelper extends AbstractBlenderHelper {
*/ */
public void loadAnimations() throws BlenderFileException { public void loadAnimations() throws BlenderFileException {
LOGGER.info("Loading animations that will be later applied to scene features."); LOGGER.info("Loading animations that will be later applied to scene features.");
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(BlockCode.BLOCK_AC00);
if (actionHeaders != null) { if (actionHeaders != null) {
for (FileBlockHeader header : actionHeaders) { for (FileBlockHeader header : actionHeaders) {
Structure actionStructure = header.getStructure(blenderContext); Structure actionStructure = header.getStructure(blenderContext);

@ -5,7 +5,6 @@ import java.util.logging.Logger;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderFileException;
@ -43,7 +42,7 @@ public class CameraHelper extends AbstractBlenderHelper {
* an exception is thrown when there are problems with the * an exception is thrown when there are problems with the
* blender file * blender file
*/ */
public CameraNode toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException { public Camera toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion >= 250) { if (blenderVersion >= 250) {
return this.toCamera250(structure, blenderContext.getSceneStructure()); return this.toCamera250(structure, blenderContext.getSceneStructure());
} else { } else {
@ -63,7 +62,7 @@ public class CameraHelper extends AbstractBlenderHelper {
* an exception is thrown when there are problems with the * an exception is thrown when there are problems with the
* blender file * blender file
*/ */
private CameraNode toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException { private Camera toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException {
int width = DEFAULT_CAM_WIDTH; int width = DEFAULT_CAM_WIDTH;
int height = DEFAULT_CAM_HEIGHT; int height = DEFAULT_CAM_HEIGHT;
if (sceneStructure != null) { if (sceneStructure != null) {
@ -99,7 +98,7 @@ public class CameraHelper extends AbstractBlenderHelper {
sensor = ((Number) structure.getFieldValue(sensorName)).floatValue(); sensor = ((Number) structure.getFieldValue(sensorName)).floatValue();
} }
float focalLength = ((Number) structure.getFieldValue("lens")).floatValue(); float focalLength = ((Number) structure.getFieldValue("lens")).floatValue();
float fov = 2.0f * FastMath.atan((sensor / 2.0f) / focalLength); float fov = 2.0f * FastMath.atan(sensor / 2.0f / focalLength);
if (sensorVertical) { if (sensorVertical) {
fovY = fov * FastMath.RAD_TO_DEG; fovY = fov * FastMath.RAD_TO_DEG;
} else { } else {
@ -111,7 +110,8 @@ public class CameraHelper extends AbstractBlenderHelper {
fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
} }
camera.setFrustumPerspective(fovY, aspect, clipsta, clipend); camera.setFrustumPerspective(fovY, aspect, clipsta, clipend);
return new CameraNode(null, camera); camera.setName(structure.getName());
return camera;
} }
/** /**
@ -124,7 +124,7 @@ public class CameraHelper extends AbstractBlenderHelper {
* an exception is thrown when there are problems with the * an exception is thrown when there are problems with the
* blender file * blender file
*/ */
private CameraNode toCamera249(Structure structure) throws BlenderFileException { private Camera toCamera249(Structure structure) throws BlenderFileException {
Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
int type = ((Number) structure.getFieldValue("type")).intValue(); int type = ((Number) structure.getFieldValue("type")).intValue();
if (type != 0 && type != 1) { if (type != 0 && type != 1) {
@ -142,6 +142,7 @@ public class CameraHelper extends AbstractBlenderHelper {
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
} }
camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend); camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend);
return new CameraNode(null, camera); camera.setName(structure.getName());
return camera;
} }
} }

@ -31,6 +31,8 @@
*/ */
package com.jme3.scene.plugins.blender.file; package com.jme3.scene.plugins.blender.file;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
/** /**
@ -39,39 +41,23 @@ import com.jme3.scene.plugins.blender.BlenderContext;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class FileBlockHeader { public class FileBlockHeader {
private static final Logger LOGGER = Logger.getLogger(FileBlockHeader.class.getName());
public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; // TE00
public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; // ME00
public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; // SR00
public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; // CA00
public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; // LA00
public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; // OB00
public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; // MA00
public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; // SC00
public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; // WO00
public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; // TX00
public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; // IP00
public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; // AC00
public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; // GLOB
public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; // REND
public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; // DATA
public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; // DNA1
public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; // ENDB
/** Identifier of the file-block [4 bytes]. */ /** Identifier of the file-block [4 bytes]. */
private int code; private BlockCode code;
/** Total length of the data after the file-block-header [4 bytes]. */ /** Total length of the data after the file-block-header [4 bytes]. */
private int size; private int size;
/** /**
* Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer
* size)]. * size)].
*/ */
private long oldMemoryAddress; private long oldMemoryAddress;
/** Index of the SDNA structure [4 bytes]. */ /** Index of the SDNA structure [4 bytes]. */
private int sdnaIndex; private int sdnaIndex;
/** Number of structure located in this file-block [4 bytes]. */ /** Number of structure located in this file-block [4 bytes]. */
private int count; private int count;
/** Start position of the block's data in the stream. */ /** Start position of the block's data in the stream. */
private int blockPosition; private int blockPosition;
/** /**
* Constructor. Loads the block header from the given stream during instance creation. * Constructor. Loads the block header from the given stream during instance creation.
@ -84,13 +70,13 @@ public class FileBlockHeader {
*/ */
public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException { public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException {
inputStream.alignPosition(4); inputStream.alignPosition(4);
code = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte(); code = BlockCode.valueOf(inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte());
size = inputStream.readInt(); size = inputStream.readInt();
oldMemoryAddress = inputStream.readPointer(); oldMemoryAddress = inputStream.readPointer();
sdnaIndex = inputStream.readInt(); sdnaIndex = inputStream.readInt();
count = inputStream.readInt(); count = inputStream.readInt();
blockPosition = inputStream.getPosition(); blockPosition = inputStream.getPosition();
if (FileBlockHeader.BLOCK_DNA1 == code) { if (BlockCode.BLOCK_DNA1 == code) {
blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext)); blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext));
} else { } else {
inputStream.setPosition(blockPosition + size); inputStream.setPosition(blockPosition + size);
@ -116,7 +102,7 @@ public class FileBlockHeader {
* This method returns the code of this data block. * This method returns the code of this data block.
* @return the code of this data block * @return the code of this data block
*/ */
public int getCode() { public BlockCode getCode() {
return code; return code;
} }
@ -157,7 +143,7 @@ public class FileBlockHeader {
* @return true if this block is the last one in the file nad false otherwise * @return true if this block is the last one in the file nad false otherwise
*/ */
public boolean isLastBlock() { public boolean isLastBlock() {
return FileBlockHeader.BLOCK_ENDB == code; return BlockCode.BLOCK_ENDB == code;
} }
/** /**
@ -165,25 +151,44 @@ public class FileBlockHeader {
* @return true if this block is the SDNA block and false otherwise * @return true if this block is the SDNA block and false otherwise
*/ */
public boolean isDnaBlock() { public boolean isDnaBlock() {
return FileBlockHeader.BLOCK_DNA1 == code; return BlockCode.BLOCK_DNA1 == code;
} }
@Override @Override
public String toString() { public String toString() {
return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]"; return "FILE BLOCK HEADER [" + code.toString() + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";
} }
/** public static enum BlockCode {
* This method transforms the coded bloch id into a string value. BLOCK_ME00('M' << 24 | 'E' << 16), // mesh
* @param code BLOCK_CA00('C' << 24 | 'A' << 16), // camera
* the id of the block BLOCK_LA00('L' << 24 | 'A' << 16), // lamp
* @return the string value of the block id BLOCK_OB00('O' << 24 | 'B' << 16), // object
*/ BLOCK_MA00('M' << 24 | 'A' << 16), // material
protected String codeToString(int code) { BLOCK_SC00('S' << 24 | 'C' << 16), // scene
char c1 = (char) ((code & 0xFF000000) >> 24); BLOCK_WO00('W' << 24 | 'O' << 16), // world
char c2 = (char) ((code & 0xFF0000) >> 16); BLOCK_TX00('T' << 24 | 'X' << 16), // texture
char c3 = (char) ((code & 0xFF00) >> 8); BLOCK_IP00('I' << 24 | 'P' << 16), // ipo
char c4 = (char) (code & 0xFF); BLOCK_AC00('A' << 24 | 'C' << 16), // action
return String.valueOf(c1) + c2 + c3 + c4; BLOCK_IM00('I' << 24 | 'M' << 16), // image
BLOCK_TE00('T' << 24 | 'E' << 16), BLOCK_WM00('W' << 24 | 'M' << 16), BLOCK_SR00('S' << 24 | 'R' << 16), BLOCK_SN00('S' << 24 | 'N' << 16), BLOCK_BR00('B' << 24 | 'R' << 16), BLOCK_LS00('L' << 24 | 'S' << 16), BLOCK_GLOB('G' << 24 | 'L' << 16 | 'O' << 8 | 'B'), BLOCK_REND('R' << 24 | 'E' << 16 | 'N' << 8 | 'D'), BLOCK_DATA('D' << 24 | 'A' << 16 | 'T' << 8 | 'A'), BLOCK_DNA1('D' << 24 | 'N' << 16 | 'A' << 8 | '1'), BLOCK_ENDB('E' << 24 | 'N' << 16 | 'D' << 8 | 'B'), BLOCK_TEST('T' << 24 | 'E' << 16
| 'S' << 8 | 'T'), BLOCK_UNKN(0);
private int code;
private BlockCode(int code) {
this.code = code;
}
public static BlockCode valueOf(int code) {
for (BlockCode blockCode : BlockCode.values()) {
if (blockCode.code == code) {
return blockCode;
}
}
byte[] codeBytes = new byte[] { (byte) (code >> 24 & 0xFF), (byte) (code >> 16 & 0xFF), (byte) (code >> 8 & 0xFF), (byte) (code & 0xFF) };
LOGGER.warning("Unknown block header: " + new String(codeBytes));
return BLOCK_UNKN;
}
} }
} }

@ -254,7 +254,8 @@ public class Structure implements Cloneable {
Structure id = (Structure) fieldValue; Structure id = (Structure) fieldValue;
return id == null ? null : id.getFieldValue("name").toString().substring(2);// blender adds 2-charactes as a name prefix return id == null ? null : id.getFieldValue("name").toString().substring(2);// blender adds 2-charactes as a name prefix
} }
return null; Object name = this.getFieldValue("name", null);
return name == null ? null : name.toString().substring(2);
} }
@Override @Override

@ -40,7 +40,6 @@ import com.jme3.light.PointLight;
import com.jme3.light.SpotLight; import com.jme3.light.SpotLight;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.scene.LightNode;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
@ -67,8 +66,8 @@ public class LightHelper extends AbstractBlenderHelper {
super(blenderVersion, blenderContext); super(blenderVersion, blenderContext);
} }
public LightNode toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException { public Light toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
LightNode result = (LightNode) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); Light result = (Light) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (result != null) { if (result != null) {
return result; return result;
} }
@ -111,6 +110,7 @@ public class LightHelper extends AbstractBlenderHelper {
float g = ((Number) structure.getFieldValue("g")).floatValue(); float g = ((Number) structure.getFieldValue("g")).floatValue();
float b = ((Number) structure.getFieldValue("b")).floatValue(); float b = ((Number) structure.getFieldValue("b")).floatValue();
light.setColor(new ColorRGBA(r, g, b, 1.0f)); light.setColor(new ColorRGBA(r, g, b, 1.0f));
return new LightNode(structure.getName(), light); light.setName(structure.getName());
return light;
} }
} }

@ -1,11 +1,15 @@
package com.jme3.scene.plugins.blender.materials; package com.jme3.scene.plugins.blender.materials;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.Savable;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode; import com.jme3.material.RenderState.BlendMode;
import com.jme3.material.RenderState.FaceCullMode; import com.jme3.material.RenderState.FaceCullMode;
@ -30,7 +34,7 @@ import com.jme3.util.BufferUtils;
* This class holds the data about the material. * This class holds the data about the material.
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public final class MaterialContext { public final class MaterialContext implements Savable {
private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName()); private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName());
// texture mapping types // texture mapping types
@ -67,7 +71,7 @@ public final class MaterialContext {
int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue(); int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue();
diffuseShader = DiffuseShader.values()[diff_shader]; diffuseShader = DiffuseShader.values()[diff_shader];
ambientFactor = ((Number) structure.getFieldValue("amb")).floatValue(); ambientFactor = ((Number) structure.getFieldValue("amb")).floatValue();
if (shadeless) { if (shadeless) {
float r = ((Number) structure.getFieldValue("r")).floatValue(); float r = ((Number) structure.getFieldValue("r")).floatValue();
float g = ((Number) structure.getFieldValue("g")).floatValue(); float g = ((Number) structure.getFieldValue("g")).floatValue();
@ -107,6 +111,13 @@ public final class MaterialContext {
this.transparent = transparent; this.transparent = transparent;
} }
/**
* @return the name of the material
*/
public String getName() {
return name;
}
/** /**
* Applies material to a given geometry. * Applies material to a given geometry.
* *
@ -314,4 +325,14 @@ public final class MaterialContext {
float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();
return new ColorRGBA(r, g, b, alpha); return new ColorRGBA(r, g, b, alpha);
} }
@Override
public void write(JmeExporter e) throws IOException {
throw new IOException("Material context is not for saving! It implements savable only to be passed to another blend file as a Savable in user data!");
}
@Override
public void read(JmeImporter e) throws IOException {
throw new IOException("Material context is not for loading! It implements savable only to be passed to another blend file as a Savable in user data!");
}
} }

@ -161,12 +161,17 @@ public class MaterialHelper extends AbstractBlenderHelper {
* an exception is throw when problems with blend file occur * an exception is throw when problems with blend file occur
*/ */
public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException { public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.FINE, "Loading material.");
MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (result != null) { if (result != null) {
return result; return result;
} }
if ("ID".equals(structure.getType())) {
LOGGER.fine("Loading material from external blend file.");
return (MaterialContext) this.loadLibrary(structure);
}
LOGGER.fine("Loading material.");
result = new MaterialContext(structure, blenderContext); result = new MaterialContext(structure, blenderContext);
LOGGER.log(Level.FINE, "Material''s name: {0}", result.name); LOGGER.log(Level.FINE, "Material''s name: {0}", result.name);
Long oma = structure.getOldMemoryAddress(); Long oma = structure.getOldMemoryAddress();

@ -40,7 +40,6 @@ import java.util.Map.Entry;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.asset.BlenderKey.FeaturesToLoad;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
@ -52,7 +51,6 @@ import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.DynamicArray; import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.scene.plugins.blender.objects.Properties; import com.jme3.scene.plugins.blender.objects.Properties;
@ -106,17 +104,18 @@ public class MeshHelper extends AbstractBlenderHelper {
return temporalMesh.clone(); return temporalMesh.clone();
} }
if ("ID".equals(meshStructure.getType())) {
LOGGER.fine("Loading mesh from external blend file.");
return (TemporalMesh) this.loadLibrary(meshStructure);
}
String name = meshStructure.getName(); String name = meshStructure.getName();
LOGGER.log(Level.FINE, "Reading mesh: {0}.", name); LOGGER.log(Level.FINE, "Reading mesh: {0}.", name);
temporalMesh = new TemporalMesh(meshStructure, blenderContext); temporalMesh = new TemporalMesh(meshStructure, blenderContext);
LOGGER.fine("Loading materials."); LOGGER.fine("Loading materials.");
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
MaterialContext[] materials = null; temporalMesh.setMaterials(materialHelper.getMaterials(meshStructure, blenderContext));
if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
materials = materialHelper.getMaterials(meshStructure, blenderContext);
}
temporalMesh.setMaterials(materials);
LOGGER.fine("Reading custom properties."); LOGGER.fine("Reading custom properties.");
Properties properties = this.loadProperties(meshStructure, blenderContext); Properties properties = this.loadProperties(meshStructure, blenderContext);

@ -40,12 +40,15 @@ import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.asset.BlenderKey.FeaturesToLoad; import com.jme3.light.Light;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.LightNode;
import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -106,39 +109,34 @@ public class ObjectHelper extends AbstractBlenderHelper {
* an exception is thrown when the given data is inapropriate * an exception is thrown when the given data is inapropriate
*/ */
public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.fine("Loading blender object."); Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (loadedResult != null) {
return loadedResult;
}
LOGGER.fine("Loading blender object.");
if ("ID".equals(objectStructure.getType())) {
Node object = (Node) this.loadLibrary(objectStructure);
if (object.getParent() != null) {
LOGGER.log(Level.FINEST, "Detaching object {0}, loaded from external file, from its parent.", object);
object.getParent().detachChild(object);
}
return object;
}
int type = ((Number) objectStructure.getFieldValue("type")).intValue(); int type = ((Number) objectStructure.getFieldValue("type")).intValue();
ObjectType objectType = ObjectType.valueOf(type); ObjectType objectType = ObjectType.valueOf(type);
LOGGER.log(Level.FINE, "Type of the object: {0}.", objectType); LOGGER.log(Level.FINE, "Type of the object: {0}.", objectType);
if (objectType == ObjectType.LAMP && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.LIGHTS)) {
LOGGER.fine("Lamps are not included in loading.");
return null;
}
if (objectType == ObjectType.CAMERA && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.CAMERAS)) {
LOGGER.fine("Cameras are not included in loading.");
return null;
}
if (!blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.OBJECTS)) {
LOGGER.fine("Objects are not included in loading.");
return null;
}
int lay = ((Number) objectStructure.getFieldValue("lay")).intValue(); int lay = ((Number) objectStructure.getFieldValue("lay")).intValue();
if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) { if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) {
LOGGER.fine("The layer this object is located in is not included in loading."); LOGGER.fine("The layer this object is located in is not included in loading.");
return null; return null;
} }
LOGGER.fine("Checking if the object has not been already loaded.");
Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (loadedResult != null) {
return loadedResult;
}
blenderContext.pushParent(objectStructure); blenderContext.pushParent(objectStructure);
String name = objectStructure.getName(); String name = objectStructure.getName();
LOGGER.log(Level.FINE, "Loading obejct: {0}", name); LOGGER.log(Level.FINE, "Loading obejct: {0}", name);
int restrictflag = ((Number) objectStructure.getFieldValue("restrictflag")).intValue(); int restrictflag = ((Number) objectStructure.getFieldValue("restrictflag")).intValue();
boolean visible = (restrictflag & 0x01) != 0; boolean visible = (restrictflag & 0x01) != 0;
@ -171,7 +169,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
Pointer pMesh = (Pointer) objectStructure.getFieldValue("data"); Pointer pMesh = (Pointer) objectStructure.getFieldValue("data");
List<Structure> meshesArray = pMesh.fetchData(); List<Structure> meshesArray = pMesh.fetchData();
TemporalMesh temporalMesh = meshHelper.toTemporalMesh(meshesArray.get(0), blenderContext); TemporalMesh temporalMesh = meshHelper.toTemporalMesh(meshesArray.get(0), blenderContext);
if(temporalMesh != null) { if (temporalMesh != null) {
result.attachChild(temporalMesh); result.attachChild(temporalMesh);
} }
break; break;
@ -183,7 +181,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class); CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
Structure curveData = pCurve.fetchData().get(0); Structure curveData = pCurve.fetchData().get(0);
TemporalMesh curvesTemporalMesh = curvesHelper.toCurve(curveData, blenderContext); TemporalMesh curvesTemporalMesh = curvesHelper.toCurve(curveData, blenderContext);
if(curvesTemporalMesh != null) { if (curvesTemporalMesh != null) {
result.attachChild(curvesTemporalMesh); result.attachChild(curvesTemporalMesh);
} }
} }
@ -193,10 +191,12 @@ public class ObjectHelper extends AbstractBlenderHelper {
if (pLamp.isNotNull()) { if (pLamp.isNotNull()) {
LightHelper lightHelper = blenderContext.getHelper(LightHelper.class); LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
List<Structure> lampsArray = pLamp.fetchData(); List<Structure> lampsArray = pLamp.fetchData();
result = lightHelper.toLight(lampsArray.get(0), blenderContext); Light light = lightHelper.toLight(lampsArray.get(0), blenderContext);
if (result == null) { if (light == null) {
// probably some light type is not supported, just create a node so that we can maintain child-parent relationship for nodes // probably some light type is not supported, just create a node so that we can maintain child-parent relationship for nodes
result = new Node(name); result = new Node(name);
} else {
result = new LightNode(name, light);
} }
} }
break; break;
@ -205,19 +205,25 @@ public class ObjectHelper extends AbstractBlenderHelper {
if (pCamera.isNotNull()) { if (pCamera.isNotNull()) {
CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class); CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
List<Structure> camerasArray = pCamera.fetchData(); List<Structure> camerasArray = pCamera.fetchData();
result = cameraHelper.toCamera(camerasArray.get(0), blenderContext); Camera camera = cameraHelper.toCamera(camerasArray.get(0), blenderContext);
if (camera == null) {
// just create a node so that we can maintain child-parent relationship for nodes
result = new Node(name);
} else {
result = new CameraNode(name, camera);
}
} }
break; break;
default: default:
LOGGER.log(Level.WARNING, "Unsupported object type: {0}", type); LOGGER.log(Level.WARNING, "Unsupported object type: {0}", type);
} }
if (result != null) { if (result != null) {
LOGGER.fine("Storing loaded feature in blender context and applying markers (those will be removed before the final result is released)."); LOGGER.fine("Storing loaded feature in blender context and applying markers (those will be removed before the final result is released).");
Long oma = objectStructure.getOldMemoryAddress(); Long oma = objectStructure.getOldMemoryAddress();
blenderContext.addLoadedFeatures(oma, LoadedDataType.STRUCTURE, objectStructure); blenderContext.addLoadedFeatures(oma, LoadedDataType.STRUCTURE, objectStructure);
blenderContext.addLoadedFeatures(oma, LoadedDataType.FEATURE, result); blenderContext.addLoadedFeatures(oma, LoadedDataType.FEATURE, result);
blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress()); blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress());
if (objectType == ObjectType.ARMATURE) { if (objectType == ObjectType.ARMATURE) {
blenderContext.addMarker(ARMATURE_NODE_MARKER, result, Boolean.TRUE); blenderContext.addMarker(ARMATURE_NODE_MARKER, result, Boolean.TRUE);
@ -235,13 +241,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
for (Modifier modifier : modifiers) { for (Modifier modifier : modifiers) {
modifier.apply(result, blenderContext); modifier.apply(result, blenderContext);
} }
if (result.getChildren() != null && result.getChildren().size() > 0) { if (result.getChildren() != null && result.getChildren().size() > 0) {
if(result.getChildren().size() == 1 && result.getChild(0) instanceof TemporalMesh) { if (result.getChildren().size() == 1 && result.getChild(0) instanceof TemporalMesh) {
LOGGER.fine("Converting temporal mesh into jme geometries."); LOGGER.fine("Converting temporal mesh into jme geometries.");
((TemporalMesh)result.getChild(0)).toGeometries(); ((TemporalMesh) result.getChild(0)).toGeometries();
} }
LOGGER.fine("Applying proper scale to the geometries."); LOGGER.fine("Applying proper scale to the geometries.");
for (Spatial child : result.getChildren()) { for (Spatial child : result.getChildren()) {
if (child instanceof Geometry) { if (child instanceof Geometry) {

@ -121,7 +121,7 @@ public class TextureHelper extends AbstractBlenderHelper {
* data. The returned texture has the name set to the value of its blender * data. The returned texture has the name set to the value of its blender
* type. * type.
* *
* @param tex * @param textureStructure
* texture structure filled with data * texture structure filled with data
* @param blenderContext * @param blenderContext
* the blender context * the blender context
@ -130,23 +130,29 @@ public class TextureHelper extends AbstractBlenderHelper {
* this exception is thrown when the blend file structure is * this exception is thrown when the blend file structure is
* somehow invalid or corrupted * somehow invalid or corrupted
*/ */
public Texture getTexture(Structure tex, Structure mTex, BlenderContext blenderContext) throws BlenderFileException { public Texture getTexture(Structure textureStructure, Structure mTex, BlenderContext blenderContext) throws BlenderFileException {
Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedDataType.FEATURE); Texture result = (Texture) blenderContext.getLoadedFeature(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (result != null) { if (result != null) {
return result; return result;
} }
int type = ((Number) tex.getFieldValue("type")).intValue();
int imaflag = ((Number) tex.getFieldValue("imaflag")).intValue(); if ("ID".equals(textureStructure.getType())) {
LOGGER.fine("Loading texture from external blend file.");
return (Texture) this.loadLibrary(textureStructure);
}
int type = ((Number) textureStructure.getFieldValue("type")).intValue();
int imaflag = ((Number) textureStructure.getFieldValue("imaflag")).intValue();
switch (type) { switch (type) {
case TEX_IMAGE:// (it is first because probably this will be most commonly used) case TEX_IMAGE:// (it is first because probably this will be most commonly used)
Pointer pImage = (Pointer) tex.getFieldValue("ima"); Pointer pImage = (Pointer) textureStructure.getFieldValue("ima");
if (pImage.isNotNull()) { if (pImage.isNotNull()) {
Structure image = pImage.fetchData().get(0); Structure image = pImage.fetchData().get(0);
Texture loadedTexture = this.loadTexture(image, imaflag, blenderContext); Texture loadedTexture = this.loadImageAsTexture(image, imaflag, blenderContext);
if (loadedTexture != null) { if (loadedTexture != null) {
result = loadedTexture; result = loadedTexture;
this.applyColorbandAndColorFactors(tex, result.getImage(), blenderContext); this.applyColorbandAndColorFactors(textureStructure, result.getImage(), blenderContext);
} }
} }
break; break;
@ -160,7 +166,7 @@ public class TextureHelper extends AbstractBlenderHelper {
case TEX_MUSGRAVE: case TEX_MUSGRAVE:
case TEX_VORONOI: case TEX_VORONOI:
case TEX_DISTNOISE: case TEX_DISTNOISE:
result = new GeneratedTexture(tex, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext); result = new GeneratedTexture(textureStructure, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext);
break; break;
case TEX_NONE:// No texture, do nothing case TEX_NONE:// No texture, do nothing
break; break;
@ -169,13 +175,13 @@ public class TextureHelper extends AbstractBlenderHelper {
case TEX_PLUGIN: case TEX_PLUGIN:
case TEX_ENVMAP: case TEX_ENVMAP:
case TEX_OCEAN: case TEX_OCEAN:
LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[] { type, tex.getName() }); LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[] { type, textureStructure.getName() });
break; break;
default: default:
throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName()); throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + textureStructure.getName());
} }
if (result != null) { if (result != null) {
result.setName(tex.getName()); result.setName(textureStructure.getName());
result.setWrap(WrapMode.Repeat); result.setWrap(WrapMode.Repeat);
// decide if the mipmaps will be generated // decide if the mipmaps will be generated
@ -195,14 +201,14 @@ public class TextureHelper extends AbstractBlenderHelper {
} }
if (type != TEX_IMAGE) {// only generated textures should have this key if (type != TEX_IMAGE) {// only generated textures should have this key
result.setKey(new GeneratedTextureKey(tex.getName())); result.setKey(new GeneratedTextureKey(textureStructure.getName()));
} }
if (LOGGER.isLoggable(Level.FINE)) { if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] { result.getName(), tex.getOldMemoryAddress() }); LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] { result.getName(), textureStructure.getOldMemoryAddress() });
} }
blenderContext.addLoadedFeatures(tex.getOldMemoryAddress(), LoadedDataType.STRUCTURE, tex); blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, textureStructure);
blenderContext.addLoadedFeatures(tex.getOldMemoryAddress(), LoadedDataType.FEATURE, result); blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result);
} }
return result; return result;
} }
@ -222,30 +228,40 @@ public class TextureHelper extends AbstractBlenderHelper {
* this exception is thrown when the blend file structure is * this exception is thrown when the blend file structure is
* somehow invalid or corrupted * somehow invalid or corrupted
*/ */
protected Texture loadTexture(Structure imageStructure, int imaflag, BlenderContext blenderContext) throws BlenderFileException { public Texture loadImageAsTexture(Structure imageStructure, int imaflag, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", imageStructure.getOldMemoryAddress()); LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", imageStructure.getOldMemoryAddress());
Texture result = null; Texture result = null;
Image im = (Image) blenderContext.getLoadedFeature(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); Image im = (Image) blenderContext.getLoadedFeature(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (im == null) { if (im == null) {
String texturePath = imageStructure.getFieldValue("name").toString(); if ("ID".equals(imageStructure.getType())) {
Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile"); LOGGER.fine("Loading texture from external blend file.");
if (pPackedFile.isNull()) { result = (Texture) this.loadLibrary(imageStructure);
LOGGER.log(Level.FINE, "Reading texture from file: {0}", texturePath);
result = this.loadImageFromFile(texturePath, imaflag, blenderContext);
} else { } else {
LOGGER.fine("Packed texture. Reading directly from the blend file!"); String texturePath = imageStructure.getFieldValue("name").toString();
Structure packedFile = pPackedFile.fetchData().get(0); Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile");
Pointer pData = (Pointer) packedFile.getFieldValue("data"); if (pPackedFile.isNull()) {
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress()); LOGGER.log(Level.FINE, "Reading texture from file: {0}", texturePath);
blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition()); result = this.loadImageFromFile(texturePath, imaflag, blenderContext);
ImageLoader imageLoader = new ImageLoader(); } else {
LOGGER.fine("Packed texture. Reading directly from the blend file!");
// Should the texture be flipped? It works for sinbad .. Structure packedFile = pPackedFile.fetchData().get(0);
result = new Texture2D(imageLoader.loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true)); Pointer pData = (Pointer) packedFile.getFieldValue("data");
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress());
blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition());
// Should the texture be flipped? It works for sinbad ..
result = new Texture2D(new ImageLoader().loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true));
}
} }
} else { } else {
result = new Texture2D(im); result = new Texture2D(im);
} }
if (result != null) {// render result is not being loaded
blenderContext.addLoadedFeatures(imageStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, imageStructure);
blenderContext.addLoadedFeatures(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result.getImage());
result.setName(imageStructure.getName());
}
return result; return result;
} }
@ -524,6 +540,18 @@ public class TextureHelper extends AbstractBlenderHelper {
return result; return result;
} }
/**
* Reads the texture data from the given material or sky structure.
* @param structure
* the structure of material or sky
* @param diffuseColorArray
* array of diffuse colors
* @param skyTexture
* indicates it we're going to read sky texture or not
* @return a list of combined textures
* @throws BlenderFileException
* an exception is thrown when problems with reading the blend file occur
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<CombinedTexture> readTextureData(Structure structure, float[] diffuseColorArray, boolean skyTexture) throws BlenderFileException { public List<CombinedTexture> readTextureData(Structure structure, float[] diffuseColorArray, boolean skyTexture) throws BlenderFileException {
DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex"); DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");

@ -1,10 +1,12 @@
package com.jme3.scene.plugins.blender.textures.blending; package com.jme3.scene.plugins.blender.textures.blending;
import java.util.logging.Logger;
import jme3tools.converters.MipMapGenerator;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import java.util.logging.Logger;
import jme3tools.converters.MipMapGenerator;
/** /**
* An abstract class that contains the basic methods used by the classes that * An abstract class that contains the basic methods used by the classes that
@ -103,12 +105,12 @@ import jme3tools.converters.MipMapGenerator;
public void copyBlendingData(TextureBlender textureBlender) { public void copyBlendingData(TextureBlender textureBlender) {
if (textureBlender instanceof AbstractTextureBlender) { if (textureBlender instanceof AbstractTextureBlender) {
this.flag = ((AbstractTextureBlender) textureBlender).flag; flag = ((AbstractTextureBlender) textureBlender).flag;
this.negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture; negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture;
this.blendType = ((AbstractTextureBlender) textureBlender).blendType; blendType = ((AbstractTextureBlender) textureBlender).blendType;
this.materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone(); materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone();
this.color = ((AbstractTextureBlender) textureBlender).color.clone(); color = ((AbstractTextureBlender) textureBlender).color.clone();
this.blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor; blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor;
} else { } else {
LOGGER.warning("Cannot copy blending data from other types than " + this.getClass()); LOGGER.warning("Cannot copy blending data from other types than " + this.getClass());
} }

@ -31,13 +31,13 @@
*/ */
package com.jme3.scene.plugins.blender.textures.blending; package com.jme3.scene.plugins.blender.textures.blending;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* This class creates the texture blending class depending on the texture type. * This class creates the texture blending class depending on the texture type.
* *
@ -66,7 +66,6 @@ public class TextureBlenderFactory {
* the texture format * the texture format
* @return texture blending class * @return texture blending class
*/ */
@SuppressWarnings("deprecation")
public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) { public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) {
switch (format) { switch (format) {
case Luminance8: case Luminance8:

@ -33,6 +33,12 @@ package com.jme3.scene;
import com.jme3.export.*; import com.jme3.export.*;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* <code>UserData</code> is used to contain user data objects * <code>UserData</code> is used to contain user data objects
@ -48,28 +54,40 @@ public final class UserData implements Savable {
* shape generation should ignore them. * shape generation should ignore them.
*/ */
public static final String JME_PHYSICSIGNORE = "JmePhysicsIgnore"; public static final String JME_PHYSICSIGNORE = "JmePhysicsIgnore";
/** /**
* For geometries using shared mesh, this will specify the shared * For geometries using shared mesh, this will specify the shared
* mesh reference. * mesh reference.
*/ */
public static final String JME_SHAREDMESH = "JmeSharedMesh"; public static final String JME_SHAREDMESH = "JmeSharedMesh";
protected byte type; private static final int TYPE_INTEGER = 0;
protected Object value; private static final int TYPE_FLOAT = 1;
private static final int TYPE_BOOLEAN = 2;
private static final int TYPE_STRING = 3;
private static final int TYPE_LONG = 4;
private static final int TYPE_SAVABLE = 5;
private static final int TYPE_LIST = 6;
private static final int TYPE_MAP = 7;
private static final int TYPE_ARRAY = 8;
protected byte type;
protected Object value;
public UserData() { public UserData() {
} }
/** /**
* Creates a new <code>UserData</code> with the given * Creates a new <code>UserData</code> with the given
* type and value. * type and value.
* *
* @param type Type of data, should be between 0 and 4. * @param type
* @param value Value of the data * Type of data, should be between 0 and 8.
* @param value
* Value of the data
*/ */
public UserData(byte type, Object value) { public UserData(byte type, Object value) {
assert type >= 0 && type <= 4; assert type >= 0 && type <= 8;
this.type = type; this.type = type;
this.value = value; this.value = value;
} }
@ -85,15 +103,23 @@ public final class UserData implements Savable {
public static byte getObjectType(Object type) { public static byte getObjectType(Object type) {
if (type instanceof Integer) { if (type instanceof Integer) {
return 0; return TYPE_INTEGER;
} else if (type instanceof Float) { } else if (type instanceof Float) {
return 1; return TYPE_FLOAT;
} else if (type instanceof Boolean) { } else if (type instanceof Boolean) {
return 2; return TYPE_BOOLEAN;
} else if (type instanceof String) { } else if (type instanceof String) {
return 3; return TYPE_STRING;
} else if (type instanceof Long) { } else if (type instanceof Long) {
return 4; return TYPE_LONG;
} else if (type instanceof Savable) {
return TYPE_SAVABLE;
} else if (type instanceof List) {
return TYPE_LIST;
} else if (type instanceof Map) {
return TYPE_MAP;
} else if (type instanceof Object[]) {
return TYPE_ARRAY;
} else { } else {
throw new IllegalArgumentException("Unsupported type: " + type.getClass().getName()); throw new IllegalArgumentException("Unsupported type: " + type.getClass().getName());
} }
@ -101,56 +127,195 @@ public final class UserData implements Savable {
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write(type, "type", (byte)0); oc.write(type, "type", (byte) 0);
switch (type) { switch (type) {
case 0: case TYPE_INTEGER:
int i = (Integer) value; int i = (Integer) value;
oc.write(i, "intVal", 0); oc.write(i, "intVal", 0);
break; break;
case 1: case TYPE_FLOAT:
float f = (Float) value; float f = (Float) value;
oc.write(f, "floatVal", 0f); oc.write(f, "floatVal", 0f);
break; break;
case 2: case TYPE_BOOLEAN:
boolean b = (Boolean) value; boolean b = (Boolean) value;
oc.write(b, "boolVal", false); oc.write(b, "boolVal", false);
break; break;
case 3: case TYPE_STRING:
String s = (String) value; String s = (String) value;
oc.write(s, "strVal", null); oc.write(s, "strVal", null);
break; break;
case 4: case TYPE_LONG:
Long l = (Long) value; Long l = (Long) value;
oc.write(l, "longVal", 0l); oc.write(l, "longVal", 0l);
break; break;
case TYPE_SAVABLE:
Savable sav = (Savable) value;
oc.write(sav, "savableVal", null);
break;
case TYPE_LIST:
this.writeList(oc, (List<?>) value, "0");
break;
case TYPE_MAP:
Map<?, ?> map = (Map<?, ?>) value;
this.writeList(oc, map.keySet(), "0");
this.writeList(oc, map.values(), "1");
break;
case TYPE_ARRAY:
this.writeList(oc, Arrays.asList((Object[]) value), "0");
break;
default: default:
throw new UnsupportedOperationException(); throw new UnsupportedOperationException("Unsupported value type: " + value.getClass());
} }
} }
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
type = ic.readByte("type", (byte) 0); type = ic.readByte("type", (byte) 0);
switch (type) { switch (type) {
case 0: case TYPE_INTEGER:
value = ic.readInt("intVal", 0); value = ic.readInt("intVal", 0);
break; break;
case 1: case TYPE_FLOAT:
value = ic.readFloat("floatVal", 0f); value = ic.readFloat("floatVal", 0f);
break; break;
case 2: case TYPE_BOOLEAN:
value = ic.readBoolean("boolVal", false); value = ic.readBoolean("boolVal", false);
break; break;
case 3: case TYPE_STRING:
value = ic.readString("strVal", null); value = ic.readString("strVal", null);
break; break;
case 4: case TYPE_LONG:
value = ic.readLong("longVal", 0l); value = ic.readLong("longVal", 0l);
break; break;
case TYPE_SAVABLE:
value = ic.readSavable("savableVal", null);
break;
case TYPE_LIST:
value = this.readList(ic, "0");
break;
case TYPE_MAP:
Map<Object, Object> map = new HashMap<Object, Object>();
List<?> keys = this.readList(ic, "0");
List<?> values = this.readList(ic, "1");
for (int i = 0; i < keys.size(); ++i) {
map.put(keys.get(i), values.get(i));
}
value = map;
break;
case TYPE_ARRAY:
value = this.readList(ic, "0").toArray();
break;
default: default:
throw new UnsupportedOperationException(); throw new UnsupportedOperationException("Unknown type of stored data: " + type);
}
}
/**
* The method stores a list in the capsule.
* @param oc
* output capsule
* @param list
* the list to be stored
* @throws IOException
*/
private void writeList(OutputCapsule oc, Collection<?> list, String listName) throws IOException {
if (list != null) {
oc.write(list.size(), listName + "size", 0);
int counter = 0;
for (Object o : list) {
// t is for 'type'; v is for 'value'
if (o instanceof Integer) {
oc.write(TYPE_INTEGER, listName + "t" + counter, 0);
oc.write((Integer) o, listName + "v" + counter, 0);
} else if (o instanceof Float) {
oc.write(TYPE_FLOAT, listName + "t" + counter, 0);
oc.write((Float) o, listName + "v" + counter, 0f);
} else if (o instanceof Boolean) {
oc.write(TYPE_BOOLEAN, listName + "t" + counter, 0);
oc.write((Boolean) o, listName + "v" + counter, false);
} else if (o instanceof String || o == null) {// treat null's like Strings just to store them and keep the List like the user intended
oc.write(TYPE_STRING, listName + "t" + counter, 0);
oc.write((String) o, listName + "v" + counter, null);
} else if (o instanceof Long) {
oc.write(TYPE_LONG, listName + "t" + counter, 0);
oc.write((Long) o, listName + "v" + counter, 0L);
} else if (o instanceof Savable) {
oc.write(TYPE_SAVABLE, listName + "t" + counter, 0);
oc.write((Savable) o, listName + "v" + counter, null);
} else if(o instanceof Object[]) {
oc.write(TYPE_ARRAY, listName + "t" + counter, 0);
this.writeList(oc, Arrays.asList((Object[]) o), listName + "v" + counter);
} else if(o instanceof List) {
oc.write(TYPE_LIST, listName + "t" + counter, 0);
this.writeList(oc, (List<?>) o, listName + "v" + counter);
} else if(o instanceof Map) {
oc.write(TYPE_MAP, listName + "t" + counter, 0);
Map<?, ?> map = (Map<?, ?>) o;
this.writeList(oc, map.keySet(), listName + "v(keys)" + counter);
this.writeList(oc, map.values(), listName + "v(vals)" + counter);
} else {
throw new UnsupportedOperationException("Unsupported type stored in the list: " + o.getClass());
}
++counter;
}
} else {
oc.write(0, "size", 0);
}
}
/**
* The method loads a list from the given input capsule.
* @param ic
* the input capsule
* @return loaded list (an empty list in case its size is 0)
* @throws IOException
*/
private List<?> readList(InputCapsule ic, String listName) throws IOException {
int size = ic.readInt(listName + "size", 0);
List<Object> list = new ArrayList<Object>(size);
for (int i = 0; i < size; ++i) {
int type = ic.readInt(listName + "t" + i, 0);
switch (type) {
case TYPE_INTEGER:
list.add(ic.readInt(listName + "v" + i, 0));
break;
case TYPE_FLOAT:
list.add(ic.readFloat(listName + "v" + i, 0));
break;
case TYPE_BOOLEAN:
list.add(ic.readBoolean(listName + "v" + i, false));
break;
case TYPE_STRING:
list.add(ic.readString(listName + "v" + i, null));
break;
case TYPE_LONG:
list.add(ic.readLong(listName + "v" + i, 0L));
break;
case TYPE_SAVABLE:
list.add(ic.readSavable(listName + "v" + i, null));
break;
case TYPE_ARRAY:
list.add(this.readList(ic, listName + "v" + i).toArray());
break;
case TYPE_LIST:
list.add(this.readList(ic, listName + "v" + i));
break;
case TYPE_MAP:
Map<Object, Object> map = new HashMap<Object, Object>();
List<?> keys = this.readList(ic, listName + "v(keys)" + i);
List<?> values = this.readList(ic, listName + "v(vals)" + i);
for (int j = 0; j < keys.size(); ++j) {
map.put(keys.get(j), values.get(j));
}
list.add(map);
break;
default:
throw new UnsupportedOperationException("Unknown type of stored data in a list: " + type);
}
} }
return list;
} }
} }

Loading…
Cancel
Save