Feature: added support for loading assets linked from external blender
files.
This commit is contained in:
parent
a37f38a412
commit
eb767e7580
@ -32,36 +32,19 @@
|
||||
package com.jme3.asset;
|
||||
|
||||
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.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.material.Material;
|
||||
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.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class BlenderKey extends ModelKey {
|
||||
|
||||
protected static final int DEFAULT_FPS = 25;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
/** The root path for all the assets. */
|
||||
protected String assetRootPath;
|
||||
@ -268,6 +251,7 @@ public class BlenderKey extends ModelKey {
|
||||
* @param featuresToLoad
|
||||
* bitwise flag of FeaturesToLoad interface values
|
||||
*/
|
||||
@Deprecated
|
||||
public void includeInLoading(int featuresToLoad) {
|
||||
this.featuresToLoad |= featuresToLoad;
|
||||
}
|
||||
@ -277,10 +261,12 @@ public class BlenderKey extends ModelKey {
|
||||
* @param featuresNotToLoad
|
||||
* bitwise flag of FeaturesToLoad interface values
|
||||
*/
|
||||
@Deprecated
|
||||
public void excludeFromLoading(int featuresNotToLoad) {
|
||||
featuresToLoad &= ~featuresNotToLoad;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean shouldLoad(int featureToLoad) {
|
||||
return (featuresToLoad & featureToLoad) != 0;
|
||||
}
|
||||
@ -290,6 +276,7 @@ public class BlenderKey extends ModelKey {
|
||||
* the blender file loader.
|
||||
* @return features that will be loaded by the blender file loader
|
||||
*/
|
||||
@Deprecated
|
||||
public int getFeaturesToLoad() {
|
||||
return featuresToLoad;
|
||||
}
|
||||
@ -317,15 +304,6 @@ public class BlenderKey extends ModelKey {
|
||||
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
|
||||
* 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.
|
||||
* @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)
|
||||
*/
|
||||
@Deprecated
|
||||
public static interface FeaturesToLoad {
|
||||
|
||||
int SCENES = 0x0000FFFF;
|
||||
@ -745,281 +726,4 @@ public class BlenderKey extends ModelKey {
|
||||
*/
|
||||
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;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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.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
|
||||
@ -49,14 +66,16 @@ import com.jme3.scene.plugins.blender.objects.Properties;
|
||||
* @author Marcin Roguski
|
||||
*/
|
||||
public abstract class AbstractBlenderHelper {
|
||||
private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName());
|
||||
|
||||
/** The blender context. */
|
||||
protected BlenderContext blenderContext;
|
||||
protected BlenderContext blenderContext;
|
||||
/** 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. */
|
||||
protected boolean fixUpAxis;
|
||||
protected boolean fixUpAxis;
|
||||
/** 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
|
||||
@ -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.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;
|
||||
|
||||
/**
|
||||
@ -64,49 +65,51 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
*/
|
||||
public class BlenderContext {
|
||||
/** The blender file version. */
|
||||
private int blenderVersion;
|
||||
private int blenderVersion;
|
||||
/** The blender key. */
|
||||
private BlenderKey blenderKey;
|
||||
private BlenderKey blenderKey;
|
||||
/** The header of the file block. */
|
||||
private DnaBlockData dnaBlockData;
|
||||
private DnaBlockData dnaBlockData;
|
||||
/** The scene structure. */
|
||||
private Structure sceneStructure;
|
||||
private Structure sceneStructure;
|
||||
/** The input stream of the blend file. */
|
||||
private BlenderInputStream inputStream;
|
||||
private BlenderInputStream inputStream;
|
||||
/** The asset manager. */
|
||||
private AssetManager assetManager;
|
||||
private AssetManager assetManager;
|
||||
/** 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.
|
||||
*/
|
||||
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. */
|
||||
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
|
||||
* first object in the value table is the loaded structure and the second -
|
||||
* 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. */
|
||||
private Stack<Structure> parentStack = new Stack<Structure>();
|
||||
private Stack<Structure> parentStack = new Stack<Structure>();
|
||||
/** 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. */
|
||||
private Map<Long, List<Animation>> animations = new HashMap<Long, List<Animation>>();
|
||||
private Map<Long, List<Animation>> animations = new HashMap<Long, List<Animation>>();
|
||||
/** 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. */
|
||||
private Map<Skeleton, Node> nodesWithSkeletons = new HashMap<Skeleton, Node>();
|
||||
private Map<Skeleton, Node> nodesWithSkeletons = new HashMap<Skeleton, Node>();
|
||||
/** 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. */
|
||||
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. */
|
||||
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. */
|
||||
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.
|
||||
@ -231,10 +234,10 @@ public class BlenderContext {
|
||||
*/
|
||||
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
|
||||
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
|
||||
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
|
||||
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
|
||||
if (headers == null) {
|
||||
headers = new ArrayList<FileBlockHeader>();
|
||||
fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
|
||||
fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers);
|
||||
}
|
||||
headers.add(fileBlockHeader);
|
||||
}
|
||||
@ -258,7 +261,7 @@ public class BlenderContext {
|
||||
* the code of file blocks
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -299,7 +302,7 @@ public class BlenderContext {
|
||||
throw new IllegalArgumentException("One of the given arguments is null!");
|
||||
}
|
||||
Map<LoadedDataType, Object> map = loadedFeatures.get(oldMemoryAddress);
|
||||
if(map == null) {
|
||||
if (map == null) {
|
||||
map = new HashMap<BlenderContext.LoadedDataType, Object>();
|
||||
loadedFeatures.put(oldMemoryAddress, map);
|
||||
}
|
||||
@ -325,6 +328,48 @@ public class BlenderContext {
|
||||
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.
|
||||
*
|
||||
|
@ -33,17 +33,21 @@ package com.jme3.scene.plugins.blender;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.animation.Animation;
|
||||
import com.jme3.asset.AssetInfo;
|
||||
import com.jme3.asset.AssetLoader;
|
||||
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.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.LightNode;
|
||||
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.BlenderInputStream;
|
||||
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.Structure;
|
||||
import com.jme3.scene.plugins.blender.landscape.LandscapeHelper;
|
||||
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.meshes.MeshHelper;
|
||||
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
|
||||
import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
|
||||
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||
import com.jme3.scene.plugins.blender.particles.ParticlesHelper;
|
||||
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.
|
||||
@ -83,72 +91,130 @@ public class BlenderLoader implements AssetLoader {
|
||||
try {
|
||||
this.setup(assetInfo);
|
||||
|
||||
List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
|
||||
BlenderKey blenderKey = blenderContext.getBlenderKey();
|
||||
LoadingResults loadingResults = blenderKey.prepareLoadingResults();
|
||||
|
||||
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
|
||||
animationHelper.loadAnimations();
|
||||
|
||||
|
||||
BlenderKey blenderKey = blenderContext.getBlenderKey();
|
||||
LoadedFeatures loadedFeatures = new LoadedFeatures();
|
||||
for (FileBlockHeader block : blocks) {
|
||||
switch (block.getCode()) {
|
||||
case FileBlockHeader.BLOCK_OB00:// Object
|
||||
case BLOCK_OB00:
|
||||
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||
Object object = objectHelper.toObject(block.getStructure(blenderContext), blenderContext);
|
||||
if (object instanceof LightNode) {
|
||||
loadingResults.addLight((LightNode) object);
|
||||
} 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() });
|
||||
Node object = (Node) objectHelper.toObject(block.getStructure(blenderContext), blenderContext);
|
||||
if (LOGGER.isLoggable(Level.FINE)) {
|
||||
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { object.getName(), object.getLocalTranslation().toString(), object.getParent() == null ? "null" : object.getParent().getName() });
|
||||
}
|
||||
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;
|
||||
case BLOCK_MA00:// Material
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
|
||||
loadedFeatures.materials.add(materialContext);
|
||||
break;
|
||||
case BLOCK_ME00:// Mesh
|
||||
MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
|
||||
TemporalMesh temporalMesh = meshHelper.toTemporalMesh(block.getStructure(blenderContext), blenderContext);
|
||||
loadedFeatures.meshes.add(temporalMesh);
|
||||
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;
|
||||
case BLOCK_TE00:
|
||||
Structure textureStructure = block.getStructure(blenderContext);
|
||||
int type = ((Number) textureStructure.getFieldValue("type")).intValue();
|
||||
if (type == TextureHelper.TEX_IMAGE) {
|
||||
TextureHelper texHelper = blenderContext.getHelper(TextureHelper.class);
|
||||
Texture texture = texHelper.getTexture(textureStructure, null, blenderContext);
|
||||
if (texture != null) {// null is returned when texture has no image
|
||||
loadedFeatures.textures.add(texture);
|
||||
}
|
||||
if (this.isRootObject(loadingResults, (Node) object)) {
|
||||
loadingResults.addObject((Node) object);
|
||||
} else {
|
||||
LOGGER.fine("Only image textures can be loaded as unlinked assets. Generated textures will be applied to an existing object.");
|
||||
}
|
||||
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 FileBlockHeader.BLOCK_MA00:// Material
|
||||
// MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
// MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
|
||||
// if (blenderKey.isLoadUnlinkedAssets() && blenderKey.shouldLoad(FeaturesToLoad.MATERIALS)) {
|
||||
// loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext)));
|
||||
// }
|
||||
// break;
|
||||
case FileBlockHeader.BLOCK_SC00:// Scene
|
||||
if (blenderKey.shouldLoad(FeaturesToLoad.SCENES)) {
|
||||
sceneBlocks.add(block);
|
||||
}
|
||||
break;
|
||||
case FileBlockHeader.BLOCK_WO00:// World
|
||||
if (blenderKey.shouldLoad(FeaturesToLoad.WORLD)) {
|
||||
Structure worldStructure = block.getStructure(blenderContext);
|
||||
String worldName = worldStructure.getName();
|
||||
if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
|
||||
LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class);
|
||||
Light ambientLight = landscapeHelper.toAmbientLight(worldStructure);
|
||||
if(ambientLight != null) {
|
||||
loadingResults.addLight(new LightNode(null, ambientLight));
|
||||
}
|
||||
loadingResults.setSky(landscapeHelper.toSky(worldStructure));
|
||||
loadingResults.addFilter(landscapeHelper.toFog(worldStructure));
|
||||
loadingResults.setBackgroundColor(landscapeHelper.toBackgroundColor(worldStructure));
|
||||
}
|
||||
}
|
||||
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.bakeConstraints(blenderContext);
|
||||
|
||||
// load the scene at the very end so that the root nodes have no parent during loading or constraints applying
|
||||
for (FileBlockHeader sceneBlock : sceneBlocks) {
|
||||
loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext)));
|
||||
LOGGER.fine("Loading scenes and attaching them to the root object.");
|
||||
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
|
||||
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext)));
|
||||
}
|
||||
|
||||
return loadingResults;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new IOException(e.getLocalizedMessage(), 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.
|
||||
* @param structure
|
||||
* structure of a scene
|
||||
* @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);
|
||||
Node result = new Node(structure.getName());
|
||||
try {
|
||||
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
|
||||
for (Structure b : base) {
|
||||
Pointer pObject = (Pointer) b.getFieldValue("object");
|
||||
if (pObject.isNotNull()) {
|
||||
Structure objectStructure = pObject.fetchData().get(0);
|
||||
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
|
||||
for (Structure b : base) {
|
||||
Pointer pObject = (Pointer) b.getFieldValue("object");
|
||||
if (pObject.isNotNull()) {
|
||||
Structure objectStructure = pObject.fetchData().get(0);
|
||||
|
||||
Object object = objectHelper.toObject(objectStructure, blenderContext);
|
||||
if (object instanceof LightNode) {
|
||||
result.addLight(((LightNode) object).getLight());
|
||||
result.attachChild((LightNode) 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 (((Node) object).getParent() == null) {
|
||||
result.attachChild((Spatial) object);
|
||||
}
|
||||
Object object = objectHelper.toObject(objectStructure, blenderContext);
|
||||
if (object instanceof LightNode) {
|
||||
result.addLight(((LightNode) object).getLight());// FIXME: check if this is needed !!!
|
||||
result.attachChild((LightNode) 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 (((Node) object).getParent() == null) {
|
||||
result.attachChild((Spatial) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (BlenderFileException e) {
|
||||
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -261,7 +301,7 @@ public class BlenderLoader implements AssetLoader {
|
||||
blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext));
|
||||
blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(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)
|
||||
FileBlockHeader sceneFileBlock = null;
|
||||
do {
|
||||
@ -269,7 +309,7 @@ public class BlenderLoader implements AssetLoader {
|
||||
if (!fileBlock.isDnaBlock()) {
|
||||
blocks.add(fileBlock);
|
||||
// save the scene's file block
|
||||
if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) {
|
||||
if (fileBlock.getCode() == BlockCode.BLOCK_SC00) {
|
||||
sceneFileBlock = fileBlock;
|
||||
}
|
||||
}
|
||||
@ -287,4 +327,39 @@ public class BlenderLoader implements AssetLoader {
|
||||
blenderContext = 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;
|
||||
|
||||
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.
|
||||
*
|
||||
* @deprecated this class is deprecated; use BlenderLoader instead
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
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.BlenderInputStream;
|
||||
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.Structure;
|
||||
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||
@ -48,7 +49,7 @@ public class AnimationHelper extends AbstractBlenderHelper {
|
||||
*/
|
||||
public void loadAnimations() throws BlenderFileException {
|
||||
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) {
|
||||
for (FileBlockHeader header : actionHeaders) {
|
||||
Structure actionStructure = header.getStructure(blenderContext);
|
||||
|
@ -5,7 +5,6 @@ import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.scene.CameraNode;
|
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
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
|
||||
* blender file
|
||||
*/
|
||||
public CameraNode toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
public Camera toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
if (blenderVersion >= 250) {
|
||||
return this.toCamera250(structure, blenderContext.getSceneStructure());
|
||||
} else {
|
||||
@ -63,7 +62,7 @@ public class CameraHelper extends AbstractBlenderHelper {
|
||||
* an exception is thrown when there are problems with the
|
||||
* 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 height = DEFAULT_CAM_HEIGHT;
|
||||
if (sceneStructure != null) {
|
||||
@ -99,7 +98,7 @@ public class CameraHelper extends AbstractBlenderHelper {
|
||||
sensor = ((Number) structure.getFieldValue(sensorName)).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) {
|
||||
fovY = fov * FastMath.RAD_TO_DEG;
|
||||
} else {
|
||||
@ -111,7 +110,8 @@ public class CameraHelper extends AbstractBlenderHelper {
|
||||
fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
|
||||
}
|
||||
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
|
||||
* 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);
|
||||
int type = ((Number) structure.getFieldValue("type")).intValue();
|
||||
if (type != 0 && type != 1) {
|
||||
@ -142,6 +142,7 @@ public class CameraHelper extends AbstractBlenderHelper {
|
||||
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
|
||||
}
|
||||
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;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
|
||||
/**
|
||||
@ -39,39 +41,23 @@ import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
* @author Marcin Roguski
|
||||
*/
|
||||
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]. */
|
||||
private int code;
|
||||
private BlockCode code;
|
||||
/** 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
|
||||
* size)].
|
||||
*/
|
||||
private long oldMemoryAddress;
|
||||
private long oldMemoryAddress;
|
||||
/** Index of the SDNA structure [4 bytes]. */
|
||||
private int sdnaIndex;
|
||||
private int sdnaIndex;
|
||||
/** 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. */
|
||||
private int blockPosition;
|
||||
private int blockPosition;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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();
|
||||
oldMemoryAddress = inputStream.readPointer();
|
||||
sdnaIndex = inputStream.readInt();
|
||||
count = inputStream.readInt();
|
||||
blockPosition = inputStream.getPosition();
|
||||
if (FileBlockHeader.BLOCK_DNA1 == code) {
|
||||
if (BlockCode.BLOCK_DNA1 == code) {
|
||||
blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext));
|
||||
} else {
|
||||
inputStream.setPosition(blockPosition + size);
|
||||
@ -116,7 +102,7 @@ public class FileBlockHeader {
|
||||
* This method returns the code of this data block.
|
||||
* @return the code of this data block
|
||||
*/
|
||||
public int getCode() {
|
||||
public BlockCode getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@ -157,7 +143,7 @@ public class FileBlockHeader {
|
||||
* @return true if this block is the last one in the file nad false otherwise
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public boolean isDnaBlock() {
|
||||
return FileBlockHeader.BLOCK_DNA1 == code;
|
||||
return BlockCode.BLOCK_DNA1 == code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";
|
||||
return "FILE BLOCK HEADER [" + code.toString() + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* This method transforms the coded bloch id into a string value.
|
||||
* @param code
|
||||
* the id of the block
|
||||
* @return the string value of the block id
|
||||
*/
|
||||
protected String codeToString(int code) {
|
||||
char c1 = (char) ((code & 0xFF000000) >> 24);
|
||||
char c2 = (char) ((code & 0xFF0000) >> 16);
|
||||
char c3 = (char) ((code & 0xFF00) >> 8);
|
||||
char c4 = (char) (code & 0xFF);
|
||||
return String.valueOf(c1) + c2 + c3 + c4;
|
||||
public static enum BlockCode {
|
||||
BLOCK_ME00('M' << 24 | 'E' << 16), // mesh
|
||||
BLOCK_CA00('C' << 24 | 'A' << 16), // camera
|
||||
BLOCK_LA00('L' << 24 | 'A' << 16), // lamp
|
||||
BLOCK_OB00('O' << 24 | 'B' << 16), // object
|
||||
BLOCK_MA00('M' << 24 | 'A' << 16), // material
|
||||
BLOCK_SC00('S' << 24 | 'C' << 16), // scene
|
||||
BLOCK_WO00('W' << 24 | 'O' << 16), // world
|
||||
BLOCK_TX00('T' << 24 | 'X' << 16), // texture
|
||||
BLOCK_IP00('I' << 24 | 'P' << 16), // ipo
|
||||
BLOCK_AC00('A' << 24 | 'C' << 16), // action
|
||||
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;
|
||||
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
|
||||
|
@ -40,7 +40,6 @@ import com.jme3.light.PointLight;
|
||||
import com.jme3.light.SpotLight;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.scene.LightNode;
|
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||
@ -67,8 +66,8 @@ public class LightHelper extends AbstractBlenderHelper {
|
||||
super(blenderVersion, blenderContext);
|
||||
}
|
||||
|
||||
public LightNode toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
LightNode result = (LightNode) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE);
|
||||
public Light toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
Light result = (Light) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
@ -111,6 +110,7 @@ public class LightHelper extends AbstractBlenderHelper {
|
||||
float g = ((Number) structure.getFieldValue("g")).floatValue();
|
||||
float b = ((Number) structure.getFieldValue("b")).floatValue();
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
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.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.Savable;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.BlendMode;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
@ -30,7 +34,7 @@ import com.jme3.util.BufferUtils;
|
||||
* This class holds the data about the material.
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public final class MaterialContext {
|
||||
public final class MaterialContext implements Savable {
|
||||
private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName());
|
||||
|
||||
// texture mapping types
|
||||
@ -67,7 +71,7 @@ public final class MaterialContext {
|
||||
int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue();
|
||||
diffuseShader = DiffuseShader.values()[diff_shader];
|
||||
ambientFactor = ((Number) structure.getFieldValue("amb")).floatValue();
|
||||
|
||||
|
||||
if (shadeless) {
|
||||
float r = ((Number) structure.getFieldValue("r")).floatValue();
|
||||
float g = ((Number) structure.getFieldValue("g")).floatValue();
|
||||
@ -107,6 +111,13 @@ public final class MaterialContext {
|
||||
this.transparent = transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the material
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies material to a given geometry.
|
||||
*
|
||||
@ -314,4 +325,14 @@ public final class MaterialContext {
|
||||
float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();
|
||||
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
|
||||
*/
|
||||
public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
LOGGER.log(Level.FINE, "Loading material.");
|
||||
MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE);
|
||||
if (result != null) {
|
||||
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);
|
||||
LOGGER.log(Level.FINE, "Material''s name: {0}", result.name);
|
||||
Long oma = structure.getOldMemoryAddress();
|
||||
|
@ -40,7 +40,6 @@ import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
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.Pointer;
|
||||
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.objects.Properties;
|
||||
|
||||
@ -106,17 +104,18 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
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();
|
||||
LOGGER.log(Level.FINE, "Reading mesh: {0}.", name);
|
||||
temporalMesh = new TemporalMesh(meshStructure, blenderContext);
|
||||
|
||||
LOGGER.fine("Loading materials.");
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
MaterialContext[] materials = null;
|
||||
if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
|
||||
materials = materialHelper.getMaterials(meshStructure, blenderContext);
|
||||
}
|
||||
temporalMesh.setMaterials(materials);
|
||||
temporalMesh.setMaterials(materialHelper.getMaterials(meshStructure, blenderContext));
|
||||
|
||||
LOGGER.fine("Reading custom properties.");
|
||||
Properties properties = this.loadProperties(meshStructure, blenderContext);
|
||||
|
@ -40,12 +40,15 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
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.Matrix4f;
|
||||
import com.jme3.math.Transform;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.scene.CameraNode;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.LightNode;
|
||||
import com.jme3.scene.Mesh.Mode;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
@ -106,39 +109,34 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
* an exception is thrown when the given data is inapropriate
|
||||
*/
|
||||
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();
|
||||
ObjectType objectType = ObjectType.valueOf(type);
|
||||
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();
|
||||
if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) {
|
||||
LOGGER.fine("The layer this object is located in is not included in loading.");
|
||||
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);
|
||||
String name = objectStructure.getName();
|
||||
LOGGER.log(Level.FINE, "Loading obejct: {0}", name);
|
||||
|
||||
|
||||
int restrictflag = ((Number) objectStructure.getFieldValue("restrictflag")).intValue();
|
||||
boolean visible = (restrictflag & 0x01) != 0;
|
||||
|
||||
@ -171,7 +169,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
Pointer pMesh = (Pointer) objectStructure.getFieldValue("data");
|
||||
List<Structure> meshesArray = pMesh.fetchData();
|
||||
TemporalMesh temporalMesh = meshHelper.toTemporalMesh(meshesArray.get(0), blenderContext);
|
||||
if(temporalMesh != null) {
|
||||
if (temporalMesh != null) {
|
||||
result.attachChild(temporalMesh);
|
||||
}
|
||||
break;
|
||||
@ -183,7 +181,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
|
||||
Structure curveData = pCurve.fetchData().get(0);
|
||||
TemporalMesh curvesTemporalMesh = curvesHelper.toCurve(curveData, blenderContext);
|
||||
if(curvesTemporalMesh != null) {
|
||||
if (curvesTemporalMesh != null) {
|
||||
result.attachChild(curvesTemporalMesh);
|
||||
}
|
||||
}
|
||||
@ -193,10 +191,12 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
if (pLamp.isNotNull()) {
|
||||
LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
|
||||
List<Structure> lampsArray = pLamp.fetchData();
|
||||
result = lightHelper.toLight(lampsArray.get(0), blenderContext);
|
||||
if (result == null) {
|
||||
Light light = lightHelper.toLight(lampsArray.get(0), blenderContext);
|
||||
if (light == null) {
|
||||
// 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);
|
||||
} else {
|
||||
result = new LightNode(name, light);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -205,19 +205,25 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
if (pCamera.isNotNull()) {
|
||||
CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
|
||||
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;
|
||||
default:
|
||||
LOGGER.log(Level.WARNING, "Unsupported object type: {0}", type);
|
||||
}
|
||||
|
||||
|
||||
if (result != null) {
|
||||
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();
|
||||
blenderContext.addLoadedFeatures(oma, LoadedDataType.STRUCTURE, objectStructure);
|
||||
blenderContext.addLoadedFeatures(oma, LoadedDataType.FEATURE, result);
|
||||
|
||||
|
||||
blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress());
|
||||
if (objectType == ObjectType.ARMATURE) {
|
||||
blenderContext.addMarker(ARMATURE_NODE_MARKER, result, Boolean.TRUE);
|
||||
@ -235,13 +241,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
for (Modifier modifier : modifiers) {
|
||||
modifier.apply(result, blenderContext);
|
||||
}
|
||||
|
||||
|
||||
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.");
|
||||
((TemporalMesh)result.getChild(0)).toGeometries();
|
||||
((TemporalMesh) result.getChild(0)).toGeometries();
|
||||
}
|
||||
|
||||
|
||||
LOGGER.fine("Applying proper scale to the geometries.");
|
||||
for (Spatial child : result.getChildren()) {
|
||||
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
|
||||
* type.
|
||||
*
|
||||
* @param tex
|
||||
* @param textureStructure
|
||||
* texture structure filled with data
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
@ -130,23 +130,29 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
* this exception is thrown when the blend file structure is
|
||||
* somehow invalid or corrupted
|
||||
*/
|
||||
public Texture getTexture(Structure tex, Structure mTex, BlenderContext blenderContext) throws BlenderFileException {
|
||||
Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedDataType.FEATURE);
|
||||
public Texture getTexture(Structure textureStructure, Structure mTex, BlenderContext blenderContext) throws BlenderFileException {
|
||||
Texture result = (Texture) blenderContext.getLoadedFeature(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
|
||||
if (result != null) {
|
||||
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) {
|
||||
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()) {
|
||||
Structure image = pImage.fetchData().get(0);
|
||||
Texture loadedTexture = this.loadTexture(image, imaflag, blenderContext);
|
||||
Texture loadedTexture = this.loadImageAsTexture(image, imaflag, blenderContext);
|
||||
if (loadedTexture != null) {
|
||||
result = loadedTexture;
|
||||
this.applyColorbandAndColorFactors(tex, result.getImage(), blenderContext);
|
||||
this.applyColorbandAndColorFactors(textureStructure, result.getImage(), blenderContext);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -160,7 +166,7 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
case TEX_MUSGRAVE:
|
||||
case TEX_VORONOI:
|
||||
case TEX_DISTNOISE:
|
||||
result = new GeneratedTexture(tex, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext);
|
||||
result = new GeneratedTexture(textureStructure, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext);
|
||||
break;
|
||||
case TEX_NONE:// No texture, do nothing
|
||||
break;
|
||||
@ -169,13 +175,13 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
case TEX_PLUGIN:
|
||||
case TEX_ENVMAP:
|
||||
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;
|
||||
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) {
|
||||
result.setName(tex.getName());
|
||||
result.setName(textureStructure.getName());
|
||||
result.setWrap(WrapMode.Repeat);
|
||||
|
||||
// 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
|
||||
result.setKey(new GeneratedTextureKey(tex.getName()));
|
||||
result.setKey(new GeneratedTextureKey(textureStructure.getName()));
|
||||
}
|
||||
|
||||
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(tex.getOldMemoryAddress(), LoadedDataType.FEATURE, result);
|
||||
blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, textureStructure);
|
||||
blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -222,30 +228,40 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
* this exception is thrown when the blend file structure is
|
||||
* 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());
|
||||
Texture result = null;
|
||||
Image im = (Image) blenderContext.getLoadedFeature(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
|
||||
if (im == null) {
|
||||
String texturePath = imageStructure.getFieldValue("name").toString();
|
||||
Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile");
|
||||
if (pPackedFile.isNull()) {
|
||||
LOGGER.log(Level.FINE, "Reading texture from file: {0}", texturePath);
|
||||
result = this.loadImageFromFile(texturePath, imaflag, blenderContext);
|
||||
if ("ID".equals(imageStructure.getType())) {
|
||||
LOGGER.fine("Loading texture from external blend file.");
|
||||
result = (Texture) this.loadLibrary(imageStructure);
|
||||
} else {
|
||||
LOGGER.fine("Packed texture. Reading directly from the blend file!");
|
||||
Structure packedFile = pPackedFile.fetchData().get(0);
|
||||
Pointer pData = (Pointer) packedFile.getFieldValue("data");
|
||||
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress());
|
||||
blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition());
|
||||
ImageLoader imageLoader = new ImageLoader();
|
||||
String texturePath = imageStructure.getFieldValue("name").toString();
|
||||
Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile");
|
||||
if (pPackedFile.isNull()) {
|
||||
LOGGER.log(Level.FINE, "Reading texture from file: {0}", texturePath);
|
||||
result = this.loadImageFromFile(texturePath, imaflag, blenderContext);
|
||||
} else {
|
||||
LOGGER.fine("Packed texture. Reading directly from the blend file!");
|
||||
Structure packedFile = pPackedFile.fetchData().get(0);
|
||||
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(imageLoader.loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true));
|
||||
// Should the texture be flipped? It works for sinbad ..
|
||||
result = new Texture2D(new ImageLoader().loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -524,6 +540,18 @@ public class TextureHelper extends AbstractBlenderHelper {
|
||||
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")
|
||||
public List<CombinedTexture> readTextureData(Structure structure, float[] diffuseColorArray, boolean skyTexture) throws BlenderFileException {
|
||||
DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");
|
||||
|
@ -1,10 +1,12 @@
|
||||
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.materials.MaterialHelper;
|
||||
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
|
||||
@ -103,12 +105,12 @@ import jme3tools.converters.MipMapGenerator;
|
||||
|
||||
public void copyBlendingData(TextureBlender textureBlender) {
|
||||
if (textureBlender instanceof AbstractTextureBlender) {
|
||||
this.flag = ((AbstractTextureBlender) textureBlender).flag;
|
||||
this.negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture;
|
||||
this.blendType = ((AbstractTextureBlender) textureBlender).blendType;
|
||||
this.materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone();
|
||||
this.color = ((AbstractTextureBlender) textureBlender).color.clone();
|
||||
this.blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor;
|
||||
flag = ((AbstractTextureBlender) textureBlender).flag;
|
||||
negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture;
|
||||
blendType = ((AbstractTextureBlender) textureBlender).blendType;
|
||||
materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone();
|
||||
color = ((AbstractTextureBlender) textureBlender).color.clone();
|
||||
blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor;
|
||||
} else {
|
||||
LOGGER.warning("Cannot copy blending data from other types than " + this.getClass());
|
||||
}
|
||||
|
@ -31,13 +31,13 @@
|
||||
*/
|
||||
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.texture.Image;
|
||||
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.
|
||||
*
|
||||
@ -66,7 +66,6 @@ public class TextureBlenderFactory {
|
||||
* the texture format
|
||||
* @return texture blending class
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) {
|
||||
switch (format) {
|
||||
case Luminance8:
|
||||
|
@ -33,6 +33,12 @@ package com.jme3.scene;
|
||||
|
||||
import com.jme3.export.*;
|
||||
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
|
||||
@ -48,28 +54,40 @@ public final class UserData implements Savable {
|
||||
* shape generation should ignore them.
|
||||
*/
|
||||
public static final String JME_PHYSICSIGNORE = "JmePhysicsIgnore";
|
||||
|
||||
|
||||
/**
|
||||
* For geometries using shared mesh, this will specify the shared
|
||||
* mesh reference.
|
||||
*/
|
||||
public static final String JME_SHAREDMESH = "JmeSharedMesh";
|
||||
|
||||
protected byte type;
|
||||
protected Object value;
|
||||
public static final String JME_SHAREDMESH = "JmeSharedMesh";
|
||||
|
||||
private static final int TYPE_INTEGER = 0;
|
||||
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() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>UserData</code> with the given
|
||||
* Creates a new <code>UserData</code> with the given
|
||||
* type and value.
|
||||
*
|
||||
* @param type Type of data, should be between 0 and 4.
|
||||
* @param value Value of the data
|
||||
* @param type
|
||||
* Type of data, should be between 0 and 8.
|
||||
* @param value
|
||||
* Value of the data
|
||||
*/
|
||||
public UserData(byte type, Object value) {
|
||||
assert type >= 0 && type <= 4;
|
||||
assert type >= 0 && type <= 8;
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
@ -85,15 +103,23 @@ public final class UserData implements Savable {
|
||||
|
||||
public static byte getObjectType(Object type) {
|
||||
if (type instanceof Integer) {
|
||||
return 0;
|
||||
return TYPE_INTEGER;
|
||||
} else if (type instanceof Float) {
|
||||
return 1;
|
||||
return TYPE_FLOAT;
|
||||
} else if (type instanceof Boolean) {
|
||||
return 2;
|
||||
return TYPE_BOOLEAN;
|
||||
} else if (type instanceof String) {
|
||||
return 3;
|
||||
return TYPE_STRING;
|
||||
} 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 {
|
||||
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 {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(type, "type", (byte)0);
|
||||
oc.write(type, "type", (byte) 0);
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
case TYPE_INTEGER:
|
||||
int i = (Integer) value;
|
||||
oc.write(i, "intVal", 0);
|
||||
break;
|
||||
case 1:
|
||||
case TYPE_FLOAT:
|
||||
float f = (Float) value;
|
||||
oc.write(f, "floatVal", 0f);
|
||||
break;
|
||||
case 2:
|
||||
case TYPE_BOOLEAN:
|
||||
boolean b = (Boolean) value;
|
||||
oc.write(b, "boolVal", false);
|
||||
break;
|
||||
case 3:
|
||||
case TYPE_STRING:
|
||||
String s = (String) value;
|
||||
oc.write(s, "strVal", null);
|
||||
break;
|
||||
case 4:
|
||||
case TYPE_LONG:
|
||||
Long l = (Long) value;
|
||||
oc.write(l, "longVal", 0l);
|
||||
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:
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException("Unsupported value type: " + value.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
type = ic.readByte("type", (byte) 0);
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
case TYPE_INTEGER:
|
||||
value = ic.readInt("intVal", 0);
|
||||
break;
|
||||
case 1:
|
||||
case TYPE_FLOAT:
|
||||
value = ic.readFloat("floatVal", 0f);
|
||||
break;
|
||||
case 2:
|
||||
case TYPE_BOOLEAN:
|
||||
value = ic.readBoolean("boolVal", false);
|
||||
break;
|
||||
case 3:
|
||||
case TYPE_STRING:
|
||||
value = ic.readString("strVal", null);
|
||||
break;
|
||||
case 4:
|
||||
case TYPE_LONG:
|
||||
value = ic.readLong("longVal", 0l);
|
||||
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:
|
||||
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…
x
Reference in New Issue
Block a user