From b34649d399770af7f48cac4fab8836b41c076b26 Mon Sep 17 00:00:00 2001 From: jmekaelthas Date: Tue, 27 Oct 2015 21:05:32 +0100 Subject: [PATCH] Bugfix: fix to importing blend files with linked content. --- .../main/java/com/jme3/asset/BlenderKey.java | 4 + .../blender/AbstractBlenderHelper.java | 87 ++--------- .../scene/plugins/blender/BlenderContext.java | 137 ++++++++++++++++-- .../scene/plugins/blender/BlenderLoader.java | 86 ++++++++--- .../blender/landscape/LandscapeHelper.java | 1 + 5 files changed, 205 insertions(+), 110 deletions(-) diff --git a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java index c412c07b3..3b72088ab 100644 --- a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java +++ b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.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; } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java index e11101c14..142f757e2 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java @@ -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 pathsToCheck = new ArrayList(); - 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> linkedData = loadedAsset.getUserData("linkedData"); + for (Entry> entry : linkedData.entrySet()) { String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey(); - - List scenes = (List) entry.getValue().get("scenes"); - for (Node scene : scenes) { - blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene); - } - List objects = (List) entry.getValue().get("objects"); - for (Node object : objects) { - blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object); - } - List meshes = (List) entry.getValue().get("meshes"); - for (TemporalMesh mesh : meshes) { - blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh); - } - List materials = (List) entry.getValue().get("materials"); - for (MaterialContext materialContext : materials) { - blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext); - } - List textures = (List) entry.getValue().get("textures"); - for (Texture texture : textures) { - blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture); - } - List images = (List) entry.getValue().get("images"); - for (Texture image : images) { - blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image); - } - List animations = (List) entry.getValue().get("animations"); - for (Animation animation : animations) { - blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation); - } - List cameras = (List) entry.getValue().get("cameras"); - for (Camera camera : cameras) { - blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera); - } - List lights = (List) 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 filters = (List) 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); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java index 6e8042b09..58114cf28 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java @@ -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 blocks; + protected List blocks = new ArrayList(); /** * 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 headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode()); if (headers == null) { @@ -242,6 +248,13 @@ public class BlenderContext { headers.add(fileBlockHeader); } + /** + * @return the block headers + */ + public List 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 linkedFeatures = this.linkedFeatures.get(blenderFilePath); - if (linkedFeatures == null) { - linkedFeatures = new HashMap(); - 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 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 scenes = (List) linkedFeatures.get("scenes"); + if(scenes != null) { + for(Node scene : scenes) { + if(featureName.equals(scene.getName())) { + return scene; + } + } + } + } else if("OB".equals(namePrefix)) { + List features = (List) linkedFeatures.get("objects"); + if(features != null) { + for(Node feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("ME".equals(namePrefix)) { + List features = (List) linkedFeatures.get("meshes"); + if(features != null) { + for(Node feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("MA".equals(namePrefix)) { + List features = (List) linkedFeatures.get("materials"); + if(features != null) { + for(MaterialContext feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("TX".equals(namePrefix)) { + List features = (List) linkedFeatures.get("textures"); + if(features != null) { + for(Texture feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("IM".equals(namePrefix)) { + List features = (List) linkedFeatures.get("images"); + if(features != null) { + for(Texture feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("AC".equals(namePrefix)) { + List features = (List) linkedFeatures.get("animations"); + if(features != null) { + for(Animation feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("CA".equals(namePrefix)) { + List features = (List) linkedFeatures.get("cameras"); + if(features != null) { + for(Camera feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("LA".equals(namePrefix)) { + List features = (List) linkedFeatures.get("lights"); + if(features != null) { + for(Light feature : features) { + if(featureName.equals(feature.getName())) { + return feature; + } + } + } + } else if("FI".equals(featureName)) { + List features = (List) 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() + " ]"; + } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java index dd0160063..f519cb9b5 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java @@ -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 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 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(); + List blocks = new ArrayList(); 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; + } + } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java index 8466d5cce..32160c13a 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java @@ -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;