All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.math.Transform Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2023 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.math;

import com.jme3.export.*;
import com.jme3.util.TempVars;

import java.io.IOException;

/**
 * A 3-D coordinate transform composed of translation, rotation, and scaling.
 * The order of application is: scale, then rotate, then translate.
 *
 * 

Started July 16, 2004 * * @author Jack Lindamood * @author Joshua Slack */ public final class Transform implements Savable, Cloneable, java.io.Serializable { static final long serialVersionUID = 1; /** * Shared instance of the identity transform. Do not modify! */ public static final Transform IDENTITY = new Transform(); /** * Rotation component. */ private Quaternion rot = new Quaternion(); /** * Translation component: an offset for each local axis. */ private Vector3f translation = new Vector3f(); /** * Scaling component: a scale factor for each local axis. */ private Vector3f scale = new Vector3f(1, 1, 1); /** * Instantiates a coordinate transform without scaling. * * @param translation the desired translation (not null, unaffected) * @param rot the desired rotation (not null, unaffected) */ public Transform(Vector3f translation, Quaternion rot) { this.translation.set(translation); this.rot.set(rot); } /** * Instantiates a coordinate transform with scaling. * * @param translation the desired translation (not null, unaffected) * @param rot the desired rotation (not null, unaffected) * @param scale the desired scaling (not null, unaffected) */ public Transform(Vector3f translation, Quaternion rot, Vector3f scale) { this(translation, rot); this.scale.set(scale); } /** * Instantiates a translation-only transform. * * @param translation the desired translation (not null, unaffected) */ public Transform(Vector3f translation) { this(translation, Quaternion.IDENTITY); } /** * Instantiates a rotation-only transform. * * @param rot the desired rotation (not null, unaffected) */ public Transform(Quaternion rot) { this(Vector3f.ZERO, rot); } /** * Instantiates an identity transform: no translation, no rotation, and no * scaling. */ public Transform() { this(Vector3f.ZERO, Quaternion.IDENTITY); } /** * Sets the rotation component to the argument. * * @param rot the desired rotation value (not null, unaffected) * @return the (modified) current instance (for chaining) */ public Transform setRotation(Quaternion rot) { assert Quaternion.isValidQuaternion(rot) : "Invalid rotation " + rot; this.rot.set(rot); return this; } /** * Sets the translation component to the argument. * * @param trans the desired offsets (not null, unaffected) * @return the (modified) current instance (for chaining) */ public Transform setTranslation(Vector3f trans) { assert Vector3f.isValidVector(trans) : "Invalid translation " + trans; this.translation.set(trans); return this; } /** * Returns the translation component. * * @return the pre-existing instance (not null) */ public Vector3f getTranslation() { return translation; } /** * Sets the scaling component to the argument. * * @param scale the desired scale factors (not null, unaffected) * @return the (modified) current instance (for chaining) */ public Transform setScale(Vector3f scale) { assert Vector3f.isValidVector(scale) : "Invalid scale " + scale; this.scale.set(scale); return this; } /** * Sets the scaling component to the argument. This yields uniform scaling. * * @param scale the desired scale factor for all local axes * @return the (modified) current instance (for chaining) */ public Transform setScale(float scale) { assert Float.isFinite(scale) : "Invalid scale " + scale; this.scale.set(scale, scale, scale); return this; } /** * Returns the scaling component. * * @return the pre-existing instance (not null) */ public Vector3f getScale() { return scale; } /** * Copies the translation component to the argument. If the argument is * null, a new Vector3f is created to hold the value. Either way, the * current instance is unaffected, unless the argument is its scaling * component. * * @param trans storage for the result (modified if not null) * @return the translation offsets (either trans or a new * Vector3f) */ public Vector3f getTranslation(Vector3f trans) { if (trans == null) { trans = new Vector3f(); } trans.set(this.translation); return trans; } /** * Copies the rotation component to the argument. If the argument is null, a * new Quaternion is created to hold the value. Either way, the current * instance is unaffected. * * @param quat storage for the result (modified if not null) * @return the rotation value (either quat or a new Quaternion) */ public Quaternion getRotation(Quaternion quat) { if (quat == null) { quat = new Quaternion(); } quat.set(rot); return quat; } /** * Returns the rotation component. * * @return the pre-existing instance (not null) */ public Quaternion getRotation() { return rot; } /** * Copies the scaling component to the argument. If the argument is null, a * new Vector3f is created to hold the value. Either way, the current * instance is unaffected, unless the argument is its translation component. * * @param scale storage for the result (modified if not null) * @return the scale factors (either scale or a new Vector3f) */ public Vector3f getScale(Vector3f scale) { if (scale == null) { scale = new Vector3f(); } scale.set(this.scale); return scale; } /** * Interpolates quickly between the specified transforms, using * {@link Quaternion#nlerp(com.jme3.math.Quaternion, float)} and * {@link Vector3f#interpolateLocal(com.jme3.math.Vector3f, com.jme3.math.Vector3f, float)}. * * @param t1 the desired value when delta=0 (not null, * unaffected unless it's this) * @param t2 the desired value when delta=1 (not null, * unaffected unless it's this) * @param delta the fractional change amount */ public void interpolateTransforms(Transform t1, Transform t2, float delta) { this.rot.set(t1.rot); this.rot.nlerp(t2.rot, delta); this.translation.interpolateLocal(t1.translation, t2.translation, delta); this.scale.interpolateLocal(t1.scale, t2.scale, delta); } /** * Combines with the argument and returns the (modified) current instance. * This method is used to combine Node and Spatial transforms. * * @param parent the parent transform (not null, {@code parent.rot.norm()} * approximately equal to 1, unaffected unless it's this) * @return the (modified) current instance (for chaining) */ public Transform combineWithParent(Transform parent) { //applying parent scale to local scale scale.multLocal(parent.scale); //applying parent rotation to local rotation. parent.rot.mult(rot, rot); //applying parent scale to local translation. translation.multLocal(parent.scale); //applying parent rotation to local translation, then applying parent translation to local translation. //Note that parent.rot.multLocal(translation) doesn't modify "parent.rot" but "translation" parent.rot .multLocal(translation) .addLocal(parent.translation); return this; } /** * Sets the translation component to the specified values. * * @param x the desired X offset * @param y the desired Y offset * @param z the desired Z offset * @return the (modified) current instance (for chaining) */ public Transform setTranslation(float x, float y, float z) { assert Float.isFinite(x) && Float.isFinite(y) && Float.isFinite(z) : "Invalid translation " + x + ", " + y + ", " + z; translation.set(x, y, z); return this; } /** * Sets the scaling component to the specified values. * * @param x the desired X scale factor * @param y the desired Y scale factor * @param z the desired Z scale factor * @return the (modified) current instance (for chaining) */ public Transform setScale(float x, float y, float z) { assert Float.isFinite(x) && Float.isFinite(y) && Float.isFinite(z) : "Invalid scale " + x + ", " + y + ", " + z; scale.set(x, y, z); return this; } /** * Transforms the specified coordinates and returns the result in * store. If the store is null, a new Vector3f is * created to hold the value. Either way, the current instance is * unaffected, unless store is its translation or scaling. *

* The transform's quaternion is assumed to be normalized. No error checking * is performed; the caller should ensure that {@code rot.norm()} is * approximately equal to 1. * * @param in the coordinates to transform (not null, unaffected) * @param store storage for the result (modified if not null) * @return the transformed coordinates (either store or a new * Vector3f) */ public Vector3f transformVector(final Vector3f in, Vector3f store) { if (store == null) { store = new Vector3f(); } // multiply with scale first, then rotate, finally translate (cf. // Eberly) return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation); } /** * Applies the inverse transform to the specified coordinates and returns * the result in store. If the store is null, a * new Vector3f is created to hold the value. Either way, the current * instance is unaffected, unless store is its translation or * scaling. *

* The transform's quaternion is assumed to be normalized. No error checking * is performed; the caller should ensure that {@code rot.norm()} is * approximately equal to 1. * * @param in the coordinates to transform (not null, unaffected unless it's * store) * @param store storage for the result (modified if not null) * @return the transformed coordinates (either store or a new * Vector3f) */ public Vector3f transformInverseVector(final Vector3f in, Vector3f store) { if (store == null) { store = new Vector3f(); } // The author of this code should've looked above and taken the inverse of that, // but for some reason, they didn't. // in.subtract(translation, store).divideLocal(scale); // rot.inverse().mult(store, store); in.subtract(translation, store); rot.inverse().mult(store, store); store.divideLocal(scale); return store; } /** * Creates an equivalent transform matrix. The current instance is * unaffected. * * @return a new Matrix4f */ public Matrix4f toTransformMatrix() { return toTransformMatrix(null); } /** * Converts to an equivalent transform matrix. The current instance is * unaffected. * * @param store storage for the result (modified if not null) * @return a transform matrix (either store or a new Matrix4f) */ public Matrix4f toTransformMatrix(Matrix4f store) { if (store == null) { store = new Matrix4f(); } store.setTranslation(translation); rot.toTransformMatrix(store); store.setScale(scale); return store; } /** * Sets the current instance from a transform matrix. Any reflection or shear in the * matrix is lost -- in other words, it may not be possible to recreate the * original matrix from the result. * *

After this method is invoked, all components of {@code scale} will be * non-negative, even if {@code mat} includes reflection. * * @param mat the input matrix (not null, unaffected) */ public void fromTransformMatrix(Matrix4f mat) { TempVars vars = TempVars.get(); translation.set(mat.toTranslationVector(vars.vect1)); rot.set(mat.toRotationQuat(vars.quat1)); scale.set(mat.toScaleVector(vars.vect2)); vars.release(); } /** * Returns the inverse. The current instance is unaffected. * *

Assumes (but does not verify) that the scale factors are all positive. * If any component of {@code scale} is negative or zero, the result is * undefined. * * @return a new Transform */ public Transform invert() { Transform t = new Transform(); t.fromTransformMatrix(toTransformMatrix().invertLocal()); return t; } /** * Sets the current instance to the identity transform: translation=(0,0,0) * scaling=(1,1,1) rotation=(0,0,0,1). */ public void loadIdentity() { translation.set(0, 0, 0); scale.set(1, 1, 1); rot.set(0, 0, 0, 1); } /** * Tests for exact identity. The current instance is unaffected. * * @return true if equal to {@link #IDENTITY}, otherwise false */ public boolean isIdentity() { return translation.x == 0f && translation.y == 0f && translation.z == 0f && scale.x == 1f && scale.y == 1f && scale.z == 1f && rot.w == 1f && rot.x == 0f && rot.y == 0f && rot.z == 0f; } /** * Returns a hash code. If two transforms have identical values, they * will have the same hash code. The current instance is unaffected. * * @return a 32-bit value for use in hashing */ @Override public int hashCode() { int hash = 7; hash = 89 * hash + rot.hashCode(); hash = 89 * hash + translation.hashCode(); hash = 89 * hash + scale.hashCode(); return hash; } /** * Tests for exact equality with the argument, distinguishing -0 from 0. If * {@code obj} is null, false is returned. Either way, the current instance * is unaffected. * * @param obj the object to compare (may be null, unaffected) * @return true if {@code this} and {@code obj} have identical values, * otherwise false */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Transform other = (Transform) obj; return this.translation.equals(other.translation) && this.scale.equals(other.scale) && this.rot.equals(other.rot); } /** * Returns a string representation of the transform, which is unaffected. * For example, the identity transform is represented by: *

     * Transform[ 0.0, 0.0, 0.0]
     * [ 0.0, 0.0, 0.0, 1.0]
     * [ 1.0 , 1.0, 1.0]
     * 
* * @return the string representation (not null, not empty) */ @Override public String toString() { return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n" + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n" + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]"; } /** * Copies all 3 components from the argument. * * @param transform The Transform to copy (not null, unaffected) * @return the (modified) current instance (for chaining) */ public Transform set(Transform transform) { this.translation.set(transform.translation); this.rot.set(transform.rot); this.scale.set(transform.scale); return this; } /** * Serializes to the argument, for example when saving to a J3O file. The * current instance is unaffected. * * @param e (not null) * @throws IOException from the exporter */ @Override public void write(JmeExporter e) throws IOException { OutputCapsule capsule = e.getCapsule(this); capsule.write(rot, "rot", Quaternion.IDENTITY); capsule.write(translation, "translation", Vector3f.ZERO); capsule.write(scale, "scale", Vector3f.UNIT_XYZ); } /** * De-serializes from the argument, for example when loading from a J3O * file. * * @param importer (not null) * @throws IOException from the importer */ @Override public void read(JmeImporter importer) throws IOException { InputCapsule capsule = importer.getCapsule(this); rot.set((Quaternion) capsule.readSavable("rot", Quaternion.IDENTITY)); translation.set((Vector3f) capsule.readSavable("translation", Vector3f.ZERO)); scale.set((Vector3f) capsule.readSavable("scale", Vector3f.UNIT_XYZ)); } /** * Creates a copy. The current instance is unaffected. * * @return a new instance, equivalent to the current one */ @Override public Transform clone() { try { Transform tq = (Transform) super.clone(); tq.rot = rot.clone(); tq.scale = scale.clone(); tq.translation = translation.clone(); return tq; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy