Bugfix: fix to importing blend files with linked content.

cleanup_build_scripts
jmekaelthas 9 years ago
parent edaf49c00c
commit b34649d399
  1. 4
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  2. 87
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  3. 137
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  4. 86
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  5. 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java

@ -230,18 +230,22 @@ public class BlenderKey extends ModelKey {
}
/**
* Not used any more.
* This method sets the asset root path.
* @param assetRootPath
* the assets root path
*/
@Deprecated
public void setAssetRootPath(String assetRootPath) {
this.assetRootPath = assetRootPath;
}
/**
* Not used any more.
* This method returns the asset root path.
* @return the asset root path
*/
@Deprecated
public String getAssetRootPath() {
return assetRootPath;
}

@ -31,8 +31,6 @@
*/
package com.jme3.scene.plugins.blender;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -40,25 +38,17 @@ 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.light.Light;
import com.jme3.math.FastMath;
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.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
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.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
@ -157,7 +147,6 @@ public abstract class AbstractBlenderHelper {
* @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()) {
@ -167,79 +156,21 @@ public abstract class AbstractBlenderHelper {
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);
}
BlenderKey blenderKey = new BlenderKey(path);
blenderKey.setLoadUnlinkedAssets(true);
try {
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
} catch (AssetNotFoundException e) {
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path);
}
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);
}
blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue());
}
} else {
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);

