| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- /*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- package com.jme3.scene.plugins.blender.objects;
- import java.util.Collection;
- import java.util.List;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import com.jme3.asset.BlenderKey.FeaturesToLoad;
- import com.jme3.math.Matrix4f;
- import com.jme3.math.Quaternion;
- import com.jme3.math.Transform;
- import com.jme3.math.Vector3f;
- import com.jme3.scene.CameraNode;
- import com.jme3.scene.Geometry;
- import com.jme3.scene.LightNode;
- import com.jme3.scene.Node;
- import com.jme3.scene.Spatial;
- import com.jme3.scene.Spatial.CullHint;
- import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
- import com.jme3.scene.plugins.blender.BlenderContext;
- import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
- import com.jme3.scene.plugins.blender.cameras.CameraHelper;
- import com.jme3.scene.plugins.blender.constraints.Constraint;
- import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
- import com.jme3.scene.plugins.blender.curves.CurvesHelper;
- import com.jme3.scene.plugins.blender.exceptions.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.lights.LightHelper;
- import com.jme3.scene.plugins.blender.meshes.MeshHelper;
- import com.jme3.scene.plugins.blender.modifiers.Modifier;
- import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
- /**
- * A class that is used in object calculations.
- * @author Marcin Roguski (Kaelthas)
- */
- public class ObjectHelper extends AbstractBlenderHelper {
- private static final Logger LOGGER = Logger.getLogger(ObjectHelper.class.getName());
- protected static final int OBJECT_TYPE_EMPTY = 0;
- protected static final int OBJECT_TYPE_MESH = 1;
- protected static final int OBJECT_TYPE_CURVE = 2;
- protected static final int OBJECT_TYPE_SURF = 3;
- protected static final int OBJECT_TYPE_TEXT = 4;
- protected static final int OBJECT_TYPE_METABALL = 5;
- protected static final int OBJECT_TYPE_LAMP = 10;
- protected static final int OBJECT_TYPE_CAMERA = 11;
- protected static final int OBJECT_TYPE_WAVE = 21;
- protected static final int OBJECT_TYPE_LATTICE = 22;
- protected static final int OBJECT_TYPE_ARMATURE = 25;
-
- /**
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in
- * different blender versions.
- * @param blenderVersion
- * the version read from the blend file
- * @param fixUpAxis
- * a variable that indicates if the Y asxis is the UP axis or not
- */
- public ObjectHelper(String blenderVersion, boolean fixUpAxis) {
- super(blenderVersion, fixUpAxis);
- }
- /**
- * This method reads the given structure and createn an object that represents the data.
- * @param objectStructure
- * the object's structure
- * @param blenderContext
- * the blender context
- * @return blener's object representation
- * @throws BlenderFileException
- * an exception is thrown when the given data is inapropriate
- */
- public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
- Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
- if(loadedResult != null) {
- return loadedResult;
- }
- blenderContext.pushParent(objectStructure);
- //get object data
- int type = ((Number)objectStructure.getFieldValue("type")).intValue();
- String name = objectStructure.getName();
- LOGGER.log(Level.INFO, "Loading obejct: {0}", name);
- int restrictflag = ((Number)objectStructure.getFieldValue("restrictflag")).intValue();
- boolean visible = (restrictflag & 0x01) != 0;
- Object result = null;
- Pointer pParent = (Pointer)objectStructure.getFieldValue("parent");
- Object parent = blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
- if(parent == null && pParent.isNotNull()) {
- Structure parentStructure = pParent.fetchData(blenderContext.getInputStream()).get(0);
- parent = this.toObject(parentStructure, blenderContext);
- }
- Transform t = this.getTransformation(objectStructure, blenderContext);
-
- try {
- switch(type) {
- case OBJECT_TYPE_EMPTY:
- LOGGER.log(Level.INFO, "Importing empty.");
- Node empty = new Node(name);
- empty.setLocalTransform(t);
- if(parent instanceof Node) {
- ((Node) parent).attachChild(empty);
- }
- empty.updateModelBound();
- result = empty;
- break;
- case OBJECT_TYPE_MESH:
- LOGGER.log(Level.INFO, "Importing mesh.");
- Node node = new Node(name);
- node.setCullHint(visible ? CullHint.Always : CullHint.Inherit);
- //reading mesh
- MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
- Pointer pMesh = (Pointer)objectStructure.getFieldValue("data");
- List<Structure> meshesArray = pMesh.fetchData(blenderContext.getInputStream());
- List<Geometry> geometries = meshHelper.toMesh(meshesArray.get(0), blenderContext);
- if (geometries != null){
- for(Geometry geometry : geometries) {
- node.attachChild(geometry);
- }
- }
- node.setLocalTransform(t);
- //reading and applying all modifiers
- ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class);
- Collection<Modifier> modifiers = modifierHelper.readModifiers(objectStructure, blenderContext);
- for(Modifier modifier : modifiers) {
- modifier.apply(node, blenderContext);
- }
- //setting the parent
- if(parent instanceof Node) {
- ((Node)parent).attachChild(node);
- }
- node.updateModelBound();//I prefer do calculate bounding box here than read it from the file
- result = node;
- break;
- case OBJECT_TYPE_SURF:
- case OBJECT_TYPE_CURVE:
- LOGGER.log(Level.INFO, "Importing curve/nurb.");
- Pointer pCurve = (Pointer)objectStructure.getFieldValue("data");
- if(pCurve.isNotNull()) {
- CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
- Structure curveData = pCurve.fetchData(blenderContext.getInputStream()).get(0);
- List<Geometry> curves = curvesHelper.toCurve(curveData, blenderContext);
- result = new Node(name);
- for(Geometry curve : curves) {
- ((Node)result).attachChild(curve);
- }
- ((Node)result).setLocalTransform(t);
- }
- break;
- case OBJECT_TYPE_LAMP:
- LOGGER.log(Level.INFO, "Importing lamp.");
- Pointer pLamp = (Pointer)objectStructure.getFieldValue("data");
- if(pLamp.isNotNull()) {
- LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
- List<Structure> lampsArray = pLamp.fetchData(blenderContext.getInputStream());
- LightNode light = lightHelper.toLight(lampsArray.get(0), blenderContext);
- if(light!=null) {
- light.setName(name);
- light.setLocalTransform(t);
- }
- result = light;
- }
- break;
- case OBJECT_TYPE_CAMERA:
- Pointer pCamera = (Pointer)objectStructure.getFieldValue("data");
- if(pCamera.isNotNull()) {
- CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
- List<Structure> camerasArray = pCamera.fetchData(blenderContext.getInputStream());
- CameraNode camera = cameraHelper.toCamera(camerasArray.get(0));
- camera.setName(name);
- camera.setLocalTransform(t);
- result = camera;
- }
- break;
- case OBJECT_TYPE_ARMATURE:
- //need to create an empty node to properly create parent-children relationships between nodes
- Node armature = new Node(name);
- armature.setLocalTransform(t);
- //TODO: modifiers for armature ????
- if(parent instanceof Node) {
- ((Node)parent).attachChild(armature);
- }
- armature.updateModelBound();//I prefer do calculate bounding box here than read it from the file
- result = armature;
- break;
- default:
- LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
- }
- } finally {
- blenderContext.popParent();
- }
-
- if(result != null) {
- blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
-
- //loading constraints connected with this object
- ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
- constraintHelper.loadConstraints(objectStructure, blenderContext);
-
- //baking constraints
- List<Constraint> objectConstraints = blenderContext.getConstraints(objectStructure.getOldMemoryAddress());
- if(objectConstraints!=null) {
- for(Constraint objectConstraint : objectConstraints) {
- objectConstraint.bake();
- }
- }
-
- //reading custom properties
- if(blenderContext.getBlenderKey().isLoadObjectProperties()) {
- Properties properties = this.loadProperties(objectStructure, blenderContext);
- //the loaded property is a group property, so we need to get each value and set it to Spatial
- if(result instanceof Spatial && properties != null && properties.getValue() != null) {
- this.applyProperties((Spatial) result, properties);
- }
- }
- }
- return result;
- }
-
- /**
- * This method calculates local transformation for the object. Parentage is taken under consideration.
- * @param objectStructure
- * the object's structure
- * @return objects transformation relative to its parent
- */
- @SuppressWarnings("unchecked")
- public Transform getTransformation(Structure objectStructure, BlenderContext blenderContext) {
- //these are transformations in global space
- DynamicArray<Number> loc = (DynamicArray<Number>)objectStructure.getFieldValue("loc");
- DynamicArray<Number> size = (DynamicArray<Number>)objectStructure.getFieldValue("size");
- DynamicArray<Number> rot = (DynamicArray<Number>)objectStructure.getFieldValue("rot");
- //load parent inverse matrix
- Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
- Matrix4f parentInv = pParent.isNull() ? Matrix4f.IDENTITY : this.getMatrix(objectStructure, "parentinv");
-
- //create the global matrix (without the scale)
- Matrix4f globalMatrix = new Matrix4f();
- globalMatrix.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
- globalMatrix.setRotationQuaternion(new Quaternion().fromAngles(rot.get(0).floatValue(), rot.get(1).floatValue(), rot.get(2).floatValue()));
- //compute local matrix
- Matrix4f localMatrix = parentInv.mult(globalMatrix);
- Vector3f translation = localMatrix.toTranslationVector();
- Quaternion rotation = localMatrix.toRotationQuat();
- Vector3f scale = this.getScale(parentInv).multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
-
- if(fixUpAxis) {
- float y = translation.y;
- translation.y = translation.z;
- translation.z = -y;
-
- y = rotation.getY();
- float z = rotation.getZ();
- rotation.set(rotation.getX(), z, -y, rotation.getW());
-
- y=scale.y;
- scale.y = scale.z;
- scale.z = y;
- }
-
- //create the result
- Transform t = new Transform(translation, rotation);
- t.setScale(scale);
- return t;
- }
- /**
- * This method returns the matrix of a given name for the given structure.
- * The matrix is NOT transformed if Y axis is up - the raw data is loaded from the blender file.
- * @param structure
- * the structure with matrix data
- * @param matrixName
- * the name of the matrix
- * @return the required matrix
- */
- public Matrix4f getMatrix(Structure structure, String matrixName) {
- return this.getMatrix(structure, matrixName, false);
- }
-
- /**
- * This method returns the matrix of a given name for the given structure.
- * It takes up axis into consideration.
- * @param structure
- * the structure with matrix data
- * @param matrixName
- * the name of the matrix
- * @return the required matrix
- */
- @SuppressWarnings("unchecked")
- public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
- Matrix4f result = new Matrix4f();
- DynamicArray<Number> obmat = (DynamicArray<Number>)structure.getFieldValue(matrixName);
- int rowAndColumnSize = Math.abs((int)Math.sqrt(obmat.getTotalSize()));//the matrix must be square
- for(int i = 0; i < rowAndColumnSize; ++i) {
- for(int j = 0; j < rowAndColumnSize; ++j) {
- result.set(i, j, obmat.get(j, i).floatValue());
- }
- }
- if(applyFixUpAxis && fixUpAxis) {
- Vector3f translation = result.toTranslationVector();
- Quaternion rotation = result.toRotationQuat();
- Vector3f scale = this.getScale(result);
-
- float y = translation.y;
- translation.y = translation.z;
- translation.z = -y;
-
- y = rotation.getY();
- float z = rotation.getZ();
- rotation.set(rotation.getX(), z, -y, rotation.getW());
-
- y=scale.y;
- scale.y = scale.z;
- scale.z = y;
-
- result.loadIdentity();
- result.setTranslation(translation);
- result.setRotationQuaternion(rotation);
- result.setScale(scale);
- }
- return result;
- }
- /**
- * This method returns the scale from the given matrix.
- *
- * @param matrix
- * the transformation matrix
- * @return the scale from the given matrix
- */
- public Vector3f getScale(Matrix4f matrix) {
- float scaleX = (float) Math.sqrt(matrix.m00 * matrix.m00 + matrix.m10 * matrix.m10 + matrix.m20 * matrix.m20);
- float scaleY = (float) Math.sqrt(matrix.m01 * matrix.m01 + matrix.m11 * matrix.m11 + matrix.m21 * matrix.m21);
- float scaleZ = (float) Math.sqrt(matrix.m02 * matrix.m02 + matrix.m12 * matrix.m12 + matrix.m22 * matrix.m22);
- return new Vector3f(scaleX, scaleY, scaleZ);
- }
-
- @Override
- public void clearState() {
- fixUpAxis = false;
- }
-
- @Override
- public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
- int lay = ((Number) structure.getFieldValue("lay")).intValue();
- return (lay & blenderContext.getBlenderKey().getLayersToLoad()) != 0
- && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0;
- }
- }
|