• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.scene.plugins.blender.objects;
33 
34 import java.util.Collection;
35 import java.util.List;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38 
39 import com.jme3.asset.BlenderKey.FeaturesToLoad;
40 import com.jme3.light.DirectionalLight;
41 import com.jme3.light.Light;
42 import com.jme3.light.PointLight;
43 import com.jme3.light.SpotLight;
44 import com.jme3.math.Matrix4f;
45 import com.jme3.math.Quaternion;
46 import com.jme3.math.Transform;
47 import com.jme3.math.Vector3f;
48 import com.jme3.renderer.Camera;
49 import com.jme3.scene.Geometry;
50 import com.jme3.scene.Node;
51 import com.jme3.scene.Spatial;
52 import com.jme3.scene.Spatial.CullHint;
53 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
54 import com.jme3.scene.plugins.blender.BlenderContext;
55 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
56 import com.jme3.scene.plugins.blender.cameras.CameraHelper;
57 import com.jme3.scene.plugins.blender.constraints.Constraint;
58 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
59 import com.jme3.scene.plugins.blender.curves.CurvesHelper;
60 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
61 import com.jme3.scene.plugins.blender.file.DynamicArray;
62 import com.jme3.scene.plugins.blender.file.Pointer;
63 import com.jme3.scene.plugins.blender.file.Structure;
64 import com.jme3.scene.plugins.blender.lights.LightHelper;
65 import com.jme3.scene.plugins.blender.meshes.MeshHelper;
66 import com.jme3.scene.plugins.blender.modifiers.Modifier;
67 import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
68 
69 /**
70  * A class that is used in object calculations.
71  * @author Marcin Roguski (Kaelthas)
72  */
73 public class ObjectHelper extends AbstractBlenderHelper {
74 	private static final Logger			LOGGER		= Logger.getLogger(ObjectHelper.class.getName());
75 
76 	protected static final int		OBJECT_TYPE_EMPTY			= 0;
77 	protected static final int		OBJECT_TYPE_MESH			= 1;
78 	protected static final int		OBJECT_TYPE_CURVE			= 2;
79 	protected static final int		OBJECT_TYPE_SURF			= 3;
80 	protected static final int		OBJECT_TYPE_TEXT			= 4;
81 	protected static final int		OBJECT_TYPE_METABALL		= 5;
82 	protected static final int		OBJECT_TYPE_LAMP			= 10;
83 	protected static final int		OBJECT_TYPE_CAMERA			= 11;
84 	protected static final int		OBJECT_TYPE_WAVE			= 21;
85 	protected static final int		OBJECT_TYPE_LATTICE			= 22;
86 	protected static final int		OBJECT_TYPE_ARMATURE		= 25;
87 
88 	/**
89 	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in
90 	 * different blender versions.
91 	 * @param blenderVersion
92 	 *        the version read from the blend file
93 	 * @param fixUpAxis
94      *        a variable that indicates if the Y asxis is the UP axis or not
95 	 */
ObjectHelper(String blenderVersion, boolean fixUpAxis)96 	public ObjectHelper(String blenderVersion, boolean fixUpAxis) {
97 		super(blenderVersion, fixUpAxis);
98 	}
99 
100 	/**
101 	 * This method reads the given structure and createn an object that represents the data.
102 	 * @param objectStructure
103 	 *            the object's structure
104 	 * @param blenderContext
105 	 *            the blender context
106 	 * @return blener's object representation
107 	 * @throws BlenderFileException
108 	 *             an exception is thrown when the given data is inapropriate
109 	 */
toObject(Structure objectStructure, BlenderContext blenderContext)110 	public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
111 		Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
112 		if(loadedResult != null) {
113 			return loadedResult;
114 		}
115 
116 		blenderContext.pushParent(objectStructure);
117 
118 		//get object data
119 		int type = ((Number)objectStructure.getFieldValue("type")).intValue();
120 		String name = objectStructure.getName();
121 		LOGGER.log(Level.INFO, "Loading obejct: {0}", name);
122 
123 		int restrictflag = ((Number)objectStructure.getFieldValue("restrictflag")).intValue();
124 		boolean visible = (restrictflag & 0x01) != 0;
125 		Object result = null;
126 
127 		Pointer pParent = (Pointer)objectStructure.getFieldValue("parent");
128 		Object parent = blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
129 		if(parent == null && pParent.isNotNull()) {
130 			Structure parentStructure = pParent.fetchData(blenderContext.getInputStream()).get(0);
131 			parent = this.toObject(parentStructure, blenderContext);
132 		}
133 
134 		Transform t = this.getTransformation(objectStructure, blenderContext);
135 
136 		try {
137 			switch(type) {
138 				case OBJECT_TYPE_EMPTY:
139 					LOGGER.log(Level.INFO, "Importing empty.");
140 					Node empty = new Node(name);
141 					empty.setLocalTransform(t);
142 					if(parent instanceof Node) {
143 						((Node) parent).attachChild(empty);
144 					}
145 					empty.updateModelBound();
146 					result = empty;
147 					break;
148 				case OBJECT_TYPE_MESH:
149 					LOGGER.log(Level.INFO, "Importing mesh.");
150 					Node node = new Node(name);
151 					node.setCullHint(visible ? CullHint.Always : CullHint.Inherit);
152 
153 					//reading mesh
154 					MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
155 					Pointer pMesh = (Pointer)objectStructure.getFieldValue("data");
156 					List<Structure> meshesArray = pMesh.fetchData(blenderContext.getInputStream());
157 					List<Geometry> geometries = meshHelper.toMesh(meshesArray.get(0), blenderContext);
158 					if (geometries != null){
159                         for(Geometry geometry : geometries) {
160                             node.attachChild(geometry);
161                         }
162                     }
163 					node.setLocalTransform(t);
164 
165 					//reading and applying all modifiers
166 					ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class);
167 					Collection<Modifier> modifiers = modifierHelper.readModifiers(objectStructure, blenderContext);
168 					for(Modifier modifier : modifiers) {
169 						modifier.apply(node, blenderContext);
170 					}
171 
172 					//setting the parent
173 					if(parent instanceof Node) {
174 						((Node)parent).attachChild(node);
175 					}
176 					node.updateModelBound();//I prefer do calculate bounding box here than read it from the file
177 					result = node;
178 					break;
179 				case OBJECT_TYPE_SURF:
180 				case OBJECT_TYPE_CURVE:
181 					LOGGER.log(Level.INFO, "Importing curve/nurb.");
182 					Pointer pCurve = (Pointer)objectStructure.getFieldValue("data");
183 					if(pCurve.isNotNull()) {
184 						CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
185 						Structure curveData = pCurve.fetchData(blenderContext.getInputStream()).get(0);
186 						List<Geometry> curves = curvesHelper.toCurve(curveData, blenderContext);
187 						result = new Node(name);
188 						for(Geometry curve : curves) {
189 							((Node)result).attachChild(curve);
190 						}
191 						((Node)result).setLocalTransform(t);
192 					}
193 					break;
194 				case OBJECT_TYPE_LAMP:
195 					LOGGER.log(Level.INFO, "Importing lamp.");
196 					Pointer pLamp = (Pointer)objectStructure.getFieldValue("data");
197 					if(pLamp.isNotNull()) {
198 						LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
199 						List<Structure> lampsArray = pLamp.fetchData(blenderContext.getInputStream());
200 						Light light = lightHelper.toLight(lampsArray.get(0), blenderContext);
201 						if(light!=null) {
202 							light.setName(name);
203 						}
204 						if(light instanceof PointLight) {
205 							((PointLight)light).setPosition(t.getTranslation());
206 						} else if(light instanceof DirectionalLight) {
207 							Quaternion quaternion = t.getRotation();
208 							Vector3f[] axes = new Vector3f[3];
209 							quaternion.toAxes(axes);
210 							if(fixUpAxis) {
211 								((DirectionalLight)light).setDirection(axes[1].negate());//-Z is the direction axis of area lamp in blender
212 							} else {
213 								((DirectionalLight)light).setDirection(axes[2].negate());
214 							}
215 						} else if(light instanceof SpotLight) {
216 							((SpotLight)light).setPosition(t.getTranslation());
217 
218 							Quaternion quaternion = t.getRotation();
219 							Vector3f[] axes = new Vector3f[3];
220 							quaternion.toAxes(axes);
221 							if(fixUpAxis) {
222 								((SpotLight)light).setDirection(axes[1].negate());//-Z is the direction axis of area lamp in blender
223 							} else {
224 								((SpotLight)light).setDirection(axes[2].negate());
225 							}
226 						} else {
227 							LOGGER.log(Level.WARNING, "Unknown type of light: {0}", light);
228 						}
229 						result = light;
230 					}
231 					break;
232 				case OBJECT_TYPE_CAMERA:
233 					Pointer pCamera = (Pointer)objectStructure.getFieldValue("data");
234 					if(pCamera.isNotNull()) {
235 						CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
236 						List<Structure> camerasArray = pCamera.fetchData(blenderContext.getInputStream());
237 						Camera camera = cameraHelper.toCamera(camerasArray.get(0));
238 						camera.setLocation(t.getTranslation());
239 						camera.setRotation(t.getRotation());
240 						result = camera;
241 					}
242 					break;
243 				case OBJECT_TYPE_ARMATURE:
244 					//need to create an empty node to properly create parent-children relationships between nodes
245 					Node armature = new Node(name);
246 					armature.setLocalTransform(t);
247 					//TODO: modifiers for armature ????
248 					if(parent instanceof Node) {
249 						((Node)parent).attachChild(armature);
250 					}
251 					armature.updateModelBound();//I prefer do calculate bounding box here than read it from the file
252 					result = armature;
253 					break;
254 				default:
255 					LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
256 			}
257 		} finally {
258 			blenderContext.popParent();
259 		}
260 
261 		if(result != null) {
262 			blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
263 
264 			//loading constraints connected with this object
265 			ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
266 			constraintHelper.loadConstraints(objectStructure, blenderContext);
267 
268 			//baking constraints
269 			List<Constraint> objectConstraints = blenderContext.getConstraints(objectStructure.getOldMemoryAddress());
270 			if(objectConstraints!=null) {
271 				for(Constraint objectConstraint : objectConstraints) {
272 					objectConstraint.bake();
273 				}
274 			}
275 
276 			//reading custom properties
277 			Properties properties = this.loadProperties(objectStructure, blenderContext);
278 			if(result instanceof Spatial && properties != null && properties.getValue() != null) {
279 				((Spatial)result).setUserData("properties", properties);
280 			}
281 		}
282 		return result;
283 	}
284 
285 	/**
286 	 * This method calculates local transformation for the object. Parentage is taken under consideration.
287 	 * @param objectStructure
288 	 *        the object's structure
289 	 * @return objects transformation relative to its parent
290 	 */
291 	@SuppressWarnings("unchecked")
getTransformation(Structure objectStructure, BlenderContext blenderContext)292 	public Transform getTransformation(Structure objectStructure, BlenderContext blenderContext) {
293 		//these are transformations in global space
294 		DynamicArray<Number> loc = (DynamicArray<Number>)objectStructure.getFieldValue("loc");
295 		DynamicArray<Number> size = (DynamicArray<Number>)objectStructure.getFieldValue("size");
296 		DynamicArray<Number> rot = (DynamicArray<Number>)objectStructure.getFieldValue("rot");
297 
298 		//load parent inverse matrix
299 		Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
300 		Matrix4f parentInv = pParent.isNull() ? Matrix4f.IDENTITY : this.getMatrix(objectStructure, "parentinv");
301 
302 		//create the global matrix (without the scale)
303 		Matrix4f globalMatrix = new Matrix4f();
304 		globalMatrix.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
305 		globalMatrix.setRotationQuaternion(new Quaternion().fromAngles(rot.get(0).floatValue(), rot.get(1).floatValue(), rot.get(2).floatValue()));
306 		//compute local matrix
307 		Matrix4f localMatrix = parentInv.mult(globalMatrix);
308 
309 		Vector3f translation = localMatrix.toTranslationVector();
310 		Quaternion rotation = localMatrix.toRotationQuat();
311 		Vector3f scale = this.getScale(parentInv).multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
312 
313 		if(fixUpAxis) {
314 			float y = translation.y;
315 			translation.y = translation.z;
316 			translation.z = -y;
317 
318 			y = rotation.getY();
319 			float z = rotation.getZ();
320 			rotation.set(rotation.getX(), z, -y, rotation.getW());
321 
322 			y=scale.y;
323 			scale.y = scale.z;
324 			scale.z = y;
325 		}
326 
327 		//create the result
328 		Transform t = new Transform(translation, rotation);
329 		t.setScale(scale);
330 		return t;
331 	}
332 
333 	/**
334 	 * This method returns the matrix of a given name for the given structure.
335 	 * The matrix is NOT transformed if Y axis is up - the raw data is loaded from the blender file.
336 	 * @param structure
337 	 *        the structure with matrix data
338 	 * @param matrixName
339 	 * 		  the name of the matrix
340 	 * @return the required matrix
341 	 */
getMatrix(Structure structure, String matrixName)342 	public Matrix4f getMatrix(Structure structure, String matrixName) {
343 		return this.getMatrix(structure, matrixName, false);
344 	}
345 
346 	/**
347 	 * This method returns the matrix of a given name for the given structure.
348 	 * It takes up axis into consideration.
349 	 * @param structure
350 	 *        the structure with matrix data
351 	 * @param matrixName
352 	 * 		  the name of the matrix
353 	 * @return the required matrix
354 	 */
355 	@SuppressWarnings("unchecked")
getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis)356 	public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
357 		Matrix4f result = new Matrix4f();
358 		DynamicArray<Number> obmat = (DynamicArray<Number>)structure.getFieldValue(matrixName);
359 		int rowAndColumnSize = Math.abs((int)Math.sqrt(obmat.getTotalSize()));//the matrix must be square
360 		for(int i = 0; i < rowAndColumnSize; ++i) {
361 			for(int j = 0; j < rowAndColumnSize; ++j) {
362 				result.set(i, j, obmat.get(j, i).floatValue());
363 			}
364 		}
365 		if(applyFixUpAxis && fixUpAxis) {
366         	Vector3f translation = result.toTranslationVector();
367             Quaternion rotation = result.toRotationQuat();
368             Vector3f scale = this.getScale(result);
369 
370 			float y = translation.y;
371 			translation.y = translation.z;
372 			translation.z = -y;
373 
374 			y = rotation.getY();
375 			float z = rotation.getZ();
376 			rotation.set(rotation.getX(), z, -y, rotation.getW());
377 
378 			y=scale.y;
379 			scale.y = scale.z;
380 			scale.z = y;
381 
382 			result.loadIdentity();
383 			result.setTranslation(translation);
384 			result.setRotationQuaternion(rotation);
385 			result.setScale(scale);
386         }
387 		return result;
388 	}
389 
390 	/**
391 	 * This method returns the scale from the given matrix.
392 	 *
393 	 * @param matrix
394 	 *            the transformation matrix
395 	 * @return the scale from the given matrix
396 	 */
getScale(Matrix4f matrix)397 	public Vector3f getScale(Matrix4f matrix) {
398 		float scaleX = (float) Math.sqrt(matrix.m00 * matrix.m00 + matrix.m10 * matrix.m10 + matrix.m20 * matrix.m20);
399 		float scaleY = (float) Math.sqrt(matrix.m01 * matrix.m01 + matrix.m11 * matrix.m11 + matrix.m21 * matrix.m21);
400 		float scaleZ = (float) Math.sqrt(matrix.m02 * matrix.m02 + matrix.m12 * matrix.m12 + matrix.m22 * matrix.m22);
401 		return new Vector3f(scaleX, scaleY, scaleZ);
402 	}
403 
404 	@Override
clearState()405 	public void clearState() {
406 		fixUpAxis = false;
407 	}
408 
409 	@Override
shouldBeLoaded(Structure structure, BlenderContext blenderContext)410 	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
411 		int lay = ((Number) structure.getFieldValue("lay")).intValue();
412         return ((lay & blenderContext.getBlenderKey().getLayersToLoad()) != 0
413                 && (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0);
414 	}
415 }
416