@ -44,8 +44,11 @@ import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey;
import com.jme3.light.Light;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.animations.BlenderAction;
import com.jme3.scene.plugins.blender.animations.BoneContext;
@ -55,6 +58,8 @@ import com.jme3.scene.plugins.blender.file.DnaBlockData;
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.materials.MaterialContext;
import com.jme3.texture.Texture;
/**
* The class that stores temporary data and manages it during loading the belnd
@ -77,7 +82,7 @@ public class BlenderContext {
/** The asset manager. */
private AssetManager assetManager;
/** The blocks read from the file. */
protected List<FileBlockHeader> blocks;
protected List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
/**
* A map containing the file block headers. The key is the old memory address.
*/
@ -233,6 +238,7 @@ public class BlenderContext {
* the block header to store
*/
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
blocks.add(fileBlockHeader);
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) {
@ -242,6 +248,13 @@ public class BlenderContext {
headers.add(fileBlockHeader);
}
/**
* @return the block headers
*/
public List<FileBlockHeader> getBlocks() {
return blocks;
}
/**
* This method returns the block header of a given memory address. If the
* header is not present then null is returned.
@ -332,22 +345,14 @@ public class BlenderContext {
* The method adds linked content to the blender context.
* @param blenderFilePath
* the path of linked blender file
* @param featureName
* the linked feature name
* @param featureGroup
* the linked feature group (ie. scenes, materials, meshes, etc.)
* @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);
}
}
@Deprecated
public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) {
// the method is deprecated and empty at the moment
}
/**
@ -358,9 +363,106 @@ public class BlenderContext {
* the feature name we want to get
* @return linked feature or null if none was found
*/
@SuppressWarnings("unchecked")
public Object getLinkedFeature(String blenderFilePath, String featureName) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
return linkedFeatures != null ? linkedFeatures.get(featureName) : null;
if(linkedFeatures != null) {
String namePrefix = (featureName.charAt(0) + "" + featureName.charAt(1)).toUpperCase();
featureName = featureName.substring(2);
if("SC".equals(namePrefix)) {
List<Node> scenes = (List<Node>) linkedFeatures.get("scenes");
if(scenes != null) {
for(Node scene : scenes) {
if(featureName.equals(scene.getName())) {
return scene;
}
}
}
} else if("OB".equals(namePrefix)) {
List<Node> features = (List<Node>) linkedFeatures.get("objects");
if(features != null) {
for(Node feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("ME".equals(namePrefix)) {
List<Node> features = (List<Node>) linkedFeatures.get("meshes");
if(features != null) {
for(Node feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("MA".equals(namePrefix)) {
List<MaterialContext> features = (List<MaterialContext>) linkedFeatures.get("materials");
if(features != null) {
for(MaterialContext feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("TX".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("textures");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("IM".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("images");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("AC".equals(namePrefix)) {
List<Animation> features = (List<Animation>) linkedFeatures.get("animations");
if(features != null) {
for(Animation feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("CA".equals(namePrefix)) {
List<Camera> features = (List<Camera>) linkedFeatures.get("cameras");
if(features != null) {
for(Camera feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("LA".equals(namePrefix)) {
List<Light> features = (List<Light>) linkedFeatures.get("lights");
if(features != null) {
for(Light feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("FI".equals(featureName)) {
List<Filter> features = (List<Filter>) linkedFeatures.get("lights");
if(features != null) {
for(Filter feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
}
}
return null;
}
/**
@ -659,4 +761,9 @@ public class BlenderContext {
public static enum LoadedDataType {
STRUCTURE, FEATURE, TEMPORAL_MESH;
}
@Override
public String toString() {
return blenderKey == null ? "BlenderContext [key = null]" : "BlenderContext [ key = " + blenderKey.toString() + " ]";
}
}

@ -31,6 +31,9 @@
*/
package com.jme3.scene.plugins.blender;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@ -41,9 +44,13 @@ import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetLoader;
import com.jme3.asset.AssetLocator;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey;
import com.jme3.asset.ModelKey;
import com.jme3.asset.StreamAssetInfo;
import com.jme3.light.Light;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
@ -81,22 +88,17 @@ import com.jme3.texture.Texture;
public class BlenderLoader implements AssetLoader {
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
/** The blocks read from the file. */
protected List<FileBlockHeader> blocks;
/** The blender context. */
protected BlenderContext blenderContext;
@Override
public Spatial load(AssetInfo assetInfo) throws IOException {
try {
this.setup(assetInfo);
BlenderContext blenderContext = this.setup(assetInfo);
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blocks) {
for (FileBlockHeader block : blenderContext.getBlocks()) {
switch (block.getCode()) {
case BLOCK_OB00:
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
@ -181,7 +183,7 @@ public class BlenderLoader implements AssetLoader {
LOGGER.fine("Loading scenes and attaching them to the root object.");
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext)));
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext), blenderContext));
}
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
@ -220,7 +222,7 @@ public class BlenderLoader implements AssetLoader {
} catch (Exception e) {
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
} finally {
this.clear();
this.clear(assetInfo);
}
}
@ -228,11 +230,12 @@ public class BlenderLoader implements AssetLoader {
* This method converts the given structure to a scene node.
* @param structure
* structure of a scene
* @param blenderContext the blender context
* @return scene's node
* @throws BlenderFileException
* an exception throw when problems with blender file occur
*/
private Node toScene(Structure structure) throws BlenderFileException {
private Node toScene(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Node result = new Node(structure.getName());
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
@ -265,7 +268,7 @@ public class BlenderLoader implements AssetLoader {
* @throws BlenderFileException
* an exception is throw when something wrong happens with blender file
*/
protected void setup(AssetInfo assetInfo) throws BlenderFileException {
protected BlenderContext setup(AssetInfo assetInfo) throws BlenderFileException {
// registering loaders
ModelKey modelKey = (ModelKey) assetInfo.getKey();
BlenderKey blenderKey;
@ -273,16 +276,15 @@ public class BlenderLoader implements AssetLoader {
blenderKey = (BlenderKey) modelKey;
} else {
blenderKey = new BlenderKey(modelKey.getName());
blenderKey.setAssetRootPath(modelKey.getFolder());
}
// opening stream
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
// reading blocks
blocks = new ArrayList<FileBlockHeader>();
List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
FileBlockHeader fileBlock;
blenderContext = new BlenderContext();
BlenderContext blenderContext = new BlenderContext();
blenderContext.setBlenderVersion(inputStream.getVersionNumber());
blenderContext.setAssetManager(assetInfo.getManager());
blenderContext.setInputStream(inputStream);
@ -317,15 +319,19 @@ public class BlenderLoader implements AssetLoader {
if (sceneFileBlock != null) {
blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
}
// adding locator for linked content
assetInfo.getManager().registerLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
return blenderContext;
}
/**
* The internal data is only needed during loading so make it unreachable so that the GC can release
* that memory (which can be quite large amount).
*/
protected void clear() {
blenderContext = null;
blocks = null;
protected void clear(AssetInfo assetInfo) {
assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
}
/**
@ -362,4 +368,50 @@ public class BlenderLoader implements AssetLoader {
*/
private ColorRGBA backgroundColor = ColorRGBA.Gray;
}
public static class LinkedContentLocator implements AssetLocator {
private File rootFolder;
@Override
public void setRootPath(String rootPath) {
rootFolder = new File(rootPath);
if(rootFolder.isFile()) {
rootFolder = rootFolder.getParentFile();
}
}
@SuppressWarnings("rawtypes")
@Override
public AssetInfo locate(AssetManager manager, AssetKey key) {
if(key instanceof BlenderKey) {
File linkedAbsoluteFile = new File(key.getName());
if(linkedAbsoluteFile.exists() && linkedAbsoluteFile.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedAbsoluteFile));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentAssetFolder = new File(rootFolder, linkedAbsoluteFile.getName());
if(linkedFileInCurrentAssetFolder.exists() && linkedFileInCurrentAssetFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentAssetFolder));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentFolder = new File(".", linkedAbsoluteFile.getName());
if(linkedFileInCurrentFolder.exists() && linkedFileInCurrentFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentFolder));
} catch (FileNotFoundException e) {
return null;
}
}
}
return null;
}
}
}

@ -81,6 +81,7 @@ public class LandscapeHelper extends AbstractBlenderHelper {
if ((mode & MODE_MIST) != 0) {
LOGGER.fine("Loading fog.");
result = new FogFilter();
result.setName("FIfog");
result.setFogColor(this.toBackgroundColor(worldStructure));
}
return result;

Loading…
Cancel
Save