2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
35 import java.io.IOException;
36 import java.io.Serializable;
37 import java.util.logging.Logger;
39 import com.jme.scene.Spatial;
40 import com.jme.system.JmeException;
41 import com.jme.util.export.InputCapsule;
42 import com.jme.util.export.JMEExporter;
43 import com.jme.util.export.JMEImporter;
44 import com.jme.util.export.OutputCapsule;
45 import com.jme.util.export.Savable;
48 * TransformMatrix holds a rotation (Matrix3f) and translation (Vector3f) for point manipulation
50 * @author Jack Lindamood
51 * @author Joshua Slack
53 public class TransformMatrix implements Serializable, Savable, Cloneable {
54 private static final Logger logger = Logger.getLogger(TransformMatrix.class
57 // TODO: Clean up and standardize this class's functionality
58 private static final long serialVersionUID = 1L;
60 private Matrix3f rot=new Matrix3f();
61 private Vector3f translation=new Vector3f();
62 private Vector3f scale=new Vector3f(1,1,1);
65 * Constructor instantiates a new <code>TransformMatrix</code> that is set to the
66 * identity matrix by default.
69 public TransformMatrix() {
73 * Constructor instantiates a new <code>TransformMatrix</code> that is set to the
74 * provided matrix. This constructor copies a given matrix. If the
75 * provided matrix is null, the constructor sets the matrix to the
77 * @param mat the matrix to copy.
79 public TransformMatrix(TransformMatrix mat) {
84 * Constructor instantiates a new <code>TransformMatrix</code> that has rotation
85 * and translation defined by its parameters
86 * @param myRot The given rotation, as a <code>Quaternion</code>
87 * @param myPos The given translation, as a <code>Vector3f</code>
89 public TransformMatrix(Quaternion myRot, Vector3f myPos) {
91 translation.set(myPos);
95 * <code>set</code> transfers the contents of a given matrix to this
96 * matrix. If a null matrix is supplied, this matrix is set to the
98 * @param matrix the matrix to copy.
100 public void set(TransformMatrix matrix) {
101 if (matrix == null) {
104 rot.copy(matrix.rot);
105 translation.set(matrix.translation);
106 scale.set(matrix.scale);
113 * <code>set</code> defines the values of the matrix based on a supplied
114 * <code>Quaternion</code>. It should be noted that all previous values
115 * will be overridden.
116 * @param quaternion the quaternion to create a rotational matrix from.
118 public void set(Quaternion quaternion) {
125 * <code>loadIdentity</code> sets this matrix to the identity matrix,
126 * namely all zeros with ones along the diagonal.
129 public void loadIdentity() {
136 * Multiplies every value in the matrix by a scalar
139 public void mult(float scalar) {
140 rot.multLocal(scalar);
141 translation.mult(scalar);
142 scale.multLocal(scalar);
146 * <code>multLocal</code> multiplies this matrix with another matrix and stores
147 * the result back in this, returning this. if null is passed, nothing happens
148 * This function changes this matrix to what the child would look like if this were applied as it's parent
149 * @param child The matrix to multiply by
150 * @param tempStore A temporary Vector3f object for this TransformMatrix to use during the calculation.
151 * @return this matrix after multiplication
153 public TransformMatrix multLocal(TransformMatrix child,Vector3f tempStore){
154 this.scale.multLocal(child.scale);
155 this.translation.addLocal(rot.mult(child.translation,tempStore).multLocal(child.scale));
156 this.rot.multLocal(child.rot);
161 * Sets this transform to an interpolation between the start and end transforms. Note that
162 * this function isn't very efficient as it has to create 2 new Quaternions to do the
163 * rotation interpolation
164 * @param start Begining transform (delta=0)
165 * @param end Ending transform (delta=1)
166 * @param delta Value between 0.0 and 1.0 to show which side the transform leans towards
168 public void interpolateTransforms(TransformMatrix start,TransformMatrix end,float delta){
169 this.translation.set(start.translation).interpolate(end.translation,delta);
170 this.scale.set(start.scale).interpolate(end.scale,delta);
171 Quaternion q1=new Quaternion(),q2=new Quaternion();
172 start.getRotation(q1);
175 this.setRotationQuaternion(q1);
179 * Sets this transform to an interpolation between the start and end transforms. Same as above but doesn't
180 * create 2 new Quaternions
181 * @param start Begining transform (delta=0)
182 * @param end Ending transform (delta=1)
183 * @param delta Value between 0.0 and 1.0 to show which side the transform leans towards
184 * @param q1 A temporary Quaternion
185 * @param q2 Another temporary Quaternion
187 public void interpolateTransforms(TransformMatrix start,TransformMatrix end,float delta,Quaternion q1,Quaternion q2){
188 this.translation.set(start.translation).interpolate(end.translation,delta);
189 this.scale.set(start.scale).interpolate(end.scale,delta);
190 start.getRotation(q1);
193 this.setRotationQuaternion(q1);
198 * <code>mult</code> multiplies a normal about a transform matrix and
199 * stores the result back in vec. The resulting vector is returned
200 * with translational ignored.
201 * @param vec the rotation normal.
202 * @return The given Vector3f, after rotation
204 public Vector3f multNormal(Vector3f vec) {
206 logger.warning("Source vector is null, null result returned.");
209 return rot.multLocal(vec);
213 * <code>mult</code> multiplies a vector about a transform matrix. The
214 * resulting vector is saved in vec and returned.
215 * @param vec The point to rotate.
216 * @return The rotated vector.
218 public Vector3f multPoint(Vector3f vec) {
220 logger.warning("Source vector is null, null result returned.");
223 return rot.multLocal(vec).multLocal(scale).addLocal(translation);
228 * Sets the rotation matrix to the given rotation matrix via a copy. If null is supplied, the identity is set
229 * @param rot The new rotation
231 public void setRotation(Matrix3f rot){
236 * <code>setTranslation</code> will set the matrix's translation values.
237 * @param transArray the new values for the translation.
238 * @throws JmeException if translation is null or not size 3.
240 public void setTranslation(float[] transArray) {
241 if (transArray == null || transArray.length != 3) {
242 throw new JmeException("Translation size must be 3.");
244 translation.x = transArray[0];
245 translation.y = transArray[1];
246 translation.z = transArray[2];
249 /** <code>setTranslation</code> will copy the given Vector3f's values
250 * into this Matrix's translational component
254 public void setTranslation(Vector3f trans){
256 throw new JmeException("Vector3f translation must be non-null");
258 translation.set(trans);
262 * Sets the Transform's Translational component
263 * @param x New X translation
264 * @param y New Y translation
265 * @param z New Z translation
267 public void setTranslation(float x,float y,float z){
268 translation.set(x,y,z);
272 * Sets the rotational component of this transform to the matrix represented
273 * by an Euler rotation about x, y, then z.
274 * @param x The X rotation, in radians
275 * @param y The Y rotation, in radians
276 * @param z The Z rotation, in radians
278 public void setEulerRot(float x,float y,float z){
279 double A = Math.cos(x);
280 double B = Math.sin(x);
281 double C = Math.cos(y);
282 double D = Math.sin(y);
283 double E = Math.cos(z);
284 double F = Math.sin(z);
287 rot.m00 = (float) (C * E);
288 rot.m01 = (float) (BD * E + -(A * F));
289 rot.m02 = (float) (AD * E + B * F);
290 rot.m10 = (float) (C * F);
291 rot.m11 = (float) (BD * F + A * E);
292 rot.m12 = (float) (AD * F + -(B * E));
293 rot.m20 = (float) -D;
294 rot.m21 = (float) (B * C);
295 rot.m22 = (float) (A * C);
299 * <code>setRotationQuaternion</code> builds a rotation from a
300 * <code>Quaternion</code>.
301 * @param quat The quaternion to build the rotation from.
302 * @throws JmeException if quat is null.
304 public void setRotationQuaternion(Quaternion quat) {
306 throw new JmeException("Quat may not be null.");
312 * <code>invertRotInPlace</code> inverts the rotational component of this Matrix
315 private void invertRotInPlace() {
331 * Stores the rotational part of this matrix into the passed matrix.
332 * Will create a new Matrix3f if given matrix is null. Returns the
333 * given matrix after it has been loaded with rotation values, to allow
336 * @param rotStore The matrix to store rotation values
337 * @return The given matrix with updated values
339 public Matrix3f getRotation(Matrix3f rotStore){
340 if (rotStore==null) rotStore=new Matrix3f();
346 * Stores the translational part of this matrix into the passed matrix.
347 * Will create a new Vector3f if given vector is null. Returns the
348 * given vector after it has been loaded with translation values, to allow
351 * @param tranStore The vector to store translation values
352 * @return The given Vector with updated values
354 public Vector3f getTranslation(Vector3f tranStore){
355 if (tranStore==null) tranStore=new Vector3f();
356 tranStore.set(translation);
361 * Stores the rotational part of this matrix into the passed Quaternion.
362 * Will create a new Quaternion if given quaternion is null. Returns the
363 * given Quaternion after it has been loaded with rotation values, to allow
366 * @param rotStore The Quat to store translation values
367 * @return The given Vector with updated values
369 public Quaternion getRotation(Quaternion rotStore){
370 if (rotStore==null) rotStore=new Quaternion();
371 rotStore.fromRotationMatrix(rot);
376 * <code>toString</code> returns the string representation of this object.
377 * It is simply a toString() call of the rotational matrix and the translational vector
378 * @return the string representation of this object.
380 public String toString() {
381 return "com.jme.math.TransformMatrix\n[\n"+
382 rot.toString() + ":" +
383 translation.toString() + ":" +
388 * <code>inverse</code> turns this matrix into it's own inverse
390 public void inverse() {
392 rot.multLocal(translation);
393 translation.multLocal(-1);
394 scale.set(1/scale.x,1/scale.y,1/scale.z);
398 * <code>setEulerRot</code> is equivalent to
399 * setEulerRot(eulerVec.x,eulverVec.y,eulverVec.z){
400 * @param eulerVec A Vector3f representing the new rotation in Euler angles
402 public void setEulerRot(Vector3f eulerVec) {
403 this.setEulerRot(eulerVec.x,eulerVec.y,eulerVec.z);
407 * <code>set</code> changes this matrix's rotational and translational components
408 * to that represented by the given parameters
409 * @param rotation The new rotaiton
410 * @param translation The new translation
412 public void set(Quaternion rotation, Vector3f translation) {
414 this.setTranslation(translation);
418 * Sets this TransformMatrix's scale to the given scale (x,y,z)
419 * @param scale The new scale
421 public void setScale(Vector3f scale) {
422 this.scale.set(scale);
426 * Sets this TransformMatrix's scale to the given x,y,z
427 * @param x The x scale
428 * @param y The y scale
429 * @param z The z scale
431 public void setScale(float x, float y, float z) {
436 * Returns this TransformMatrix's scale factor
437 * @param storeS The place to store the current scale factor
438 * @return The given scale factor
440 public Vector3f getScale(Vector3f storeS) {
441 if (storeS==null) storeS=new Vector3f();
442 return storeS.set(this.scale);
446 * Applies this TransformMatrix to the given spatial, by updating the spatial's local translation, rotation, scale.
447 * @param spatial The spatial to update
449 public void applyToSpatial(Spatial spatial) {
450 spatial.setLocalTranslation(translation);
451 spatial.setLocalRotation(rot);
452 spatial.setLocalScale(scale);
456 * Combines this TransformMatrix with a parent TransformMatrix.
457 * @param parent The parent matrix.
458 * @return This matrix, after it has been updated by it's parent.
460 public TransformMatrix combineWithParent(TransformMatrix parent){
461 this.scale.multLocal(parent.scale);
462 this.rot.multLocal(parent.rot);
463 parent.rot.multLocal(this.translation).multLocal(parent.scale).addLocal(parent.translation);
468 public void write(JMEExporter e) throws IOException {
469 OutputCapsule capsule = e.getCapsule(this);
470 capsule.write(rot, "rot", new Matrix3f());
471 capsule.write(translation, "translation", Vector3f.ZERO);
472 capsule.write(scale, "scale", Vector3f.UNIT_XYZ);
475 public void read(JMEImporter e) throws IOException {
476 InputCapsule capsule = e.getCapsule(this);
477 rot = (Matrix3f)capsule.readSavable("rot", new Matrix3f());
478 translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO.clone());
479 scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ.clone());
482 public Class<? extends TransformMatrix> getClassTag() {
483 return this.getClass();
487 public TransformMatrix clone() {
489 TransformMatrix tm = (TransformMatrix) super.clone();
490 tm.rot = rot.clone();
491 tm.scale = scale.clone();
492 tm.translation = translation.clone();
494 } catch (CloneNotSupportedException e) {
495 throw new AssertionError();