diff --git a/.gitmodules b/.gitmodules
index 169f56543..286659e33 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "slime-java-commons"]
- path = slime-java-commons
- url = https://github.com/Eirenliel/slime-java-commons.git
[submodule "solarxr-protocol"]
path = solarxr-protocol
url = https://github.com/SlimeVR/SolarXR-Protocol.git
diff --git a/build.gradle b/build.gradle
index 88fbd3ba4..eaceb86d4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -42,7 +42,6 @@ allprojects {
}
dependencies {
- implementation project(':slime-java-commons')
implementation project(":solarxr-protocol")
diff --git a/java/com/jme3/math/ColorRGBA.java b/java/com/jme3/math/ColorRGBA.java
new file mode 100644
index 000000000..a6b8a62f2
--- /dev/null
+++ b/java/com/jme3/math/ColorRGBA.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2009-2012 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 io.eiren.math.FloatMath;
+
+
+/**
+ * ColorRGBA defines a color made from a collection of red, green
+ * and blue values. An alpha value determines is transparency. All values must
+ * be between 0 and 1. If any value is set higher or lower than these
+ * constraints they are clamped to the min or max. That is, if a value smaller
+ * than zero is set the value clamps to zero. If a value higher than 1 is
+ * passed, that value is clamped to 1. However, because the attributes r, g, b,
+ * a are public for efficiency reasons, they can be directly modified with
+ * invalid values. The client should take care when directly addressing the
+ * values. A call to clamp will assure that the values are within the
+ * constraints.
+ *
+ * @author Mark Powell
+ * @version $Id: ColorRGBA.java,v 1.29 2007/09/09 18:25:14 irrisor Exp $
+ */
+public final class ColorRGBA implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+ /**
+ * The color black (0,0,0).
+ */
+ public static final ColorRGBA Black = new ColorRGBA(0f, 0f, 0f, 1f);
+ /**
+ * The color white (1,1,1).
+ */
+ public static final ColorRGBA White = new ColorRGBA(1f, 1f, 1f, 1f);
+ /**
+ * The color gray (.2,.2,.2).
+ */
+ public static final ColorRGBA DarkGray = new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f);
+ /**
+ * The color gray (.5,.5,.5).
+ */
+ public static final ColorRGBA Gray = new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f);
+ /**
+ * The color gray (.8,.8,.8).
+ */
+ public static final ColorRGBA LightGray = new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f);
+ /**
+ * The color red (1,0,0).
+ */
+ public static final ColorRGBA Red = new ColorRGBA(1f, 0f, 0f, 1f);
+ /**
+ * The color green (0,1,0).
+ */
+ public static final ColorRGBA Green = new ColorRGBA(0f, 1f, 0f, 1f);
+ /**
+ * The color blue (0,0,1).
+ */
+ public static final ColorRGBA Blue = new ColorRGBA(0f, 0f, 1f, 1f);
+ /**
+ * The color yellow (1,1,0).
+ */
+ public static final ColorRGBA Yellow = new ColorRGBA(1f, 1f, 0f, 1f);
+ /**
+ * The color magenta (1,0,1).
+ */
+ public static final ColorRGBA Magenta = new ColorRGBA(1f, 0f, 1f, 1f);
+ /**
+ * The color cyan (0,1,1).
+ */
+ public static final ColorRGBA Cyan = new ColorRGBA(0f, 1f, 1f, 1f);
+ /**
+ * The color orange (251/255, 130/255,0).
+ */
+ public static final ColorRGBA Orange = new ColorRGBA(251f / 255f, 130f / 255f, 0f, 1f);
+ /**
+ * The color brown (65/255, 40/255, 25/255).
+ */
+ public static final ColorRGBA Brown = new ColorRGBA(65f / 255f, 40f / 255f, 25f / 255f, 1f);
+ /**
+ * The color pink (1, 0.68, 0.68).
+ */
+ public static final ColorRGBA Pink = new ColorRGBA(1f, 0.68f, 0.68f, 1f);
+ /**
+ * The black color with no alpha (0, 0, 0, 0).
+ */
+ public static final ColorRGBA BlackNoAlpha = new ColorRGBA(0f, 0f, 0f, 0f);
+ /**
+ * The red component of the color. 0 is none and 1 is maximum red.
+ */
+ public float r;
+ /**
+ * The green component of the color. 0 is none and 1 is maximum green.
+ */
+ public float g;
+ /**
+ * The blue component of the color. 0 is none and 1 is maximum blue.
+ */
+ public float b;
+ /**
+ * The alpha component of the color. 0 is transparent and 1 is opaque.
+ */
+ public float a;
+
+ /**
+ * Constructor instantiates a new ColorRGBA object. This color
+ * is the default "white" with all values 1.
+ */
+ public ColorRGBA() {
+ r = g = b = a = 1.0f;
+ }
+
+ /**
+ * Constructor instantiates a new ColorRGBA object. The values
+ * are defined as passed parameters. These values are then clamped to insure
+ * that they are between 0 and 1.
+ *
+ * @param r The red component of this color.
+ * @param g The green component of this ColorRGBA.
+ * @param b The blue component of this ColorRGBA.
+ * @param a The alpha component of this ColorRGBA.
+ */
+ public ColorRGBA(float r, float g, float b, float a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ /**
+ * Copy constructor creates a new ColorRGBA object, based on a
+ * provided color.
+ *
+ * @param rgba The ColorRGBA object to copy.
+ */
+ public ColorRGBA(ColorRGBA rgba) {
+ this.a = rgba.a;
+ this.r = rgba.r;
+ this.g = rgba.g;
+ this.b = rgba.b;
+ }
+
+ /**
+ * set sets the RGBA values of this ColorRGBA. The
+ * values are then clamped to insure that they are between 0 and 1.
+ *
+ * @param r The red component of this color.
+ * @param g The green component of this color.
+ * @param b The blue component of this color.
+ * @param a The alpha component of this color.
+ * @return this
+ */
+ public ColorRGBA set(float r, float g, float b, float a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ return this;
+ }
+
+ /**
+ * set sets the values of this ColorRGBA to those
+ * set by a parameter color.
+ *
+ * @param rgba The color to set this ColorRGBA to.
+ * @return this
+ */
+ public ColorRGBA set(ColorRGBA rgba) {
+ if (rgba == null) {
+ r = 0;
+ g = 0;
+ b = 0;
+ a = 0;
+ } else {
+ r = rgba.r;
+ g = rgba.g;
+ b = rgba.b;
+ a = rgba.a;
+ }
+ return this;
+ }
+
+ public ColorRGBA setR(float r) {
+ this.r = r;
+ return this;
+ }
+
+ public ColorRGBA setG(float g) {
+ this.g = g;
+ return this;
+ }
+
+ public ColorRGBA setB(float b) {
+ this.b = b;
+ return this;
+ }
+
+ public ColorRGBA setA(float a) {
+ this.a = a;
+ return this;
+ }
+
+ /**
+ * clamp insures that all values are between 0 and 1. If any
+ * are less than 0 they are set to zero. If any are more than 1 they are set
+ * to one.
+ */
+ public void clamp() {
+ if (r < 0) {
+ r = 0;
+ } else if (r > 1) {
+ r = 1;
+ }
+
+ if (g < 0) {
+ g = 0;
+ } else if (g > 1) {
+ g = 1;
+ }
+
+ if (b < 0) {
+ b = 0;
+ } else if (b > 1) {
+ b = 1;
+ }
+
+ if (a < 0) {
+ a = 0;
+ } else if (a > 1) {
+ a = 1;
+ }
+ }
+
+ /**
+ * getColorArray retrieves the color values of this
+ * ColorRGBA as a four element float array in the
+ * order: r,g,b,a.
+ *
+ * @return The float array that contains the color components.
+ */
+ public float[] getColorArray() {
+ return new float[] { r, g, b, a };
+ }
+
+ /**
+ * Stores the current r,g,b,a values into the given array. The given array
+ * must have a length of 4 or greater, or an array index out of bounds
+ * exception will be thrown.
+ *
+ * @param store The float array to store the values into.
+ * @return The float array after storage.
+ */
+ public float[] getColorArray(float[] store) {
+ store[0] = r;
+ store[1] = g;
+ store[2] = b;
+ store[3] = a;
+ return store;
+ }
+
+ /**
+ * Retrieves the alpha component value of this ColorRGBA.
+ *
+ * @return The alpha component value.
+ */
+ public float getAlpha() {
+ return a;
+ }
+
+ /**
+ * Retrieves the red component value of this ColorRGBA.
+ *
+ * @return The red component value.
+ */
+ public float getRed() {
+ return r;
+ }
+
+ /**
+ * Retrieves the blue component value of this ColorRGBA.
+ *
+ * @return The blue component value.
+ */
+ public float getBlue() {
+ return b;
+ }
+
+ /**
+ * Retrieves the green component value of this ColorRGBA.
+ *
+ * @return The green component value.
+ */
+ public float getGreen() {
+ return g;
+ }
+
+ /**
+ * Sets this ColorRGBA to the interpolation by changeAmnt from
+ * this to the finalColor: this=(1-changeAmnt)*this + changeAmnt *
+ * finalColor
+ *
+ * @param finalColor The final color to interpolate towards.
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
+ * change from this towards finalColor.
+ */
+ public void interpolate(ColorRGBA finalColor, float changeAmnt) {
+ this.r = (1 - changeAmnt) * this.r + changeAmnt * finalColor.r;
+ this.g = (1 - changeAmnt) * this.g + changeAmnt * finalColor.g;
+ this.b = (1 - changeAmnt) * this.b + changeAmnt * finalColor.b;
+ this.a = (1 - changeAmnt) * this.a + changeAmnt * finalColor.a;
+ }
+
+ /**
+ * Sets this ColorRGBA to the interpolation by changeAmnt from
+ * beginColor to finalColor: this=(1-changeAmnt)*beginColor + changeAmnt *
+ * finalColor
+ *
+ * @param beginColor The begining color (changeAmnt=0).
+ * @param finalColor The final color to interpolate towards (changeAmnt=1).
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from beginColor towards finalColor.
+ */
+ public void interpolate(ColorRGBA beginColor, ColorRGBA finalColor, float changeAmnt) {
+ this.r = (1 - changeAmnt) * beginColor.r + changeAmnt * finalColor.r;
+ this.g = (1 - changeAmnt) * beginColor.g + changeAmnt * finalColor.g;
+ this.b = (1 - changeAmnt) * beginColor.b + changeAmnt * finalColor.b;
+ this.a = (1 - changeAmnt) * beginColor.a + changeAmnt * finalColor.a;
+ }
+
+ /**
+ * randomColor is a utility method that generates a random
+ * opaque color.
+ *
+ * @return a random ColorRGBA with an alpha set to 1.
+ */
+ public static ColorRGBA randomColor() {
+ ColorRGBA rVal = new ColorRGBA(0, 0, 0, 1);
+ rVal.r = FastMath.nextRandomFloat();
+ rVal.g = FastMath.nextRandomFloat();
+ rVal.b = FastMath.nextRandomFloat();
+ return rVal;
+ }
+
+ /**
+ * Multiplies each r,g,b,a of this ColorRGBA by the
+ * corresponding r,g,b,a of the given color and returns the result as a new
+ * ColorRGBA. Used as a way of combining colors and lights.
+ *
+ * @param c The color to multiply by.
+ * @return The new ColorRGBA. this*c
+ */
+ public ColorRGBA mult(ColorRGBA c) {
+ return new ColorRGBA(c.r * r, c.g * g, c.b * b, c.a * a);
+ }
+
+ /**
+ * Multiplies each r,g,b,a of this ColorRGBA by the given
+ * scalar and returns the result as a new ColorRGBA. Used as a
+ * way of making colors dimmer or brighter.
+ *
+ * @param scalar The scalar to multiply by.
+ * @return The new ColorRGBA. this*scalar
+ */
+ public ColorRGBA mult(float scalar) {
+ return new ColorRGBA(scalar * r, scalar * g, scalar * b, scalar * a);
+ }
+
+ /**
+ * Multiplies each r,g,b,a of this ColorRGBA by the given
+ * scalar and returns the result (this). Used as a way of making colors
+ * dimmer or brighter.
+ *
+ * @param scalar The scalar to multiply by.
+ * @return this*c
+ */
+ public ColorRGBA multLocal(float scalar) {
+ this.r *= scalar;
+ this.g *= scalar;
+ this.b *= scalar;
+ this.a *= scalar;
+ return this;
+ }
+
+ /**
+ * Adds each r,g,b,a of this ColorRGBA by the corresponding
+ * r,g,b,a of the given color and returns the result as a new
+ * ColorRGBA. Used as a way of combining colors and lights.
+ *
+ * @param c The color to add.
+ * @return The new ColorRGBA. this+c
+ */
+ public ColorRGBA add(ColorRGBA c) {
+ return new ColorRGBA(c.r + r, c.g + g, c.b + b, c.a + a);
+ }
+
+ /**
+ * Adds each r,g,b,a of this ColorRGBA by the r,g,b,a the given
+ * color and returns the result (this). Used as a way of combining colors
+ * and lights.
+ *
+ * @param c The color to add.
+ * @return this+c
+ */
+ public ColorRGBA addLocal(ColorRGBA c) {
+ set(c.r + r, c.g + g, c.b + b, c.a + a);
+ return this;
+ }
+
+ /**
+ * Multiplies alpha component by the given scalar and returns the result as
+ * a new color.
+ */
+ public ColorRGBA dilute(float scalarA) {
+ return new ColorRGBA(r, g, b, a * scalarA);
+ }
+
+ /**
+ * Multiplies alpha component by the given scalar and returns the result
+ * (this).
+ */
+ public ColorRGBA diluteLocal(float scalarA) {
+ this.a *= scalarA;
+ return this;
+ }
+
+ /**
+ * toString returns the string representation of this
+ * ColorRGBA. The format of the string is:
+ * : [R=RR.RRRR, G=GG.GGGG, B=BB.BBBB, A=AA.AAAA]
+ *
+ * @return The string representation of this ColorRGBA.
+ */
+ @Override
+ public String toString() {
+ return "Color[" + r + ", " + g + ", " + b + ", " + a + "]";
+ }
+
+ @Override
+ public ColorRGBA clone() {
+ try {
+ return (ColorRGBA) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this ColorRGBA into the given float
+ * array.
+ *
+ * @param floats The float array to take this
+ * ColorRGBA. If null, a new float[4] is created.
+ * @return The array, with r,g,b,a float values in that order.
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[4];
+ }
+ floats[0] = r;
+ floats[1] = g;
+ floats[2] = b;
+ floats[3] = a;
+ return floats;
+ }
+
+ public ColorRGBA fromArray(float[] floats) {
+ r = floats[0];
+ g = floats[1];
+ b = floats[2];
+ a = floats[3];
+ return this;
+ }
+
+ /**
+ * equals returns true if this ColorRGBA is
+ * logically equivalent to a given color. That is, if all the components of
+ * the two colors are the same. False is returned otherwise.
+ *
+ * @param o The object to compare against.
+ * @return true if the colors are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ColorRGBA)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ ColorRGBA comp = (ColorRGBA) o;
+ if (!FloatMath.equalsWithEpsilon(r, comp.r, FastMath.ZERO_TOLERANCE)) {
+ return false;
+ }
+ if (!FloatMath.equalsWithEpsilon(g, comp.g, FastMath.ZERO_TOLERANCE)) {
+ return false;
+ }
+ if (!FloatMath.equalsWithEpsilon(b, comp.b, FastMath.ZERO_TOLERANCE)) {
+ return false;
+ }
+ if (!FloatMath.equalsWithEpsilon(a, comp.a, FastMath.ZERO_TOLERANCE)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * hashCode returns a unique code for this
+ * ColorRGBA based on its values. If two colors are logically
+ * equivalent, they will return the same hash code value.
+ *
+ * @return The hash code value of this ColorRGBA.
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(r);
+ hash += 37 * hash + Float.floatToIntBits(g);
+ hash += 37 * hash + Float.floatToIntBits(b);
+ hash += 37 * hash + Float.floatToIntBits(a);
+ return hash;
+ }
+
+ /**
+ * Retrieves the component values of this ColorRGBA as a four
+ * element byte array in the order: r,g,b,a.
+ *
+ * @return the byte array that contains the color components.
+ */
+ public byte[] asBytesRGBA() {
+ byte[] store = new byte[4];
+ store[0] = (byte) ((int) (r * 255) & 0xFF);
+ store[1] = (byte) ((int) (g * 255) & 0xFF);
+ store[2] = (byte) ((int) (b * 255) & 0xFF);
+ store[3] = (byte) ((int) (a * 255) & 0xFF);
+ return store;
+ }
+
+ /**
+ * Retrieves the component values of this ColorRGBA as an
+ * int in a,r,g,b order. Bits 24-31 are alpha, 16-23 are red,
+ * 8-15 are green, 0-7 are blue.
+ *
+ * @return The integer representation of this ColorRGBA in
+ * a,r,g,b order.
+ */
+ public int asIntARGB() {
+ int argb = (((int) (a * 255) & 0xFF) << 24)
+ | (((int) (r * 255) & 0xFF) << 16)
+ | (((int) (g * 255) & 0xFF) << 8)
+ | (((int) (b * 255) & 0xFF));
+ return argb;
+ }
+
+ /**
+ * Retrieves the component values of this ColorRGBA as an
+ * int in r,g,b,a order. Bits 24-31 are red, 16-23 are green,
+ * 8-15 are blue, 0-7 are alpha.
+ *
+ * @return The integer representation of this ColorRGBA in
+ * r,g,b,a order.
+ */
+ public int asIntRGBA() {
+ int rgba = (((int) (r * 255) & 0xFF) << 24)
+ | (((int) (g * 255) & 0xFF) << 16)
+ | (((int) (b * 255) & 0xFF) << 8)
+ | (((int) (a * 255) & 0xFF));
+ return rgba;
+ }
+
+ /**
+ * Retrieves the component values of this ColorRGBA as an
+ * int in a,b,g,r order. Bits 24-31 are alpha, 16-23 are blue,
+ * 8-15 are green, 0-7 are red.
+ *
+ * @return The integer representation of this ColorRGBA in
+ * a,b,g,r order.
+ */
+ public int asIntABGR() {
+ int abgr = (((int) (a * 255) & 0xFF) << 24)
+ | (((int) (b * 255) & 0xFF) << 16)
+ | (((int) (g * 255) & 0xFF) << 8)
+ | (((int) (r * 255) & 0xFF));
+ return abgr;
+ }
+
+ /**
+ * Sets the component values of this ColorRGBA with the given
+ * combined ARGB int. Bits 24-31 are alpha, bits 16-23 are red,
+ * bits 8-15 are green, bits 0-7 are blue.
+ *
+ * @param color The integer ARGB value used to set this
+ * ColorRGBA.
+ */
+ public void fromIntARGB(int color) {
+ a = ((byte) (color >> 24) & 0xFF) / 255f;
+ r = ((byte) (color >> 16) & 0xFF) / 255f;
+ g = ((byte) (color >> 8) & 0xFF) / 255f;
+ b = ((byte) (color) & 0xFF) / 255f;
+ }
+
+ /**
+ * Sets the RGBA values of this ColorRGBA with the given
+ * combined RGBA value Bits 24-31 are red, bits 16-23 are green, bits 8-15
+ * are blue, bits 0-7 are alpha.
+ *
+ * @param color The integer RGBA value used to set this object.
+ */
+ public void fromIntRGBA(int color) {
+ r = ((byte) (color >> 24) & 0xFF) / 255f;
+ g = ((byte) (color >> 16) & 0xFF) / 255f;
+ b = ((byte) (color >> 8) & 0xFF) / 255f;
+ a = ((byte) (color) & 0xFF) / 255f;
+ }
+
+ /**
+ * Transform this ColorRGBA to a Vector3f using x
+ * = r, y = g, z = b. The Alpha value is not used. This method is useful to
+ * use for shaders assignment.
+ *
+ * @return A Vector3f containing the RGB value of this
+ * ColorRGBA.
+ */
+ public Vector3f toVector3f() {
+ return new Vector3f(r, g, b);
+ }
+
+ /**
+ * Transform this ColorRGBA to a Vector4f using x
+ * = r, y = g, z = b, w = a. This method is useful to use for shaders
+ * assignment.
+ *
+ * @return A Vector4f containing the RGBA value of this
+ * ColorRGBA.
+ */
+ public Vector4f toVector4f() {
+ return new Vector4f(r, g, b, a);
+ }
+}
diff --git a/java/com/jme3/math/FastMath.java b/java/com/jme3/math/FastMath.java
new file mode 100644
index 000000000..ed0917d14
--- /dev/null
+++ b/java/com/jme3/math/FastMath.java
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (c) 2009-2012 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 java.util.Random;
+
+
+/**
+ * FastMath provides 'fast' math approximations and float
+ * equivalents of Math functions. These are all used as static values and
+ * functions.
+ *
+ * @author Various
+ * @version $Id: FastMath.java,v 1.45 2007/08/26 08:44:20 irrisor Exp $
+ */
+final public class FastMath {
+
+ private FastMath() {
+ }
+
+ /** A "close to zero" double epsilon value for use */
+ public static final double DBL_EPSILON = 2.220446049250313E-16d;
+ /** A "close to zero" float epsilon value for use */
+ public static final float FLT_EPSILON = 1.1920928955078125E-7f;
+ /** A "close to zero" float epsilon value for use */
+ public static final float ZERO_TOLERANCE = 0.0001f;
+ public static final float ONE_THIRD = 1f / 3f;
+ /** The value PI as a float. (180 degrees) */
+ public static final float PI = (float) Math.PI;
+ /** The value 2PI as a float. (360 degrees) */
+ public static final float TWO_PI = 2.0f * PI;
+ /** The value PI/2 as a float. (90 degrees) */
+ public static final float HALF_PI = 0.5f * PI;
+ /** The value PI/4 as a float. (45 degrees) */
+ public static final float QUARTER_PI = 0.25f * PI;
+ /** The value 1/PI as a float. */
+ public static final float INV_PI = 1.0f / PI;
+ /** The value 1/(2PI) as a float. */
+ public static final float INV_TWO_PI = 1.0f / TWO_PI;
+ /** A value to multiply a degree value by, to convert it to radians. */
+ public static final float DEG_TO_RAD = PI / 180.0f;
+ /** A value to multiply a radian value by, to convert it to degrees. */
+ public static final float RAD_TO_DEG = 180.0f / PI;
+ /** A precreated random object for random numbers. */
+ public static final Random rand = new Random(System.currentTimeMillis());
+
+ /**
+ * Returns true if the number is a power of 2 (2,4,8,16...)
+ *
+ * A good implementation found on the Java boards. note: a number is a power
+ * of two if and only if it is the smallest number with that number of
+ * significant bits. Therefore, if you subtract 1, you know that the new
+ * number will have fewer bits, so ANDing the original number with anything
+ * less than it will give 0.
+ *
+ * @param number The number to test.
+ * @return True if it is a power of two.
+ */
+ public static boolean isPowerOfTwo(int number) {
+ return (number > 0) && (number & (number - 1)) == 0;
+ }
+
+ public static int nearestPowerOfTwo(int number) {
+ return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2)));
+ }
+
+ /**
+ * Linear interpolation from startValue to endValue by the given percent.
+ * Basically: ((1 - percent) * startValue) + (percent * endValue)
+ *
+ * @param scale scale value to use. if 1, use endValue, if 0, use
+ * startValue.
+ * @param startValue Begining value. 0% of f
+ * @param endValue ending value. 100% of f
+ * @return The interpolated value between startValue and endValue.
+ */
+ public static float interpolateLinear(float scale, float startValue, float endValue) {
+ if (startValue == endValue) {
+ return startValue;
+ }
+ if (scale <= 0f) {
+ return startValue;
+ }
+ if (scale >= 1f) {
+ return endValue;
+ }
+ return ((1f - scale) * startValue) + (scale * endValue);
+ }
+
+ /**
+ * Linear interpolation from startValue to endValue by the given percent.
+ * Basically: ((1 - percent) * startValue) + (percent * endValue)
+ *
+ * @param scale scale value to use. if 1, use endValue, if 0, use
+ * startValue.
+ * @param startValue Begining value. 0% of f
+ * @param endValue ending value. 100% of f
+ * @param store a vector3f to store the result
+ * @return The interpolated value between startValue and endValue.
+ */
+ public static Vector3f interpolateLinear(
+ float scale,
+ Vector3f startValue,
+ Vector3f endValue,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.x = interpolateLinear(scale, startValue.x, endValue.x);
+ store.y = interpolateLinear(scale, startValue.y, endValue.y);
+ store.z = interpolateLinear(scale, startValue.z, endValue.z);
+ return store;
+ }
+
+ /**
+ * Linear interpolation from startValue to endValue by the given percent.
+ * Basically: ((1 - percent) * startValue) + (percent * endValue)
+ *
+ * @param scale scale value to use. if 1, use endValue, if 0, use
+ * startValue.
+ * @param startValue Begining value. 0% of f
+ * @param endValue ending value. 100% of f
+ * @return The interpolated value between startValue and endValue.
+ */
+ public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue) {
+ return interpolateLinear(scale, startValue, endValue, null);
+ }
+
+ /**
+ * Linear extrapolation from startValue to endValue by the given scale. if
+ * scale is between 0 and 1 this method returns the same result as
+ * interpolateLinear if the scale is over 1 the value is linearly
+ * extrapolated. Note that the end value is the value for a scale of 1.
+ *
+ * @param scale the scale for extrapolation
+ * @param startValue the starting value (scale = 0)
+ * @param endValue the end value (scale = 1)
+ * @return an extrapolation for the given parameters
+ */
+ public static float extrapolateLinear(float scale, float startValue, float endValue) {
+// if (scale <= 0f) {
+// return startValue;
+// }
+ return ((1f - scale) * startValue) + (scale * endValue);
+ }
+
+ /**
+ * Linear extrapolation from startValue to endValue by the given scale. if
+ * scale is between 0 and 1 this method returns the same result as
+ * interpolateLinear if the scale is over 1 the value is linearly
+ * extrapolated. Note that the end value is the value for a scale of 1.
+ *
+ * @param scale the scale for extrapolation
+ * @param startValue the starting value (scale = 0)
+ * @param endValue the end value (scale = 1)
+ * @param store an initialized vector to store the return value
+ * @return an extrapolation for the given parameters
+ */
+ public static Vector3f extrapolateLinear(
+ float scale,
+ Vector3f startValue,
+ Vector3f endValue,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+// if (scale <= 1f) {
+// return interpolateLinear(scale, startValue, endValue, store);
+// }
+ store.x = extrapolateLinear(scale, startValue.x, endValue.x);
+ store.y = extrapolateLinear(scale, startValue.y, endValue.y);
+ store.z = extrapolateLinear(scale, startValue.z, endValue.z);
+ return store;
+ }
+
+ /**
+ * Linear extrapolation from startValue to endValue by the given scale. if
+ * scale is between 0 and 1 this method returns the same result as
+ * interpolateLinear if the scale is over 1 the value is linearly
+ * extrapolated. Note that the end value is the value for a scale of 1.
+ *
+ * @param scale the scale for extrapolation
+ * @param startValue the starting value (scale = 0)
+ * @param endValue the end value (scale = 1)
+ * @return an extrapolation for the given parameters
+ */
+ public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue) {
+ return extrapolateLinear(scale, startValue, endValue, null);
+ }
+
+ /**
+ * Interpolate a spline between at least 4 control points following the
+ * Catmull-Rom equation. here is the interpolation matrix m = [ 0.0 1.0 0.0
+ * 0.0 ] [-T 0.0 T 0.0 ] [ 2T T-3 3-2T -T ] [-T 2-T T-2 T ] where T is the
+ * curve tension the result is a value between p1 and p2, t=0 for p1, t=1
+ * for p2
+ *
+ * @param u value from 0 to 1
+ * @param T The tension of the curve
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return catmull-Rom interpolation
+ */
+ public static float interpolateCatmullRom(
+ float u,
+ float T,
+ float p0,
+ float p1,
+ float p2,
+ float p3
+ ) {
+ float c1, c2, c3, c4;
+ c1 = p1;
+ c2 = -1.0f * T * p0 + T * p2;
+ c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3;
+ c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3;
+
+ return (float) (((c4 * u + c3) * u + c2) * u + c1);
+ }
+
+ /**
+ * Interpolate a spline between at least 4 control points following the
+ * Catmull-Rom equation. here is the interpolation matrix m = [ 0.0 1.0 0.0
+ * 0.0 ] [-T 0.0 T 0.0 ] [ 2T T-3 3-2T -T ] [-T 2-T T-2 T ] where T is the
+ * tension of the curve the result is a value between p1 and p2, t=0 for p1,
+ * t=1 for p2
+ *
+ * @param u value from 0 to 1
+ * @param T The tension of the curve
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @param store a Vector3f to store the result
+ * @return catmull-Rom interpolation
+ */
+ public static Vector3f interpolateCatmullRom(
+ float u,
+ float T,
+ Vector3f p0,
+ Vector3f p1,
+ Vector3f p2,
+ Vector3f p3,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.x = interpolateCatmullRom(u, T, p0.x, p1.x, p2.x, p3.x);
+ store.y = interpolateCatmullRom(u, T, p0.y, p1.y, p2.y, p3.y);
+ store.z = interpolateCatmullRom(u, T, p0.z, p1.z, p2.z, p3.z);
+ return store;
+ }
+
+ /**
+ * Interpolate a spline between at least 4 control points following the
+ * Catmull-Rom equation. here is the interpolation matrix m = [ 0.0 1.0 0.0
+ * 0.0 ] [-T 0.0 T 0.0 ] [ 2T T-3 3-2T -T ] [-T 2-T T-2 T ] where T is the
+ * tension of the curve the result is a value between p1 and p2, t=0 for p1,
+ * t=1 for p2
+ *
+ * @param u value from 0 to 1
+ * @param T The tension of the curve
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return catmull-Rom interpolation
+ */
+ public static Vector3f interpolateCatmullRom(
+ float u,
+ float T,
+ Vector3f p0,
+ Vector3f p1,
+ Vector3f p2,
+ Vector3f p3
+ ) {
+ return interpolateCatmullRom(u, T, p0, p1, p2, p3, null);
+ }
+
+ /**
+ * Interpolate a spline between at least 4 control points following the
+ * Bezier equation. here is the interpolation matrix m = [ -1.0 3.0 -3.0 1.0
+ * ] [ 3.0 -6.0 3.0 0.0 ] [ -3.0 3.0 0.0 0.0 ] [ 1.0 0.0 0.0 0.0 ] where T
+ * is the curve tension the result is a value between p1 and p3, t=0 for p1,
+ * t=1 for p3
+ *
+ * @param u value from 0 to 1
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return Bezier interpolation
+ */
+ public static float interpolateBezier(float u, float p0, float p1, float p2, float p3) {
+ float oneMinusU = 1.0f - u;
+ float oneMinusU2 = oneMinusU * oneMinusU;
+ float u2 = u * u;
+ return p0 * oneMinusU2 * oneMinusU
+ + 3.0f * p1 * u * oneMinusU2
+ + 3.0f * p2 * u2 * oneMinusU
+ + p3 * u2 * u;
+ }
+
+ /**
+ * Interpolate a spline between at least 4 control points following the
+ * Bezier equation. here is the interpolation matrix m = [ -1.0 3.0 -3.0 1.0
+ * ] [ 3.0 -6.0 3.0 0.0 ] [ -3.0 3.0 0.0 0.0 ] [ 1.0 0.0 0.0 0.0 ] where T
+ * is the tension of the curve the result is a value between p1 and p3, t=0
+ * for p1, t=1 for p3
+ *
+ * @param u value from 0 to 1
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @param store a Vector3f to store the result
+ * @return Bezier interpolation
+ */
+ public static Vector3f interpolateBezier(
+ float u,
+ Vector3f p0,
+ Vector3f p1,
+ Vector3f p2,
+ Vector3f p3,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.x = interpolateBezier(u, p0.x, p1.x, p2.x, p3.x);
+ store.y = interpolateBezier(u, p0.y, p1.y, p2.y, p3.y);
+ store.z = interpolateBezier(u, p0.z, p1.z, p2.z, p3.z);
+ return store;
+ }
+
+ /**
+ * Interpolate a spline between at least 4 control points following the
+ * Bezier equation. here is the interpolation matrix m = [ -1.0 3.0 -3.0 1.0
+ * ] [ 3.0 -6.0 3.0 0.0 ] [ -3.0 3.0 0.0 0.0 ] [ 1.0 0.0 0.0 0.0 ] where T
+ * is the tension of the curve the result is a value between p1 and p3, t=0
+ * for p1, t=1 for p3
+ *
+ * @param u value from 0 to 1
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return Bezier interpolation
+ */
+ public static Vector3f interpolateBezier(
+ float u,
+ Vector3f p0,
+ Vector3f p1,
+ Vector3f p2,
+ Vector3f p3
+ ) {
+ return interpolateBezier(u, p0, p1, p2, p3, null);
+ }
+
+ /**
+ * Compute the lenght on a catmull rom spline between control point 1 and 2
+ *
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @param startRange the starting range on the segment (use 0)
+ * @param endRange the end range on the segment (use 1)
+ * @param curveTension the curve tension
+ * @return the length of the segment
+ */
+ public static float getCatmullRomP1toP2Length(
+ Vector3f p0,
+ Vector3f p1,
+ Vector3f p2,
+ Vector3f p3,
+ float startRange,
+ float endRange,
+ float curveTension
+ ) {
+
+ float epsilon = 0.001f;
+ float middleValue = (startRange + endRange) * 0.5f;
+ Vector3f start = p1.clone();
+ if (startRange != 0) {
+ FastMath.interpolateCatmullRom(startRange, curveTension, p0, p1, p2, p3, start);
+ }
+ Vector3f end = p2.clone();
+ if (endRange != 1) {
+ FastMath.interpolateCatmullRom(endRange, curveTension, p0, p1, p2, p3, end);
+ }
+ Vector3f middle = FastMath.interpolateCatmullRom(middleValue, curveTension, p0, p1, p2, p3);
+ float l = end.subtract(start).length();
+ float l1 = middle.subtract(start).length();
+ float l2 = end.subtract(middle).length();
+ float len = l1 + l2;
+ if (l + epsilon < len) {
+ l1 = getCatmullRomP1toP2Length(p0, p1, p2, p3, startRange, middleValue, curveTension);
+ l2 = getCatmullRomP1toP2Length(p0, p1, p2, p3, middleValue, endRange, curveTension);
+ }
+ l = l1 + l2;
+ return l;
+ }
+
+ /**
+ * Compute the lenght on a bezier spline between control point 1 and 2
+ *
+ * @param p0 control point 0
+ * @param p1 control point 1
+ * @param p2 control point 2
+ * @param p3 control point 3
+ * @return the length of the segment
+ */
+ public static float getBezierP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
+ float delta = 0.02f, t = 0.0f, result = 0.0f;
+ Vector3f v1 = p0.clone(), v2 = new Vector3f();
+ while (t <= 1.0f) {
+ FastMath.interpolateBezier(t, p0, p1, p2, p3, v2);
+ result += v1.subtractLocal(v2).length();
+ v1.set(v2);
+ t += delta;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the arc cosine of a value.
+ * Special cases:
+ *
+ *
If fValue is smaller than -1, then the result is PI.
+ *
If the argument is greater than 1, then the result is 0.
+ *
+ *
+ * @param fValue The value to arc cosine.
+ * @return The angle, in radians.
+ * @see java.lang.Math#acos(double)
+ */
+ public static float acos(float fValue) {
+ if (-1.0f < fValue) {
+ if (fValue < 1.0f) {
+ return (float) Math.acos(fValue);
+ }
+
+ return 0.0f;
+ }
+
+ return PI;
+ }
+
+ /**
+ * Returns the arc sine of a value.
+ * Special cases:
+ *
+ *
If fValue is smaller than -1, then the result is -HALF_PI.
+ *
If the argument is greater than 1, then the result is HALF_PI.
+ *
+ *
+ * @param fValue The value to arc sine.
+ * @return the angle in radians.
+ * @see java.lang.Math#asin(double)
+ */
+ public static float asin(float fValue) {
+ if (-1.0f < fValue) {
+ if (fValue < 1.0f) {
+ return (float) Math.asin(fValue);
+ }
+
+ return HALF_PI;
+ }
+
+ return -HALF_PI;
+ }
+
+ /**
+ * Returns the arc tangent of an angle given in radians.
+ *
+ * @param fValue The angle, in radians.
+ * @return fValue's atan
+ * @see java.lang.Math#atan(double)
+ */
+ public static float atan(float fValue) {
+ return (float) Math.atan(fValue);
+ }
+
+ /**
+ * A direct call to Math.atan2.
+ *
+ * @param fY
+ * @param fX
+ * @return Math.atan2(fY,fX)
+ * @see java.lang.Math#atan2(double, double)
+ */
+ public static float atan2(float fY, float fX) {
+ return (float) Math.atan2(fY, fX);
+ }
+
+ /**
+ * Rounds a fValue up. A call to Math.ceil
+ *
+ * @param fValue The value.
+ * @return The fValue rounded up
+ * @see java.lang.Math#ceil(double)
+ */
+ public static float ceil(float fValue) {
+ return (float) Math.ceil(fValue);
+ }
+
+ /**
+ * Returns cosine of an angle. Direct call to java.lang.Math
+ *
+ * @see Math#cos(double)
+ * @param v The angle to cosine.
+ * @return the cosine of the angle.
+ */
+ public static float cos(float v) {
+ return (float) Math.cos(v);
+ }
+
+ /**
+ * Returns the sine of an angle. Direct call to java.lang.Math
+ *
+ * @see Math#sin(double)
+ * @param v The angle to sine.
+ * @return the sine of the angle.
+ */
+ public static float sin(float v) {
+ return (float) Math.sin(v);
+ }
+
+ /**
+ * Returns E^fValue
+ *
+ * @param fValue Value to raise to a power.
+ * @return The value E^fValue
+ * @see java.lang.Math#exp(double)
+ */
+ public static float exp(float fValue) {
+ return (float) Math.exp(fValue);
+ }
+
+ /**
+ * Returns Absolute value of a float.
+ *
+ * @param fValue The value to abs.
+ * @return The abs of the value.
+ * @see java.lang.Math#abs(float)
+ */
+ public static float abs(float fValue) {
+ if (fValue < 0) {
+ return -fValue;
+ }
+ return fValue;
+ }
+
+ /**
+ * Returns a number rounded down.
+ *
+ * @param fValue The value to round
+ * @return The given number rounded down
+ * @see java.lang.Math#floor(double)
+ */
+ public static float floor(float fValue) {
+ return (float) Math.floor(fValue);
+ }
+
+ /**
+ * Returns 1/sqrt(fValue)
+ *
+ * @param fValue The value to process.
+ * @return 1/sqrt(fValue)
+ * @see java.lang.Math#sqrt(double)
+ */
+ public static float invSqrt(float fValue) {
+ return (float) (1.0f / Math.sqrt(fValue));
+ }
+
+ public static float fastInvSqrt(float x) {
+ float xhalf = 0.5f * x;
+ int i = Float.floatToIntBits(x); // get bits for floating value
+ i = 0x5f375a86 - (i >> 1); // gives initial guess y0
+ x = Float.intBitsToFloat(i); // convert bits back to float
+ x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases
+ // accuracy
+ return x;
+ }
+
+ /**
+ * Returns the log base E of a value.
+ *
+ * @param fValue The value to log.
+ * @return The log of fValue base E
+ * @see java.lang.Math#log(double)
+ */
+ public static float log(float fValue) {
+ return (float) Math.log(fValue);
+ }
+
+ /**
+ * Returns the logarithm of value with given base, calculated as
+ * log(value)/log(base), so that pow(base, return)==value (contributed by
+ * vear)
+ *
+ * @param value The value to log.
+ * @param base Base of logarithm.
+ * @return The logarithm of value with given base
+ */
+ public static float log(float value, float base) {
+ return (float) (Math.log(value) / Math.log(base));
+ }
+
+ /**
+ * Returns a number raised to an exponent power. fBase^fExponent
+ *
+ * @param fBase The base value (IE 2)
+ * @param fExponent The exponent value (IE 3)
+ * @return base raised to exponent (IE 8)
+ * @see java.lang.Math#pow(double, double)
+ */
+ public static float pow(float fBase, float fExponent) {
+ return (float) Math.pow(fBase, fExponent);
+ }
+
+ /**
+ * Returns the value squared. fValue ^ 2
+ *
+ * @param fValue The vaule to square.
+ * @return The square of the given value.
+ */
+ public static float sqr(float fValue) {
+ return fValue * fValue;
+ }
+
+ /**
+ * Returns the square root of a given value.
+ *
+ * @param fValue The value to sqrt.
+ * @return The square root of the given value.
+ * @see java.lang.Math#sqrt(double)
+ */
+ public static float sqrt(float fValue) {
+ return (float) Math.sqrt(fValue);
+ }
+
+ /**
+ * Returns the tangent of a value. If USE_FAST_TRIG is enabled, an
+ * approximate value is returned. Otherwise, a direct value is used.
+ *
+ * @param fValue The value to tangent, in radians.
+ * @return The tangent of fValue.
+ * @see java.lang.Math#tan(double)
+ */
+ public static float tan(float fValue) {
+ return (float) Math.tan(fValue);
+ }
+
+ /**
+ * Returns 1 if the number is positive, -1 if the number is negative, and 0
+ * otherwise
+ *
+ * @param iValue The integer to examine.
+ * @return The integer's sign.
+ */
+ public static int sign(int iValue) {
+ if (iValue > 0) {
+ return 1;
+ }
+ if (iValue < 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns 1 if the number is positive, -1 if the number is negative, and 0
+ * otherwise
+ *
+ * @param fValue The float to examine.
+ * @return The float's sign.
+ */
+ public static float sign(float fValue) {
+ return Math.signum(fValue);
+ }
+
+ /**
+ * Given 3 points in a 2d plane, this function computes if the points going
+ * from A-B-C are moving counter clock wise.
+ *
+ * @param p0 Point 0.
+ * @param p1 Point 1.
+ * @param p2 Point 2.
+ * @return 1 If they are CCW, -1 if they are not CCW, 0 if p2 is between p0
+ * and p1.
+ */
+ public static int counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) {
+ float dx1, dx2, dy1, dy2;
+ dx1 = p1.x - p0.x;
+ dy1 = p1.y - p0.y;
+ dx2 = p2.x - p0.x;
+ dy2 = p2.y - p0.y;
+ if (dx1 * dy2 > dy1 * dx2) {
+ return 1;
+ }
+ if (dx1 * dy2 < dy1 * dx2) {
+ return -1;
+ }
+ if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0)) {
+ return -1;
+ }
+ if ((dx1 * dx1 + dy1 * dy1) < (dx2 * dx2 + dy2 * dy2)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Test if a point is inside a triangle. 1 if the point is on the ccw side,
+ * -1 if the point is on the cw side, and 0 if it is on neither.
+ *
+ * @param t0 First point of the triangle.
+ * @param t1 Second point of the triangle.
+ * @param t2 Third point of the triangle.
+ * @param p The point to test.
+ * @return Value 1 or -1 if inside triangle, 0 otherwise.
+ */
+ public static int pointInsideTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p) {
+ int val1 = counterClockwise(t0, t1, p);
+ if (val1 == 0) {
+ return 1;
+ }
+ int val2 = counterClockwise(t1, t2, p);
+ if (val2 == 0) {
+ return 1;
+ }
+ if (val2 != val1) {
+ return 0;
+ }
+ int val3 = counterClockwise(t2, t0, p);
+ if (val3 == 0) {
+ return 1;
+ }
+ if (val3 != val1) {
+ return 0;
+ }
+ return val3;
+ }
+
+ /**
+ * A method that computes normal for a triangle defined by three vertices.
+ *
+ * @param v1 first vertex
+ * @param v2 second vertex
+ * @param v3 third vertex
+ * @return a normal for the face
+ */
+ public static Vector3f computeNormal(Vector3f v1, Vector3f v2, Vector3f v3) {
+ Vector3f a1 = v1.subtract(v2);
+ Vector3f a2 = v3.subtract(v2);
+ return a2.crossLocal(a1).normalizeLocal();
+ }
+
+ /**
+ * Returns the determinant of a 4x4 matrix.
+ */
+ public static float determinant(
+ double m00,
+ double m01,
+ double m02,
+ double m03,
+ double m10,
+ double m11,
+ double m12,
+ double m13,
+ double m20,
+ double m21,
+ double m22,
+ double m23,
+ double m30,
+ double m31,
+ double m32,
+ double m33
+ ) {
+
+ double det01 = m20 * m31 - m21 * m30;
+ double det02 = m20 * m32 - m22 * m30;
+ double det03 = m20 * m33 - m23 * m30;
+ double det12 = m21 * m32 - m22 * m31;
+ double det13 = m21 * m33 - m23 * m31;
+ double det23 = m22 * m33 - m23 * m32;
+ return (float) (m00 * (m11 * det23 - m12 * det13 + m13 * det12)
+ - m01
+ * (m10 * det23 - m12 * det03 + m13 * det02)
+ + m02
+ * (m10 * det13 - m11 * det03 + m13 * det01)
+ - m03
+ * (m10 * det12 - m11 * det02 + m12 * det01));
+ }
+
+ /**
+ * Returns a random float between 0 and 1.
+ *
+ * @return A random float between 0.0f (inclusive) to 1.0f
+ * (exclusive).
+ */
+ public static float nextRandomFloat() {
+ return rand.nextFloat();
+ }
+
+ /**
+ * Returns a random integer between min and max.
+ *
+ * @return A random int between min (inclusive) to max
+ * (inclusive).
+ */
+ public static int nextRandomInt(int min, int max) {
+ return (int) (nextRandomFloat() * (max - min + 1)) + min;
+ }
+
+ public static int nextRandomInt() {
+ return rand.nextInt();
+ }
+
+ /**
+ * Converts a point from Spherical coordinates to Cartesian (using positive
+ * Y as up) and stores the results in the store var.
+ */
+ public static Vector3f sphericalToCartesian(
+ Vector3f sphereCoords,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.y = sphereCoords.x * FastMath.sin(sphereCoords.z);
+ float a = sphereCoords.x * FastMath.cos(sphereCoords.z);
+ store.x = a * FastMath.cos(sphereCoords.y);
+ store.z = a * FastMath.sin(sphereCoords.y);
+
+ return store;
+ }
+
+ /**
+ * Converts a point from Cartesian coordinates (using positive Y as up) to
+ * Spherical and stores the results in the store var. (Radius, Azimuth,
+ * Polar)
+ */
+ public static Vector3f cartesianToSpherical(
+ Vector3f cartCoords,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ float x = cartCoords.x;
+ if (x == 0) {
+ x = FastMath.FLT_EPSILON;
+ }
+ store.x = FastMath
+ .sqrt(
+ (x * x)
+ + (cartCoords.y * cartCoords.y)
+ + (cartCoords.z * cartCoords.z)
+ );
+ store.y = FastMath.atan(cartCoords.z / x);
+ if (x < 0) {
+ store.y += FastMath.PI;
+ }
+ store.z = FastMath.asin(cartCoords.y / store.x);
+ return store;
+ }
+
+ /**
+ * Converts a point from Spherical coordinates to Cartesian (using positive
+ * Z as up) and stores the results in the store var.
+ */
+ public static Vector3f sphericalToCartesianZ(
+ Vector3f sphereCoords,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.z = sphereCoords.x * FastMath.sin(sphereCoords.z);
+ float a = sphereCoords.x * FastMath.cos(sphereCoords.z);
+ store.x = a * FastMath.cos(sphereCoords.y);
+ store.y = a * FastMath.sin(sphereCoords.y);
+
+ return store;
+ }
+
+ /**
+ * Converts a point from Cartesian coordinates (using positive Z as up) to
+ * Spherical and stores the results in the store var. (Radius, Azimuth,
+ * Polar)
+ */
+ public static Vector3f cartesianZToSpherical(
+ Vector3f cartCoords,
+ Vector3f store
+ ) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ float x = cartCoords.x;
+ if (x == 0) {
+ x = FastMath.FLT_EPSILON;
+ }
+ store.x = FastMath
+ .sqrt(
+ (x * x)
+ + (cartCoords.y * cartCoords.y)
+ + (cartCoords.z * cartCoords.z)
+ );
+ store.z = FastMath.atan(cartCoords.z / x);
+ if (x < 0) {
+ store.z += FastMath.PI;
+ }
+ store.y = FastMath.asin(cartCoords.y / store.x);
+ return store;
+ }
+
+ /**
+ * Takes an value and expresses it in terms of min to max.
+ *
+ * @param val - the angle to normalize (in radians)
+ * @return the normalized angle (also in radians)
+ */
+ public static float normalize(float val, float min, float max) {
+ if (Float.isInfinite(val) || Float.isNaN(val)) {
+ return 0f;
+ }
+ float range = max - min;
+ while (val > max) {
+ val -= range;
+ }
+ while (val < min) {
+ val += range;
+ }
+ return val;
+ }
+
+ /**
+ * @param x the value whose sign is to be adjusted.
+ * @param y the value whose sign is to be used.
+ * @return x with its sign changed to match the sign of y.
+ */
+ public static float copysign(float x, float y) {
+ if (y >= 0 && x <= -0) {
+ return -x;
+ } else if (y < 0 && x >= 0) {
+ return -x;
+ } else {
+ return x;
+ }
+ }
+
+ /**
+ * Take a float input and clamp it between min and max.
+ *
+ * @param input
+ * @param min
+ * @param max
+ * @return clamped input
+ */
+ public static float clamp(float input, float min, float max) {
+ return (input < min) ? min : (input > max) ? max : input;
+ }
+
+ /**
+ * Clamps the given float to be between 0 and 1.
+ *
+ * @param input
+ * @return input clamped between 0 and 1.
+ */
+ public static float saturate(float input) {
+ return clamp(input, 0f, 1f);
+ }
+
+ /**
+ * Converts a single precision (32 bit) floating point value into half
+ * precision (16 bit).
+ *
+ *
+ * Source:
+ *
+ * http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
+ * broken link
+ *
+ * @param half The half floating point value as a short.
+ * @return floating point value of the half.
+ */
+ public static float convertHalfToFloat(short half) {
+ switch ((int) half) {
+ case 0x0000:
+ return 0f;
+ case 0x8000:
+ return -0f;
+ case 0x7c00:
+ return Float.POSITIVE_INFINITY;
+ case 0xfc00:
+ return Float.NEGATIVE_INFINITY;
+ // TODO: Support for NaN?
+ default:
+ return Float
+ .intBitsToFloat(
+ ((half & 0x8000) << 16)
+ | (((half & 0x7c00) + 0x1C000) << 13)
+ | ((half & 0x03FF) << 13)
+ );
+ }
+ }
+
+ public static short convertFloatToHalf(float flt) {
+ if (Float.isNaN(flt)) {
+ throw new UnsupportedOperationException("NaN to half conversion not supported!");
+ } else if (flt == Float.POSITIVE_INFINITY) {
+ return (short) 0x7c00;
+ } else if (flt == Float.NEGATIVE_INFINITY) {
+ return (short) 0xfc00;
+ } else if (flt == 0f) {
+ return (short) 0x0000;
+ } else if (flt == -0f) {
+ return (short) 0x8000;
+ } else if (flt > 65504f) {
+ // max value supported by half float
+ return 0x7bff;
+ } else if (flt < -65504f) {
+ return (short) (0x7bff | 0x8000);
+ } else if (flt > 0f && flt < 5.96046E-8f) {
+ return 0x0001;
+ } else if (flt < 0f && flt > -5.96046E-8f) {
+ return (short) 0x8001;
+ }
+
+ int f = Float.floatToIntBits(flt);
+ return (short) (((f >> 16) & 0x8000)
+ | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
+ | ((f >> 13) & 0x03ff));
+ }
+}
diff --git a/java/com/jme3/math/Matrix3f.java b/java/com/jme3/math/Matrix3f.java
new file mode 100644
index 000000000..76e745c27
--- /dev/null
+++ b/java/com/jme3/math/Matrix3f.java
@@ -0,0 +1,1362 @@
+/*
+ * Copyright (c) 2009-2012 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 java.nio.FloatBuffer;
+import java.util.logging.Logger;
+
+import com.jme3.util.TempVars;
+
+
+/**
+ * Matrix3f defines a 3x3 matrix. Matrix data is maintained
+ * internally and is accessible via the get and set methods. Convenience methods
+ * are used for matrix operations as well as generating a matrix from a given
+ * set of values.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Matrix3f implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Matrix3f.class.getName());
+ protected float m00, m01, m02;
+ protected float m10, m11, m12;
+ protected float m20, m21, m22;
+ public static final Matrix3f ZERO = new Matrix3f(0, 0, 0, 0, 0, 0, 0, 0, 0);
+ public static final Matrix3f IDENTITY = new Matrix3f();
+
+ /**
+ * Constructor instantiates a new Matrix3f object. The initial
+ * values for the matrix is that of the identity matrix.
+ *
+ */
+ public Matrix3f() {
+ loadIdentity();
+ }
+
+ /**
+ * constructs a matrix with the given values.
+ *
+ * @param m00 0x0 in the matrix.
+ * @param m01 0x1 in the matrix.
+ * @param m02 0x2 in the matrix.
+ * @param m10 1x0 in the matrix.
+ * @param m11 1x1 in the matrix.
+ * @param m12 1x2 in the matrix.
+ * @param m20 2x0 in the matrix.
+ * @param m21 2x1 in the matrix.
+ * @param m22 2x2 in the matrix.
+ */
+ public Matrix3f(
+ float m00,
+ float m01,
+ float m02,
+ float m10,
+ float m11,
+ float m12,
+ float m20,
+ float m21,
+ float m22
+ ) {
+
+ this.m00 = m00;
+ this.m01 = m01;
+ this.m02 = m02;
+ this.m10 = m10;
+ this.m11 = m11;
+ this.m12 = m12;
+ this.m20 = m20;
+ this.m21 = m21;
+ this.m22 = m22;
+ }
+
+ /**
+ * Copy constructor that creates a new Matrix3f object that is
+ * the same as the provided matrix.
+ *
+ * @param mat the matrix to copy.
+ */
+ public Matrix3f(Matrix3f mat) {
+ set(mat);
+ }
+
+ /**
+ * Takes the absolute value of all matrix fields locally.
+ */
+ public void absoluteLocal() {
+ m00 = FastMath.abs(m00);
+ m01 = FastMath.abs(m01);
+ m02 = FastMath.abs(m02);
+ m10 = FastMath.abs(m10);
+ m11 = FastMath.abs(m11);
+ m12 = FastMath.abs(m12);
+ m20 = FastMath.abs(m20);
+ m21 = FastMath.abs(m21);
+ m22 = FastMath.abs(m22);
+ }
+
+ /**
+ * copy transfers the contents of a given matrix to this
+ * matrix. If a null matrix is supplied, this matrix is set to the identity
+ * matrix.
+ *
+ * @param matrix the matrix to copy.
+ * @return this
+ */
+ public Matrix3f set(Matrix3f matrix) {
+ if (null == matrix) {
+ loadIdentity();
+ } else {
+ m00 = matrix.m00;
+ m01 = matrix.m01;
+ m02 = matrix.m02;
+ m10 = matrix.m10;
+ m11 = matrix.m11;
+ m12 = matrix.m12;
+ m20 = matrix.m20;
+ m21 = matrix.m21;
+ m22 = matrix.m22;
+ }
+ return this;
+ }
+
+ /**
+ * get retrieves a value from the matrix at the given position.
+ * If the position is invalid a JmeException is thrown.
+ *
+ * @param i the row index.
+ * @param j the colum index.
+ * @return the value at (i, j).
+ */
+ @SuppressWarnings("fallthrough")
+ public float get(int i, int j) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ return m00;
+ case 1:
+ return m01;
+ case 2:
+ return m02;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ return m10;
+ case 1:
+ return m11;
+ case 2:
+ return m12;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ return m20;
+ case 1:
+ return m21;
+ case 2:
+ return m22;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ * get(float[]) returns the matrix in row-major or column-major
+ * order.
+ *
+ * @param data The array to return the data into. This array can be 9 or 16
+ * floats in size. Only the upper 3x3 are assigned to in the case of a 16
+ * element array.
+ * @param rowMajor True for row major storage in the array (translation in
+ * elements 3, 7, 11 for a 4x4), false for column major (translation in
+ * elements 12, 13, 14 for a 4x4).
+ */
+ public void get(float[] data, boolean rowMajor) {
+ if (data.length == 9) {
+ if (rowMajor) {
+ data[0] = m00;
+ data[1] = m01;
+ data[2] = m02;
+ data[3] = m10;
+ data[4] = m11;
+ data[5] = m12;
+ data[6] = m20;
+ data[7] = m21;
+ data[8] = m22;
+ } else {
+ data[0] = m00;
+ data[1] = m10;
+ data[2] = m20;
+ data[3] = m01;
+ data[4] = m11;
+ data[5] = m21;
+ data[6] = m02;
+ data[7] = m12;
+ data[8] = m22;
+ }
+ } else if (data.length == 16) {
+ if (rowMajor) {
+ data[0] = m00;
+ data[1] = m01;
+ data[2] = m02;
+ data[4] = m10;
+ data[5] = m11;
+ data[6] = m12;
+ data[8] = m20;
+ data[9] = m21;
+ data[10] = m22;
+ } else {
+ data[0] = m00;
+ data[1] = m10;
+ data[2] = m20;
+ data[4] = m01;
+ data[5] = m11;
+ data[6] = m21;
+ data[8] = m02;
+ data[9] = m12;
+ data[10] = m22;
+ }
+ } else {
+ throw new IndexOutOfBoundsException("Array size must be 9 or 16 in Matrix3f.get().");
+ }
+ }
+
+ /**
+ * Normalize this matrix and store the result in the store parameter that is
+ * returned.
+ *
+ * Note that the original matrix is not altered.
+ *
+ * @param store the matrix to store the result of the normalization. If this
+ * parameter is null a new one is created
+ * @return the normalized matrix
+ */
+ public Matrix3f normalize(Matrix3f store) {
+ if (store == null) {
+ store = new Matrix3f();
+ }
+
+ float mag = 1.0f
+ / FastMath
+ .sqrt(
+ m00 * m00
+ + m10 * m10
+ + m20 * m20
+ );
+
+ store.m00 = m00 * mag;
+ store.m10 = m10 * mag;
+ store.m20 = m20 * mag;
+
+ mag = 1.0f
+ / FastMath
+ .sqrt(
+ m01 * m01
+ + m11 * m11
+ + m21 * m21
+ );
+
+ store.m01 = m01 * mag;
+ store.m11 = m11 * mag;
+ store.m21 = m21 * mag;
+
+ store.m02 = store.m10 * store.m21 - store.m11 * store.m20;
+ store.m12 = store.m01 * store.m20 - store.m00 * store.m21;
+ store.m22 = store.m00 * store.m11 - store.m01 * store.m10;
+ return store;
+ }
+
+ /**
+ * Normalize this matrix
+ *
+ * @return this matrix once normalized.
+ */
+ public Matrix3f normalizeLocal() {
+ return normalize(this);
+ }
+
+ /**
+ * getColumn returns one of three columns specified by the
+ * parameter. This column is returned as a Vector3f object.
+ *
+ * @param i the column to retrieve. Must be between 0 and 2.
+ * @return the column specified by the index.
+ */
+ public Vector3f getColumn(int i) {
+ return getColumn(i, null);
+ }
+
+ /**
+ * getColumn returns one of three columns specified by the
+ * parameter. This column is returned as a Vector3f object.
+ *
+ * @param i the column to retrieve. Must be between 0 and 2.
+ * @param store the vector object to store the result in. if null, a new one
+ * is created.
+ * @return the column specified by the index.
+ */
+ public Vector3f getColumn(int i, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ switch (i) {
+ case 0:
+ store.x = m00;
+ store.y = m10;
+ store.z = m20;
+ break;
+ case 1:
+ store.x = m01;
+ store.y = m11;
+ store.z = m21;
+ break;
+ case 2:
+ store.x = m02;
+ store.y = m12;
+ store.z = m22;
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ return store;
+ }
+
+ /**
+ * getColumn returns one of three rows as specified by the
+ * parameter. This row is returned as a Vector3f object.
+ *
+ * @param i the row to retrieve. Must be between 0 and 2.
+ * @return the row specified by the index.
+ */
+ public Vector3f getRow(int i) {
+ return getRow(i, null);
+ }
+
+ /**
+ * getRow returns one of three rows as specified by the
+ * parameter. This row is returned as a Vector3f object.
+ *
+ * @param i the row to retrieve. Must be between 0 and 2.
+ * @param store the vector object to store the result in. if null, a new one
+ * is created.
+ * @return the row specified by the index.
+ */
+ public Vector3f getRow(int i, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ switch (i) {
+ case 0:
+ store.x = m00;
+ store.y = m01;
+ store.z = m02;
+ break;
+ case 1:
+ store.x = m10;
+ store.y = m11;
+ store.z = m12;
+ break;
+ case 2:
+ store.x = m20;
+ store.y = m21;
+ store.z = m22;
+ break;
+ default:
+ logger.warning("Invalid row index.");
+ throw new IllegalArgumentException("Invalid row index. " + i);
+ }
+ return store;
+ }
+
+ /**
+ * fillFloatBuffer fills a FloatBuffer object with the matrix
+ * data.
+ *
+ * @param fb the buffer to fill, starting at current position. Must have
+ * room for 9 more floats.
+ * @return matrix data as a FloatBuffer. (position is advanced by 9 and any
+ * limit set is not changed).
+ */
+ public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) {
+// if (columnMajor){
+// fb.put(m00).put(m10).put(m20);
+// fb.put(m01).put(m11).put(m21);
+// fb.put(m02).put(m12).put(m22);
+// }else{
+// fb.put(m00).put(m01).put(m02);
+// fb.put(m10).put(m11).put(m12);
+// fb.put(m20).put(m21).put(m22);
+// }
+
+ TempVars vars = TempVars.get();
+
+
+ fillFloatArray(vars.matrixWrite, columnMajor);
+ fb.put(vars.matrixWrite, 0, 9);
+
+ vars.release();
+
+ return fb;
+ }
+
+ public void fillFloatArray(float[] f, boolean columnMajor) {
+ if (columnMajor) {
+ f[0] = m00;
+ f[1] = m10;
+ f[2] = m20;
+ f[3] = m01;
+ f[4] = m11;
+ f[5] = m21;
+ f[6] = m02;
+ f[7] = m12;
+ f[8] = m22;
+ } else {
+ f[0] = m00;
+ f[1] = m01;
+ f[2] = m02;
+ f[3] = m10;
+ f[4] = m11;
+ f[5] = m12;
+ f[6] = m20;
+ f[7] = m21;
+ f[8] = m22;
+ }
+ }
+
+ /**
+ *
+ * setColumn sets a particular column of this matrix to that
+ * represented by the provided vector.
+ *
+ * @param i the column to set.
+ * @param column the data to set.
+ * @return this
+ */
+ public Matrix3f setColumn(int i, Vector3f column) {
+
+ if (column == null) {
+ logger.warning("Column is null. Ignoring.");
+ return this;
+ }
+ switch (i) {
+ case 0:
+ m00 = column.x;
+ m10 = column.y;
+ m20 = column.z;
+ break;
+ case 1:
+ m01 = column.x;
+ m11 = column.y;
+ m21 = column.z;
+ break;
+ case 2:
+ m02 = column.x;
+ m12 = column.y;
+ m22 = column.z;
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ return this;
+ }
+
+ /**
+ *
+ * setRow sets a particular row of this matrix to that
+ * represented by the provided vector.
+ *
+ * @param i the row to set.
+ * @param row the data to set.
+ * @return this
+ */
+ public Matrix3f setRow(int i, Vector3f row) {
+
+ if (row == null) {
+ logger.warning("Row is null. Ignoring.");
+ return this;
+ }
+ switch (i) {
+ case 0:
+ m00 = row.x;
+ m01 = row.y;
+ m02 = row.z;
+ break;
+ case 1:
+ m10 = row.x;
+ m11 = row.y;
+ m12 = row.z;
+ break;
+ case 2:
+ m20 = row.x;
+ m21 = row.y;
+ m22 = row.z;
+ break;
+ default:
+ logger.warning("Invalid row index.");
+ throw new IllegalArgumentException("Invalid row index. " + i);
+ }
+ return this;
+ }
+
+ /**
+ * set places a given value into the matrix at the given
+ * position. If the position is invalid a JmeException is
+ * thrown.
+ *
+ * @param i the row index.
+ * @param j the colum index.
+ * @param value the value for (i, j).
+ * @return this
+ */
+ @SuppressWarnings("fallthrough")
+ public Matrix3f set(int i, int j, float value) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ m00 = value;
+ return this;
+ case 1:
+ m01 = value;
+ return this;
+ case 2:
+ m02 = value;
+ return this;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ m10 = value;
+ return this;
+ case 1:
+ m11 = value;
+ return this;
+ case 2:
+ m12 = value;
+ return this;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ m20 = value;
+ return this;
+ case 1:
+ m21 = value;
+ return this;
+ case 2:
+ m22 = value;
+ return this;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ *
+ * set sets the values of the matrix to those supplied by the
+ * 3x3 two dimenion array.
+ *
+ * @param matrix the new values of the matrix.
+ * @throws JmeException if the array is not of size 9.
+ * @return this
+ */
+ public Matrix3f set(float[][] matrix) {
+ if (matrix.length != 3 || matrix[0].length != 3) {
+ throw new IllegalArgumentException(
+ "Array must be of size 9."
+ );
+ }
+
+ m00 = matrix[0][0];
+ m01 = matrix[0][1];
+ m02 = matrix[0][2];
+ m10 = matrix[1][0];
+ m11 = matrix[1][1];
+ m12 = matrix[1][2];
+ m20 = matrix[2][0];
+ m21 = matrix[2][1];
+ m22 = matrix[2][2];
+
+ return this;
+ }
+
+ /**
+ * Recreate Matrix using the provided axis.
+ *
+ * @param uAxis Vector3f
+ * @param vAxis Vector3f
+ * @param wAxis Vector3f
+ */
+ public void fromAxes(Vector3f uAxis, Vector3f vAxis, Vector3f wAxis) {
+ m00 = uAxis.x;
+ m10 = uAxis.y;
+ m20 = uAxis.z;
+
+ m01 = vAxis.x;
+ m11 = vAxis.y;
+ m21 = vAxis.z;
+
+ m02 = wAxis.x;
+ m12 = wAxis.y;
+ m22 = wAxis.z;
+ }
+
+ /**
+ * set sets the values of this matrix from an array of values
+ * assuming that the data is rowMajor order;
+ *
+ * @param matrix the matrix to set the value to.
+ * @return this
+ */
+ public Matrix3f set(float[] matrix) {
+ return set(matrix, true);
+ }
+
+ /**
+ * set sets the values of this matrix from an array of values;
+ *
+ * @param matrix the matrix to set the value to.
+ * @param rowMajor whether the incoming data is in row or column major
+ * order.
+ * @return this
+ */
+ public Matrix3f set(float[] matrix, boolean rowMajor) {
+ if (matrix.length != 9) {
+ throw new IllegalArgumentException(
+ "Array must be of size 9."
+ );
+ }
+
+ if (rowMajor) {
+ m00 = matrix[0];
+ m01 = matrix[1];
+ m02 = matrix[2];
+ m10 = matrix[3];
+ m11 = matrix[4];
+ m12 = matrix[5];
+ m20 = matrix[6];
+ m21 = matrix[7];
+ m22 = matrix[8];
+ } else {
+ m00 = matrix[0];
+ m01 = matrix[3];
+ m02 = matrix[6];
+ m10 = matrix[1];
+ m11 = matrix[4];
+ m12 = matrix[7];
+ m20 = matrix[2];
+ m21 = matrix[5];
+ m22 = matrix[8];
+ }
+ return this;
+ }
+
+ /**
+ *
+ * set defines the values of the matrix based on a supplied
+ * Quaternion. It should be noted that all previous values will
+ * be overridden.
+ *
+ * @param quaternion the quaternion to create a rotational matrix from.
+ * @return this
+ */
+ public Matrix3f set(Quaternion quaternion) {
+ return quaternion.toRotationMatrix(this);
+ }
+
+ /**
+ * loadIdentity sets this matrix to the identity matrix. Where
+ * all values are zero except those along the diagonal which are one.
+ *
+ */
+ public void loadIdentity() {
+ m01 = m02 = m10 = m12 = m20 = m21 = 0;
+ m00 = m11 = m22 = 1;
+ }
+
+ /**
+ * @return true if this matrix is identity
+ */
+ public boolean isIdentity() {
+ return (m00 == 1 && m01 == 0 && m02 == 0)
+ && (m10 == 0 && m11 == 1 && m12 == 0)
+ && (m20 == 0 && m21 == 0 && m22 == 1);
+ }
+
+ /**
+ * fromAngleAxis sets this matrix4f to the values specified by
+ * an angle and an axis of rotation. This method creates an object, so use
+ * fromAngleNormalAxis if your axis is already normalized.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param axis the axis of rotation.
+ */
+ public void fromAngleAxis(float angle, Vector3f axis) {
+ Vector3f normAxis = axis.normalize();
+ fromAngleNormalAxis(angle, normAxis);
+ }
+
+ /**
+ * fromAngleNormalAxis sets this matrix4f to the values
+ * specified by an angle and a normalized axis of rotation.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param axis the axis of rotation (already normalized).
+ */
+ public void fromAngleNormalAxis(float angle, Vector3f axis) {
+ float fCos = FastMath.cos(angle);
+ float fSin = FastMath.sin(angle);
+ float fOneMinusCos = ((float) 1.0) - fCos;
+ float fX2 = axis.x * axis.x;
+ float fY2 = axis.y * axis.y;
+ float fZ2 = axis.z * axis.z;
+ float fXYM = axis.x * axis.y * fOneMinusCos;
+ float fXZM = axis.x * axis.z * fOneMinusCos;
+ float fYZM = axis.y * axis.z * fOneMinusCos;
+ float fXSin = axis.x * fSin;
+ float fYSin = axis.y * fSin;
+ float fZSin = axis.z * fSin;
+
+ m00 = fX2 * fOneMinusCos + fCos;
+ m01 = fXYM - fZSin;
+ m02 = fXZM + fYSin;
+ m10 = fXYM + fZSin;
+ m11 = fY2 * fOneMinusCos + fCos;
+ m12 = fYZM - fXSin;
+ m20 = fXZM - fYSin;
+ m21 = fYZM + fXSin;
+ m22 = fZ2 * fOneMinusCos + fCos;
+ }
+
+ /**
+ * mult multiplies this matrix by a given matrix. The result
+ * matrix is returned as a new object. If the given matrix is null, a null
+ * matrix is returned.
+ *
+ * @param mat the matrix to multiply this matrix by.
+ * @return the result matrix.
+ */
+ public Matrix3f mult(Matrix3f mat) {
+ return mult(mat, null);
+ }
+
+ /**
+ * mult multiplies this matrix by a given matrix. The result
+ * matrix is returned as a new object.
+ *
+ * @param mat the matrix to multiply this matrix by.
+ * @param product the matrix to store the result in. if null, a new matrix3f
+ * is created. It is safe for mat and product to be the same object.
+ * @return a matrix3f object containing the result of this operation
+ */
+ public Matrix3f mult(Matrix3f mat, Matrix3f product) {
+
+ float temp00, temp01, temp02;
+ float temp10, temp11, temp12;
+ float temp20, temp21, temp22;
+
+ if (product == null) {
+ product = new Matrix3f();
+ }
+ temp00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20;
+ temp01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21;
+ temp02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22;
+ temp10 = m10 * mat.m00 + m11 * mat.m10 + m12 * mat.m20;
+ temp11 = m10 * mat.m01 + m11 * mat.m11 + m12 * mat.m21;
+ temp12 = m10 * mat.m02 + m11 * mat.m12 + m12 * mat.m22;
+ temp20 = m20 * mat.m00 + m21 * mat.m10 + m22 * mat.m20;
+ temp21 = m20 * mat.m01 + m21 * mat.m11 + m22 * mat.m21;
+ temp22 = m20 * mat.m02 + m21 * mat.m12 + m22 * mat.m22;
+
+ product.m00 = temp00;
+ product.m01 = temp01;
+ product.m02 = temp02;
+ product.m10 = temp10;
+ product.m11 = temp11;
+ product.m12 = temp12;
+ product.m20 = temp20;
+ product.m21 = temp21;
+ product.m22 = temp22;
+
+ return product;
+ }
+
+ /**
+ * mult multiplies this matrix by a given Vector3f
+ * object. The result vector is returned. If the given vector is null, null
+ * will be returned.
+ *
+ * @param vec the vector to multiply this matrix by.
+ * @return the result vector.
+ */
+ public Vector3f mult(Vector3f vec) {
+ return mult(vec, null);
+ }
+
+ /**
+ * Multiplies this 3x3 matrix by the 1x3 Vector vec and stores the result in
+ * product.
+ *
+ * @param vec The Vector3f to multiply.
+ * @param product The Vector3f to store the result, it is safe for this to
+ * be the same as vec.
+ * @return The given product vector.
+ */
+ public Vector3f mult(Vector3f vec, Vector3f product) {
+
+ if (null == product) {
+ product = new Vector3f();
+ }
+
+ float x = vec.x;
+ float y = vec.y;
+ float z = vec.z;
+
+ product.x = m00 * x + m01 * y + m02 * z;
+ product.y = m10 * x + m11 * y + m12 * z;
+ product.z = m20 * x + m21 * y + m22 * z;
+ return product;
+ }
+
+ /**
+ * multLocal multiplies this matrix internally by a given float
+ * scale factor.
+ *
+ * @param scale the value to scale by.
+ * @return this Matrix3f
+ */
+ public Matrix3f multLocal(float scale) {
+ m00 *= scale;
+ m01 *= scale;
+ m02 *= scale;
+ m10 *= scale;
+ m11 *= scale;
+ m12 *= scale;
+ m20 *= scale;
+ m21 *= scale;
+ m22 *= scale;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies this matrix by a given
+ * Vector3f object. The result vector is stored inside the
+ * passed vector, then returned . If the given vector is null, null will be
+ * returned.
+ *
+ * @param vec the vector to multiply this matrix by.
+ * @return The passed vector after multiplication
+ */
+ public Vector3f multLocal(Vector3f vec) {
+ if (vec == null) {
+ return null;
+ }
+ float x = vec.x;
+ float y = vec.y;
+ vec.x = m00 * x + m01 * y + m02 * vec.z;
+ vec.y = m10 * x + m11 * y + m12 * vec.z;
+ vec.z = m20 * x + m21 * y + m22 * vec.z;
+ return vec;
+ }
+
+ /**
+ * mult multiplies this matrix by a given matrix. The result
+ * matrix is saved in the current matrix. If the given matrix is null,
+ * nothing happens. The current matrix is returned. This is equivalent to
+ * this*=mat
+ *
+ * @param mat the matrix to multiply this matrix by.
+ * @return This matrix, after the multiplication
+ */
+ public Matrix3f multLocal(Matrix3f mat) {
+ return mult(mat, this);
+ }
+
+ /**
+ * Transposes this matrix in place. Returns this matrix for chaining
+ *
+ * @return This matrix after transpose
+ */
+ public Matrix3f transposeLocal() {
+// float[] tmp = new float[9];
+// get(tmp, false);
+// set(tmp, true);
+
+ float tmp = m01;
+ m01 = m10;
+ m10 = tmp;
+
+ tmp = m02;
+ m02 = m20;
+ m20 = tmp;
+
+ tmp = m12;
+ m12 = m21;
+ m21 = tmp;
+
+ return this;
+ }
+
+ /**
+ * Inverts this matrix as a new Matrix3f.
+ *
+ * @return The new inverse matrix
+ */
+ public Matrix3f invert() {
+ return invert(null);
+ }
+
+ /**
+ * Inverts this matrix and stores it in the given store.
+ *
+ * @return The store
+ */
+ public Matrix3f invert(Matrix3f store) {
+ if (store == null) {
+ store = new Matrix3f();
+ }
+
+ float det = determinant();
+ if (FastMath.abs(det) <= FastMath.FLT_EPSILON) {
+ return store.zero();
+ }
+
+ store.m00 = m11 * m22 - m12 * m21;
+ store.m01 = m02 * m21 - m01 * m22;
+ store.m02 = m01 * m12 - m02 * m11;
+ store.m10 = m12 * m20 - m10 * m22;
+ store.m11 = m00 * m22 - m02 * m20;
+ store.m12 = m02 * m10 - m00 * m12;
+ store.m20 = m10 * m21 - m11 * m20;
+ store.m21 = m01 * m20 - m00 * m21;
+ store.m22 = m00 * m11 - m01 * m10;
+
+ store.multLocal(1f / det);
+ return store;
+ }
+
+ /**
+ * Inverts this matrix locally.
+ *
+ * @return this
+ */
+ public Matrix3f invertLocal() {
+ float det = determinant();
+ if (FastMath.abs(det) <= 0f) {
+ return zero();
+ }
+
+ float f00 = m11 * m22 - m12 * m21;
+ float f01 = m02 * m21 - m01 * m22;
+ float f02 = m01 * m12 - m02 * m11;
+ float f10 = m12 * m20 - m10 * m22;
+ float f11 = m00 * m22 - m02 * m20;
+ float f12 = m02 * m10 - m00 * m12;
+ float f20 = m10 * m21 - m11 * m20;
+ float f21 = m01 * m20 - m00 * m21;
+ float f22 = m00 * m11 - m01 * m10;
+
+ m00 = f00;
+ m01 = f01;
+ m02 = f02;
+ m10 = f10;
+ m11 = f11;
+ m12 = f12;
+ m20 = f20;
+ m21 = f21;
+ m22 = f22;
+
+ multLocal(1f / det);
+ return this;
+ }
+
+ /**
+ * Returns a new matrix representing the adjoint of this matrix.
+ *
+ * @return The adjoint matrix
+ */
+ public Matrix3f adjoint() {
+ return adjoint(null);
+ }
+
+ /**
+ * Places the adjoint of this matrix in store (creates store if null.)
+ *
+ * @param store The matrix to store the result in. If null, a new matrix is
+ * created.
+ * @return store
+ */
+ public Matrix3f adjoint(Matrix3f store) {
+ if (store == null) {
+ store = new Matrix3f();
+ }
+
+ store.m00 = m11 * m22 - m12 * m21;
+ store.m01 = m02 * m21 - m01 * m22;
+ store.m02 = m01 * m12 - m02 * m11;
+ store.m10 = m12 * m20 - m10 * m22;
+ store.m11 = m00 * m22 - m02 * m20;
+ store.m12 = m02 * m10 - m00 * m12;
+ store.m20 = m10 * m21 - m11 * m20;
+ store.m21 = m01 * m20 - m00 * m21;
+ store.m22 = m00 * m11 - m01 * m10;
+
+ return store;
+ }
+
+ /**
+ * determinant generates the determinant of this matrix.
+ *
+ * @return the determinant
+ */
+ public float determinant() {
+ float fCo00 = m11 * m22 - m12 * m21;
+ float fCo10 = m12 * m20 - m10 * m22;
+ float fCo20 = m10 * m21 - m11 * m20;
+ float fDet = m00 * fCo00 + m01 * fCo10 + m02 * fCo20;
+ return fDet;
+ }
+
+ /**
+ * Sets all of the values in this matrix to zero.
+ *
+ * @return this matrix
+ */
+ public Matrix3f zero() {
+ m00 = m01 = m02 = m10 = m11 = m12 = m20 = m21 = m22 = 0.0f;
+ return this;
+ }
+
+ /**
+ * transposelocally transposes this Matrix. This is
+ * inconsistent with general value vs local semantics, but is preserved for
+ * backwards compatibility. Use transposeNew() to transpose to a new object
+ * (value).
+ *
+ * @return this object for chaining.
+ */
+ public Matrix3f transpose() {
+ return transposeLocal();
+ }
+
+ /**
+ * transposeNew returns a transposed version of this matrix.
+ *
+ * @return The new Matrix3f object.
+ */
+ public Matrix3f transposeNew() {
+ Matrix3f ret = new Matrix3f(m00, m10, m20, m01, m11, m21, m02, m12, m22);
+ return ret;
+ }
+
+ /**
+ * toString returns the string representation of this object.
+ * It is in a format of a 3x3 matrix. For example, an identity matrix would
+ * be represented by the following string. com.jme.math.Matrix3f
+ * [
+ * 1.0 0.0 0.0
+ * 0.0 1.0 0.0
+ * 0.0 0.0 1.0
+ * ]
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("Matrix3f\n[\n");
+ result.append(" ");
+ result.append(m00);
+ result.append(" ");
+ result.append(m01);
+ result.append(" ");
+ result.append(m02);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m10);
+ result.append(" ");
+ result.append(m11);
+ result.append(" ");
+ result.append(m12);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m20);
+ result.append(" ");
+ result.append(m21);
+ result.append(" ");
+ result.append(m22);
+ result.append(" \n]");
+ return result.toString();
+ }
+
+ /**
+ *
+ * hashCode returns the hash code value as an integer and is
+ * supported for the benefit of hashing based collection classes such as
+ * Hashtable, HashMap, HashSet etc.
+ *
+ * @return the hashcode for this instance of Matrix4f.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash = 37 * hash + Float.floatToIntBits(m00);
+ hash = 37 * hash + Float.floatToIntBits(m01);
+ hash = 37 * hash + Float.floatToIntBits(m02);
+
+ hash = 37 * hash + Float.floatToIntBits(m10);
+ hash = 37 * hash + Float.floatToIntBits(m11);
+ hash = 37 * hash + Float.floatToIntBits(m12);
+
+ hash = 37 * hash + Float.floatToIntBits(m20);
+ hash = 37 * hash + Float.floatToIntBits(m21);
+ hash = 37 * hash + Float.floatToIntBits(m22);
+
+ return hash;
+ }
+
+ /**
+ * are these two matrices the same? they are is they both have the same mXX
+ * values.
+ *
+ * @param o the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Matrix3f) || o == null) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Matrix3f comp = (Matrix3f) o;
+ if (Float.compare(m00, comp.m00) != 0) {
+ return false;
+ }
+ if (Float.compare(m01, comp.m01) != 0) {
+ return false;
+ }
+ if (Float.compare(m02, comp.m02) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m10, comp.m10) != 0) {
+ return false;
+ }
+ if (Float.compare(m11, comp.m11) != 0) {
+ return false;
+ }
+ if (Float.compare(m12, comp.m12) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m20, comp.m20) != 0) {
+ return false;
+ }
+ if (Float.compare(m21, comp.m21) != 0) {
+ return false;
+ }
+ if (Float.compare(m22, comp.m22) != 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * A function for creating a rotation matrix that rotates a vector called
+ * "start" into another vector called "end".
+ *
+ * @param start normalized non-zero starting vector
+ * @param end normalized non-zero ending vector
+ * @see "Tomas M�ller, John Hughes \"Efficiently Building a Matrix to Rotate
+ * \ One Vector to Another\" Journal of Graphics Tools, 4(4):1-4, 1999"
+ */
+ public void fromStartEndVectors(Vector3f start, Vector3f end) {
+ Vector3f v = new Vector3f();
+ float e, h, f;
+
+ start.cross(end, v);
+ e = start.dot(end);
+ f = (e < 0) ? -e : e;
+
+ // if "from" and "to" vectors are nearly parallel
+ if (f > 1.0f - FastMath.ZERO_TOLERANCE) {
+ Vector3f u = new Vector3f();
+ Vector3f x = new Vector3f();
+ float c1, c2, c3; /* coefficients for later use */
+ int i, j;
+
+ x.x = (start.x > 0.0) ? start.x : -start.x;
+ x.y = (start.y > 0.0) ? start.y : -start.y;
+ x.z = (start.z > 0.0) ? start.z : -start.z;
+
+ if (x.x < x.y) {
+ if (x.x < x.z) {
+ x.x = 1.0f;
+ x.y = x.z = 0.0f;
+ } else {
+ x.z = 1.0f;
+ x.x = x.y = 0.0f;
+ }
+ } else {
+ if (x.y < x.z) {
+ x.y = 1.0f;
+ x.x = x.z = 0.0f;
+ } else {
+ x.z = 1.0f;
+ x.x = x.y = 0.0f;
+ }
+ }
+
+ u.x = x.x - start.x;
+ u.y = x.y - start.y;
+ u.z = x.z - start.z;
+ v.x = x.x - end.x;
+ v.y = x.y - end.y;
+ v.z = x.z - end.z;
+
+ c1 = 2.0f / u.dot(u);
+ c2 = 2.0f / v.dot(v);
+ c3 = c1 * c2 * u.dot(v);
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ float val = -c1 * u.get(i) * u.get(j)
+ - c2
+ * v.get(i)
+ * v.get(j)
+ + c3 * v.get(i) * u.get(j);
+ set(i, j, val);
+ }
+ float val = get(i, i);
+ set(i, i, val + 1.0f);
+ }
+ } else {
+ // the most common case, unless "start"="end", or "start"=-"end"
+ float hvx, hvz, hvxy, hvxz, hvyz;
+ h = 1.0f / (1.0f + e);
+ hvx = h * v.x;
+ hvz = h * v.z;
+ hvxy = hvx * v.y;
+ hvxz = hvx * v.z;
+ hvyz = hvz * v.y;
+ set(0, 0, e + hvx * v.x);
+ set(0, 1, hvxy - v.z);
+ set(0, 2, hvxz + v.y);
+
+ set(1, 0, hvxy + v.z);
+ set(1, 1, e + h * v.y * v.y);
+ set(1, 2, hvyz - v.x);
+
+ set(2, 0, hvxz - v.y);
+ set(2, 1, hvyz + v.x);
+ set(2, 2, e + hvz * v.z);
+ }
+ }
+
+ /**
+ * scale scales the operation performed by this matrix on a
+ * per-component basis.
+ *
+ * @param scale The scale applied to each of the X, Y and Z output values.
+ */
+ public void scale(Vector3f scale) {
+ m00 *= scale.x;
+ m10 *= scale.x;
+ m20 *= scale.x;
+ m01 *= scale.y;
+ m11 *= scale.y;
+ m21 *= scale.y;
+ m02 *= scale.z;
+ m12 *= scale.z;
+ m22 *= scale.z;
+ }
+
+ static boolean equalIdentity(Matrix3f mat) {
+ if (Math.abs(mat.m00 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m11 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m22 - 1) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m01) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m02) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m10) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m12) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m20) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m21) > 1e-4) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public Matrix3f clone() {
+ try {
+ return (Matrix3f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+}
diff --git a/java/com/jme3/math/Matrix4f.java b/java/com/jme3/math/Matrix4f.java
new file mode 100644
index 000000000..b490b7067
--- /dev/null
+++ b/java/com/jme3/math/Matrix4f.java
@@ -0,0 +1,2277 @@
+/*
+ * Copyright (c) 2009-2012 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 java.nio.FloatBuffer;
+import java.util.logging.Logger;
+
+import com.jme3.util.TempVars;
+
+
+/**
+ * Matrix4f defines and maintains a 4x4 matrix in row major order.
+ * This matrix is intended for use in a translation and rotational capacity. It
+ * provides convenience methods for creating the matrix from a multitude of
+ * sources.
+ *
+ * Matrices are stored assuming column vectors on the right, with the
+ * translation in the rightmost column. Element numbering is row,column, so m03
+ * is the zeroth row, third column, which is the "x" translation part. This
+ * means that the implicit storage order is column major. However, the get() and
+ * set() functions on float arrays default to row major order!
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Matrix4f implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Matrix4f.class.getName());
+ public float m00, m01, m02, m03;
+ public float m10, m11, m12, m13;
+ public float m20, m21, m22, m23;
+ public float m30, m31, m32, m33;
+ public static final Matrix4f ZERO = new Matrix4f(
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ public static final Matrix4f IDENTITY = new Matrix4f();
+
+ /**
+ * Constructor instantiates a new Matrix that is set to the
+ * identity matrix.
+ *
+ */
+ public Matrix4f() {
+ loadIdentity();
+ }
+
+ /**
+ * constructs a matrix with the given values.
+ */
+ public Matrix4f(
+ float m00,
+ float m01,
+ float m02,
+ float m03,
+ float m10,
+ float m11,
+ float m12,
+ float m13,
+ float m20,
+ float m21,
+ float m22,
+ float m23,
+ float m30,
+ float m31,
+ float m32,
+ float m33
+ ) {
+
+ this.m00 = m00;
+ this.m01 = m01;
+ this.m02 = m02;
+ this.m03 = m03;
+ this.m10 = m10;
+ this.m11 = m11;
+ this.m12 = m12;
+ this.m13 = m13;
+ this.m20 = m20;
+ this.m21 = m21;
+ this.m22 = m22;
+ this.m23 = m23;
+ this.m30 = m30;
+ this.m31 = m31;
+ this.m32 = m32;
+ this.m33 = m33;
+ }
+
+ /**
+ * Create a new Matrix4f, given data in column-major format.
+ *
+ * @param array An array of 16 floats in column-major format (translation in
+ * elements 12, 13 and 14).
+ */
+ public Matrix4f(float[] array) {
+ set(array, false);
+ }
+
+ /**
+ * Constructor instantiates a new Matrix that is set to the
+ * provided matrix. This constructor copies a given Matrix. If the provided
+ * matrix is null, the constructor sets the matrix to the identity.
+ *
+ * @param mat the matrix to copy.
+ */
+ public Matrix4f(Matrix4f mat) {
+ copy(mat);
+ }
+
+ /**
+ * copy transfers the contents of a given matrix to this
+ * matrix. If a null matrix is supplied, this matrix is set to the identity
+ * matrix.
+ *
+ * @param matrix the matrix to copy.
+ */
+ public void copy(Matrix4f matrix) {
+ if (null == matrix) {
+ loadIdentity();
+ } else {
+ m00 = matrix.m00;
+ m01 = matrix.m01;
+ m02 = matrix.m02;
+ m03 = matrix.m03;
+ m10 = matrix.m10;
+ m11 = matrix.m11;
+ m12 = matrix.m12;
+ m13 = matrix.m13;
+ m20 = matrix.m20;
+ m21 = matrix.m21;
+ m22 = matrix.m22;
+ m23 = matrix.m23;
+ m30 = matrix.m30;
+ m31 = matrix.m31;
+ m32 = matrix.m32;
+ m33 = matrix.m33;
+ }
+ }
+
+ public void fromFrame(Vector3f location, Vector3f direction, Vector3f up, Vector3f left) {
+ loadIdentity();
+
+ TempVars vars = TempVars.get();
+
+ Vector3f f = vars.vect1.set(direction);
+ Vector3f s = vars.vect2.set(f).crossLocal(up);
+ Vector3f u = vars.vect3.set(s).crossLocal(f);
+// s.normalizeLocal();
+// u.normalizeLocal();
+
+ m00 = s.x;
+ m01 = s.y;
+ m02 = s.z;
+
+ m10 = u.x;
+ m11 = u.y;
+ m12 = u.z;
+
+ m20 = -f.x;
+ m21 = -f.y;
+ m22 = -f.z;
+
+// m00 = -left.x;
+// m10 = -left.y;
+// m20 = -left.z;
+//
+// m01 = up.x;
+// m11 = up.y;
+// m21 = up.z;
+//
+// m02 = -direction.x;
+// m12 = -direction.y;
+// m22 = -direction.z;
+//
+
+ Matrix4f transMatrix = vars.tempMat4;
+ transMatrix.loadIdentity();
+ transMatrix.m03 = -location.x;
+ transMatrix.m13 = -location.y;
+ transMatrix.m23 = -location.z;
+ this.multLocal(transMatrix);
+
+ vars.release();
+
+// transMatrix.multLocal(this);
+
+// set(transMatrix);
+ }
+
+ /**
+ * get retrieves the values of this object into a float array
+ * in row-major order.
+ *
+ * @param matrix the matrix to set the values into.
+ */
+ public void get(float[] matrix) {
+ get(matrix, true);
+ }
+
+ /**
+ * set retrieves the values of this object into a float array.
+ *
+ * @param matrix the matrix to set the values into.
+ * @param rowMajor whether the outgoing data is in row or column major
+ * order.
+ */
+ public void get(float[] matrix, boolean rowMajor) {
+ if (matrix.length != 16) {
+ throw new IllegalArgumentException(
+ "Array must be of size 16."
+ );
+ }
+
+ if (rowMajor) {
+ matrix[0] = m00;
+ matrix[1] = m01;
+ matrix[2] = m02;
+ matrix[3] = m03;
+ matrix[4] = m10;
+ matrix[5] = m11;
+ matrix[6] = m12;
+ matrix[7] = m13;
+ matrix[8] = m20;
+ matrix[9] = m21;
+ matrix[10] = m22;
+ matrix[11] = m23;
+ matrix[12] = m30;
+ matrix[13] = m31;
+ matrix[14] = m32;
+ matrix[15] = m33;
+ } else {
+ matrix[0] = m00;
+ matrix[4] = m01;
+ matrix[8] = m02;
+ matrix[12] = m03;
+ matrix[1] = m10;
+ matrix[5] = m11;
+ matrix[9] = m12;
+ matrix[13] = m13;
+ matrix[2] = m20;
+ matrix[6] = m21;
+ matrix[10] = m22;
+ matrix[14] = m23;
+ matrix[3] = m30;
+ matrix[7] = m31;
+ matrix[11] = m32;
+ matrix[15] = m33;
+ }
+ }
+
+ /**
+ * get retrieves a value from the matrix at the given position.
+ * If the position is invalid a JmeException is thrown.
+ *
+ * @param i the row index.
+ * @param j the colum index.
+ * @return the value at (i, j).
+ */
+ @SuppressWarnings("fallthrough")
+ public float get(int i, int j) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ return m00;
+ case 1:
+ return m01;
+ case 2:
+ return m02;
+ case 3:
+ return m03;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ return m10;
+ case 1:
+ return m11;
+ case 2:
+ return m12;
+ case 3:
+ return m13;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ return m20;
+ case 1:
+ return m21;
+ case 2:
+ return m22;
+ case 3:
+ return m23;
+ }
+ case 3:
+ switch (j) {
+ case 0:
+ return m30;
+ case 1:
+ return m31;
+ case 2:
+ return m32;
+ case 3:
+ return m33;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ * getColumn returns one of three columns specified by the
+ * parameter. This column is returned as a float array of length 4.
+ *
+ * @param i the column to retrieve. Must be between 0 and 3.
+ * @return the column specified by the index.
+ */
+ public float[] getColumn(int i) {
+ return getColumn(i, null);
+ }
+
+ /**
+ * getColumn returns one of three columns specified by the
+ * parameter. This column is returned as a float[4].
+ *
+ * @param i the column to retrieve. Must be between 0 and 3.
+ * @param store the float array to store the result in. if null, a new one
+ * is created.
+ * @return the column specified by the index.
+ */
+ public float[] getColumn(int i, float[] store) {
+ if (store == null) {
+ store = new float[4];
+ }
+ switch (i) {
+ case 0:
+ store[0] = m00;
+ store[1] = m10;
+ store[2] = m20;
+ store[3] = m30;
+ break;
+ case 1:
+ store[0] = m01;
+ store[1] = m11;
+ store[2] = m21;
+ store[3] = m31;
+ break;
+ case 2:
+ store[0] = m02;
+ store[1] = m12;
+ store[2] = m22;
+ store[3] = m32;
+ break;
+ case 3:
+ store[0] = m03;
+ store[1] = m13;
+ store[2] = m23;
+ store[3] = m33;
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ return store;
+ }
+
+ /**
+ *
+ * setColumn sets a particular column of this matrix to that
+ * represented by the provided vector.
+ *
+ * @param i the column to set.
+ * @param column the data to set.
+ */
+ public void setColumn(int i, float[] column) {
+
+ if (column == null) {
+ logger.warning("Column is null. Ignoring.");
+ return;
+ }
+ switch (i) {
+ case 0:
+ m00 = column[0];
+ m10 = column[1];
+ m20 = column[2];
+ m30 = column[3];
+ break;
+ case 1:
+ m01 = column[0];
+ m11 = column[1];
+ m21 = column[2];
+ m31 = column[3];
+ break;
+ case 2:
+ m02 = column[0];
+ m12 = column[1];
+ m22 = column[2];
+ m32 = column[3];
+ break;
+ case 3:
+ m03 = column[0];
+ m13 = column[1];
+ m23 = column[2];
+ m33 = column[3];
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+ }
+
+ /**
+ * set places a given value into the matrix at the given
+ * position. If the position is invalid a JmeException is
+ * thrown.
+ *
+ * @param i the row index.
+ * @param j the colum index.
+ * @param value the value for (i, j).
+ */
+ @SuppressWarnings("fallthrough")
+ public void set(int i, int j, float value) {
+ switch (i) {
+ case 0:
+ switch (j) {
+ case 0:
+ m00 = value;
+ return;
+ case 1:
+ m01 = value;
+ return;
+ case 2:
+ m02 = value;
+ return;
+ case 3:
+ m03 = value;
+ return;
+ }
+ case 1:
+ switch (j) {
+ case 0:
+ m10 = value;
+ return;
+ case 1:
+ m11 = value;
+ return;
+ case 2:
+ m12 = value;
+ return;
+ case 3:
+ m13 = value;
+ return;
+ }
+ case 2:
+ switch (j) {
+ case 0:
+ m20 = value;
+ return;
+ case 1:
+ m21 = value;
+ return;
+ case 2:
+ m22 = value;
+ return;
+ case 3:
+ m23 = value;
+ return;
+ }
+ case 3:
+ switch (j) {
+ case 0:
+ m30 = value;
+ return;
+ case 1:
+ m31 = value;
+ return;
+ case 2:
+ m32 = value;
+ return;
+ case 3:
+ m33 = value;
+ return;
+ }
+ }
+
+ logger.warning("Invalid matrix index.");
+ throw new IllegalArgumentException("Invalid indices into matrix.");
+ }
+
+ /**
+ * set sets the values of this matrix from an array of values.
+ *
+ * @param matrix the matrix to set the value to.
+ * @throws JmeException if the array is not of size 16.
+ */
+ public void set(float[][] matrix) {
+ if (matrix.length != 4 || matrix[0].length != 4) {
+ throw new IllegalArgumentException(
+ "Array must be of size 16."
+ );
+ }
+
+ m00 = matrix[0][0];
+ m01 = matrix[0][1];
+ m02 = matrix[0][2];
+ m03 = matrix[0][3];
+ m10 = matrix[1][0];
+ m11 = matrix[1][1];
+ m12 = matrix[1][2];
+ m13 = matrix[1][3];
+ m20 = matrix[2][0];
+ m21 = matrix[2][1];
+ m22 = matrix[2][2];
+ m23 = matrix[2][3];
+ m30 = matrix[3][0];
+ m31 = matrix[3][1];
+ m32 = matrix[3][2];
+ m33 = matrix[3][3];
+ }
+
+
+ /**
+ * Sets the values of this matrix
+ */
+ public void set(
+ float m00,
+ float m01,
+ float m02,
+ float m03,
+ float m10,
+ float m11,
+ float m12,
+ float m13,
+ float m20,
+ float m21,
+ float m22,
+ float m23,
+ float m30,
+ float m31,
+ float m32,
+ float m33
+ ) {
+
+ this.m00 = m00;
+ this.m01 = m01;
+ this.m02 = m02;
+ this.m03 = m03;
+ this.m10 = m10;
+ this.m11 = m11;
+ this.m12 = m12;
+ this.m13 = m13;
+ this.m20 = m20;
+ this.m21 = m21;
+ this.m22 = m22;
+ this.m23 = m23;
+ this.m30 = m30;
+ this.m31 = m31;
+ this.m32 = m32;
+ this.m33 = m33;
+ }
+
+ /**
+ * set sets the values of this matrix from another matrix.
+ *
+ * @param matrix the matrix to read the value from.
+ */
+ public Matrix4f set(Matrix4f matrix) {
+ m00 = matrix.m00;
+ m01 = matrix.m01;
+ m02 = matrix.m02;
+ m03 = matrix.m03;
+ m10 = matrix.m10;
+ m11 = matrix.m11;
+ m12 = matrix.m12;
+ m13 = matrix.m13;
+ m20 = matrix.m20;
+ m21 = matrix.m21;
+ m22 = matrix.m22;
+ m23 = matrix.m23;
+ m30 = matrix.m30;
+ m31 = matrix.m31;
+ m32 = matrix.m32;
+ m33 = matrix.m33;
+ return this;
+ }
+
+ /**
+ * set sets the values of this matrix from an array of values
+ * assuming that the data is rowMajor order;
+ *
+ * @param matrix the matrix to set the value to.
+ */
+ public void set(float[] matrix) {
+ set(matrix, true);
+ }
+
+ /**
+ * set sets the values of this matrix from an array of values;
+ *
+ * @param matrix the matrix to set the value to.
+ * @param rowMajor whether the incoming data is in row or column major
+ * order.
+ */
+ public void set(float[] matrix, boolean rowMajor) {
+ if (matrix.length != 16) {
+ throw new IllegalArgumentException(
+ "Array must be of size 16."
+ );
+ }
+
+ if (rowMajor) {
+ m00 = matrix[0];
+ m01 = matrix[1];
+ m02 = matrix[2];
+ m03 = matrix[3];
+ m10 = matrix[4];
+ m11 = matrix[5];
+ m12 = matrix[6];
+ m13 = matrix[7];
+ m20 = matrix[8];
+ m21 = matrix[9];
+ m22 = matrix[10];
+ m23 = matrix[11];
+ m30 = matrix[12];
+ m31 = matrix[13];
+ m32 = matrix[14];
+ m33 = matrix[15];
+ } else {
+ m00 = matrix[0];
+ m01 = matrix[4];
+ m02 = matrix[8];
+ m03 = matrix[12];
+ m10 = matrix[1];
+ m11 = matrix[5];
+ m12 = matrix[9];
+ m13 = matrix[13];
+ m20 = matrix[2];
+ m21 = matrix[6];
+ m22 = matrix[10];
+ m23 = matrix[14];
+ m30 = matrix[3];
+ m31 = matrix[7];
+ m32 = matrix[11];
+ m33 = matrix[15];
+ }
+ }
+
+ public Matrix4f transpose() {
+ float[] tmp = new float[16];
+ get(tmp, true);
+ Matrix4f mat = new Matrix4f(tmp);
+ return mat;
+ }
+
+ /**
+ * transpose locally transposes this Matrix.
+ *
+ * @return this object for chaining.
+ */
+ public Matrix4f transposeLocal() {
+ float tmp = m01;
+ m01 = m10;
+ m10 = tmp;
+
+ tmp = m02;
+ m02 = m20;
+ m20 = tmp;
+
+ tmp = m03;
+ m03 = m30;
+ m30 = tmp;
+
+ tmp = m12;
+ m12 = m21;
+ m21 = tmp;
+
+ tmp = m13;
+ m13 = m31;
+ m31 = tmp;
+
+ tmp = m23;
+ m23 = m32;
+ m32 = tmp;
+
+ return this;
+ }
+
+ /**
+ * fillFloatBuffer fills a FloatBuffer object with the matrix
+ * data.
+ *
+ * @param fb the buffer to fill, must be correct size
+ * @return matrix data as a FloatBuffer.
+ */
+ public FloatBuffer fillFloatBuffer(FloatBuffer fb) {
+ return fillFloatBuffer(fb, false);
+ }
+
+ /**
+ * fillFloatBuffer fills a FloatBuffer object with the matrix
+ * data.
+ *
+ * @param fb the buffer to fill, starting at current position. Must have
+ * room for 16 more floats.
+ * @param columnMajor if true, this buffer should be filled with column
+ * major data, otherwise it will be filled row major.
+ * @return matrix data as a FloatBuffer. (position is advanced by 16 and any
+ * limit set is not changed).
+ */
+ public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) {
+// if (columnMajor) {
+// fb.put(m00).put(m10).put(m20).put(m30);
+// fb.put(m01).put(m11).put(m21).put(m31);
+// fb.put(m02).put(m12).put(m22).put(m32);
+// fb.put(m03).put(m13).put(m23).put(m33);
+// } else {
+// fb.put(m00).put(m01).put(m02).put(m03);
+// fb.put(m10).put(m11).put(m12).put(m13);
+// fb.put(m20).put(m21).put(m22).put(m23);
+// fb.put(m30).put(m31).put(m32).put(m33);
+// }
+
+ TempVars vars = TempVars.get();
+
+
+ fillFloatArray(vars.matrixWrite, columnMajor);
+ fb.put(vars.matrixWrite, 0, 16);
+
+ vars.release();
+
+ return fb;
+ }
+
+ public void fillFloatArray(float[] f, boolean columnMajor) {
+ if (columnMajor) {
+ f[0] = m00;
+ f[1] = m10;
+ f[2] = m20;
+ f[3] = m30;
+ f[4] = m01;
+ f[5] = m11;
+ f[6] = m21;
+ f[7] = m31;
+ f[8] = m02;
+ f[9] = m12;
+ f[10] = m22;
+ f[11] = m32;
+ f[12] = m03;
+ f[13] = m13;
+ f[14] = m23;
+ f[15] = m33;
+ } else {
+ f[0] = m00;
+ f[1] = m01;
+ f[2] = m02;
+ f[3] = m03;
+ f[4] = m10;
+ f[5] = m11;
+ f[6] = m12;
+ f[7] = m13;
+ f[8] = m20;
+ f[9] = m21;
+ f[10] = m22;
+ f[11] = m23;
+ f[12] = m30;
+ f[13] = m31;
+ f[14] = m32;
+ f[15] = m33;
+ }
+ }
+
+ /**
+ * readFloatBuffer reads value for this matrix from a
+ * FloatBuffer.
+ *
+ * @param fb the buffer to read from, must be correct size
+ * @return this data as a FloatBuffer.
+ */
+ public Matrix4f readFloatBuffer(FloatBuffer fb) {
+ return readFloatBuffer(fb, false);
+ }
+
+ /**
+ * readFloatBuffer reads value for this matrix from a
+ * FloatBuffer.
+ *
+ * @param fb the buffer to read from, must be correct size
+ * @param columnMajor if true, this buffer should be filled with column
+ * major data, otherwise it will be filled row major.
+ * @return this data as a FloatBuffer.
+ */
+ public Matrix4f readFloatBuffer(FloatBuffer fb, boolean columnMajor) {
+
+ if (columnMajor) {
+ m00 = fb.get();
+ m10 = fb.get();
+ m20 = fb.get();
+ m30 = fb.get();
+ m01 = fb.get();
+ m11 = fb.get();
+ m21 = fb.get();
+ m31 = fb.get();
+ m02 = fb.get();
+ m12 = fb.get();
+ m22 = fb.get();
+ m32 = fb.get();
+ m03 = fb.get();
+ m13 = fb.get();
+ m23 = fb.get();
+ m33 = fb.get();
+ } else {
+ m00 = fb.get();
+ m01 = fb.get();
+ m02 = fb.get();
+ m03 = fb.get();
+ m10 = fb.get();
+ m11 = fb.get();
+ m12 = fb.get();
+ m13 = fb.get();
+ m20 = fb.get();
+ m21 = fb.get();
+ m22 = fb.get();
+ m23 = fb.get();
+ m30 = fb.get();
+ m31 = fb.get();
+ m32 = fb.get();
+ m33 = fb.get();
+ }
+ return this;
+ }
+
+ /**
+ * loadIdentity sets this matrix to the identity matrix, namely
+ * all zeros with ones along the diagonal.
+ *
+ */
+ public void loadIdentity() {
+ m01 = m02 = m03 = 0.0f;
+ m10 = m12 = m13 = 0.0f;
+ m20 = m21 = m23 = 0.0f;
+ m30 = m31 = m32 = 0.0f;
+ m00 = m11 = m22 = m33 = 1.0f;
+ }
+
+ public void fromFrustum(
+ float near,
+ float far,
+ float left,
+ float right,
+ float top,
+ float bottom,
+ boolean parallel
+ ) {
+ loadIdentity();
+ if (parallel) {
+ // scale
+ m00 = 2.0f / (right - left);
+ // m11 = 2.0f / (bottom - top);
+ m11 = 2.0f / (top - bottom);
+ m22 = -2.0f / (far - near);
+ m33 = 1f;
+
+ // translation
+ m03 = -(right + left) / (right - left);
+ // m31 = -(bottom + top) / (bottom - top);
+ m13 = -(top + bottom) / (top - bottom);
+ m23 = -(far + near) / (far - near);
+ } else {
+ m00 = (2.0f * near) / (right - left);
+ m11 = (2.0f * near) / (top - bottom);
+ m32 = -1.0f;
+ m33 = -0.0f;
+
+ // A
+ m02 = (right + left) / (right - left);
+
+ // B
+ m12 = (top + bottom) / (top - bottom);
+
+ // C
+ m22 = -(far + near) / (far - near);
+
+ // D
+ m23 = -(2.0f * far * near) / (far - near);
+ }
+ }
+
+ /**
+ * fromAngleAxis sets this matrix4f to the values specified by
+ * an angle and an axis of rotation. This method creates an object, so use
+ * fromAngleNormalAxis if your axis is already normalized.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param axis the axis of rotation.
+ */
+ public void fromAngleAxis(float angle, Vector3f axis) {
+ Vector3f normAxis = axis.normalize();
+ fromAngleNormalAxis(angle, normAxis);
+ }
+
+ /**
+ * fromAngleNormalAxis sets this matrix4f to the values
+ * specified by an angle and a normalized axis of rotation.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param axis the axis of rotation (already normalized).
+ */
+ public void fromAngleNormalAxis(float angle, Vector3f axis) {
+ zero();
+ m33 = 1;
+
+ float fCos = FastMath.cos(angle);
+ float fSin = FastMath.sin(angle);
+ float fOneMinusCos = ((float) 1.0) - fCos;
+ float fX2 = axis.x * axis.x;
+ float fY2 = axis.y * axis.y;
+ float fZ2 = axis.z * axis.z;
+ float fXYM = axis.x * axis.y * fOneMinusCos;
+ float fXZM = axis.x * axis.z * fOneMinusCos;
+ float fYZM = axis.y * axis.z * fOneMinusCos;
+ float fXSin = axis.x * fSin;
+ float fYSin = axis.y * fSin;
+ float fZSin = axis.z * fSin;
+
+ m00 = fX2 * fOneMinusCos + fCos;
+ m01 = fXYM - fZSin;
+ m02 = fXZM + fYSin;
+ m10 = fXYM + fZSin;
+ m11 = fY2 * fOneMinusCos + fCos;
+ m12 = fYZM - fXSin;
+ m20 = fXZM - fYSin;
+ m21 = fYZM + fXSin;
+ m22 = fZ2 * fOneMinusCos + fCos;
+ }
+
+ /**
+ * mult multiplies this matrix by a scalar.
+ *
+ * @param scalar the scalar to multiply this matrix by.
+ */
+ public void multLocal(float scalar) {
+ m00 *= scalar;
+ m01 *= scalar;
+ m02 *= scalar;
+ m03 *= scalar;
+ m10 *= scalar;
+ m11 *= scalar;
+ m12 *= scalar;
+ m13 *= scalar;
+ m20 *= scalar;
+ m21 *= scalar;
+ m22 *= scalar;
+ m23 *= scalar;
+ m30 *= scalar;
+ m31 *= scalar;
+ m32 *= scalar;
+ m33 *= scalar;
+ }
+
+ public Matrix4f mult(float scalar) {
+ Matrix4f out = new Matrix4f();
+ out.set(this);
+ out.multLocal(scalar);
+ return out;
+ }
+
+ public Matrix4f mult(float scalar, Matrix4f store) {
+ store.set(this);
+ store.multLocal(scalar);
+ return store;
+ }
+
+ /**
+ * mult multiplies this matrix with another matrix. The result
+ * matrix will then be returned. This matrix will be on the left hand side,
+ * while the parameter matrix will be on the right.
+ *
+ * @param in2 the matrix to multiply this matrix by.
+ * @return the resultant matrix
+ */
+ public Matrix4f mult(Matrix4f in2) {
+ return mult(in2, null);
+ }
+
+ /**
+ * mult multiplies this matrix with another matrix. The result
+ * matrix will then be returned. This matrix will be on the left hand side,
+ * while the parameter matrix will be on the right.
+ *
+ * @param in2 the matrix to multiply this matrix by.
+ * @param store where to store the result. It is safe for in2 and store to
+ * be the same object.
+ * @return the resultant matrix
+ */
+ public Matrix4f mult(Matrix4f in2, Matrix4f store) {
+ if (store == null) {
+ store = new Matrix4f();
+ }
+
+ float temp00, temp01, temp02, temp03;
+ float temp10, temp11, temp12, temp13;
+ float temp20, temp21, temp22, temp23;
+ float temp30, temp31, temp32, temp33;
+
+ temp00 = m00 * in2.m00
+ + m01 * in2.m10
+ + m02 * in2.m20
+ + m03 * in2.m30;
+ temp01 = m00 * in2.m01
+ + m01 * in2.m11
+ + m02 * in2.m21
+ + m03 * in2.m31;
+ temp02 = m00 * in2.m02
+ + m01 * in2.m12
+ + m02 * in2.m22
+ + m03 * in2.m32;
+ temp03 = m00 * in2.m03
+ + m01 * in2.m13
+ + m02 * in2.m23
+ + m03 * in2.m33;
+
+ temp10 = m10 * in2.m00
+ + m11 * in2.m10
+ + m12 * in2.m20
+ + m13 * in2.m30;
+ temp11 = m10 * in2.m01
+ + m11 * in2.m11
+ + m12 * in2.m21
+ + m13 * in2.m31;
+ temp12 = m10 * in2.m02
+ + m11 * in2.m12
+ + m12 * in2.m22
+ + m13 * in2.m32;
+ temp13 = m10 * in2.m03
+ + m11 * in2.m13
+ + m12 * in2.m23
+ + m13 * in2.m33;
+
+ temp20 = m20 * in2.m00
+ + m21 * in2.m10
+ + m22 * in2.m20
+ + m23 * in2.m30;
+ temp21 = m20 * in2.m01
+ + m21 * in2.m11
+ + m22 * in2.m21
+ + m23 * in2.m31;
+ temp22 = m20 * in2.m02
+ + m21 * in2.m12
+ + m22 * in2.m22
+ + m23 * in2.m32;
+ temp23 = m20 * in2.m03
+ + m21 * in2.m13
+ + m22 * in2.m23
+ + m23 * in2.m33;
+
+ temp30 = m30 * in2.m00
+ + m31 * in2.m10
+ + m32 * in2.m20
+ + m33 * in2.m30;
+ temp31 = m30 * in2.m01
+ + m31 * in2.m11
+ + m32 * in2.m21
+ + m33 * in2.m31;
+ temp32 = m30 * in2.m02
+ + m31 * in2.m12
+ + m32 * in2.m22
+ + m33 * in2.m32;
+ temp33 = m30 * in2.m03
+ + m31 * in2.m13
+ + m32 * in2.m23
+ + m33 * in2.m33;
+
+ store.m00 = temp00;
+ store.m01 = temp01;
+ store.m02 = temp02;
+ store.m03 = temp03;
+ store.m10 = temp10;
+ store.m11 = temp11;
+ store.m12 = temp12;
+ store.m13 = temp13;
+ store.m20 = temp20;
+ store.m21 = temp21;
+ store.m22 = temp22;
+ store.m23 = temp23;
+ store.m30 = temp30;
+ store.m31 = temp31;
+ store.m32 = temp32;
+ store.m33 = temp33;
+
+ return store;
+ }
+
+ /**
+ * mult multiplies this matrix with another matrix. The results
+ * are stored internally and a handle to this matrix will then be returned.
+ * This matrix will be on the left hand side, while the parameter matrix
+ * will be on the right.
+ *
+ * @param in2 the matrix to multiply this matrix by.
+ * @return the resultant matrix
+ */
+ public Matrix4f multLocal(Matrix4f in2) {
+ return mult(in2, this);
+ }
+
+ /**
+ * mult multiplies a vector about a rotation matrix. The
+ * resulting vector is returned as a new Vector3f.
+ *
+ * @param vec vec to multiply against.
+ * @return the rotated vector.
+ */
+ public Vector3f mult(Vector3f vec) {
+ return mult(vec, null);
+ }
+
+ /**
+ * mult multiplies a vector about a rotation matrix and adds
+ * translation. The resulting vector is returned.
+ *
+ * @param vec vec to multiply against.
+ * @param store a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f mult(Vector3f vec, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m01 * vy + m02 * vz + m03;
+ store.y = m10 * vx + m11 * vy + m12 * vz + m13;
+ store.z = m20 * vx + m21 * vy + m22 * vz + m23;
+
+ return store;
+ }
+
+ /**
+ * mult multiplies a Vector4f about a rotation
+ * matrix. The resulting vector is returned as a new Vector4f.
+ *
+ * @param vec vec to multiply against.
+ * @return the rotated vector.
+ */
+ public Vector4f mult(Vector4f vec) {
+ return mult(vec, null);
+ }
+
+ /**
+ * mult multiplies a Vector4f about a rotation
+ * matrix. The resulting vector is returned.
+ *
+ * @param vec vec to multiply against.
+ * @param store a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector4f mult(Vector4f vec, Vector4f store) {
+ if (null == vec) {
+ logger.warning("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Vector4f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z, vw = vec.w;
+ store.x = m00 * vx + m01 * vy + m02 * vz + m03 * vw;
+ store.y = m10 * vx + m11 * vy + m12 * vz + m13 * vw;
+ store.z = m20 * vx + m21 * vy + m22 * vz + m23 * vw;
+ store.w = m30 * vx + m31 * vy + m32 * vz + m33 * vw;
+
+ return store;
+ }
+
+ /**
+ * mult multiplies a vector about a rotation matrix. The
+ * resulting vector is returned.
+ *
+ * @param vec vec to multiply against.
+ *
+ * @return the rotated vector.
+ */
+ public Vector4f multAcross(Vector4f vec) {
+ return multAcross(vec, null);
+ }
+
+ /**
+ * mult multiplies a vector about a rotation matrix. The
+ * resulting vector is returned.
+ *
+ * @param vec vec to multiply against.
+ * @param store a vector to store the result in. created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector4f multAcross(Vector4f vec, Vector4f store) {
+ if (null == vec) {
+ logger.warning("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Vector4f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z, vw = vec.w;
+ store.x = m00 * vx + m10 * vy + m20 * vz + m30 * vw;
+ store.y = m01 * vx + m11 * vy + m21 * vz + m31 * vw;
+ store.z = m02 * vx + m12 * vy + m22 * vz + m32 * vw;
+ store.w = m03 * vx + m13 * vy + m23 * vz + m33 * vw;
+
+ return store;
+ }
+
+ /**
+ * multNormal multiplies a vector about a rotation matrix, but
+ * does not add translation. The resulting vector is returned.
+ *
+ * @param vec vec to multiply against.
+ * @param store a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f multNormal(Vector3f vec, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m01 * vy + m02 * vz;
+ store.y = m10 * vx + m11 * vy + m12 * vz;
+ store.z = m20 * vx + m21 * vy + m22 * vz;
+
+ return store;
+ }
+
+ /**
+ * multNormal multiplies a vector about a rotation matrix, but
+ * does not add translation. The resulting vector is returned.
+ *
+ * @param vec vec to multiply against.
+ * @param store a vector to store the result in. Created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f multNormalAcross(Vector3f vec, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m10 * vy + m20 * vz;
+ store.y = m01 * vx + m11 * vy + m21 * vz;
+ store.z = m02 * vx + m12 * vy + m22 * vz;
+
+ return store;
+ }
+
+ /**
+ * mult multiplies a vector about a rotation matrix and adds
+ * translation. The w value is returned as a result of multiplying the last
+ * column of the matrix by 1.0
+ *
+ * @param vec vec to multiply against.
+ * @param store a vector to store the result in.
+ * @return the W value
+ */
+ public float multProj(Vector3f vec, Vector3f store) {
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m01 * vy + m02 * vz + m03;
+ store.y = m10 * vx + m11 * vy + m12 * vz + m13;
+ store.z = m20 * vx + m21 * vy + m22 * vz + m23;
+ return m30 * vx + m31 * vy + m32 * vz + m33;
+ }
+
+ /**
+ * mult multiplies a vector about a rotation matrix. The
+ * resulting vector is returned.
+ *
+ * @param vec vec to multiply against.
+ * @param store a vector to store the result in. created if null is passed.
+ * @return the rotated vector.
+ */
+ public Vector3f multAcross(Vector3f vec, Vector3f store) {
+ if (null == vec) {
+ logger.warning("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+ store.x = m00 * vx + m10 * vy + m20 * vz + m30 * 1;
+ store.y = m01 * vx + m11 * vy + m21 * vz + m31 * 1;
+ store.z = m02 * vx + m12 * vy + m22 * vz + m32 * 1;
+
+ return store;
+ }
+
+ /**
+ * mult multiplies a quaternion about a matrix. The resulting
+ * vector is returned.
+ *
+ * @param vec vec to multiply against.
+ * @param store a quaternion to store the result in. created if null is
+ * passed.
+ * @return store = this * vec
+ */
+ public Quaternion mult(Quaternion vec, Quaternion store) {
+
+ if (null == vec) {
+ logger.warning("Source vector is null, null result returned.");
+ return null;
+ }
+ if (store == null) {
+ store = new Quaternion();
+ }
+
+ float x = m00 * vec.x + m10 * vec.y + m20 * vec.z + m30 * vec.w;
+ float y = m01 * vec.x + m11 * vec.y + m21 * vec.z + m31 * vec.w;
+ float z = m02 * vec.x + m12 * vec.y + m22 * vec.z + m32 * vec.w;
+ float w = m03 * vec.x + m13 * vec.y + m23 * vec.z + m33 * vec.w;
+ store.x = x;
+ store.y = y;
+ store.z = z;
+ store.w = w;
+
+ return store;
+ }
+
+ /**
+ * mult multiplies an array of 4 floats against this rotation
+ * matrix. The results are stored directly in the array. (vec4f x mat4f)
+ *
+ * @param vec4f float array (size 4) to multiply against the matrix.
+ * @return the vec4f for chaining.
+ */
+ public float[] mult(float[] vec4f) {
+ if (null == vec4f || vec4f.length != 4) {
+ logger.warning("invalid array given, must be nonnull and length 4");
+ return null;
+ }
+
+ float x = vec4f[0], y = vec4f[1], z = vec4f[2], w = vec4f[3];
+
+ vec4f[0] = m00 * x + m01 * y + m02 * z + m03 * w;
+ vec4f[1] = m10 * x + m11 * y + m12 * z + m13 * w;
+ vec4f[2] = m20 * x + m21 * y + m22 * z + m23 * w;
+ vec4f[3] = m30 * x + m31 * y + m32 * z + m33 * w;
+
+ return vec4f;
+ }
+
+ /**
+ * mult multiplies an array of 4 floats against this rotation
+ * matrix. The results are stored directly in the array. (vec4f x mat4f)
+ *
+ * @param vec4f float array (size 4) to multiply against the matrix.
+ * @return the vec4f for chaining.
+ */
+ public float[] multAcross(float[] vec4f) {
+ if (null == vec4f || vec4f.length != 4) {
+ logger.warning("invalid array given, must be nonnull and length 4");
+ return null;
+ }
+
+ float x = vec4f[0], y = vec4f[1], z = vec4f[2], w = vec4f[3];
+
+ vec4f[0] = m00 * x + m10 * y + m20 * z + m30 * w;
+ vec4f[1] = m01 * x + m11 * y + m21 * z + m31 * w;
+ vec4f[2] = m02 * x + m12 * y + m22 * z + m32 * w;
+ vec4f[3] = m03 * x + m13 * y + m23 * z + m33 * w;
+
+ return vec4f;
+ }
+
+ /**
+ * Inverts this matrix as a new Matrix4f.
+ *
+ * @return The new inverse matrix
+ */
+ public Matrix4f invert() {
+ return invert(null);
+ }
+
+ /**
+ * Inverts this matrix and stores it in the given store.
+ *
+ * @return The store
+ */
+ public Matrix4f invert(Matrix4f store) {
+ if (store == null) {
+ store = new Matrix4f();
+ }
+
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+ float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+
+ if (FastMath.abs(fDet) <= 0f) {
+ throw new ArithmeticException("This matrix cannot be inverted");
+ }
+
+ store.m00 = +m11 * fB5 - m12 * fB4 + m13 * fB3;
+ store.m10 = -m10 * fB5 + m12 * fB2 - m13 * fB1;
+ store.m20 = +m10 * fB4 - m11 * fB2 + m13 * fB0;
+ store.m30 = -m10 * fB3 + m11 * fB1 - m12 * fB0;
+ store.m01 = -m01 * fB5 + m02 * fB4 - m03 * fB3;
+ store.m11 = +m00 * fB5 - m02 * fB2 + m03 * fB1;
+ store.m21 = -m00 * fB4 + m01 * fB2 - m03 * fB0;
+ store.m31 = +m00 * fB3 - m01 * fB1 + m02 * fB0;
+ store.m02 = +m31 * fA5 - m32 * fA4 + m33 * fA3;
+ store.m12 = -m30 * fA5 + m32 * fA2 - m33 * fA1;
+ store.m22 = +m30 * fA4 - m31 * fA2 + m33 * fA0;
+ store.m32 = -m30 * fA3 + m31 * fA1 - m32 * fA0;
+ store.m03 = -m21 * fA5 + m22 * fA4 - m23 * fA3;
+ store.m13 = +m20 * fA5 - m22 * fA2 + m23 * fA1;
+ store.m23 = -m20 * fA4 + m21 * fA2 - m23 * fA0;
+ store.m33 = +m20 * fA3 - m21 * fA1 + m22 * fA0;
+
+ float fInvDet = 1.0f / fDet;
+ store.multLocal(fInvDet);
+
+ return store;
+ }
+
+ /**
+ * Inverts this matrix locally.
+ *
+ * @return this
+ */
+ public Matrix4f invertLocal() {
+
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+ float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+
+ if (FastMath.abs(fDet) <= 0f) {
+ return zero();
+ }
+
+ float f00 = +m11 * fB5 - m12 * fB4 + m13 * fB3;
+ float f10 = -m10 * fB5 + m12 * fB2 - m13 * fB1;
+ float f20 = +m10 * fB4 - m11 * fB2 + m13 * fB0;
+ float f30 = -m10 * fB3 + m11 * fB1 - m12 * fB0;
+ float f01 = -m01 * fB5 + m02 * fB4 - m03 * fB3;
+ float f11 = +m00 * fB5 - m02 * fB2 + m03 * fB1;
+ float f21 = -m00 * fB4 + m01 * fB2 - m03 * fB0;
+ float f31 = +m00 * fB3 - m01 * fB1 + m02 * fB0;
+ float f02 = +m31 * fA5 - m32 * fA4 + m33 * fA3;
+ float f12 = -m30 * fA5 + m32 * fA2 - m33 * fA1;
+ float f22 = +m30 * fA4 - m31 * fA2 + m33 * fA0;
+ float f32 = -m30 * fA3 + m31 * fA1 - m32 * fA0;
+ float f03 = -m21 * fA5 + m22 * fA4 - m23 * fA3;
+ float f13 = +m20 * fA5 - m22 * fA2 + m23 * fA1;
+ float f23 = -m20 * fA4 + m21 * fA2 - m23 * fA0;
+ float f33 = +m20 * fA3 - m21 * fA1 + m22 * fA0;
+
+ m00 = f00;
+ m01 = f01;
+ m02 = f02;
+ m03 = f03;
+ m10 = f10;
+ m11 = f11;
+ m12 = f12;
+ m13 = f13;
+ m20 = f20;
+ m21 = f21;
+ m22 = f22;
+ m23 = f23;
+ m30 = f30;
+ m31 = f31;
+ m32 = f32;
+ m33 = f33;
+
+ float fInvDet = 1.0f / fDet;
+ multLocal(fInvDet);
+
+ return this;
+ }
+
+ /**
+ * Returns a new matrix representing the adjoint of this matrix.
+ *
+ * @return The adjoint matrix
+ */
+ public Matrix4f adjoint() {
+ return adjoint(null);
+ }
+
+ public void setTransform(Vector3f position, Vector3f scale, Matrix3f rotMat) {
+ // Ordering:
+ // 1. Scale
+ // 2. Rotate
+ // 3. Translate
+
+ // Set up final matrix with scale, rotation and translation
+ m00 = scale.x * rotMat.m00;
+ m01 = scale.y * rotMat.m01;
+ m02 = scale.z * rotMat.m02;
+ m03 = position.x;
+ m10 = scale.x * rotMat.m10;
+ m11 = scale.y * rotMat.m11;
+ m12 = scale.z * rotMat.m12;
+ m13 = position.y;
+ m20 = scale.x * rotMat.m20;
+ m21 = scale.y * rotMat.m21;
+ m22 = scale.z * rotMat.m22;
+ m23 = position.z;
+
+ // No projection term
+ m30 = 0;
+ m31 = 0;
+ m32 = 0;
+ m33 = 1;
+ }
+
+ /**
+ * Places the adjoint of this matrix in store (creates store if null.)
+ *
+ * @param store The matrix to store the result in. If null, a new matrix is
+ * created.
+ * @return store
+ */
+ public Matrix4f adjoint(Matrix4f store) {
+ if (store == null) {
+ store = new Matrix4f();
+ }
+
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+
+ store.m00 = +m11 * fB5 - m12 * fB4 + m13 * fB3;
+ store.m10 = -m10 * fB5 + m12 * fB2 - m13 * fB1;
+ store.m20 = +m10 * fB4 - m11 * fB2 + m13 * fB0;
+ store.m30 = -m10 * fB3 + m11 * fB1 - m12 * fB0;
+ store.m01 = -m01 * fB5 + m02 * fB4 - m03 * fB3;
+ store.m11 = +m00 * fB5 - m02 * fB2 + m03 * fB1;
+ store.m21 = -m00 * fB4 + m01 * fB2 - m03 * fB0;
+ store.m31 = +m00 * fB3 - m01 * fB1 + m02 * fB0;
+ store.m02 = +m31 * fA5 - m32 * fA4 + m33 * fA3;
+ store.m12 = -m30 * fA5 + m32 * fA2 - m33 * fA1;
+ store.m22 = +m30 * fA4 - m31 * fA2 + m33 * fA0;
+ store.m32 = -m30 * fA3 + m31 * fA1 - m32 * fA0;
+ store.m03 = -m21 * fA5 + m22 * fA4 - m23 * fA3;
+ store.m13 = +m20 * fA5 - m22 * fA2 + m23 * fA1;
+ store.m23 = -m20 * fA4 + m21 * fA2 - m23 * fA0;
+ store.m33 = +m20 * fA3 - m21 * fA1 + m22 * fA0;
+
+ return store;
+ }
+
+ /**
+ * determinant generates the determinate of this matrix.
+ *
+ * @return the determinate
+ */
+ public float determinant() {
+ float fA0 = m00 * m11 - m01 * m10;
+ float fA1 = m00 * m12 - m02 * m10;
+ float fA2 = m00 * m13 - m03 * m10;
+ float fA3 = m01 * m12 - m02 * m11;
+ float fA4 = m01 * m13 - m03 * m11;
+ float fA5 = m02 * m13 - m03 * m12;
+ float fB0 = m20 * m31 - m21 * m30;
+ float fB1 = m20 * m32 - m22 * m30;
+ float fB2 = m20 * m33 - m23 * m30;
+ float fB3 = m21 * m32 - m22 * m31;
+ float fB4 = m21 * m33 - m23 * m31;
+ float fB5 = m22 * m33 - m23 * m32;
+ float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+ return fDet;
+ }
+
+ /**
+ * Sets all of the values in this matrix to zero.
+ *
+ * @return this matrix
+ */
+ public Matrix4f zero() {
+ m00 = m01 = m02 = m03 = 0.0f;
+ m10 = m11 = m12 = m13 = 0.0f;
+ m20 = m21 = m22 = m23 = 0.0f;
+ m30 = m31 = m32 = m33 = 0.0f;
+ return this;
+ }
+
+ public Matrix4f add(Matrix4f mat) {
+ Matrix4f result = new Matrix4f();
+ result.m00 = this.m00 + mat.m00;
+ result.m01 = this.m01 + mat.m01;
+ result.m02 = this.m02 + mat.m02;
+ result.m03 = this.m03 + mat.m03;
+ result.m10 = this.m10 + mat.m10;
+ result.m11 = this.m11 + mat.m11;
+ result.m12 = this.m12 + mat.m12;
+ result.m13 = this.m13 + mat.m13;
+ result.m20 = this.m20 + mat.m20;
+ result.m21 = this.m21 + mat.m21;
+ result.m22 = this.m22 + mat.m22;
+ result.m23 = this.m23 + mat.m23;
+ result.m30 = this.m30 + mat.m30;
+ result.m31 = this.m31 + mat.m31;
+ result.m32 = this.m32 + mat.m32;
+ result.m33 = this.m33 + mat.m33;
+ return result;
+ }
+
+ /**
+ * add adds the values of a parameter matrix to this matrix.
+ *
+ * @param mat the matrix to add to this.
+ */
+ public void addLocal(Matrix4f mat) {
+ m00 += mat.m00;
+ m01 += mat.m01;
+ m02 += mat.m02;
+ m03 += mat.m03;
+ m10 += mat.m10;
+ m11 += mat.m11;
+ m12 += mat.m12;
+ m13 += mat.m13;
+ m20 += mat.m20;
+ m21 += mat.m21;
+ m22 += mat.m22;
+ m23 += mat.m23;
+ m30 += mat.m30;
+ m31 += mat.m31;
+ m32 += mat.m32;
+ m33 += mat.m33;
+ }
+
+ public Vector3f toTranslationVector() {
+ return new Vector3f(m03, m13, m23);
+ }
+
+ public void toTranslationVector(Vector3f vector) {
+ vector.set(m03, m13, m23);
+ }
+
+ public Quaternion toRotationQuat() {
+ Quaternion quat = new Quaternion();
+ quat.fromRotationMatrix(toRotationMatrix());
+ return quat;
+ }
+
+ public void toRotationQuat(Quaternion q) {
+ q.fromRotationMatrix(toRotationMatrix());
+ }
+
+ public Matrix3f toRotationMatrix() {
+ return new Matrix3f(m00, m01, m02, m10, m11, m12, m20, m21, m22);
+ }
+
+ public void toRotationMatrix(Matrix3f mat) {
+ mat.m00 = m00;
+ mat.m01 = m01;
+ mat.m02 = m02;
+ mat.m10 = m10;
+ mat.m11 = m11;
+ mat.m12 = m12;
+ mat.m20 = m20;
+ mat.m21 = m21;
+ mat.m22 = m22;
+ }
+
+ /**
+ * Retreives the scale vector from the matrix.
+ *
+ * @return the scale vector
+ */
+ public Vector3f toScaleVector() {
+ Vector3f result = new Vector3f();
+ this.toScaleVector(result);
+ return result;
+ }
+
+ /**
+ * Retreives the scale vector from the matrix and stores it into a given
+ * vector.
+ *
+ * @param the vector where the scale will be stored
+ */
+ public void toScaleVector(Vector3f vector) {
+ float scaleX = (float) Math.sqrt(m00 * m00 + m10 * m10 + m20 * m20);
+ float scaleY = (float) Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21);
+ float scaleZ = (float) Math.sqrt(m02 * m02 + m12 * m12 + m22 * m22);
+ vector.set(scaleX, scaleY, scaleZ);
+ }
+
+ public void setScale(float x, float y, float z) {
+ m00 *= x;
+ m11 *= y;
+ m22 *= z;
+ }
+
+ public void setScale(Vector3f scale) {
+ m00 *= scale.x;
+ m11 *= scale.y;
+ m22 *= scale.z;
+ }
+
+ /**
+ * setTranslation will set the matrix's translation values.
+ *
+ * @param translation the new values for the translation.
+ * @throws JmeException if translation is not size 3.
+ */
+ public void setTranslation(float[] translation) {
+ if (translation.length != 3) {
+ throw new IllegalArgumentException(
+ "Translation size must be 3."
+ );
+ }
+ m03 = translation[0];
+ m13 = translation[1];
+ m23 = translation[2];
+ }
+
+ /**
+ * setTranslation will set the matrix's translation values.
+ *
+ * @param x value of the translation on the x axis
+ * @param y value of the translation on the y axis
+ * @param z value of the translation on the z axis
+ */
+ public void setTranslation(float x, float y, float z) {
+ m03 = x;
+ m13 = y;
+ m23 = z;
+ }
+
+ /**
+ * setTranslation will set the matrix's translation values.
+ *
+ * @param translation the new values for the translation.
+ */
+ public void setTranslation(Vector3f translation) {
+ m03 = translation.x;
+ m13 = translation.y;
+ m23 = translation.z;
+ }
+
+ /**
+ * setInverseTranslation will set the matrix's inverse
+ * translation values.
+ *
+ * @param translation the new values for the inverse translation.
+ * @throws JmeException if translation is not size 3.
+ */
+ public void setInverseTranslation(float[] translation) {
+ if (translation.length != 3) {
+ throw new IllegalArgumentException(
+ "Translation size must be 3."
+ );
+ }
+ m03 = -translation[0];
+ m13 = -translation[1];
+ m23 = -translation[2];
+ }
+
+ /**
+ * angleRotation sets this matrix to that of a rotation about
+ * three axes (x, y, z). Where each axis has a specified rotation in
+ * degrees. These rotations are expressed in a single Vector3f
+ * object.
+ *
+ * @param angles the angles to rotate.
+ */
+ public void angleRotation(Vector3f angles) {
+ float angle;
+ float sr, sp, sy, cr, cp, cy;
+
+ angle = (angles.z * FastMath.DEG_TO_RAD);
+ sy = FastMath.sin(angle);
+ cy = FastMath.cos(angle);
+ angle = (angles.y * FastMath.DEG_TO_RAD);
+ sp = FastMath.sin(angle);
+ cp = FastMath.cos(angle);
+ angle = (angles.x * FastMath.DEG_TO_RAD);
+ sr = FastMath.sin(angle);
+ cr = FastMath.cos(angle);
+
+ // matrix = (Z * Y) * X
+ m00 = cp * cy;
+ m10 = cp * sy;
+ m20 = -sp;
+ m01 = sr * sp * cy + cr * -sy;
+ m11 = sr * sp * sy + cr * cy;
+ m21 = sr * cp;
+ m02 = (cr * sp * cy + -sr * -sy);
+ m12 = (cr * sp * sy + -sr * cy);
+ m22 = cr * cp;
+ m03 = 0.0f;
+ m13 = 0.0f;
+ m23 = 0.0f;
+ }
+
+ /**
+ * setRotationQuaternion builds a rotation from a
+ * Quaternion.
+ *
+ * @param quat the quaternion to build the rotation from.
+ * @throws NullPointerException if quat is null.
+ */
+ public void setRotationQuaternion(Quaternion quat) {
+ quat.toRotationMatrix(this);
+ }
+
+ /**
+ * setInverseRotationRadians builds an inverted rotation from
+ * Euler angles that are in radians.
+ *
+ * @param angles the Euler angles in radians.
+ * @throws JmeException if angles is not size 3.
+ */
+ public void setInverseRotationRadians(float[] angles) {
+ if (angles.length != 3) {
+ throw new IllegalArgumentException(
+ "Angles must be of size 3."
+ );
+ }
+ double cr = FastMath.cos(angles[0]);
+ double sr = FastMath.sin(angles[0]);
+ double cp = FastMath.cos(angles[1]);
+ double sp = FastMath.sin(angles[1]);
+ double cy = FastMath.cos(angles[2]);
+ double sy = FastMath.sin(angles[2]);
+
+ m00 = (float) (cp * cy);
+ m10 = (float) (cp * sy);
+ m20 = (float) (-sp);
+
+ double srsp = sr * sp;
+ double crsp = cr * sp;
+
+ m01 = (float) (srsp * cy - cr * sy);
+ m11 = (float) (srsp * sy + cr * cy);
+ m21 = (float) (sr * cp);
+
+ m02 = (float) (crsp * cy + sr * sy);
+ m12 = (float) (crsp * sy - sr * cy);
+ m22 = (float) (cr * cp);
+ }
+
+ /**
+ * setInverseRotationDegrees builds an inverted rotation from
+ * Euler angles that are in degrees.
+ *
+ * @param angles the Euler angles in degrees.
+ * @throws JmeException if angles is not size 3.
+ */
+ public void setInverseRotationDegrees(float[] angles) {
+ if (angles.length != 3) {
+ throw new IllegalArgumentException(
+ "Angles must be of size 3."
+ );
+ }
+ float vec[] = new float[3];
+ vec[0] = (angles[0] * FastMath.RAD_TO_DEG);
+ vec[1] = (angles[1] * FastMath.RAD_TO_DEG);
+ vec[2] = (angles[2] * FastMath.RAD_TO_DEG);
+ setInverseRotationRadians(vec);
+ }
+
+ /**
+ *
+ * inverseTranslateVect translates a given Vector3f by the
+ * translation part of this matrix.
+ *
+ * @param vec the Vector3f data to be translated.
+ * @throws JmeException if the size of the Vector3f is not 3.
+ */
+ public void inverseTranslateVect(float[] vec) {
+ if (vec.length != 3) {
+ throw new IllegalArgumentException(
+ "vec must be of size 3."
+ );
+ }
+
+ vec[0] = vec[0] - m03;
+ vec[1] = vec[1] - m13;
+ vec[2] = vec[2] - m23;
+ }
+
+ /**
+ *
+ * inverseTranslateVect translates a given Vector3f by the
+ * translation part of this matrix.
+ *
+ * @param data the Vector3f to be translated.
+ * @throws JmeException if the size of the Vector3f is not 3.
+ */
+ public void inverseTranslateVect(Vector3f data) {
+ data.x -= m03;
+ data.y -= m13;
+ data.z -= m23;
+ }
+
+ /**
+ *
+ * inverseTranslateVect translates a given Vector3f by the
+ * translation part of this matrix.
+ *
+ * @param data the Vector3f to be translated.
+ * @throws JmeException if the size of the Vector3f is not 3.
+ */
+ public void translateVect(Vector3f data) {
+ data.x += m03;
+ data.y += m13;
+ data.z += m23;
+ }
+
+ /**
+ *
+ * inverseRotateVect rotates a given Vector3f by the rotation
+ * part of this matrix.
+ *
+ * @param vec the Vector3f to be rotated.
+ */
+ public void inverseRotateVect(Vector3f vec) {
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+
+ vec.x = vx * m00 + vy * m10 + vz * m20;
+ vec.y = vx * m01 + vy * m11 + vz * m21;
+ vec.z = vx * m02 + vy * m12 + vz * m22;
+ }
+
+ public void rotateVect(Vector3f vec) {
+ float vx = vec.x, vy = vec.y, vz = vec.z;
+
+ vec.x = vx * m00 + vy * m01 + vz * m02;
+ vec.y = vx * m10 + vy * m11 + vz * m12;
+ vec.z = vx * m20 + vy * m21 + vz * m22;
+ }
+
+ /**
+ * toString returns the string representation of this object.
+ * It is in a format of a 4x4 matrix. For example, an identity matrix would
+ * be represented by the following string. com.jme.math.Matrix3f
+ * [
+ * 1.0 0.0 0.0 0.0
+ * 0.0 1.0 0.0 0.0
+ * 0.0 0.0 1.0 0.0
+ * 0.0 0.0 0.0 1.0
+ * ]
+ *
+ * @return the string representation of this object.
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("Matrix4f\n[\n");
+ result.append(" ");
+ result.append(m00);
+ result.append(" ");
+ result.append(m01);
+ result.append(" ");
+ result.append(m02);
+ result.append(" ");
+ result.append(m03);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m10);
+ result.append(" ");
+ result.append(m11);
+ result.append(" ");
+ result.append(m12);
+ result.append(" ");
+ result.append(m13);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m20);
+ result.append(" ");
+ result.append(m21);
+ result.append(" ");
+ result.append(m22);
+ result.append(" ");
+ result.append(m23);
+ result.append(" \n");
+ result.append(" ");
+ result.append(m30);
+ result.append(" ");
+ result.append(m31);
+ result.append(" ");
+ result.append(m32);
+ result.append(" ");
+ result.append(m33);
+ result.append(" \n]");
+ return result.toString();
+ }
+
+ /**
+ *
+ * hashCode returns the hash code value as an integer and is
+ * supported for the benefit of hashing based collection classes such as
+ * Hashtable, HashMap, HashSet etc.
+ *
+ * @return the hashcode for this instance of Matrix4f.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash = 37 * hash + Float.floatToIntBits(m00);
+ hash = 37 * hash + Float.floatToIntBits(m01);
+ hash = 37 * hash + Float.floatToIntBits(m02);
+ hash = 37 * hash + Float.floatToIntBits(m03);
+
+ hash = 37 * hash + Float.floatToIntBits(m10);
+ hash = 37 * hash + Float.floatToIntBits(m11);
+ hash = 37 * hash + Float.floatToIntBits(m12);
+ hash = 37 * hash + Float.floatToIntBits(m13);
+
+ hash = 37 * hash + Float.floatToIntBits(m20);
+ hash = 37 * hash + Float.floatToIntBits(m21);
+ hash = 37 * hash + Float.floatToIntBits(m22);
+ hash = 37 * hash + Float.floatToIntBits(m23);
+
+ hash = 37 * hash + Float.floatToIntBits(m30);
+ hash = 37 * hash + Float.floatToIntBits(m31);
+ hash = 37 * hash + Float.floatToIntBits(m32);
+ hash = 37 * hash + Float.floatToIntBits(m33);
+
+ return hash;
+ }
+
+ /**
+ * are these two matrices the same? they are is they both have the same mXX
+ * values.
+ *
+ * @param o the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Matrix4f) || o == null) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Matrix4f comp = (Matrix4f) o;
+ if (Float.compare(m00, comp.m00) != 0) {
+ return false;
+ }
+ if (Float.compare(m01, comp.m01) != 0) {
+ return false;
+ }
+ if (Float.compare(m02, comp.m02) != 0) {
+ return false;
+ }
+ if (Float.compare(m03, comp.m03) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m10, comp.m10) != 0) {
+ return false;
+ }
+ if (Float.compare(m11, comp.m11) != 0) {
+ return false;
+ }
+ if (Float.compare(m12, comp.m12) != 0) {
+ return false;
+ }
+ if (Float.compare(m13, comp.m13) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m20, comp.m20) != 0) {
+ return false;
+ }
+ if (Float.compare(m21, comp.m21) != 0) {
+ return false;
+ }
+ if (Float.compare(m22, comp.m22) != 0) {
+ return false;
+ }
+ if (Float.compare(m23, comp.m23) != 0) {
+ return false;
+ }
+
+ if (Float.compare(m30, comp.m30) != 0) {
+ return false;
+ }
+ if (Float.compare(m31, comp.m31) != 0) {
+ return false;
+ }
+ if (Float.compare(m32, comp.m32) != 0) {
+ return false;
+ }
+ if (Float.compare(m33, comp.m33) != 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return true if this matrix is identity
+ */
+ public boolean isIdentity() {
+ return (m00 == 1 && m01 == 0 && m02 == 0 && m03 == 0)
+ && (m10 == 0 && m11 == 1 && m12 == 0 && m13 == 0)
+ && (m20 == 0 && m21 == 0 && m22 == 1 && m23 == 0)
+ && (m30 == 0 && m31 == 0 && m32 == 0 && m33 == 1);
+ }
+
+ /**
+ * Apply a scale to this matrix.
+ *
+ * @param scale the scale to apply
+ */
+ public void scale(Vector3f scale) {
+ m00 *= scale.getX();
+ m10 *= scale.getX();
+ m20 *= scale.getX();
+ m30 *= scale.getX();
+ m01 *= scale.getY();
+ m11 *= scale.getY();
+ m21 *= scale.getY();
+ m31 *= scale.getY();
+ m02 *= scale.getZ();
+ m12 *= scale.getZ();
+ m22 *= scale.getZ();
+ m32 *= scale.getZ();
+ }
+
+ static boolean equalIdentity(Matrix4f mat) {
+ if (Math.abs(mat.m00 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m11 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m22 - 1) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m33 - 1) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m01) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m02) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m03) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m10) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m12) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m13) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m20) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m21) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m23) > 1e-4) {
+ return false;
+ }
+
+ if (Math.abs(mat.m30) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m31) > 1e-4) {
+ return false;
+ }
+ if (Math.abs(mat.m32) > 1e-4) {
+ return false;
+ }
+
+ return true;
+ }
+
+ // XXX: This tests more solid than converting the q to a matrix and
+ // multiplying... why?
+ public void multLocal(Quaternion rotation) {
+ Vector3f axis = new Vector3f();
+ float angle = rotation.toAngleAxis(axis);
+ Matrix4f matrix4f = new Matrix4f();
+ matrix4f.fromAngleAxis(angle, axis);
+ multLocal(matrix4f);
+ }
+
+ @Override
+ public Matrix4f clone() {
+ try {
+ return (Matrix4f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+}
diff --git a/java/com/jme3/math/Quaternion.java b/java/com/jme3/math/Quaternion.java
new file mode 100644
index 000000000..3db929e1b
--- /dev/null
+++ b/java/com/jme3/math/Quaternion.java
@@ -0,0 +1,1721 @@
+/*
+ * Copyright (c) 2009-2012 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 java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.logging.Logger;
+
+import com.jme3.util.TempVars;
+
+import io.eiren.math.FloatMath;
+
+
+/**
+ * Quaternion defines a single example of a more general class of
+ * hypercomplex numbers. Quaternions extends a rotation in three dimensions to a
+ * rotation in four dimensions. This avoids "gimbal lock" and allows for smooth
+ * continuous rotation.
+ *
+ * Quaternion is defined by four floating point numbers: {x y z w}.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Quaternion implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Quaternion.class.getName());
+ /**
+ * Represents the identity quaternion rotation (0, 0, 0, 1).
+ */
+ public static final Quaternion IDENTITY = new Quaternion();
+ public static final Quaternion DIRECTION_Z = new Quaternion();
+ public static final Quaternion ZERO = new Quaternion(0, 0, 0, 0);
+
+ public static final Quaternion X_90_DEG = new Quaternion()
+ .fromAngleNormalAxis(FastMath.HALF_PI, Vector3f.UNIT_X);
+ public static final Quaternion X_180_DEG = new Quaternion()
+ .fromAngleNormalAxis(FastMath.PI, Vector3f.UNIT_X);
+ public static final Quaternion X_270_DEG = new Quaternion()
+ .fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_X);
+ public static final Quaternion Y_90_DEG = new Quaternion()
+ .fromAngleNormalAxis(FastMath.HALF_PI, Vector3f.UNIT_Y);
+ public static final Quaternion Y_180_DEG = new Quaternion()
+ .fromAngleNormalAxis(FastMath.PI, Vector3f.UNIT_Y);
+ public static final Quaternion Y_270_DEG = new Quaternion()
+ .fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);
+ public static final Quaternion Z_90_DEG = new Quaternion()
+ .fromAngleNormalAxis(FastMath.HALF_PI, Vector3f.UNIT_Z);
+ public static final Quaternion Z_180_DEG = new Quaternion()
+ .fromAngleNormalAxis(FastMath.PI, Vector3f.UNIT_Z);
+ public static final Quaternion Z_270_DEG = new Quaternion()
+ .fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Z);
+
+ static {
+ DIRECTION_Z.fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
+ }
+ protected float x, y, z, w;
+
+ /**
+ * Constructor instantiates a new Quaternion object
+ * initializing all values to zero, except w which is initialized to 1.
+ *
+ */
+ public Quaternion() {
+ x = 0;
+ y = 0;
+ z = 0;
+ w = 1;
+ }
+
+ /**
+ * Constructor instantiates a new Quaternion object from the
+ * given list of parameters.
+ *
+ * @param x the x value of the quaternion.
+ * @param y the y value of the quaternion.
+ * @param z the z value of the quaternion.
+ * @param w the w value of the quaternion.
+ */
+ public Quaternion(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public float getW() {
+ return w;
+ }
+
+ /**
+ * sets the data in a Quaternion object from the given list of
+ * parameters.
+ *
+ * @param x the x value of the quaternion.
+ * @param y the y value of the quaternion.
+ * @param z the z value of the quaternion.
+ * @param w the w value of the quaternion.
+ * @return this
+ */
+ public Quaternion set(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ return this;
+ }
+
+ /**
+ * Sets the data in this Quaternion object to be equal to the
+ * passed Quaternion object. The values are copied producing a
+ * new object.
+ *
+ * @param q The Quaternion to copy values from.
+ * @return this
+ */
+ public Quaternion set(Quaternion q) {
+ this.x = q.x;
+ this.y = q.y;
+ this.z = q.z;
+ this.w = q.w;
+ return this;
+ }
+
+ /**
+ * Constructor instantiates a new Quaternion object from a
+ * collection of rotation angles.
+ *
+ * @param angles the angles of rotation (x, y, z) that will define the
+ * Quaternion.
+ */
+ public Quaternion(float[] angles) {
+ fromAngles(angles);
+ }
+
+ /**
+ * Constructor instantiates a new Quaternion object from an
+ * interpolation between two other quaternions.
+ *
+ * @param q1 the first quaternion.
+ * @param q2 the second quaternion.
+ * @param interp the amount to interpolate between the two quaternions.
+ */
+ public Quaternion(Quaternion q1, Quaternion q2, float interp) {
+ slerp(q1, q2, interp);
+ }
+
+ /**
+ * Constructor instantiates a new Quaternion object from an
+ * existing quaternion, creating a copy.
+ *
+ * @param q the quaternion to copy.
+ */
+ public Quaternion(Quaternion q) {
+ this.x = q.x;
+ this.y = q.y;
+ this.z = q.z;
+ this.w = q.w;
+ }
+
+ /**
+ * Sets this Quaternion to {0, 0, 0, 1}. Same as calling set(0,0,0,1).
+ */
+ public void loadIdentity() {
+ x = y = z = 0;
+ w = 1;
+ }
+
+ /**
+ * @return true if this Quaternion is {0,0,0,1}
+ */
+ public boolean isIdentity() {
+ if (x == 0 && y == 0 && z == 0 && w == 1) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * fromAngles builds a quaternion from the Euler rotation
+ * angles (y,r,p).
+ *
+ * @param angles the Euler angles of rotation (in radians).
+ */
+ public Quaternion fromAngles(float[] angles) {
+ if (angles.length != 3) {
+ throw new IllegalArgumentException("Angles array must have three elements");
+ }
+
+ return fromAngles(angles[0], angles[1], angles[2]);
+ }
+
+ /**
+ * fromAngles builds a Quaternion from the Euler rotation
+ * angles (x,y,z) aka (pitch, yaw, rall)). Note that we are applying in
+ * order: (y, z, x) aka (yaw, roll, pitch) but we've ordered them in x, y,
+ * and z for convenience.
+ *
+ * @see http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm
+ *
+ * @param xAngle the Euler pitch of rotation (in radians). (aka Attitude,
+ * often rot around x)
+ * @param yAngle the Euler yaw of rotation (in radians). (aka Heading, often
+ * rot around y)
+ * @param zAngle the Euler roll of rotation (in radians). (aka Bank, often
+ * rot around z)
+ */
+ public Quaternion fromAngles(float xAngle, float yAngle, float zAngle) {
+ float angle;
+ float sinY, sinZ, sinX, cosY, cosZ, cosX;
+ angle = zAngle * 0.5f;
+ sinZ = FastMath.sin(angle);
+ cosZ = FastMath.cos(angle);
+ angle = yAngle * 0.5f;
+ sinY = FastMath.sin(angle);
+ cosY = FastMath.cos(angle);
+ angle = xAngle * 0.5f;
+ sinX = FastMath.sin(angle);
+ cosX = FastMath.cos(angle);
+
+ // variables used to reduce multiplication calls.
+ float cosYXcosZ = cosY * cosZ;
+ float sinYXsinZ = sinY * sinZ;
+ float cosYXsinZ = cosY * sinZ;
+ float sinYXcosZ = sinY * cosZ;
+
+ w = (cosYXcosZ * cosX - sinYXsinZ * sinX);
+ x = (cosYXcosZ * sinX + sinYXsinZ * cosX);
+ y = (sinYXcosZ * cosX + cosYXsinZ * sinX);
+ z = (cosYXsinZ * cosX - sinYXcosZ * sinX);
+
+ normalizeLocal();
+ return this;
+ }
+
+ /**
+ * toAngles returns this quaternion converted to Euler rotation
+ * angles (yaw,roll,pitch).
+ * Note that the result is not always 100% accurate due to the implications
+ * of euler angles.
+ *
+ * @see http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm
+ *
+ * @param angles the float[] in which the angles should be stored, or null
+ * if you want a new float[] to be created
+ * @return the float[] in which the angles are stored.
+ */
+ public float[] toAngles(float[] angles) {
+ if (angles == null) {
+ angles = new float[3];
+ } else if (angles.length != 3) {
+ throw new IllegalArgumentException("Angles array must have three elements");
+ }
+
+ float sqw = w * w;
+ float sqx = x * x;
+ float sqy = y * y;
+ float sqz = z * z;
+ float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise
+ // is correction factor
+ float test = x * y + z * w;
+ if (test > 0.499 * unit) { // singularity at north pole
+ angles[1] = 2 * FastMath.atan2(x, w);
+ angles[2] = FastMath.HALF_PI;
+ angles[0] = 0;
+ } else if (test < -0.499 * unit) { // singularity at south pole
+ angles[1] = -2 * FastMath.atan2(x, w);
+ angles[2] = -FastMath.HALF_PI;
+ angles[0] = 0;
+ } else {
+ angles[1] = FastMath.atan2(2 * y * w - 2 * x * z, sqx - sqy - sqz + sqw); // yaw
+ // or
+ // bank
+ angles[2] = FastMath.asin(2 * test / unit); // roll or heading
+ angles[0] = FastMath.atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + sqw); // pitch
+ // or
+ // attitude
+ }
+ return angles;
+ }
+
+ /**
+ * Returns Euler rotation angle around x axis (pitch).
+ *
+ * @return
+ * @see #toAngles(float[])
+ */
+ public float getPitch() {
+ float sqw = w * w;
+ float sqx = x * x;
+ float sqy = y * y;
+ float sqz = z * z;
+ float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise
+ // is correction factor
+ float test = x * y + z * w;
+ if (test > 0.499 * unit) { // singularity at north pole
+ return 0;
+ } else if (test < -0.499 * unit) { // singularity at south pole
+ return 0;
+ } else {
+ return FastMath.atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + sqw); // pitch
+ // or
+ // attitude
+ }
+ }
+
+ /**
+ * Returns Euler rotation angle around y axis (yaw).
+ *
+ * @return
+ * @see #toAngles(float[])
+ */
+ public float getYaw() {
+ float sqw = w * w;
+ float sqx = x * x;
+ float sqy = y * y;
+ float sqz = z * z;
+ float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise
+ // is correction factor
+ float test = x * y + z * w;
+ if (test > 0.499 * unit) { // singularity at north pole
+ return 2 * FastMath.atan2(x, w);
+ } else if (test < -0.499 * unit) { // singularity at south pole
+ return -2 * FastMath.atan2(x, w);
+ } else {
+ return FastMath.atan2(2 * y * w - 2 * x * z, sqx - sqy - sqz + sqw); // yaw
+ // or
+ // bank
+ }
+ }
+
+ /**
+ * Returns Euler rotation angle around z axis (roll).
+ *
+ * @return
+ * @see #toAngles(float[])
+ */
+ public float getRoll() {
+ float sqw = w * w;
+ float sqx = x * x;
+ float sqy = y * y;
+ float sqz = z * z;
+ float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise
+ // is correction factor
+ float test = x * y + z * w;
+ if (test > 0.499 * unit) { // singularity at north pole
+ return FastMath.HALF_PI;
+ } else if (test < -0.499 * unit) { // singularity at south pole
+ return -FastMath.HALF_PI;
+ } else {
+ return FastMath.asin(2 * test / unit); // roll or heading
+ }
+ }
+
+ /**
+ *
+ * fromRotationMatrix generates a quaternion from a supplied
+ * matrix. This matrix is assumed to be a rotational matrix.
+ *
+ * @param matrix the matrix that defines the rotation.
+ */
+ public Quaternion fromRotationMatrix(Matrix3f matrix) {
+ return fromRotationMatrix(
+ matrix.m00,
+ matrix.m01,
+ matrix.m02,
+ matrix.m10,
+ matrix.m11,
+ matrix.m12,
+ matrix.m20,
+ matrix.m21,
+ matrix.m22
+ );
+ }
+
+ public Quaternion fromRotationMatrix(
+ float m00,
+ float m01,
+ float m02,
+ float m10,
+ float m11,
+ float m12,
+ float m20,
+ float m21,
+ float m22
+ ) {
+ // first normalize the forward (F), up (U) and side (S) vectors of the
+ // rotation matrix
+ // so that the scale does not affect the rotation
+ float lengthSquared = m00 * m00 + m10 * m10 + m20 * m20;
+ if (lengthSquared != 1f && lengthSquared != 0f) {
+ lengthSquared = 1.0f / FastMath.sqrt(lengthSquared);
+ m00 *= lengthSquared;
+ m10 *= lengthSquared;
+ m20 *= lengthSquared;
+ }
+ lengthSquared = m01 * m01 + m11 * m11 + m21 * m21;
+ if (lengthSquared != 1f && lengthSquared != 0f) {
+ lengthSquared = 1.0f / FastMath.sqrt(lengthSquared);
+ m01 *= lengthSquared;
+ m11 *= lengthSquared;
+ m21 *= lengthSquared;
+ }
+ lengthSquared = m02 * m02 + m12 * m12 + m22 * m22;
+ if (lengthSquared != 1f && lengthSquared != 0f) {
+ lengthSquared = 1.0f / FastMath.sqrt(lengthSquared);
+ m02 *= lengthSquared;
+ m12 *= lengthSquared;
+ m22 *= lengthSquared;
+ }
+
+ // Use the Graphics Gems code, from
+ // ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z
+ // *NOT* the "Matrix and Quaternions FAQ", which has errors!
+
+ // the trace is the sum of the diagonal elements; see
+ // http://mathworld.wolfram.com/MatrixTrace.html
+ float t = m00 + m11 + m22;
+
+ // we protect the division by s by ensuring that s>=1
+ if (t >= 0) { // |w| >= .5
+ float s = FastMath.sqrt(t + 1); // |s|>=1 ...
+ w = 0.5f * s;
+ s = 0.5f / s; // so this division isn't bad
+ x = (m21 - m12) * s;
+ y = (m02 - m20) * s;
+ z = (m10 - m01) * s;
+ } else if ((m00 > m11) && (m00 > m22)) {
+ float s = FastMath.sqrt(1.0f + m00 - m11 - m22); // |s|>=1
+ x = s * 0.5f; // |x| >= .5
+ s = 0.5f / s;
+ y = (m10 + m01) * s;
+ z = (m02 + m20) * s;
+ w = (m21 - m12) * s;
+ } else if (m11 > m22) {
+ float s = FastMath.sqrt(1.0f + m11 - m00 - m22); // |s|>=1
+ y = s * 0.5f; // |y| >= .5
+ s = 0.5f / s;
+ x = (m10 + m01) * s;
+ z = (m21 + m12) * s;
+ w = (m02 - m20) * s;
+ } else {
+ float s = FastMath.sqrt(1.0f + m22 - m00 - m11); // |s|>=1
+ z = s * 0.5f; // |z| >= .5
+ s = 0.5f / s;
+ x = (m02 + m20) * s;
+ y = (m21 + m12) * s;
+ w = (m10 - m01) * s;
+ }
+
+ return this;
+ }
+
+ /**
+ * toRotationMatrix converts this quaternion to a rotational
+ * matrix. Note: the result is created from a normalized version of this
+ * quat.
+ *
+ * @return the rotation matrix representation of this quaternion.
+ */
+ public Matrix3f toRotationMatrix() {
+ Matrix3f matrix = new Matrix3f();
+ return toRotationMatrix(matrix);
+ }
+
+ /**
+ * toRotationMatrix converts this quaternion to a rotational
+ * matrix. The result is stored in result.
+ *
+ * @param result The Matrix3f to store the result in.
+ * @return the rotation matrix representation of this quaternion.
+ */
+ public Matrix3f toRotationMatrix(Matrix3f result) {
+
+ float norm = norm();
+ // we explicitly test norm against one here, saving a division
+ // at the cost of a test and branch. Is it worth it?
+ float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0;
+
+ // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
+ // will be used 2-4 times each.
+ float xs = x * s;
+ float ys = y * s;
+ float zs = z * s;
+ float xx = x * xs;
+ float xy = x * ys;
+ float xz = x * zs;
+ float xw = w * xs;
+ float yy = y * ys;
+ float yz = y * zs;
+ float yw = w * ys;
+ float zz = z * zs;
+ float zw = w * zs;
+
+ // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
+ result.m00 = 1 - (yy + zz);
+ result.m01 = (xy - zw);
+ result.m02 = (xz + yw);
+ result.m10 = (xy + zw);
+ result.m11 = 1 - (xx + zz);
+ result.m12 = (yz - xw);
+ result.m20 = (xz - yw);
+ result.m21 = (yz + xw);
+ result.m22 = 1 - (xx + yy);
+
+ return result;
+ }
+
+ /**
+ * toRotationMatrix converts this quaternion to a rotational
+ * matrix. The result is stored in result. 4th row and 4th column values are
+ * untouched. Note: the result is created from a normalized version of this
+ * quat.
+ *
+ * @param result The Matrix4f to store the result in.
+ * @return the rotation matrix representation of this quaternion.
+ */
+ public Matrix4f toRotationMatrix(Matrix4f result) {
+ TempVars tempv = TempVars.get();
+ Vector3f originalScale = tempv.vect1;
+
+ result.toScaleVector(originalScale);
+ result.setScale(1, 1, 1);
+ float norm = norm();
+ // we explicitly test norm against one here, saving a division
+ // at the cost of a test and branch. Is it worth it?
+ float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0;
+
+ // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
+ // will be used 2-4 times each.
+ float xs = x * s;
+ float ys = y * s;
+ float zs = z * s;
+ float xx = x * xs;
+ float xy = x * ys;
+ float xz = x * zs;
+ float xw = w * xs;
+ float yy = y * ys;
+ float yz = y * zs;
+ float yw = w * ys;
+ float zz = z * zs;
+ float zw = w * zs;
+
+ // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
+ result.m00 = 1 - (yy + zz);
+ result.m01 = (xy - zw);
+ result.m02 = (xz + yw);
+ result.m10 = (xy + zw);
+ result.m11 = 1 - (xx + zz);
+ result.m12 = (yz - xw);
+ result.m20 = (xz - yw);
+ result.m21 = (yz + xw);
+ result.m22 = 1 - (xx + yy);
+
+ result.setScale(originalScale);
+
+ tempv.release();
+
+ return result;
+ }
+
+ /**
+ * getRotationColumn returns one of three columns specified by
+ * the parameter. This column is returned as a Vector3f object.
+ *
+ * @param i the column to retrieve. Must be between 0 and 2.
+ * @return the column specified by the index.
+ */
+ public Vector3f getRotationColumn(int i) {
+ return getRotationColumn(i, null);
+ }
+
+ /**
+ * getRotationColumn returns one of three columns specified by
+ * the parameter. This column is returned as a Vector3f object.
+ * The value is retrieved as if this quaternion was first normalized.
+ *
+ * @param i the column to retrieve. Must be between 0 and 2.
+ * @param store the vector object to store the result in. if null, a new one
+ * is created.
+ * @return the column specified by the index.
+ */
+ public Vector3f getRotationColumn(int i, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+
+ float norm = norm();
+ if (norm != 1.0f) {
+ norm = FastMath.invSqrt(norm);
+ }
+
+ float xx = x * x * norm;
+ float xy = x * y * norm;
+ float xz = x * z * norm;
+ float xw = x * w * norm;
+ float yy = y * y * norm;
+ float yz = y * z * norm;
+ float yw = y * w * norm;
+ float zz = z * z * norm;
+ float zw = z * w * norm;
+
+ switch (i) {
+ case 0:
+ store.x = 1 - 2 * (yy + zz);
+ store.y = 2 * (xy + zw);
+ store.z = 2 * (xz - yw);
+ break;
+ case 1:
+ store.x = 2 * (xy - zw);
+ store.y = 1 - 2 * (xx + zz);
+ store.z = 2 * (yz + xw);
+ break;
+ case 2:
+ store.x = 2 * (xz + yw);
+ store.y = 2 * (yz - xw);
+ store.z = 1 - 2 * (xx + yy);
+ break;
+ default:
+ logger.warning("Invalid column index.");
+ throw new IllegalArgumentException("Invalid column index. " + i);
+ }
+
+ return store;
+ }
+
+ /**
+ * Gets three rows of rotation matrix.
+ *
+ * The same as transposed columns from {@linkplain #getRotationColumn}.
+ */
+ public void getRotationBasis(Vector3f v1, Vector3f v2, Vector3f v3) {
+ // This source code from toRotationMatrix method
+ float norm = norm();
+ float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0;
+ //@formatter:off
+ float xs = x * s; float ys = y * s; float zs = z * s;
+ float xx = x * xs; float xy = x * ys; float xz = x * zs;
+ float xw = w * xs; float yy = y * ys; float yz = y * zs;
+ float yw = w * ys; float zz = z * zs; float zw = w * zs;
+ //@formatter:on
+ v1.set(1f - yy - zz, xy + zw, xz - yw);
+ v2.set(xy - zw, 1f - xx - zz, yz + xw);
+ v3.set(xz + yw, yz - xw, 1f - xx - yy);
+ }
+
+ /**
+ * fromAngleAxis sets this quaternion to the values specified
+ * by an angle and an axis of rotation. This method creates an object, so
+ * use fromAngleNormalAxis if your axis is already normalized.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param axis the axis of rotation.
+ * @return this quaternion
+ */
+ public Quaternion fromAngleAxis(float angle, Vector3f axis) {
+ Vector3f normAxis = axis.normalize();
+ fromAngleNormalAxis(angle, normAxis);
+ return this;
+ }
+
+ /**
+ * fromAngleAxis sets this quaternion to the values specified
+ * by an angle and an axis of rotation. This method creates an object, so
+ * use fromAngleNormalAxis if your axis is already normalized.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param x
+ * @param y
+ * @param z the axis of rotation.
+ * @return this quaternion
+ */
+ public Quaternion fromAngleAxis(float angle, float x, float y, float z) {
+ float length = x * x + y * y + z * z;
+ if (length != 1f && length != 0f) {
+ length = 1.0f / FastMath.sqrt(length);
+ return fromAngleNormalAxis(angle, x * length, y * length, z * length);
+ } else {
+ return fromAngleNormalAxis(angle, x, y, z);
+ }
+ }
+
+ /**
+ * fromAngleNormalAxis sets this quaternion to the values
+ * specified by an angle and a normalized axis of rotation.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param axis the axis of rotation (already normalized).
+ */
+ public Quaternion fromAngleNormalAxis(float angle, Vector3f axis) {
+ if (axis.x == 0 && axis.y == 0 && axis.z == 0) {
+ loadIdentity();
+ } else {
+ float halfAngle = 0.5f * angle;
+ float sin = FastMath.sin(halfAngle);
+ w = FastMath.cos(halfAngle);
+ x = sin * axis.x;
+ y = sin * axis.y;
+ z = sin * axis.z;
+ }
+ return this;
+ }
+
+ /**
+ * fromAngleNormalAxis sets this quaternion to the values
+ * specified by an angle and a normalized axis of rotation.
+ *
+ * @param angle the angle to rotate (in radians).
+ * @param x
+ * @param y
+ * @param z the axis of rotation (already normalized).
+ */
+ public Quaternion fromAngleNormalAxis(float angle, float ax, float ay, float az) {
+ if (ax == 0 && ay == 0 && az == 0) {
+ loadIdentity();
+ } else {
+ float halfAngle = 0.5f * angle;
+ float sin = FastMath.sin(halfAngle);
+ w = FastMath.cos(halfAngle);
+ x = sin * ax;
+ y = sin * ay;
+ z = sin * az;
+ }
+ return this;
+ }
+
+ /**
+ * toAngleAxis sets a given angle and axis to that represented
+ * by the current quaternion. The values are stored as follows: The axis is
+ * provided as a parameter and built by the method, the angle is returned as
+ * a float.
+ *
+ * @param axisStore the object we'll store the computed axis in.
+ * @return the angle of rotation in radians.
+ */
+ public float toAngleAxis(Vector3f axisStore) {
+ float sqrLength = x * x + y * y + z * z;
+ float angle;
+ if (sqrLength == 0.0f) {
+ angle = 0.0f;
+ if (axisStore != null) {
+ axisStore.x = 1.0f;
+ axisStore.y = 0.0f;
+ axisStore.z = 0.0f;
+ }
+ } else {
+ angle = (2.0f * FastMath.acos(w));
+ if (axisStore != null) {
+ float invLength = (1.0f / FastMath.sqrt(sqrLength));
+ axisStore.x = x * invLength;
+ axisStore.y = y * invLength;
+ axisStore.z = z * invLength;
+ }
+ }
+
+ return angle;
+ }
+
+ public float angleBetween(Quaternion q2) {
+ float w = this.w * q2.w + this.x * q2.x + this.y * q2.y + this.z * q2.z;
+ float x = this.w * q2.x - this.x * q2.w - this.y * q2.z + this.z * q2.y;
+ float y = this.w * q2.y + this.x * q2.z - this.y * q2.w - this.z * q2.x;
+ float z = this.w * q2.z - this.x * q2.y + this.y * q2.x - this.z * q2.w;
+
+ // compute cosine and sine of the angle between
+ // do so in a numerically stable way
+ return FastMath.atan2(FastMath.sqrt(x * x + y * y + z * z), w);
+ }
+
+ public Quaternion pureSlerpLocal(Quaternion q2, float t) {
+ // make it nice and symmetrical
+ Quaternion q1 = this;
+
+ // get q2 relative to q1
+ float rw = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+ float rx = q1.w * q2.x - q1.x * q2.w - q1.y * q2.z + q1.z * q2.y;
+ float ry = q1.w * q2.y + q1.x * q2.z - q1.y * q2.w - q1.z * q2.x;
+ float rz = q1.w * q2.z - q1.x * q2.y + q1.y * q2.x - q1.z * q2.w;
+
+ // compute theta robustly
+ float theta = FastMath.atan2(FastMath.sqrt(rx * rx + ry * ry + rz * rz), rw);
+
+ // compute interpolation variables
+ float s0 = FastMath.sin((1.0f - t) * theta);
+ float s1 = FastMath.sin(t * theta);
+
+ // compute interpolated quaternion
+ float sw = s0 * q1.w + s1 * q2.w;
+ float sx = s0 * q1.x + s1 * q2.x;
+ float sy = s0 * q1.y + s1 * q2.y;
+ float sz = s0 * q1.z + s1 * q2.z;
+
+ // compute the length of the quaternion
+ float mag = FastMath.sqrt(sw * sw + sx * sx + sy * sy + sz * sz);
+
+ if (mag > 0.0f) {
+ float iMag = 1.0f / mag;
+ this.w = iMag * sw;
+ this.x = iMag * sx;
+ this.y = iMag * sy;
+ this.z = iMag * sz;
+
+ } else if (t >= 0.5f) {
+ this.w = q2.w;
+ this.x = q2.x;
+ this.y = q2.y;
+ this.z = q2.z;
+ }
+ // else this == q1, no need to do anything.
+
+ return this;
+ }
+
+ /**
+ * @deprecated Direct call to {@link #slerpLocal()}.
+ */
+ public Quaternion slerp(Quaternion q2, float t) {
+ return this.slerpLocal(q2, t);
+ }
+
+ /**
+ * Sets the values of this normalized quaternion from itself to the
+ * normalized quaternion q2 by t
+ *
+ * @param q2 Final interpolation value
+ * @param t The amount diffrence
+ */
+ public Quaternion slerpLocal(Quaternion q2, float t) {
+ // make it nice and symmetrical
+ Quaternion q1 = this;
+
+ float rw = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+
+ if (rw < 0) {
+ return this.pureSlerpLocal(q2.negate(), t);
+ } else {
+ return this.pureSlerpLocal(q2, t);
+ }
+ }
+
+
+ public Quaternion pureSlerp(Quaternion q1, Quaternion q2, float t) {
+ return set(q1).pureSlerpLocal(q2, t);
+ }
+
+ /**
+ * slerp sets this quaternion's value as an interpolation
+ * between two other normalized quaternions.
+ *
+ * @param q1 the first quaternion.
+ * @param q2 the second quaternion.
+ * @param t the amount to interpolate between the two quaternions.
+ */
+ public Quaternion slerp(Quaternion q1, Quaternion q2, float t) {
+ float rw = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+
+ if (rw < 0) {
+ return pureSlerp(q1, q2.negate(), t);
+ } else {
+ return pureSlerp(q1, q2, t);
+ }
+ }
+
+ /**
+ * Sets the values of this quaternion to the nlerp from itself to q2 by
+ * blend.
+ *
+ * @param q2
+ * @param blend
+ */
+ public void nlerp(Quaternion q2, float blend) {
+ float dot = dot(q2);
+ float blendI = 1.0f - blend;
+ if (dot < 0.0f) {
+ x = blendI * x - blend * q2.x;
+ y = blendI * y - blend * q2.y;
+ z = blendI * z - blend * q2.z;
+ w = blendI * w - blend * q2.w;
+ } else {
+ x = blendI * x + blend * q2.x;
+ y = blendI * y + blend * q2.y;
+ z = blendI * z + blend * q2.z;
+ w = blendI * w + blend * q2.w;
+ }
+ normalizeLocal();
+ }
+
+ /**
+ * add adds the values of this quaternion to those of the
+ * parameter quaternion. The result is returned as a new quaternion.
+ *
+ * @param q the quaternion to add to this.
+ * @return the new quaternion.
+ */
+ public Quaternion add(Quaternion q) {
+ return new Quaternion(x + q.x, y + q.y, z + q.z, w + q.w);
+ }
+
+ /**
+ * add adds the values of this quaternion to those of the
+ * parameter quaternion. The result is stored in this Quaternion.
+ *
+ * @param q the quaternion to add to this.
+ * @return This Quaternion after addition.
+ */
+ public Quaternion addLocal(Quaternion q) {
+ this.x += q.x;
+ this.y += q.y;
+ this.z += q.z;
+ this.w += q.w;
+ return this;
+ }
+
+ /**
+ * subtract subtracts the values of the parameter quaternion
+ * from those of this quaternion. The result is returned as a new
+ * quaternion.
+ *
+ * @param q the quaternion to subtract from this.
+ * @return the new quaternion.
+ */
+ public Quaternion subtract(Quaternion q) {
+ return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w);
+ }
+
+ /**
+ * subtract subtracts the values of the parameter quaternion
+ * from those of this quaternion. The result is stored in this Quaternion.
+ *
+ * @param q the quaternion to subtract from this.
+ * @return This Quaternion after subtraction.
+ */
+ public Quaternion subtractLocal(Quaternion q) {
+ this.x -= q.x;
+ this.y -= q.y;
+ this.z -= q.z;
+ this.w -= q.w;
+ return this;
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter quaternion.
+ * The result is returned as a new quaternion. It should be noted that
+ * quaternion multiplication is not commutative so q * p != p * q.
+ *
+ * @param q the quaternion to multiply this quaternion by.
+ * @return the new quaternion.
+ */
+ public Quaternion mult(Quaternion q) {
+ return mult(q, null);
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter quaternion.
+ * The result is returned as a new quaternion. It should be noted that
+ * quaternion multiplication is not commutative so q * p != p * q.
+ *
+ * It IS safe for q and res to be the same object. It IS NOT safe for this
+ * and res to be the same object.
+ *
+ * @param q the quaternion to multiply this quaternion by.
+ * @param res the quaternion to store the result in.
+ * @return the new quaternion.
+ */
+ public Quaternion mult(Quaternion q, Quaternion res) {
+ if (res == null) {
+ res = new Quaternion();
+ }
+ float qw = q.w, qx = q.x, qy = q.y, qz = q.z;
+ res.x = x * qw + y * qz - z * qy + w * qx;
+ res.y = -x * qz + y * qw + z * qx + w * qy;
+ res.z = x * qy - y * qx + z * qw + w * qz;
+ res.w = -x * qx - y * qy - z * qz + w * qw;
+ return res;
+ }
+
+ /**
+ * apply multiplies this quaternion by a parameter matrix
+ * internally.
+ *
+ * @param matrix the matrix to apply to this quaternion.
+ */
+ public void apply(Matrix3f matrix) {
+ float oldX = x, oldY = y, oldZ = z, oldW = w;
+ fromRotationMatrix(matrix);
+ float tempX = x, tempY = y, tempZ = z, tempW = w;
+
+ x = oldX * tempW + oldY * tempZ - oldZ * tempY + oldW * tempX;
+ y = -oldX * tempZ + oldY * tempW + oldZ * tempX + oldW * tempY;
+ z = oldX * tempY - oldY * tempX + oldZ * tempW + oldW * tempZ;
+ w = -oldX * tempX - oldY * tempY - oldZ * tempZ + oldW * tempW;
+ }
+
+ /**
+ *
+ * fromAxes creates a Quaternion that represents
+ * the coordinate system defined by three axes. These axes are assumed to be
+ * orthogonal and no error checking is applied. Thus, the user must insure
+ * that the three axes being provided indeed represents a proper right
+ * handed coordinate system.
+ *
+ * @param axis the array containing the three vectors representing the
+ * coordinate system.
+ */
+ public Quaternion fromAxes(Vector3f[] axis) {
+ if (axis.length != 3) {
+ throw new IllegalArgumentException("Axis array must have three elements");
+ }
+ return fromAxes(axis[0], axis[1], axis[2]);
+ }
+
+ /**
+ *
+ * fromAxes creates a Quaternion that represents
+ * the coordinate system defined by three axes. These axes are assumed to be
+ * orthogonal and no error checking is applied. Thus, the user must insure
+ * that the three axes being provided indeed represents a proper right
+ * handed coordinate system.
+ *
+ * @param xAxis vector representing the x-axis of the coordinate system.
+ * @param yAxis vector representing the y-axis of the coordinate system.
+ * @param zAxis vector representing the z-axis of the coordinate system.
+ */
+ public Quaternion fromAxes(Vector3f xAxis, Vector3f yAxis, Vector3f zAxis) {
+ return fromRotationMatrix(
+ xAxis.x,
+ yAxis.x,
+ zAxis.x,
+ xAxis.y,
+ yAxis.y,
+ zAxis.y,
+ xAxis.z,
+ yAxis.z,
+ zAxis.z
+ );
+ }
+
+ /**
+ *
+ * toAxes takes in an array of three vectors. Each vector
+ * corresponds to an axis of the coordinate system defined by the quaternion
+ * rotation.
+ *
+ * @param axis the array of vectors to be filled.
+ */
+ public void toAxes(Vector3f axis[]) {
+ Matrix3f tempMat = toRotationMatrix();
+ axis[0] = tempMat.getColumn(0, axis[0]);
+ axis[1] = tempMat.getColumn(1, axis[1]);
+ axis[2] = tempMat.getColumn(2, axis[2]);
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter vector. The
+ * result is returned as a new vector.
+ *
+ * @param v the vector to multiply this quaternion by.
+ * @return the new vector.
+ */
+ public Vector3f mult(Vector3f v) {
+ return mult(v, null);
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter vector. The
+ * result is stored in the supplied vector
+ *
+ * @param v the vector to multiply this quaternion by.
+ * @return v
+ */
+ public Vector3f multLocal(Vector3f v) {
+ float tempX, tempY;
+ tempX = w * w * v.x
+ + 2 * y * w * v.z
+ - 2 * z * w * v.y
+ + x * x * v.x
+ + 2 * y * x * v.y
+ + 2 * z * x * v.z
+ - z * z * v.x
+ - y * y * v.x;
+ tempY = 2 * x * y * v.x
+ + y * y * v.y
+ + 2 * z * y * v.z
+ + 2 * w * z * v.x
+ - z * z * v.y
+ + w * w * v.y
+ - 2 * x * w * v.z
+ - x * x * v.y;
+ v.z = 2 * x * z * v.x
+ + 2 * y * z * v.y
+ + z * z * v.z
+ - 2 * w * y * v.x
+ - y * y * v.z
+ + 2 * w * x * v.y
+ - x * x * v.z
+ + w * w * v.z;
+ v.x = tempX;
+ v.y = tempY;
+ return v;
+ }
+
+ /**
+ * Multiplies this Quaternion by the supplied quaternion. The result is
+ * stored in this Quaternion, which is also returned for chaining. Similar
+ * to this *= q.
+ *
+ * @param q The Quaternion to multiply this one by.
+ * @return This Quaternion, after multiplication.
+ */
+ public Quaternion multLocal(Quaternion q) {
+ float x1 = x * q.w + y * q.z - z * q.y + w * q.x;
+ float y1 = -x * q.z + y * q.w + z * q.x + w * q.y;
+ float z1 = x * q.y - y * q.x + z * q.w + w * q.z;
+ w = -x * q.x - y * q.y - z * q.z + w * q.w;
+ x = x1;
+ y = y1;
+ z = z1;
+ return this;
+ }
+
+ /**
+ * Multiplies this Quaternion by the supplied quaternion. The result is
+ * stored in this Quaternion, which is also returned for chaining. Similar
+ * to this *= q.
+ *
+ * @param qx - quat x value
+ * @param qy - quat y value
+ * @param qz - quat z value
+ * @param qw - quat w value
+ *
+ * @return This Quaternion, after multiplication.
+ */
+ public Quaternion multLocal(float qx, float qy, float qz, float qw) {
+ float x1 = x * qw + y * qz - z * qy + w * qx;
+ float y1 = -x * qz + y * qw + z * qx + w * qy;
+ float z1 = x * qy - y * qx + z * qw + w * qz;
+ w = -x * qx - y * qy - z * qz + w * qw;
+ x = x1;
+ y = y1;
+ z = z1;
+ return this;
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter vector. The
+ * result is returned as a new vector.
+ *
+ * @param v the vector to multiply this quaternion by.
+ * @param store the vector to store the result in. It IS safe for v and
+ * store to be the same object.
+ * @return the result vector.
+ */
+ public Vector3f mult(Vector3f v, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ if (v.x == 0 && v.y == 0 && v.z == 0) {
+ store.set(0, 0, 0);
+ } else {
+ float vx = v.x, vy = v.y, vz = v.z;
+ store.x = w * w * vx
+ + 2 * y * w * vz
+ - 2 * z * w * vy
+ + x * x * vx
+ + 2 * y * x * vy
+ + 2 * z * x * vz
+ - z * z * vx
+ - y * y * vx;
+ store.y = 2 * x * y * vx
+ + y * y * vy
+ + 2 * z * y * vz
+ + 2 * w * z * vx
+ - z * z * vy
+ + w * w * vy
+ - 2 * x * w * vz
+ - x * x * vy;
+ store.z = 2 * x * z * vx
+ + 2 * y * z * vy
+ + z * z * vz
+ - 2 * w * y * vx
+ - y * y * vz
+ + 2 * w * x * vy
+ - x * x * vz
+ + w * w * vz;
+ }
+ return store;
+ }
+
+ /**
+ * @return X component of vector rotated by quaternion
+ */
+ public float multX(float vx, float vy, float vz) {
+ return w * w * vx
+ + 2 * y * w * vz
+ - 2 * z * w * vy
+ + x * x * vx
+ + 2 * y * x * vy
+ + 2 * z * x * vz
+ - z * z * vx
+ - y * y * vx;
+ }
+
+ /**
+ * @return Y component of vector rotated by quaternion
+ */
+ public float multY(float vx, float vy, float vz) {
+ return 2 * x * y * vx
+ + y * y * vy
+ + 2 * z * y * vz
+ + 2 * w * z * vx
+ - z * z * vy
+ + w * w * vy
+ - 2 * x * w * vz
+ - x * x * vy;
+ }
+
+ /**
+ * @return Z component of vector rotated by quaternion
+ */
+ public float multZ(float vx, float vy, float vz) {
+ return 2 * x * z * vx
+ + 2 * y * z * vy
+ + z * z * vz
+ - 2 * w * y * vx
+ - y * y * vz
+ + 2 * w * x * vy
+ - x * x * vz
+ + w * w * vz;
+ }
+
+ /**
+ * Rotate X axis aligned vector.
+ */
+ public Vector3f multAxisX(float vx, Vector3f store) {
+ if (store == null)
+ store = new Vector3f();
+ store.x = (w * w + x * x - z * z - y * y) * vx;
+ store.y = 2f * (x * y + w * z) * vx;
+ store.z = 2f * (x * z - w * y) * vx;
+ return store;
+ }
+
+ /**
+ * Rotate Y axis aligned vector.
+ */
+ public Vector3f multAxisY(float vy, Vector3f store) {
+ if (store == null)
+ store = new Vector3f();
+ store.x = 2f * (y * x - z * w) * vy;
+ store.y = (y * y - z * z + w * w - x * x) * vy;
+ store.z = 2f * (y * z + w * x) * vy;
+ return store;
+ }
+
+ /**
+ * Rotate Z axis aligned vector.
+ */
+ public Vector3f multAxisZ(float vz, Vector3f store) {
+ if (store == null)
+ store = new Vector3f();
+ store.x = 2f * (y * w + z * x) * vz;
+ store.y = 2f * (z * y - x * w) * vz;
+ store.z = (z * z - y * y - x * x + w * w) * vz;
+ return store;
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter vector. The
+ * result is returned as a new vector.
+ *
+ * @param vx
+ * @param vy
+ * @param vz the vector to multiply this quaternion by.
+ * @param store the vector to store the result in. It IS safe for v and
+ * store to be the same object.
+ * @return the result vector.
+ */
+ public Vector3f mult(float vx, float vy, float vz, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ if (vx == 0 && vy == 0 && vz == 0) {
+ store.set(0, 0, 0);
+ } else {
+ store.x = w * w * vx
+ + 2 * y * w * vz
+ - 2 * z * w * vy
+ + x * x * vx
+ + 2 * y * x * vy
+ + 2 * z * x * vz
+ - z * z * vx
+ - y * y * vx;
+ store.y = 2 * x * y * vx
+ + y * y * vy
+ + 2 * z * y * vz
+ + 2 * w * z * vx
+ - z * z * vy
+ + w * w * vy
+ - 2 * x * w * vz
+ - x * x * vy;
+ store.z = 2 * x * z * vx
+ + 2 * y * z * vy
+ + z * z * vz
+ - 2 * w * y * vx
+ - y * y * vz
+ + 2 * w * x * vy
+ - x * x * vz
+ + w * w * vz;
+ }
+ return store;
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter scalar. The
+ * result is returned as a new quaternion.
+ *
+ * @param scalar the quaternion to multiply this quaternion by.
+ * @return the new quaternion.
+ */
+ public Quaternion mult(float scalar) {
+ return new Quaternion(scalar * x, scalar * y, scalar * z, scalar * w);
+ }
+
+ /**
+ * mult multiplies this quaternion by a parameter scalar. The
+ * result is stored locally.
+ *
+ * @param scalar the quaternion to multiply this quaternion by.
+ * @return this.
+ */
+ public Quaternion multLocal(float scalar) {
+ w *= scalar;
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+ /**
+ * dot calculates and returns the dot product of this
+ * quaternion with that of the parameter quaternion.
+ *
+ * @param q the quaternion to calculate the dot product of.
+ * @return the dot product of this and the parameter quaternion.
+ */
+ public float dot(Quaternion q) {
+ return w * q.w + x * q.x + y * q.y + z * q.z;
+ }
+
+ /**
+ * norm returns the norm of this quaternion. This is the dot
+ * product of this quaternion with itself.
+ *
+ * @return the norm of the quaternion.
+ */
+ public float norm() {
+ return w * w + x * x + y * y + z * z;
+ }
+
+ /**
+ * normalizeLocal normalizes the current
+ * Quaternion. The result is stored internally.
+ */
+ public Quaternion normalizeLocal() {
+ float n = FastMath.invSqrt(norm());
+ x *= n;
+ y *= n;
+ z *= n;
+ w *= n;
+ return this;
+ }
+
+ /**
+ * normalize returns the normalized Quaternion.
+ */
+ public Quaternion normalize() {
+ Quaternion q = this.clone();
+
+ float n = FastMath.invSqrt(q.norm());
+ q.x *= n;
+ q.y *= n;
+ q.z *= n;
+ q.w *= n;
+ return q;
+ }
+
+ /**
+ * inverse returns the inverse of this quaternion as a new
+ * quaternion. If this quaternion does not have an inverse (if its normal is
+ * 0 or less), then null is returned.
+ *
+ * @return the inverse of this quaternion or null if the inverse does not
+ * exist.
+ */
+ public Quaternion inverse() {
+ float norm = norm();
+ if (norm > 0.0) {
+ float invNorm = 1.0f / norm;
+ return new Quaternion(-x * invNorm, -y * invNorm, -z * invNorm, w * invNorm);
+ }
+ // return an invalid result to flag the error
+ return null;
+ }
+
+ /**
+ * inverse returns the inverse of this quaternion. If this
+ * quaternion does not have an inverse (if its normal is 0 or less), then
+ * null is returned.
+ *
+ * @return the inverse of this quaternion or null if the inverse does not
+ * exist.
+ */
+ public Quaternion inverse(Quaternion store) {
+ float norm = norm();
+ if (norm > 0.0) {
+ float invNorm = 1.0f / norm;
+ return store.set(-x * invNorm, -y * invNorm, -z * invNorm, w * invNorm);
+ }
+ // return an invalid result to flag the error
+ return null;
+ }
+
+ /**
+ * inverse calculates the inverse of this quaternion and
+ * returns this quaternion after it is calculated. If this quaternion does
+ * not have an inverse (if it's normal is 0 or less), nothing happens
+ *
+ * @return the inverse of this quaternion
+ */
+ public Quaternion inverseLocal() {
+ float norm = norm();
+ if (norm > 0.0) {
+ float invNorm = 1.0f / norm;
+ x *= -invNorm;
+ y *= -invNorm;
+ z *= -invNorm;
+ w *= invNorm;
+ }
+ return this;
+ }
+
+ /**
+ * negateLocal inverts the values of the quaternion and returns
+ * it.
+ */
+ public Quaternion negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ w = -w;
+ return this;
+ }
+
+ /**
+ * negate returns a negated copy of the quaternion.
+ */
+ public Quaternion negate() {
+ return new Quaternion(-x, -y, -z, -w);
+ }
+
+ /**
+ *
+ * toString creates the string representation of this
+ * Quaternion. The values of the quaternion are displaced (x,
+ * y, z, w), in the following manner:
+ * (x, y, z, w)
+ *
+ * @return the string representation of this object.
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ", " + w + ")";
+ }
+
+ /**
+ * equals determines if two quaternions are logically equal,
+ * that is, if the values of (x, y, z, w) are the same for both quaternions.
+ *
+ * @param o the object to compare for equality
+ * @return true if they are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Quaternion)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Quaternion comp = (Quaternion) o;
+ if (Float.compare(x, comp.x) != 0) {
+ return false;
+ }
+ if (Float.compare(y, comp.y) != 0) {
+ return false;
+ }
+ if (Float.compare(z, comp.z) != 0) {
+ return false;
+ }
+ if (Float.compare(w, comp.w) != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * hashCode returns the hash code value as an integer and is
+ * supported for the benefit of hashing based collection classes such as
+ * Hashtable, HashMap, HashSet etc.
+ *
+ * @return the hashcode for this instance of Quaternion.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash = 37 * hash + Float.floatToIntBits(x);
+ hash = 37 * hash + Float.floatToIntBits(y);
+ hash = 37 * hash + Float.floatToIntBits(z);
+ hash = 37 * hash + Float.floatToIntBits(w);
+ return hash;
+
+ }
+
+ /**
+ * readExternal builds a quaternion from an
+ * ObjectInput object.
+ * NOTE: Used with serialization. Not to be called manually.
+ *
+ * @param in the ObjectInput value to read from.
+ * @throws IOException if the ObjectInput value has problems reading a
+ * float.
+ * @see java.io.Externalizable
+ */
+ public void readExternal(ObjectInput in) throws IOException {
+ x = in.readFloat();
+ y = in.readFloat();
+ z = in.readFloat();
+ w = in.readFloat();
+ }
+
+ /**
+ * writeExternal writes this quaternion out to a
+ * ObjectOutput object. NOTE: Used with serialization. Not to
+ * be called manually.
+ *
+ * @param out the object to write to.
+ * @throws IOException if writing to the ObjectOutput fails.
+ * @see java.io.Externalizable
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeFloat(x);
+ out.writeFloat(y);
+ out.writeFloat(z);
+ out.writeFloat(w);
+ }
+
+ /**
+ * lookAt is a convienence method for auto-setting the
+ * quaternion based on a direction and an up vector. It computes the
+ * rotation to transform the z-axis to point into 'direction' and the y-axis
+ * to 'up'.
+ *
+ * @param direction where to look at in terms of local coordinates
+ * @param up a vector indicating the local up direction. (typically {0, 1,
+ * 0} in jME.)
+ */
+ public void lookAt(Vector3f direction, Vector3f up) {
+ TempVars vars = TempVars.get();
+ vars.vect3.set(direction).normalizeLocal();
+ vars.vect1.set(up).crossLocal(direction).normalizeLocal();
+ vars.vect2.set(direction).crossLocal(vars.vect1).normalizeLocal();
+ fromAxes(vars.vect1, vars.vect2, vars.vect3);
+ vars.release();
+ }
+
+ /**
+ * @return A new quaternion that describes a rotation that would point you
+ * in the exact opposite direction of this Quaternion.
+ */
+ public Quaternion opposite() {
+ return opposite(null);
+ }
+
+ /**
+ * FIXME: This seems to have singularity type issues with angle == 0,
+ * possibly others such as PI.
+ *
+ * @param store A Quaternion to store our result in. If null, a new one is
+ * created.
+ * @return The store quaternion (or a new Quaterion, if store is null) that
+ * describes a rotation that would point you in the exact opposite direction
+ * of this Quaternion.
+ */
+ public Quaternion opposite(Quaternion store) {
+ if (store == null) {
+ store = new Quaternion();
+ }
+
+ Vector3f axis = new Vector3f();
+ float angle = toAngleAxis(axis);
+
+ store.fromAngleAxis(FastMath.PI + angle, axis);
+ return store;
+ }
+
+ /**
+ * @return This Quaternion, altered to describe a rotation that would point
+ * you in the exact opposite direction of where it is pointing currently.
+ */
+ public Quaternion oppositeLocal() {
+ return opposite(this);
+ }
+
+ @Override
+ public Quaternion clone() {
+ try {
+ return (Quaternion) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Sets this quaternion to be a rotation from vec1 to vec2.
+ *
+ * Based on implementation from here:
+ * https://github.com/toji/gl-matrix/blob/f0583ef53e94bc7e78b78c8a24f09ed5e2f7a20c/src/gl-matrix/quat.js#L54
+ *
+ * @param vec1
+ * @param vec2
+ * @return this quaternion
+ */
+ public Quaternion angleBetweenVectors(Vector3f vec1, Vector3f vec2) {
+ float dot = vec1.dot(vec2);
+ if (FloatMath.lessOrEqualsWithEpsilon(dot, -1)) {
+ Vector3f cross = vec1.cross(Vector3f.UNIT_X);
+ if (FloatMath.lessOrEqualsToZero(cross.length()))
+ cross = vec1.cross(Vector3f.UNIT_Y);
+ cross.normalizeLocal();
+ fromAngleAxis(FloatMath.PI, cross);
+ } else if (FloatMath.greaterOrEqualsWithEpsilon(dot, 1)) {
+ loadIdentity();
+ } else {
+ Vector3f cross = vec1.cross(vec2);
+ x = cross.x;
+ y = cross.y;
+ z = cross.z;
+ w = 1 + dot;
+ normalizeLocal();
+ }
+ return this;
+ }
+
+ public static boolean isIdentity(Quaternion q) {
+ if (Float.compare(q.x, 0) != 0) {
+ return false;
+ }
+ if (Float.compare(q.y, 0) != 0) {
+ return false;
+ }
+ if (Float.compare(q.z, 0) != 0) {
+ return false;
+ }
+ if (Float.compare(q.w, 1) != 0) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/com/jme3/math/Transform.java b/java/com/jme3/math/Transform.java
new file mode 100644
index 000000000..f6cac7b5f
--- /dev/null
+++ b/java/com/jme3/math/Transform.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2009-2012 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;
+
+/**
+ * Started Date: Jul 16, 2004
+ *
+ * Represents a translation, rotation and scale in one object.
+ *
+ * @author Jack Lindamood
+ * @author Joshua Slack
+ */
+public final class Transform implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ public static final Transform IDENTITY = new Transform();
+
+ private Quaternion rot = new Quaternion();
+ private Vector3f translation = new Vector3f();
+ private Vector3f scale = new Vector3f(1, 1, 1);
+
+ public Transform(Vector3f translation, Quaternion rot) {
+ this.translation.set(translation);
+ this.rot.set(rot);
+ }
+
+ public Transform(Vector3f translation, Quaternion rot, Vector3f scale) {
+ this(translation, rot);
+ this.scale.set(scale);
+ }
+
+ public Transform(Vector3f translation) {
+ this(translation, Quaternion.IDENTITY);
+ }
+
+ public Transform(Quaternion rot) {
+ this(Vector3f.ZERO, rot);
+ }
+
+ public Transform() {
+ this(Vector3f.ZERO, Quaternion.IDENTITY);
+ }
+
+ /**
+ * Sets this rotation to the given Quaternion value.
+ *
+ * @param rot The new rotation for this matrix.
+ * @return this
+ */
+ public Transform setRotation(Quaternion rot) {
+ this.rot.set(rot);
+ return this;
+ }
+
+ /**
+ * Sets this translation to the given value.
+ *
+ * @param trans The new translation for this matrix.
+ * @return this
+ */
+ public Transform setTranslation(Vector3f trans) {
+ this.translation.set(trans);
+ return this;
+ }
+
+ /**
+ * Return the translation vector in this matrix.
+ *
+ * @return translation vector.
+ */
+ public Vector3f getTranslation() {
+ return translation;
+ }
+
+ /**
+ * Sets this scale to the given value.
+ *
+ * @param scale The new scale for this matrix.
+ * @return this
+ */
+ public Transform setScale(Vector3f scale) {
+ this.scale.set(scale);
+ return this;
+ }
+
+ /**
+ * Sets this scale to the given value.
+ *
+ * @param scale The new scale for this matrix.
+ * @return this
+ */
+ public Transform setScale(float scale) {
+ this.scale.set(scale, scale, scale);
+ return this;
+ }
+
+ /**
+ * Return the scale vector in this matrix.
+ *
+ * @return scale vector.
+ */
+ public Vector3f getScale() {
+ return scale;
+ }
+
+ /**
+ * Stores this translation value into the given vector3f. If trans is null,
+ * a new vector3f is created to hold the value. The value, once stored, is
+ * returned.
+ *
+ * @param trans The store location for this matrix's translation.
+ * @return The value of this matrix's translation.
+ */
+ public Vector3f getTranslation(Vector3f trans) {
+ if (trans == null)
+ trans = new Vector3f();
+ trans.set(this.translation);
+ return trans;
+ }
+
+ /**
+ * Stores this rotation value into the given Quaternion. If quat is null, a
+ * new Quaternion is created to hold the value. The value, once stored, is
+ * returned.
+ *
+ * @param quat The store location for this matrix's rotation.
+ * @return The value of this matrix's rotation.
+ */
+ public Quaternion getRotation(Quaternion quat) {
+ if (quat == null)
+ quat = new Quaternion();
+ quat.set(rot);
+ return quat;
+ }
+
+ /**
+ * Return the rotation quaternion in this matrix.
+ *
+ * @return rotation quaternion.
+ */
+ public Quaternion getRotation() {
+ return rot;
+ }
+
+ /**
+ * Stores this scale value into the given vector3f. If scale is null, a new
+ * vector3f is created to hold the value. The value, once stored, is
+ * returned.
+ *
+ * @param scale The store location for this matrix's scale.
+ * @return The value of this matrix's scale.
+ */
+ public Vector3f getScale(Vector3f scale) {
+ if (scale == null)
+ scale = new Vector3f();
+ scale.set(this.scale);
+ return scale;
+ }
+
+ /**
+ * Sets this matrix to the interpolation between the first matrix and the
+ * second by delta amount.
+ *
+ * @param t1 The begining transform.
+ * @param t2 The ending transform.
+ * @param delta An amount between 0 and 1 representing how far to
+ * interpolate from t1 to t2.
+ */
+ public void interpolateTransforms(Transform t1, Transform t2, float delta) {
+ this.rot.slerp(t1.rot, t2.rot, delta);
+ this.translation.interpolate(t1.translation, t2.translation, delta);
+ this.scale.interpolate(t1.scale, t2.scale, delta);
+ }
+
+ /**
+ * Changes the values of this matrix acording to it's parent. Very similar
+ * to the concept of Node/Spatial transforms.
+ *
+ * @param parent The parent matrix.
+ * @return This matrix, after combining.
+ */
+ public Transform combineWithParent(Transform parent) {
+ scale.multLocal(parent.scale);
+// rot.multLocal(parent.rot);
+ parent.rot.mult(rot, rot);
+
+ // This here, is evil code
+// parent
+// .rot
+// .multLocal(translation)
+// .multLocal(parent.scale)
+// .addLocal(parent.translation);
+
+ translation.multLocal(parent.scale);
+ parent.rot
+ .multLocal(translation)
+ .addLocal(parent.translation);
+ return this;
+ }
+
+ /**
+ * Same as {@link #combineWithParent(Transform)}, but assumes that rotation
+ * is global, so it's not modified.
+ *
+ * @param parent
+ * @return
+ */
+ public Transform combineWithParentGlobalRotation(Transform parent) {
+ scale.multLocal(parent.scale);
+ translation.multLocal(parent.scale);
+
+ parent.rot
+ .multLocal(translation)
+ .addLocal(parent.translation);
+ return this;
+ }
+
+ /**
+ * Sets this matrix's translation to the given x,y,z values.
+ *
+ * @param x This matrix's new x translation.
+ * @param y This matrix's new y translation.
+ * @param z This matrix's new z translation.
+ * @return this
+ */
+ public Transform setTranslation(float x, float y, float z) {
+ translation.set(x, y, z);
+ return this;
+ }
+
+ /**
+ * Sets this matrix's scale to the given x,y,z values.
+ *
+ * @param x This matrix's new x scale.
+ * @param y This matrix's new y scale.
+ * @param z This matrix's new z scale.
+ * @return this
+ */
+ public Transform setScale(float x, float y, float z) {
+ scale.set(x, y, z);
+ return this;
+ }
+
+ 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);
+ }
+
+ public Vector3f transformInverseVector(final Vector3f in, Vector3f store) {
+ if (store == null)
+ store = new Vector3f();
+
+ // The author of this code should look above and take the inverse of
+ // that
+ // But for some reason, they didnt ..
+// 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;
+ }
+
+ /**
+ * Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
+ */
+ public void loadIdentity() {
+ translation.set(0, 0, 0);
+ scale.set(1, 1, 1);
+ rot.set(0, 0, 0, 1);
+ }
+
+ @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
+ + "]";
+ }
+
+ /**
+ * Sets this matrix to be equal to the given matrix.
+ *
+ * @param matrixQuat The matrix to be equal to.
+ * @return this
+ */
+ public Transform set(Transform matrixQuat) {
+ this.translation.set(matrixQuat.translation);
+ this.rot.set(matrixQuat.rot);
+ this.scale.set(matrixQuat.scale);
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((rot == null) ? 0 : rot.hashCode());
+ result = prime * result + ((scale == null) ? 0 : scale.hashCode());
+ result = prime * result + ((translation == null) ? 0 : translation.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Transform other = (Transform) obj;
+ if (rot == null) {
+ if (other.rot != null)
+ return false;
+ } else if (!rot.equals(other.rot))
+ return false;
+ if (scale == null) {
+ if (other.scale != null)
+ return false;
+ } else if (!scale.equals(other.scale))
+ return false;
+ if (translation == null) {
+ if (other.translation != null)
+ return false;
+ } else if (!translation.equals(other.translation))
+ return false;
+ return true;
+ }
+
+ @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();
+ }
+ }
+}
diff --git a/java/com/jme3/math/Vector2f.java b/java/com/jme3/math/Vector2f.java
new file mode 100644
index 000000000..00b7805c1
--- /dev/null
+++ b/java/com/jme3/math/Vector2f.java
@@ -0,0 +1,718 @@
+/*
+ * Copyright (c) 2009-2012 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 java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.logging.Logger;
+
+
+/**
+ * Vector2f defines a Vector for a two float value vector.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Vector2f implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+ private static final Logger logger = Logger.getLogger(Vector2f.class.getName());
+
+ public static final Vector2f ZERO = new Vector2f(0f, 0f);
+ public static final Vector2f UNIT_XY = new Vector2f(1f, 1f);
+
+ /**
+ * the x value of the vector.
+ */
+ public float x;
+ /**
+ * the y value of the vector.
+ */
+ public float y;
+
+ /**
+ * Creates a Vector2f with the given initial x and y values.
+ *
+ * @param x The x value of this Vector2f.
+ * @param y The y value of this Vector2f.
+ */
+ public Vector2f(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0).
+ */
+ public Vector2f() {
+ x = y = 0;
+ }
+
+ /**
+ * Creates a new Vector2f that contains the passed vector's information
+ *
+ * @param vector2f The vector to copy
+ */
+ public Vector2f(Vector2f vector2f) {
+ this.x = vector2f.x;
+ this.y = vector2f.y;
+ }
+
+ /**
+ * set the x and y values of the vector
+ *
+ * @param x the x value of the vector.
+ * @param y the y value of the vector.
+ * @return this vector
+ */
+ public Vector2f set(float x, float y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ /**
+ * set the x and y values of the vector from another vector
+ *
+ * @param vec the vector to copy from
+ * @return this vector
+ */
+ public Vector2f set(Vector2f vec) {
+ this.x = vec.x;
+ this.y = vec.y;
+ return this;
+ }
+
+ /**
+ * add adds a provided vector to this vector creating a
+ * resultant vector which is returned. If the provided vector is null, null
+ * is returned.
+ *
+ * @param vec the vector to add to this.
+ * @return the resultant vector.
+ */
+ public Vector2f add(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return new Vector2f(x + vec.x, y + vec.y);
+ }
+
+ /**
+ * addLocal adds a provided vector to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls. If the
+ * provided vector is null, null is returned.
+ *
+ * @param vec the vector to add to this vector.
+ * @return this
+ */
+ public Vector2f addLocal(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x += vec.x;
+ y += vec.y;
+ return this;
+ }
+
+ /**
+ * addLocal adds the provided values to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param addX value to add to x
+ * @param addY value to add to y
+ * @return this
+ */
+ public Vector2f addLocal(float addX, float addY) {
+ x += addX;
+ y += addY;
+ return this;
+ }
+
+ /**
+ * add adds this vector by vec and stores the
+ * result in result.
+ *
+ * @param vec The vector to add.
+ * @param result The vector to store the result in.
+ * @return The result vector, after adding.
+ */
+ public Vector2f add(Vector2f vec, Vector2f result) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ if (result == null)
+ result = new Vector2f();
+ result.x = x + vec.x;
+ result.y = y + vec.y;
+ return result;
+ }
+
+ /**
+ * dot calculates the dot product of this vector with a
+ * provided vector. If the provided vector is null, 0 is returned.
+ *
+ * @param vec the vector to dot with this vector.
+ * @return the resultant dot product of this vector and a given vector.
+ */
+ public float dot(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, 0 returned.");
+ return 0;
+ }
+ return x * vec.x + y * vec.y;
+ }
+
+ /**
+ * cross calculates the cross product of this vector with a
+ * parameter vector v.
+ *
+ * @param v the vector to take the cross product of with this.
+ * @return the cross product vector.
+ */
+ public Vector3f cross(Vector2f v) {
+ return new Vector3f(0, 0, determinant(v));
+ }
+
+ public float determinant(Vector2f v) {
+ return (x * v.y) - (y * v.x);
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from this to the
+ * finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
+ *
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
+ * change from this towards finalVec
+ */
+ public Vector2f interpolate(Vector2f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
+ return this;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from beginVec to
+ * finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
+ *
+ * @param beginVec The begining vector (delta=0)
+ * @param finalVec The final vector to interpolate towards (delta=1)
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from beginVec towards finalVec
+ */
+ public Vector2f interpolate(
+ Vector2f beginVec,
+ Vector2f finalVec,
+ float changeAmnt
+ ) {
+ this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
+ return this;
+ }
+
+ /**
+ * Check a vector... if it is null or its floats are NaN or infinite, return
+ * false. Else return true.
+ *
+ * @param vector the vector to check
+ * @return true or false as stated above.
+ */
+ public static boolean isValidVector(Vector2f vector) {
+ if (vector == null)
+ return false;
+ if (
+ Float.isNaN(vector.x)
+ ||
+ Float.isNaN(vector.y)
+ )
+ return false;
+ if (
+ Float.isInfinite(vector.x)
+ ||
+ Float.isInfinite(vector.y)
+ )
+ return false;
+ return true;
+ }
+
+ /**
+ * length calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * lengthSquared calculates the squared value of the magnitude
+ * of the vector.
+ *
+ * @return the magnitude squared of the vector.
+ */
+ public float lengthSquared() {
+ return x * x + y * y;
+ }
+
+ /**
+ * distanceSquared calculates the distance squared between this
+ * vector and vector v.
+ *
+ * @param v the second vector to determine the distance squared.
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(Vector2f v) {
+ double dx = x - v.x;
+ double dy = y - v.y;
+ return (float) (dx * dx + dy * dy);
+ }
+
+ /**
+ * distanceSquared calculates the distance squared between this
+ * vector and vector v.
+ *
+ * @param otherX The X coordinate of the v vector
+ * @param otherY The Y coordinate of the v vector
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(float otherX, float otherY) {
+ double dx = x - otherX;
+ double dy = y - otherY;
+ return (float) (dx * dx + dy * dy);
+ }
+
+ /**
+ * distance calculates the distance between this vector and
+ * vector v.
+ *
+ * @param v the second vector to determine the distance.
+ * @return the distance between the two vectors.
+ */
+ public float distance(Vector2f v) {
+ return FastMath.sqrt(distanceSquared(v));
+ }
+
+ /**
+ * mult multiplies this vector by a scalar. The resultant
+ * vector is returned.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @return the new vector.
+ */
+ public Vector2f mult(float scalar) {
+ return new Vector2f(x * scalar, y * scalar);
+ }
+
+ /**
+ * multLocal multiplies this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @return this
+ */
+ public Vector2f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to mult to this vector.
+ * @return this
+ */
+ public Vector2f multLocal(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x *= vec.x;
+ y *= vec.y;
+ return this;
+ }
+
+ /**
+ * Multiplies this Vector2f's x and y by the scalar and stores the result in
+ * product. The result is returned for chaining. Similar to
+ * product=this*scalar;
+ *
+ * @param scalar The scalar to multiply by.
+ * @param product The vector2f to store the result in.
+ * @return product, after multiplication.
+ */
+ public Vector2f mult(float scalar, Vector2f product) {
+ if (null == product) {
+ product = new Vector2f();
+ }
+
+ product.x = x * scalar;
+ product.y = y * scalar;
+ return product;
+ }
+
+ /**
+ * divide divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar the value to divide this vectors attributes by.
+ * @return the result Vector.
+ */
+ public Vector2f divide(float scalar) {
+ return new Vector2f(x / scalar, y / scalar);
+ }
+
+ /**
+ * divideLocal divides this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls. Dividing by
+ * zero will result in an exception.
+ *
+ * @param scalar the value to divides this vector by.
+ * @return this
+ */
+ public Vector2f divideLocal(float scalar) {
+ x /= scalar;
+ y /= scalar;
+ return this;
+ }
+
+ /**
+ * negate returns the negative of this vector. All values are
+ * negated and set to a new vector.
+ *
+ * @return the negated vector.
+ */
+ public Vector2f negate() {
+ return new Vector2f(-x, -y);
+ }
+
+ /**
+ * negateLocal negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector2f negateLocal() {
+ x = -x;
+ y = -y;
+ return this;
+ }
+
+ /**
+ * subtract subtracts the values of a given vector from those
+ * of this vector creating a new vector object. If the provided vector is
+ * null, an exception is thrown.
+ *
+ * @param vec the vector to subtract from this vector.
+ * @return the result vector.
+ */
+ public Vector2f subtract(Vector2f vec) {
+ return subtract(vec, null);
+ }
+
+ /**
+ * subtract subtracts the values of a given vector from those
+ * of this vector storing the result in the given vector object. If the
+ * provided vector is null, an exception is thrown.
+ *
+ * @param vec the vector to subtract from this vector.
+ * @param store the vector to store the result in. It is safe for this to be
+ * the same as vec. If null, a new vector is created.
+ * @return the result vector.
+ */
+ public Vector2f subtract(Vector2f vec, Vector2f store) {
+ if (store == null)
+ store = new Vector2f();
+ store.x = x - vec.x;
+ store.y = y - vec.y;
+ return store;
+ }
+
+ /**
+ * subtract subtracts the given x,y values from those of this
+ * vector creating a new vector object.
+ *
+ * @param valX value to subtract from x
+ * @param valY value to subtract from y
+ * @return this
+ */
+ public Vector2f subtract(float valX, float valY) {
+ return new Vector2f(x - valX, y - valY);
+ }
+
+ /**
+ * subtractLocal subtracts a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to subtract
+ * @return this
+ */
+ public Vector2f subtractLocal(Vector2f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x -= vec.x;
+ y -= vec.y;
+ return this;
+ }
+
+ /**
+ * subtractLocal subtracts the provided values from this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param valX value to subtract from x
+ * @param valY value to subtract from y
+ * @return this
+ */
+ public Vector2f subtractLocal(float valX, float valY) {
+ x -= valX;
+ y -= valY;
+ return this;
+ }
+
+ /**
+ * normalize returns the unit vector of this vector.
+ *
+ * @return unit vector of this vector.
+ */
+ public Vector2f normalize() {
+ float length = length();
+ if (length != 0) {
+ return divide(length);
+ }
+
+ return divide(1);
+ }
+
+ /**
+ * normalizeLocal makes this vector into a unit vector of
+ * itself.
+ *
+ * @return this.
+ */
+ public Vector2f normalizeLocal() {
+ float length = length();
+ if (length != 0) {
+ return divideLocal(length);
+ }
+
+ return divideLocal(1);
+ }
+
+ /**
+ * smallestAngleBetween returns (in radians) the minimum angle
+ * between two vectors. It is assumed that both this vector and the given
+ * vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector a unit vector to find the angle against
+ * @return the angle in radians.
+ */
+ public float smallestAngleBetween(Vector2f otherVector) {
+ float dotProduct = dot(otherVector);
+ float angle = FastMath.acos(dotProduct);
+ return angle;
+ }
+
+ /**
+ * angleBetween returns (in radians) the angle required to
+ * rotate a ray represented by this vector to lie colinear to a ray
+ * described by the given vector. It is assumed that both this vector and
+ * the given vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector the "destination" unit vector
+ * @return the angle in radians.
+ */
+ public float angleBetween(Vector2f otherVector) {
+ float angle = FastMath.atan2(otherVector.y, otherVector.x)
+ - FastMath.atan2(y, x);
+ return angle;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector2f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector2f setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ /**
+ * getAngle returns (in radians) the angle represented by this
+ * Vector2f as expressed by a conversion from rectangular coordinates
+ * (x, y) to polar coordinates
+ * (r, theta).
+ *
+ * @return the angle in radians. [-pi, pi)
+ */
+ public float getAngle() {
+ return FastMath.atan2(y, x);
+ }
+
+ /**
+ * zero resets this vector's data to zero internally.
+ */
+ public Vector2f zero() {
+ x = y = 0;
+ return this;
+ }
+
+ /**
+ * hashCode returns a unique code for this vector object based
+ * on it's values. If two vectors are logically equivalent, they will return
+ * the same hash code value.
+ *
+ * @return the hash code value of this vector.
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(x);
+ hash += 37 * hash + Float.floatToIntBits(y);
+ return hash;
+ }
+
+ @Override
+ public Vector2f clone() {
+ try {
+ return (Vector2f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this Vector2f into the given float[] object.
+ *
+ * @param floats The float[] to take this Vector2f. If null, a new float[2]
+ * is created.
+ * @return The array, with X, Y float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[2];
+ }
+ floats[0] = x;
+ floats[1] = y;
+ return floats;
+ }
+
+ /**
+ * are these two vectors the same? they are is they both have the same x and
+ * y values.
+ *
+ * @param o the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Vector2f)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Vector2f comp = (Vector2f) o;
+ if (Float.compare(x, comp.x) != 0)
+ return false;
+ if (Float.compare(y, comp.y) != 0)
+ return false;
+ return true;
+ }
+
+ /**
+ * toString returns the string representation of this vector
+ * object. The format of the string is such: com.jme.math.Vector2f
+ * [X=XX.XXXX, Y=YY.YYYY]
+ *
+ * @return the string representation of this vector.
+ */
+ @Override
+ public String toString() {
+ return "(" + x + ", " + y + ")";
+ }
+
+ /**
+ * Used with serialization. Not to be called manually.
+ *
+ * @param in ObjectInput
+ * @throws IOException
+ * @throws ClassNotFoundException
+ * @see java.io.Externalizable
+ */
+ public void readExternal(ObjectInput in) throws IOException,
+ ClassNotFoundException {
+ x = in.readFloat();
+ y = in.readFloat();
+ }
+
+ /**
+ * Used with serialization. Not to be called manually.
+ *
+ * @param out ObjectOutput
+ * @throws IOException
+ * @see java.io.Externalizable
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeFloat(x);
+ out.writeFloat(y);
+ }
+
+ public void rotateAroundOrigin(float angle, boolean cw) {
+ if (cw)
+ angle = -angle;
+ float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y;
+ float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y;
+ x = newX;
+ y = newY;
+ }
+}
diff --git a/java/com/jme3/math/Vector3f.java b/java/com/jme3/math/Vector3f.java
new file mode 100644
index 000000000..2d764e4ac
--- /dev/null
+++ b/java/com/jme3/math/Vector3f.java
@@ -0,0 +1,1101 @@
+/*
+ * Copyright (c) 2009-2012 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 java.util.logging.Logger;
+
+import io.eiren.math.Vector3d;
+
+/*
+ * -- Added *Local methods to cut down on object creation - JS
+ */
+
+
+/**
+ * Vector3f defines a Vector for a three float value tuple.
+ * Vector3f can represent any three dimensional value, such as a
+ * vertex, a normal, etc. Utility methods are also included to aid in
+ * mathematical calculations.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Vector3f implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Vector3f.class.getName());
+
+ public final static Vector3f ZERO = new Vector3f(0, 0, 0);
+ public final static Vector3f NAN = new Vector3f(Float.NaN, Float.NaN, Float.NaN);
+ public final static Vector3f UNIT_X = new Vector3f(1, 0, 0);
+ public final static Vector3f UNIT_Y = new Vector3f(0, 1, 0);
+ public final static Vector3f UNIT_X_Y = new Vector3f(1, 1, 0).normalizeLocal();
+ public final static Vector3f UNIT_Z = new Vector3f(0, 0, 1);
+ public final static Vector3f UNIT_X_Z = new Vector3f(1, 0, 1).normalizeLocal();
+ public final static Vector3f UNIT_Y_Z = new Vector3f(0, 1, 1).normalizeLocal();
+ public final static Vector3f UNIT_X_Y_Z = new Vector3f(1, 1, 1).normalizeLocal();
+
+
+ public final static Vector3f NEGATIVE_UNIT_X = new Vector3f(-1, 0, 0);
+ public final static Vector3f NEGATIVE_UNIT_Y = new Vector3f(0, -1, 0);
+ public final static Vector3f NEGATIVE_UNIT_Z = new Vector3f(0, 0, -1);
+
+ public final static Vector3f UNIT_XYZ = new Vector3f(1, 1, 1);
+ public final static Vector3f POSITIVE_INFINITY = new Vector3f(
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY
+ );
+ public final static Vector3f NEGATIVE_INFINITY = new Vector3f(
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY
+ );
+
+ public final static Vector3f UNIT_PX_PY_PZ = UNIT_X_Y_Z;
+ public final static Vector3f UNIT_NX_PY_PZ = new Vector3f(-1, 1, 1).normalizeLocal();
+ public final static Vector3f UNIT_PX_NY_PZ = new Vector3f(1, -1, 1).normalizeLocal();
+ public final static Vector3f UNIT_NX_NY_PZ = new Vector3f(-1, -1, 1).normalizeLocal();
+ public final static Vector3f UNIT_PX_PY_NZ = new Vector3f(1, 1, -1).normalizeLocal();
+ public final static Vector3f UNIT_NX_PY_NZ = new Vector3f(-1, 1, -1).normalizeLocal();
+ public final static Vector3f UNIT_PX_NY_NZ = new Vector3f(1, -1, -1).normalizeLocal();
+ public final static Vector3f UNIT_NX_NY_NZ = new Vector3f(-1, -1, -1).normalizeLocal();
+
+ public final static Vector3f UNIT_PX_PY_0 = UNIT_X_Y;
+ public final static Vector3f UNIT_PX_NY_0 = new Vector3f(1, -1, 0).normalizeLocal();
+ public final static Vector3f UNIT_NX_PY_0 = new Vector3f(-1, 1, 0).normalizeLocal();
+ public final static Vector3f UNIT_NX_NY_0 = new Vector3f(-1, -1, 0).normalizeLocal();
+
+ public final static Vector3f UNIT_PX_0_PZ = UNIT_X_Z;
+ public final static Vector3f UNIT_PX_0_NZ = new Vector3f(1, 0, -1).normalizeLocal();
+ public final static Vector3f UNIT_NX_0_PZ = new Vector3f(-1, 0, 1).normalizeLocal();
+ public final static Vector3f UNIT_NX_0_NZ = new Vector3f(-1, 0, -1).normalizeLocal();
+
+ public final static Vector3f UNIT_0_PY_PZ = UNIT_Y_Z;
+ public final static Vector3f UNIT_0_PY_NZ = new Vector3f(0, 1, -1).normalizeLocal();
+ public final static Vector3f UNIT_0_NY_PZ = new Vector3f(0, -1, 1).normalizeLocal();
+ public final static Vector3f UNIT_0_NY_NZ = new Vector3f(0, -1, -1).normalizeLocal();
+
+
+ /**
+ * the x value of the vector.
+ */
+ public float x;
+
+ /**
+ * the y value of the vector.
+ */
+ public float y;
+
+ /**
+ * the z value of the vector.
+ */
+ public float z;
+
+ /**
+ * Constructor instantiates a new Vector3f with default values
+ * of (0,0,0).
+ *
+ */
+ public Vector3f() {
+ x = y = z = 0;
+ }
+
+ /**
+ * Constructor instantiates a new Vector3f with provides
+ * values.
+ *
+ * @param x the x value of the vector.
+ * @param y the y value of the vector.
+ * @param z the z value of the vector.
+ */
+ public Vector3f(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /**
+ * Constructor instantiates a new Vector3f that is a copy of
+ * the provided vector
+ *
+ * @param copy The Vector3f to copy
+ */
+ public Vector3f(Vector3f copy) {
+ this.set(copy);
+ }
+
+ public Vector3f(Vector3d copy) {
+ this.set((float) copy.x, (float) copy.y, (float) copy.z);
+ }
+
+ /**
+ * set sets the x,y,z values of the vector based on passed
+ * parameters.
+ *
+ * @param x the x value of the vector.
+ * @param y the y value of the vector.
+ * @param z the z value of the vector.
+ * @return this vector
+ */
+ public Vector3f set(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ /**
+ * set sets the x,y,z values of the vector by copying the
+ * supplied vector.
+ *
+ * @param vect the vector to copy.
+ * @return this vector
+ */
+ public Vector3f set(Vector3f vect) {
+ this.x = vect.x;
+ this.y = vect.y;
+ this.z = vect.z;
+ return this;
+ }
+
+ /**
+ *
+ * add adds a provided vector to this vector creating a
+ * resultant vector which is returned. If the provided vector is null, null
+ * is returned.
+ *
+ * @param vec the vector to add to this.
+ * @return the resultant vector.
+ */
+ public Vector3f add(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return new Vector3f(x + vec.x, y + vec.y, z + vec.z);
+ }
+
+ /**
+ *
+ * add adds the values of a provided vector storing the values
+ * in the supplied vector.
+ *
+ * @param vec the vector to add to this
+ * @param result the vector to store the result in
+ * @return result returns the supplied result vector.
+ */
+ public Vector3f add(Vector3f vec, Vector3f result) {
+ result.x = x + vec.x;
+ result.y = y + vec.y;
+ result.z = z + vec.z;
+ return result;
+ }
+
+ /**
+ * addLocal adds a provided vector to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls. If the
+ * provided vector is null, null is returned.
+ *
+ * @param vec the vector to add to this vector.
+ * @return this
+ */
+ public Vector3f addLocal(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x += vec.x;
+ y += vec.y;
+ z += vec.z;
+ return this;
+ }
+
+ /**
+ *
+ * add adds the provided values to this vector, creating a new
+ * vector that is then returned.
+ *
+ * @param addX the x value to add.
+ * @param addY the y value to add.
+ * @param addZ the z value to add.
+ * @return the result vector.
+ */
+ public Vector3f add(float addX, float addY, float addZ) {
+ return new Vector3f(x + addX, y + addY, z + addZ);
+ }
+
+ /**
+ *
+ * add adds the provided values to this vector, creating a new
+ * vector that is then returned.
+ *
+ * @param addX the x value to add.
+ * @param addY the y value to add.
+ * @param addZ the z value to add.
+ * @param store the vector object to store the result in. if null, a new one
+ * is created.
+ * @return the result vector.
+ */
+ public Vector3f add(float addX, float addY, float addZ, Vector3f store) {
+ if (store == null) {
+ return new Vector3f(x + addX, y + addY, z + addZ);
+ } else {
+ return store.set(x + addX, y + addY, z + addZ);
+ }
+ }
+
+ /**
+ * addLocal adds the provided values to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param addX value to add to x
+ * @param addY value to add to y
+ * @param addZ value to add to z
+ * @return this
+ */
+ public Vector3f addLocal(float addX, float addY, float addZ) {
+ x += addX;
+ y += addY;
+ z += addZ;
+ return this;
+ }
+
+ /**
+ *
+ * scaleAdd multiplies this vector by a scalar then adds the
+ * given Vector3f.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @param add the value to add
+ */
+ public Vector3f scaleAdd(float scalar, Vector3f add) {
+ x = x * scalar + add.x;
+ y = y * scalar + add.y;
+ z = z * scalar + add.z;
+ return this;
+ }
+
+ /**
+ *
+ * scaleAdd multiplies the given vector by a scalar then adds
+ * the given vector.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @param mult the value to multiply the scalar by
+ * @param add the value to add
+ */
+ public Vector3f scaleAdd(float scalar, Vector3f mult, Vector3f add) {
+ this.x = mult.x * scalar + add.x;
+ this.y = mult.y * scalar + add.y;
+ this.z = mult.z * scalar + add.z;
+ return this;
+ }
+
+ /**
+ *
+ * dot calculates the dot product of this vector with a
+ * provided vector. If the provided vector is null, 0 is returned.
+ *
+ * @param vec the vector to dot with this vector.
+ * @return the resultant dot product of this vector and a given vector.
+ */
+ public float dot(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, 0 returned.");
+ return 0;
+ }
+ return x * vec.x + y * vec.y + z * vec.z;
+ }
+
+ public float dot(float vx, float vy, float vz) {
+ return x * vx + y * vy + z * vz;
+ }
+
+ /**
+ * cross calculates the cross product of this vector with a
+ * parameter vector v.
+ *
+ * @param v the vector to take the cross product of with this.
+ * @return the cross product vector.
+ */
+ public Vector3f cross(Vector3f v) {
+ return cross(v, null);
+ }
+
+ /**
+ * cross calculates the cross product of this vector with a
+ * parameter vector v. The result is stored in result
+ *
+ * @param v the vector to take the cross product of with this.
+ * @param result the vector to store the cross product result.
+ * @return result, after recieving the cross product vector.
+ */
+ public Vector3f cross(Vector3f v, Vector3f result) {
+ return cross(v.x, v.y, v.z, result);
+ }
+
+ /**
+ * cross calculates the cross product of this vector with a
+ * parameter vector v. The result is stored in result
+ *
+ * @param otherX x component of the vector to take the cross product of with
+ * this.
+ * @param otherY y component of the vector to take the cross product of with
+ * this.
+ * @param otherZ z component of the vector to take the cross product of with
+ * this.
+ * @param result the vector to store the cross product result.
+ * @return result, after recieving the cross product vector.
+ */
+ public Vector3f cross(float otherX, float otherY, float otherZ, Vector3f result) {
+ if (result == null)
+ result = new Vector3f();
+ float resX = ((y * otherZ) - (z * otherY));
+ float resY = ((z * otherX) - (x * otherZ));
+ float resZ = ((x * otherY) - (y * otherX));
+ result.set(resX, resY, resZ);
+ return result;
+ }
+
+ /**
+ * crossLocal calculates the cross product of this vector with
+ * a parameter vector v.
+ *
+ * @param v the vector to take the cross product of with this.
+ * @return this.
+ */
+ public Vector3f crossLocal(Vector3f v) {
+ return crossLocal(v.x, v.y, v.z);
+ }
+
+ /**
+ * crossLocal calculates the cross product of this vector with
+ * a parameter vector v.
+ *
+ * @param otherX x component of the vector to take the cross product of with
+ * this.
+ * @param otherY y component of the vector to take the cross product of with
+ * this.
+ * @param otherZ z component of the vector to take the cross product of with
+ * this.
+ * @return this.
+ */
+ public Vector3f crossLocal(float otherX, float otherY, float otherZ) {
+ float tempx = (y * otherZ) - (z * otherY);
+ float tempy = (z * otherX) - (x * otherZ);
+ z = (x * otherY) - (y * otherX);
+ x = tempx;
+ y = tempy;
+ return this;
+ }
+
+ /**
+ * Projects this vector onto another vector
+ *
+ * @param other The vector to project this vector onto
+ * @return A new vector with the projection result
+ */
+ public Vector3f project(Vector3f other) {
+ float n = this.dot(other); // A . B
+ float d = other.lengthSquared(); // |B|^2
+ return new Vector3f(other).normalizeLocal().multLocal(n / d);
+ }
+
+ /**
+ * Projects this vector onto another vector, stores the result in this
+ * vector
+ *
+ * @param other The vector to project this vector onto
+ * @return This Vector3f, set to the projection result
+ */
+ public Vector3f projectLocal(Vector3f other) {
+ float n = this.dot(other); // A . B
+ float d = other.lengthSquared(); // |B|^2
+ return set(other).normalizeLocal().multLocal(n / d);
+ }
+
+ /**
+ * Returns true if this vector is a unit vector (length() ~= 1), returns
+ * false otherwise.
+ *
+ * @return true if this vector is a unit vector (length() ~= 1), or false
+ * otherwise.
+ */
+ public boolean isUnitVector() {
+ float len = length();
+ return 0.99f < len && len < 1.01f;
+ }
+
+ /**
+ * length calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * lengthSquared calculates the squared value of the magnitude
+ * of the vector.
+ *
+ * @return the magnitude squared of the vector.
+ */
+ public float lengthSquared() {
+ return x * x + y * y + z * z;
+ }
+
+ /**
+ * distanceSquared calculates the distance squared between this
+ * vector and vector v.
+ *
+ * @param v the second vector to determine the distance squared.
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(Vector3f v) {
+ double dx = x - v.x;
+ double dy = y - v.y;
+ double dz = z - v.z;
+ return (float) (dx * dx + dy * dy + dz * dz);
+ }
+
+ /**
+ * distance calculates the distance between this vector and
+ * vector v.
+ *
+ * @param v the second vector to determine the distance.
+ * @return the distance between the two vectors.
+ */
+ public float distance(Vector3f v) {
+ return FastMath.sqrt(distanceSquared(v));
+ }
+
+ /**
+ *
+ * mult multiplies this vector by a scalar. The resultant
+ * vector is returned.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @return the new vector.
+ */
+ public Vector3f mult(float scalar) {
+ return new Vector3f(x * scalar, y * scalar, z * scalar);
+ }
+
+ /**
+ *
+ * mult multiplies this vector by a scalar. The resultant
+ * vector is supplied as the second parameter and returned.
+ *
+ * @param scalar the scalar to multiply this vector by.
+ * @param product the product to store the result in.
+ * @return product
+ */
+ public Vector3f mult(float scalar, Vector3f product) {
+ if (null == product) {
+ product = new Vector3f();
+ }
+
+ product.x = x * scalar;
+ product.y = y * scalar;
+ product.z = z * scalar;
+ return product;
+ }
+
+ /**
+ * multLocal multiplies this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @return this
+ */
+ public Vector3f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to mult to this vector.
+ * @return this
+ */
+ public Vector3f multLocal(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x *= vec.x;
+ y *= vec.y;
+ z *= vec.z;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies this vector by 3 scalars internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @return this
+ */
+ public Vector3f multLocal(float x, float y, float z) {
+ this.x *= x;
+ this.y *= y;
+ this.z *= z;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to mult to this vector.
+ * @return this
+ */
+ public Vector3f mult(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return mult(vec, null);
+ }
+
+ /**
+ * multLocal multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to mult to this vector.
+ * @param store result vector (null to create a new vector)
+ * @return this
+ */
+ public Vector3f mult(Vector3f vec, Vector3f store) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ if (store == null)
+ store = new Vector3f();
+ return store.set(x * vec.x, y * vec.y, z * vec.z);
+ }
+
+ /**
+ * divide divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar the value to divide this vectors attributes by.
+ * @return the result Vector.
+ */
+ public Vector3f divide(float scalar) {
+ scalar = 1f / scalar;
+ return new Vector3f(x * scalar, y * scalar, z * scalar);
+ }
+
+ public Vector3f divide(float scalar, Vector3f store) {
+ scalar = 1f / scalar;
+ return store.set(x * scalar, y * scalar, z * scalar);
+ }
+
+ /**
+ * divideLocal divides this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls. Dividing by
+ * zero will result in an exception.
+ *
+ * @param scalar the value to divides this vector by.
+ * @return this
+ */
+ public Vector3f divideLocal(float scalar) {
+ scalar = 1f / scalar;
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+ /**
+ * divide divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar the value to divide this vectors attributes by.
+ * @return the result Vector.
+ */
+ public Vector3f divide(Vector3f scalar) {
+ return new Vector3f(x / scalar.x, y / scalar.y, z / scalar.z);
+ }
+
+ /**
+ * divideLocal divides this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls. Dividing by
+ * zero will result in an exception.
+ *
+ * @param scalar the value to divides this vector by.
+ * @return this
+ */
+ public Vector3f divideLocal(Vector3f scalar) {
+ x /= scalar.x;
+ y /= scalar.y;
+ z /= scalar.z;
+ return this;
+ }
+
+ /**
+ *
+ * negate returns the negative of this vector. All values are
+ * negated and set to a new vector.
+ *
+ * @return the negated vector.
+ */
+ public Vector3f negate() {
+ return new Vector3f(-x, -y, -z);
+ }
+
+ /**
+ *
+ * negateLocal negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector3f negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ return this;
+ }
+
+ /**
+ *
+ * subtract subtracts the values of a given vector from those
+ * of this vector creating a new vector object. If the provided vector is
+ * null, null is returned.
+ *
+ * @param vec the vector to subtract from this vector.
+ * @return the result vector.
+ */
+ public Vector3f subtract(Vector3f vec) {
+ return new Vector3f(x - vec.x, y - vec.y, z - vec.z);
+ }
+
+ /**
+ * subtractLocal subtracts a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to subtract
+ * @return this
+ */
+ public Vector3f subtractLocal(Vector3f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x -= vec.x;
+ y -= vec.y;
+ z -= vec.z;
+ return this;
+ }
+
+ /**
+ *
+ * subtract
+ *
+ * @param vec the vector to subtract from this
+ * @param result the vector to store the result in
+ * @return result
+ */
+ public Vector3f subtract(Vector3f vec, Vector3f result) {
+ if (result == null) {
+ result = new Vector3f();
+ }
+ result.x = x - vec.x;
+ result.y = y - vec.y;
+ result.z = z - vec.z;
+ return result;
+ }
+
+ /**
+ *
+ * subtract subtracts the provided values from this vector,
+ * creating a new vector that is then returned.
+ *
+ * @param subtractX the x value to subtract.
+ * @param subtractY the y value to subtract.
+ * @param subtractZ the z value to subtract.
+ * @return the result vector.
+ */
+ public Vector3f subtract(float subtractX, float subtractY, float subtractZ) {
+ return new Vector3f(x - subtractX, y - subtractY, z - subtractZ);
+ }
+
+ /**
+ * subtractLocal subtracts the provided values from this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param subtractX the x value to subtract.
+ * @param subtractY the y value to subtract.
+ * @param subtractZ the z value to subtract.
+ * @return this
+ */
+ public Vector3f subtractLocal(float subtractX, float subtractY, float subtractZ) {
+ x -= subtractX;
+ y -= subtractY;
+ z -= subtractZ;
+ return this;
+ }
+
+ /**
+ * normalize returns the unit vector of this vector.
+ *
+ * @return unit vector of this vector.
+ */
+ public Vector3f normalize() {
+ float length = x * x + y * y + z * z;
+ if (length != 1f && length != 0f) {
+ length = 1.0f / FastMath.sqrt(length);
+ return new Vector3f(x * length, y * length, z * length);
+ }
+ return clone();
+ }
+
+ public Vector3f normalize(Vector3f store) {
+ float length = x * x + y * y + z * z;
+ if (length != 1f && length != 0f) {
+ length = 1.0f / FastMath.sqrt(length);
+ return store.set(x * length, y * length, z * length);
+ } else {
+ return store.set(this);
+ }
+ }
+
+ /**
+ * normalizeLocal makes this vector into a unit vector of
+ * itself.
+ *
+ * @return this
+ */
+ public Vector3f normalizeLocal() {
+ // NOTE: this implementation is more optimized
+ // than the old jme normalize as this method
+ // is commonly used.
+ float length = x * x + y * y + z * z;
+ if (length != 1f && length != 0f) {
+ length = 1.0f / FastMath.sqrt(length);
+ x *= length;
+ y *= length;
+ z *= length;
+ }
+ return this;
+ }
+
+ /**
+ * maxLocal computes the maximum value for each component in
+ * this and other vector. The result is stored in this vector.
+ *
+ * @param other
+ */
+ public Vector3f maxLocal(Vector3f other) {
+ x = other.x > x ? other.x : x;
+ y = other.y > y ? other.y : y;
+ z = other.z > z ? other.z : z;
+ return this;
+ }
+
+ /**
+ * minLocal computes the minimum value for each component in
+ * this and other vector. The result is stored in this vector.
+ *
+ * @param other
+ */
+ public Vector3f minLocal(Vector3f other) {
+ x = other.x < x ? other.x : x;
+ y = other.y < y ? other.y : y;
+ z = other.z < z ? other.z : z;
+ return this;
+ }
+
+ /**
+ * zero resets this vector's data to zero internally.
+ */
+ public Vector3f zero() {
+ x = y = z = 0;
+ return this;
+ }
+
+ /**
+ * angleBetween returns (in radians) the angle between two
+ * vectors. It is assumed that both this vector and the given vector are
+ * unit vectors (iow, normalized).
+ *
+ * @param otherVector a unit vector to find the angle against
+ * @return the angle in radians.
+ */
+ public float angleBetween(Vector3f otherVector) {
+ float dotProduct = dot(otherVector);
+ float angle = FastMath.acos(dotProduct);
+ return angle;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from this to the
+ * finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
+ *
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from this towards finalVec
+ */
+ public Vector3f interpolate(Vector3f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
+ this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
+ return this;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from beginVec to
+ * finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
+ *
+ * @param beginVec the beging vector (changeAmnt=0)
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from beginVec towards finalVec
+ */
+ public Vector3f interpolate(Vector3f beginVec, Vector3f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
+ this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
+ return this;
+ }
+
+ /**
+ * Check a vector... if it is null or its floats are NaN or infinite, return
+ * false. Else return true.
+ *
+ * @param vector the vector to check
+ * @return true or false as stated above.
+ */
+ public static boolean isValidVector(Vector3f vector) {
+ if (vector == null)
+ return false;
+ if (Float.isNaN(vector.x) || Float.isNaN(vector.y) || Float.isNaN(vector.z))
+ return false;
+ if (Float.isInfinite(vector.x) || Float.isInfinite(vector.y) || Float.isInfinite(vector.z))
+ return false;
+ return true;
+ }
+
+ public static void generateOrthonormalBasis(Vector3f u, Vector3f v, Vector3f w) {
+ w.normalizeLocal();
+ generateComplementBasis(u, v, w);
+ }
+
+ public static void generateComplementBasis(Vector3f u, Vector3f v, Vector3f w) {
+ float fInvLength;
+
+ if (FastMath.abs(w.x) >= FastMath.abs(w.y)) {
+ // w.x or w.z is the largest magnitude component, swap them
+ fInvLength = FastMath.invSqrt(w.x * w.x + w.z * w.z);
+ u.x = -w.z * fInvLength;
+ u.y = 0.0f;
+ u.z = +w.x * fInvLength;
+ v.x = w.y * u.z;
+ v.y = w.z * u.x - w.x * u.z;
+ v.z = -w.y * u.x;
+ } else {
+ // w.y or w.z is the largest magnitude component, swap them
+ fInvLength = FastMath.invSqrt(w.y * w.y + w.z * w.z);
+ u.x = 0.0f;
+ u.y = +w.z * fInvLength;
+ u.z = -w.y * fInvLength;
+ v.x = w.y * u.z - w.z * u.y;
+ v.y = -w.x * u.z;
+ v.z = w.x * u.y;
+ }
+ }
+
+ @Override
+ public Vector3f clone() {
+ try {
+ return (Vector3f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this Vector3f into the given float[] object.
+ *
+ * @param floats The float[] to take this Vector3f. If null, a new float[3]
+ * is created.
+ * @return The array, with X, Y, Z float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[3];
+ }
+ floats[0] = x;
+ floats[1] = y;
+ floats[2] = z;
+ return floats;
+ }
+
+ /**
+ * are these two vectors the same? they are is they both have the same x,y,
+ * and z values.
+ *
+ * @param o the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Vector3f))
+ return false;
+ if (this == o)
+ return true;
+ Vector3f comp = (Vector3f) o;
+ if (Float.compare(x, comp.x) != 0)
+ return false;
+ if (Float.compare(y, comp.y) != 0)
+ return false;
+ if (Float.compare(z, comp.z) != 0)
+ return false;
+ return true;
+ }
+
+ /**
+ * hashCode returns a unique code for this vector object based
+ * on it's values. If two vectors are logically equivalent, they will return
+ * the same hash code value.
+ *
+ * @return the hash code value of this vector.
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(x);
+ hash += 37 * hash + Float.floatToIntBits(y);
+ hash += 37 * hash + Float.floatToIntBits(z);
+ return hash;
+ }
+
+ /**
+ * toString returns the string representation of this vector.
+ * The format is:
+ *
+ * org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ]
+ *
+ * @return the string representation of this vector.
+ */
+ @Override
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ")";
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector3f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector3f setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public Vector3f setZ(float z) {
+ this.z = z;
+ return this;
+ }
+
+ /**
+ * @param index
+ * @return x value if index == 0, y value if index == 1 or z value if index
+ * == 2
+ * @throws IllegalArgumentException if index is not one of 0, 1, 2.
+ */
+ public float get(int index) {
+ switch (index) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1 or 2");
+ }
+
+ /**
+ * @param index which field index in this vector to set.
+ * @param value to set to one of x, y or z.
+ * @throws IllegalArgumentException if index is not one of 0, 1, 2.
+ */
+ public void set(int index, float value) {
+ switch (index) {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1 or 2");
+ }
+
+ public static float angleBetweenVectors(Vector2f vec1, Vector2f vec2) {
+ return (float) Math
+ .atan2(vec1.x * vec2.y - vec1.y * vec2.x, vec1.x * vec2.x + vec1.y * vec2.y);
+ }
+}
diff --git a/java/com/jme3/math/Vector4f.java b/java/com/jme3/math/Vector4f.java
new file mode 100644
index 000000000..20f101639
--- /dev/null
+++ b/java/com/jme3/math/Vector4f.java
@@ -0,0 +1,971 @@
+/*
+ * Copyright (c) 2009-2012 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 java.util.logging.Logger;
+
+
+/**
+ * Vector4f defines a Vector for a four float value tuple.
+ * Vector4f can represent any four dimensional value, such as a
+ * vertex, a normal, etc. Utility methods are also included to aid in
+ * mathematical calculations.
+ *
+ * @author Maarten Steur
+ */
+public final class Vector4f implements Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Vector4f.class.getName());
+
+ public final static Vector4f ZERO = new Vector4f(0, 0, 0, 0);
+ public final static Vector4f NAN = new Vector4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
+ public final static Vector4f UNIT_X = new Vector4f(1, 0, 0, 0);
+ public final static Vector4f UNIT_Y = new Vector4f(0, 1, 0, 0);
+ public final static Vector4f UNIT_Z = new Vector4f(0, 0, 1, 0);
+ public final static Vector4f UNIT_W = new Vector4f(0, 0, 0, 1);
+ public final static Vector4f UNIT_XYZW = new Vector4f(1, 1, 1, 1);
+ public final static Vector4f POSITIVE_INFINITY = new Vector4f(
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY,
+ Float.POSITIVE_INFINITY
+ );
+ public final static Vector4f NEGATIVE_INFINITY = new Vector4f(
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY,
+ Float.NEGATIVE_INFINITY
+ );
+
+ /**
+ * the x value of the vector.
+ */
+ public float x;
+
+ /**
+ * the y value of the vector.
+ */
+ public float y;
+
+ /**
+ * the z value of the vector.
+ */
+ public float z;
+
+ /**
+ * the w value of the vector.
+ */
+ public float w;
+
+ /**
+ * Constructor instantiates a new Vector3f with default values
+ * of (0,0,0).
+ *
+ */
+ public Vector4f() {
+ x = y = z = w = 0;
+ }
+
+ /**
+ * Constructor instantiates a new Vector4f with provides
+ * values.
+ *
+ * @param x the x value of the vector.
+ * @param y the y value of the vector.
+ * @param z the z value of the vector.
+ * @param w the w value of the vector.
+ */
+ public Vector4f(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ /**
+ * Constructor instantiates a new Vector3f that is a copy of
+ * the provided vector
+ *
+ * @param copy The Vector3f to copy
+ */
+ public Vector4f(Vector4f copy) {
+ this.set(copy);
+ }
+
+ /**
+ * set sets the x,y,z,w values of the vector based on passed
+ * parameters.
+ *
+ * @param x the x value of the vector.
+ * @param y the y value of the vector.
+ * @param z the z value of the vector.
+ * @param w the w value of the vector.
+ * @return this vector
+ */
+ public Vector4f set(float x, float y, float z, float w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ return this;
+ }
+
+ /**
+ * set sets the x,y,z values of the vector by copying the
+ * supplied vector.
+ *
+ * @param vect the vector to copy.
+ * @return this vector
+ */
+ public Vector4f set(Vector4f vect) {
+ this.x = vect.x;
+ this.y = vect.y;
+ this.z = vect.z;
+ this.w = vect.w;
+ return this;
+ }
+
+ /**
+ *
+ * add adds a provided vector to this vector creating a
+ * resultant vector which is returned. If the provided vector is null, null
+ * is returned.
+ *
+ * @param vec the vector to add to this.
+ * @return the resultant vector.
+ */
+ public Vector4f add(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return new Vector4f(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
+ }
+
+ /**
+ *
+ * add adds the values of a provided vector storing the values
+ * in the supplied vector.
+ *
+ * @param vec the vector to add to this
+ * @param result the vector to store the result in
+ * @return result returns the supplied result vector.
+ */
+ public Vector4f add(Vector4f vec, Vector4f result) {
+ result.x = x + vec.x;
+ result.y = y + vec.y;
+ result.z = z + vec.z;
+ result.w = w + vec.w;
+ return result;
+ }
+
+ /**
+ * addLocal adds a provided vector to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls. If the
+ * provided vector is null, null is returned.
+ *
+ * @param vec the vector to add to this vector.
+ * @return this
+ */
+ public Vector4f addLocal(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x += vec.x;
+ y += vec.y;
+ z += vec.z;
+ w += vec.w;
+ return this;
+ }
+
+ /**
+ *
+ * add adds the provided values to this vector, creating a new
+ * vector that is then returned.
+ *
+ * @param addX the x value to add.
+ * @param addY the y value to add.
+ * @param addZ the z value to add.
+ * @return the result vector.
+ */
+ public Vector4f add(float addX, float addY, float addZ, float addW) {
+ return new Vector4f(x + addX, y + addY, z + addZ, w + addW);
+ }
+
+ /**
+ * addLocal adds the provided values to this vector internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param addX value to add to x
+ * @param addY value to add to y
+ * @param addZ value to add to z
+ * @return this
+ */
+ public Vector4f addLocal(float addX, float addY, float addZ, float addW) {
+ x += addX;
+ y += addY;
+ z += addZ;
+ w += addW;
+ return this;
+ }
+
+ /**
+ *
+ * scaleAdd multiplies this vector by a scalar then adds the
+ * given Vector3f.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @param add the value to add
+ */
+ public Vector4f scaleAdd(float scalar, Vector4f add) {
+ x = x * scalar + add.x;
+ y = y * scalar + add.y;
+ z = z * scalar + add.z;
+ w = w * scalar + add.w;
+ return this;
+ }
+
+ /**
+ *
+ * scaleAdd multiplies the given vector by a scalar then adds
+ * the given vector.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @param mult the value to multiply the scalar by
+ * @param add the value to add
+ */
+ public Vector4f scaleAdd(float scalar, Vector4f mult, Vector4f add) {
+ this.x = mult.x * scalar + add.x;
+ this.y = mult.y * scalar + add.y;
+ this.z = mult.z * scalar + add.z;
+ this.w = mult.w * scalar + add.w;
+ return this;
+ }
+
+ /**
+ *
+ * dot calculates the dot product of this vector with a
+ * provided vector. If the provided vector is null, 0 is returned.
+ *
+ * @param vec the vector to dot with this vector.
+ * @return the resultant dot product of this vector and a given vector.
+ */
+ public float dot(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, 0 returned.");
+ return 0;
+ }
+ return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
+ }
+
+ public Vector4f project(Vector4f other) {
+ float n = this.dot(other); // A . B
+ float d = other.lengthSquared(); // |B|^2
+ return new Vector4f(other).normalizeLocal().multLocal(n / d);
+ }
+
+ /**
+ * Returns true if this vector is a unit vector (length() ~= 1), returns
+ * false otherwise.
+ *
+ * @return true if this vector is a unit vector (length() ~= 1), or false
+ * otherwise.
+ */
+ public boolean isUnitVector() {
+ float len = length();
+ return 0.99f < len && len < 1.01f;
+ }
+
+ /**
+ * length calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * lengthSquared calculates the squared value of the magnitude
+ * of the vector.
+ *
+ * @return the magnitude squared of the vector.
+ */
+ public float lengthSquared() {
+ return x * x + y * y + z * z + w * w;
+ }
+
+ /**
+ * distanceSquared calculates the distance squared between this
+ * vector and vector v.
+ *
+ * @param v the second vector to determine the distance squared.
+ * @return the distance squared between the two vectors.
+ */
+ public float distanceSquared(Vector4f v) {
+ double dx = x - v.x;
+ double dy = y - v.y;
+ double dz = z - v.z;
+ double dw = w - v.w;
+ return (float) (dx * dx + dy * dy + dz * dz + dw * dw);
+ }
+
+ /**
+ * distance calculates the distance between this vector and
+ * vector v.
+ *
+ * @param v the second vector to determine the distance.
+ * @return the distance between the two vectors.
+ */
+ public float distance(Vector4f v) {
+ return FastMath.sqrt(distanceSquared(v));
+ }
+
+ /**
+ *
+ * mult multiplies this vector by a scalar. The resultant
+ * vector is returned.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @return the new vector.
+ */
+ public Vector4f mult(float scalar) {
+ return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
+ }
+
+ /**
+ *
+ * mult multiplies this vector by a scalar. The resultant
+ * vector is supplied as the second parameter and returned.
+ *
+ * @param scalar the scalar to multiply this vector by.
+ * @param product the product to store the result in.
+ * @return product
+ */
+ public Vector4f mult(float scalar, Vector4f product) {
+ if (null == product) {
+ product = new Vector4f();
+ }
+
+ product.x = x * scalar;
+ product.y = y * scalar;
+ product.z = z * scalar;
+ product.w = w * scalar;
+ return product;
+ }
+
+ /**
+ * multLocal multiplies this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls.
+ *
+ * @param scalar the value to multiply this vector by.
+ * @return this
+ */
+ public Vector4f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ w *= scalar;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to mult to this vector.
+ * @return this
+ */
+ public Vector4f multLocal(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x *= vec.x;
+ y *= vec.y;
+ z *= vec.z;
+ w *= vec.w;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies this vector by 3 scalars internally,
+ * and returns a handle to this vector for easy chaining of calls.
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @param w
+ * @return this
+ */
+ public Vector4f multLocal(float x, float y, float z, float w) {
+ this.x *= x;
+ this.y *= y;
+ this.z *= z;
+ this.w *= w;
+ return this;
+ }
+
+ /**
+ * multLocal multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to mult to this vector.
+ * @return this
+ */
+ public Vector4f mult(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ return mult(vec, null);
+ }
+
+ /**
+ * multLocal multiplies a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to mult to this vector.
+ * @param store result vector (null to create a new vector)
+ * @return this
+ */
+ public Vector4f mult(Vector4f vec, Vector4f store) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ if (store == null)
+ store = new Vector4f();
+ return store.set(x * vec.x, y * vec.y, z * vec.z, w * vec.w);
+ }
+
+ /**
+ * divide divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar the value to divide this vectors attributes by.
+ * @return the result Vector.
+ */
+ public Vector4f divide(float scalar) {
+ scalar = 1f / scalar;
+ return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
+ }
+
+ /**
+ * divideLocal divides this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls. Dividing by
+ * zero will result in an exception.
+ *
+ * @param scalar the value to divides this vector by.
+ * @return this
+ */
+ public Vector4f divideLocal(float scalar) {
+ scalar = 1f / scalar;
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ w *= scalar;
+ return this;
+ }
+
+ /**
+ * divide divides the values of this vector by a scalar and
+ * returns the result. The values of this vector remain untouched.
+ *
+ * @param scalar the value to divide this vectors attributes by.
+ * @return the result Vector.
+ */
+ public Vector4f divide(Vector4f scalar) {
+ return new Vector4f(x / scalar.x, y / scalar.y, z / scalar.z, w / scalar.w);
+ }
+
+ /**
+ * divideLocal divides this vector by a scalar internally, and
+ * returns a handle to this vector for easy chaining of calls. Dividing by
+ * zero will result in an exception.
+ *
+ * @param scalar the value to divides this vector by.
+ * @return this
+ */
+ public Vector4f divideLocal(Vector4f scalar) {
+ x /= scalar.x;
+ y /= scalar.y;
+ z /= scalar.z;
+ w /= scalar.w;
+ return this;
+ }
+
+ /**
+ *
+ * negate returns the negative of this vector. All values are
+ * negated and set to a new vector.
+ *
+ * @return the negated vector.
+ */
+ public Vector4f negate() {
+ return new Vector4f(-x, -y, -z, -w);
+ }
+
+ /**
+ *
+ * negateLocal negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector4f negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ w = -w;
+ return this;
+ }
+
+ /**
+ *
+ * subtract subtracts the values of a given vector from those
+ * of this vector creating a new vector object. If the provided vector is
+ * null, null is returned.
+ *
+ * @param vec the vector to subtract from this vector.
+ * @return the result vector.
+ */
+ public Vector4f subtract(Vector4f vec) {
+ return new Vector4f(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
+ }
+
+ /**
+ * subtractLocal subtracts a provided vector to this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls. If the provided vector is null, null is returned.
+ *
+ * @param vec the vector to subtract
+ * @return this
+ */
+ public Vector4f subtractLocal(Vector4f vec) {
+ if (null == vec) {
+ logger.warning("Provided vector is null, null returned.");
+ return null;
+ }
+ x -= vec.x;
+ y -= vec.y;
+ z -= vec.z;
+ w -= vec.w;
+ return this;
+ }
+
+ /**
+ *
+ * subtract
+ *
+ * @param vec the vector to subtract from this
+ * @param result the vector to store the result in
+ * @return result
+ */
+ public Vector4f subtract(Vector4f vec, Vector4f result) {
+ if (result == null) {
+ result = new Vector4f();
+ }
+ result.x = x - vec.x;
+ result.y = y - vec.y;
+ result.z = z - vec.z;
+ result.w = w - vec.w;
+ return result;
+ }
+
+ /**
+ *
+ * subtract subtracts the provided values from this vector,
+ * creating a new vector that is then returned.
+ *
+ * @param subtractX the x value to subtract.
+ * @param subtractY the y value to subtract.
+ * @param subtractZ the z value to subtract.
+ * @param subtractW the w value to subtract.
+ * @return the result vector.
+ */
+ public Vector4f subtract(float subtractX, float subtractY, float subtractZ, float subtractW) {
+ return new Vector4f(x - subtractX, y - subtractY, z - subtractZ, w - subtractW);
+ }
+
+ /**
+ * subtractLocal subtracts the provided values from this vector
+ * internally, and returns a handle to this vector for easy chaining of
+ * calls.
+ *
+ * @param subtractX the x value to subtract.
+ * @param subtractY the y value to subtract.
+ * @param subtractZ the z value to subtract.
+ * @param subtractW the w value to subtract.
+ * @return this
+ */
+ public Vector4f subtractLocal(
+ float subtractX,
+ float subtractY,
+ float subtractZ,
+ float subtractW
+ ) {
+ x -= subtractX;
+ y -= subtractY;
+ z -= subtractZ;
+ w -= subtractW;
+ return this;
+ }
+
+ /**
+ * normalize returns the unit vector of this vector.
+ *
+ * @return unit vector of this vector.
+ */
+ public Vector4f normalize() {
+// float length = length();
+// if (length != 0) {
+// return divide(length);
+// }
+//
+// return divide(1);
+ float length = x * x + y * y + z * z + w * w;
+ if (length != 1f && length != 0f) {
+ length = 1.0f / FastMath.sqrt(length);
+ return new Vector4f(x * length, y * length, z * length, w * length);
+ }
+ return clone();
+ }
+
+ /**
+ * normalizeLocal makes this vector into a unit vector of
+ * itself.
+ *
+ * @return this.
+ */
+ public Vector4f normalizeLocal() {
+ // NOTE: this implementation is more optimized
+ // than the old jme normalize as this method
+ // is commonly used.
+ float length = x * x + y * y + z * z + w * w;
+ if (length != 1f && length != 0f) {
+ length = 1.0f / FastMath.sqrt(length);
+ x *= length;
+ y *= length;
+ z *= length;
+ w *= length;
+ }
+ return this;
+ }
+
+ /**
+ * maxLocal computes the maximum value for each component in
+ * this and other vector. The result is stored in this vector.
+ *
+ * @param other
+ */
+ public Vector4f maxLocal(Vector4f other) {
+ x = other.x > x ? other.x : x;
+ y = other.y > y ? other.y : y;
+ z = other.z > z ? other.z : z;
+ w = other.w > w ? other.w : w;
+ return this;
+ }
+
+ /**
+ * minLocal computes the minimum value for each component in
+ * this and other vector. The result is stored in this vector.
+ *
+ * @param other
+ */
+ public Vector4f minLocal(Vector4f other) {
+ x = other.x < x ? other.x : x;
+ y = other.y < y ? other.y : y;
+ z = other.z < z ? other.z : z;
+ w = other.w < w ? other.w : w;
+ return this;
+ }
+
+ /**
+ * zero resets this vector's data to zero internally.
+ */
+ public Vector4f zero() {
+ x = y = z = w = 0;
+ return this;
+ }
+
+ /**
+ * angleBetween returns (in radians) the angle between two
+ * vectors. It is assumed that both this vector and the given vector are
+ * unit vectors (iow, normalized).
+ *
+ * @param otherVector a unit vector to find the angle against
+ * @return the angle in radians.
+ */
+ public float angleBetween(Vector4f otherVector) {
+ float dotProduct = dot(otherVector);
+ float angle = FastMath.acos(dotProduct);
+ return angle;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from this to the
+ * finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
+ *
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from this towards finalVec
+ */
+ public Vector4f interpolate(Vector4f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
+ this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
+ this.w = (1 - changeAmnt) * this.w + changeAmnt * finalVec.w;
+ return this;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from beginVec to
+ * finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
+ *
+ * @param beginVec the beging vector (changeAmnt=0)
+ * @param finalVec The final vector to interpolate towards
+ * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
+ * change from beginVec towards finalVec
+ */
+ public Vector4f interpolate(Vector4f beginVec, Vector4f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
+ this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
+ this.w = (1 - changeAmnt) * beginVec.w + changeAmnt * finalVec.w;
+ return this;
+ }
+
+ /**
+ * Check a vector... if it is null or its floats are NaN or infinite, return
+ * false. Else return true.
+ *
+ * @param vector the vector to check
+ * @return true or false as stated above.
+ */
+ public static boolean isValidVector(Vector4f vector) {
+ if (vector == null)
+ return false;
+ if (
+ Float.isNaN(vector.x)
+ ||
+ Float.isNaN(vector.y)
+ ||
+ Float.isNaN(vector.z)
+ ||
+ Float.isNaN(vector.w)
+ )
+ return false;
+ if (
+ Float.isInfinite(vector.x)
+ ||
+ Float.isInfinite(vector.y)
+ ||
+ Float.isInfinite(vector.z)
+ ||
+ Float.isInfinite(vector.w)
+ )
+ return false;
+ return true;
+ }
+
+ @Override
+ public Vector4f clone() {
+ try {
+ return (Vector4f) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(); // can not happen
+ }
+ }
+
+ /**
+ * Saves this Vector3f into the given float[] object.
+ *
+ * @param floats The float[] to take this Vector3f. If null, a new float[3]
+ * is created.
+ * @return The array, with X, Y, Z float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[4];
+ }
+ floats[0] = x;
+ floats[1] = y;
+ floats[2] = z;
+ floats[3] = w;
+ return floats;
+ }
+
+ /**
+ * are these two vectors the same? they are is they both have the same x,y,
+ * and z values.
+ *
+ * @param o the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Vector4f)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ Vector4f comp = (Vector4f) o;
+ if (Float.compare(x, comp.x) != 0)
+ return false;
+ if (Float.compare(y, comp.y) != 0)
+ return false;
+ if (Float.compare(z, comp.z) != 0)
+ return false;
+ if (Float.compare(w, comp.w) != 0)
+ return false;
+ return true;
+ }
+
+ /**
+ * hashCode returns a unique code for this vector object based
+ * on it's values. If two vectors are logically equivalent, they will return
+ * the same hash code value.
+ *
+ * @return the hash code value of this vector.
+ */
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash += 37 * hash + Float.floatToIntBits(x);
+ hash += 37 * hash + Float.floatToIntBits(y);
+ hash += 37 * hash + Float.floatToIntBits(z);
+ hash += 37 * hash + Float.floatToIntBits(w);
+ return hash;
+ }
+
+ /**
+ * toString returns the string representation of this vector.
+ * The format is:
+ *
+ * org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ, W=WW.WWWW]
+ *
+ * @return the string representation of this vector.
+ */
+ @Override
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ", " + w + ")";
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector4f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector4f setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public Vector4f setZ(float z) {
+ this.z = z;
+ return this;
+ }
+
+ public float getW() {
+ return w;
+ }
+
+ public Vector4f setW(float w) {
+ this.w = w;
+ return this;
+ }
+
+ /**
+ * @param index
+ * @return x value if index == 0, y value if index == 1 or z value if index
+ * == 2
+ * @throws IllegalArgumentException if index is not one of 0, 1, 2.
+ */
+ public float get(int index) {
+ switch (index) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return w;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
+ }
+
+ /**
+ * @param index which field index in this vector to set.
+ * @param value to set to one of x, y, z or w.
+ * @throws IllegalArgumentException if index is not one of 0, 1, 2, 3.
+ */
+ public void set(int index, float value) {
+ switch (index) {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ case 3:
+ w = value;
+ return;
+ }
+ throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
+ }
+
+}
diff --git a/java/com/jme3/system/NanoTimer.java b/java/com/jme3/system/NanoTimer.java
new file mode 100644
index 000000000..b99dcf2dd
--- /dev/null
+++ b/java/com/jme3/system/NanoTimer.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2009-2012 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.system;
+
+/**
+ * NanoTimer is a System.nanoTime implementation of
+ * Timer. This is primarily useful for headless applications
+ * running on a server.
+ *
+ * @author Matthew D. Hicks
+ */
+public class NanoTimer extends Timer {
+
+ private static final long TIMER_RESOLUTION = 1000000000L;
+ private static final float INVERSE_TIMER_RESOLUTION = 1f / TIMER_RESOLUTION;
+
+ private long startTime;
+ private long previousTime;
+ private float tpf;
+ private float fps;
+ private long currentTime;
+
+ public NanoTimer() {
+ startTime = System.nanoTime();
+ }
+
+ /**
+ * Returns the time in seconds. The timer starts at 0.0 seconds.
+ *
+ * @return the current time in seconds
+ */
+
+ protected long getTimeInternal() {
+ return System.nanoTime() - startTime;
+ }
+
+ @Override
+ public float getTimeInSeconds() {
+ return getTime() * INVERSE_TIMER_RESOLUTION;
+ }
+
+ @Override
+ public long getTime() {
+ return currentTime;
+ }
+
+ @Override
+ public long getResolution() {
+ return TIMER_RESOLUTION;
+ }
+
+ @Override
+ public float getFrameRate() {
+ return fps;
+ }
+
+ @Override
+ public float getTimePerFrame() {
+ return tpf;
+ }
+
+ @Override
+ public void update() {
+ currentTime = getTimeInternal();
+ tpf = (currentTime - previousTime) * (1.0f / TIMER_RESOLUTION);
+ fps = 1.0f / tpf;
+ previousTime = getTime();
+ }
+
+ @Override
+ public void reset() {
+ startTime = System.nanoTime();
+ currentTime = getTimeInternal();
+ previousTime = getTime();
+ }
+}
diff --git a/java/com/jme3/system/Timer.java b/java/com/jme3/system/Timer.java
new file mode 100644
index 000000000..ea0a61dfc
--- /dev/null
+++ b/java/com/jme3/system/Timer.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2012 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.system;
+
+/**
+ * Timer is the base class for a high resolution timer. It is
+ * created from getTimer("display system")
+ *
+ * @author Mark Powell
+ * @version $Id: Timer.java,v 1.18 2007/03/09 10:19:34 rherlitz Exp $
+ */
+public abstract class Timer {
+
+ /**
+ * Returns the current time in ticks. A tick is an arbitrary measure of time
+ * defined by the timer implementation. The number of ticks per second is
+ * given by getResolution(). The timer starts at 0 ticks.
+ *
+ * @return a long value representing the current time
+ */
+ public abstract long getTime();
+
+ /**
+ * Returns the time in seconds. The timer starts at 0.0 seconds.
+ *
+ * @return the current time in seconds
+ */
+ public float getTimeInSeconds() {
+ return getTime() / (float) getResolution();
+ }
+
+ /**
+ * Returns the resolution of the timer.
+ *
+ * @return the number of timer ticks per second
+ */
+ public abstract long getResolution();
+
+ /**
+ * Returns the "calls per second". If this is called every frame, then it
+ * will return the "frames per second".
+ *
+ * @return The "calls per second".
+ */
+ public abstract float getFrameRate();
+
+ /**
+ * Returns the time, in seconds, between the last call and the current one.
+ *
+ * @return Time between this call and the last one.
+ */
+ public abstract float getTimePerFrame();
+
+ /**
+ * update recalculates the frame rate based on the previous
+ * call to update. It is assumed that update is called each frame.
+ */
+ public abstract void update();
+
+ /**
+ * Reset the timer to 0. Clear any tpf history.
+ */
+ public abstract void reset();
+}
diff --git a/java/com/jme3/util/TempVars.java b/java/com/jme3/util/TempVars.java
new file mode 100644
index 000000000..46f33010d
--- /dev/null
+++ b/java/com/jme3/util/TempVars.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2009-2012 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.util;
+
+import com.jme3.math.*;
+
+
+/**
+ * Temporary variables assigned to each thread. Engine classes may access these
+ * temp variables with TempVars.get(), all retrieved TempVars instances must be
+ * returned via TempVars.release(). This returns an available instance of the
+ * TempVar class ensuring this particular instance is never used elsewhere in
+ * the mean time.
+ */
+public class TempVars {
+
+ /**
+ * Allow X instances of TempVars in a single thread.
+ */
+ private static final int STACK_SIZE = 5;
+
+ /**
+ * TempVarsStack contains a stack of TempVars. Every time
+ * TempVars.get() is called, a new entry is added to the stack, and the
+ * index incremented. When TempVars.release() is called, the entry is
+ * checked against the current instance and then the index is decremented.
+ */
+ private static class TempVarsStack {
+
+ int index = 0;
+ TempVars[] tempVars = new TempVars[STACK_SIZE];
+ }
+
+ /**
+ * ThreadLocal to store a TempVarsStack for each thread. This ensures each
+ * thread has a single TempVarsStack that is used only in method calls in
+ * that thread.
+ */
+ private static final ThreadLocal varsLocal = new ThreadLocal() {
+
+ @Override
+ public TempVarsStack initialValue() {
+ return new TempVarsStack();
+ }
+ };
+ /**
+ * This instance of TempVars has been retrieved but not released yet.
+ */
+ private boolean isUsed = false;
+
+ private TempVars() {
+ }
+
+ /**
+ * Acquire an instance of the TempVar class. You have to release the
+ * instance after use by calling the release() method. If more than
+ * STACK_SIZE (currently 5) instances are requested in a single thread then
+ * an ArrayIndexOutOfBoundsException will be thrown.
+ *
+ * @return A TempVar instance
+ */
+ public static TempVars get() {
+ TempVarsStack stack = varsLocal.get();
+
+ TempVars instance = stack.tempVars[stack.index];
+
+ if (instance == null) {
+ // Create new
+ instance = new TempVars();
+
+ // Put it in there
+ stack.tempVars[stack.index] = instance;
+ }
+
+ stack.index++;
+
+ instance.isUsed = true;
+
+ return instance;
+ }
+
+ /**
+ * Releases this instance of TempVars. Once released, the contents of the
+ * TempVars are undefined. The TempVars must be released in the opposite
+ * order that they are retrieved, e.g. Acquiring vars1, then acquiring
+ * vars2, vars2 MUST be released first otherwise an exception will be
+ * thrown.
+ */
+ public void release() {
+ if (!isUsed) {
+ throw new IllegalStateException("This instance of TempVars was already released!");
+ }
+
+ isUsed = false;
+
+ TempVarsStack stack = varsLocal.get();
+
+ // Return it to the stack
+ stack.index--;
+
+ // Check if it is actually there
+ if (stack.tempVars[stack.index] != this) {
+ throw new IllegalStateException(
+ "An instance of TempVars has not been released in a called method!"
+ );
+ }
+ }
+
+ /**
+ * Color
+ */
+ public final ColorRGBA color = new ColorRGBA();
+ /**
+ * General vectors.
+ */
+ public final Vector3f vect1 = new Vector3f();
+ public final Vector3f vect2 = new Vector3f();
+ public final Vector3f vect3 = new Vector3f();
+ public final Vector3f vect4 = new Vector3f();
+ public final Vector3f vect5 = new Vector3f();
+ public final Vector3f vect6 = new Vector3f();
+ public final Vector3f vect7 = new Vector3f();
+ // seems the maximum number of vector used is 7 in com.jme3.bounding.java
+ public final Vector3f vect8 = new Vector3f();
+ public final Vector3f vect9 = new Vector3f();
+ public final Vector3f vect10 = new Vector3f();
+ public final Vector4f vect4f = new Vector4f();
+ public final Vector3f[] tri = { new Vector3f(),
+ new Vector3f(),
+ new Vector3f() };
+ /**
+ * 2D vector
+ */
+ public final Vector2f vect2d = new Vector2f();
+ public final Vector2f vect2d2 = new Vector2f();
+ /**
+ * General matrices.
+ */
+ public final Matrix3f tempMat3 = new Matrix3f();
+ public final Matrix4f tempMat4 = new Matrix4f();
+ public final Matrix4f tempMat42 = new Matrix4f();
+ /**
+ * General quaternions.
+ */
+ public final Quaternion quat1 = new Quaternion();
+ public final Quaternion quat2 = new Quaternion();
+
+ public final float[] matrixWrite = new float[16];
+}
diff --git a/java/io/eiren/math/FloatMath.java b/java/io/eiren/math/FloatMath.java
new file mode 100644
index 000000000..90320fd62
--- /dev/null
+++ b/java/io/eiren/math/FloatMath.java
@@ -0,0 +1,672 @@
+package io.eiren.math;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
+
+
+public class FloatMath {
+
+ public static final float PI = (float) Math.PI;
+ public static final float TWO_PI = (float) (Math.PI * 2);
+ public static final float ANGLE_EPSILON = 0.028f; // in degrees (float
+ // epsilon for sin/cos)
+ public static final float ANGLE_EPSILON_RAD = toRad(ANGLE_EPSILON);
+
+ public static final float ZERO_TOLERANCE_F = FastMath.ZERO_TOLERANCE;
+ public static final double ZERO_TOLERANCE_D = 0.0001d;
+
+ public static final float SQRT_TWO = (float) Math.sqrt(2f);
+ public static final float INV_SQRT_TWO = 1f / SQRT_TWO;
+ public static final float SQRT_THREE = (float) Math.sqrt(3f);
+ public static final float INV_SQRT_THREE = 1f / SQRT_THREE;
+ public static final float TWO_FPI = PI * 2;
+
+ public static final float SIN_75_DEG = 0.965926f;
+ public static final float SIN_60_DEG = 0.866025f;
+ public static final float SIN_45_DEG = 0.707107f;
+ public static final float SIN_30_DEG = 0.5f;
+ public static final float SIN_15_DEG = 0.258819f;
+
+ public static final float COS_75_DEG = 0.258819f;
+ public static final float COS_60_DEG = 0.5f;
+ public static final float COS_45_DEG = 0.707107f;
+ public static final float COS_30_DEG = 0.866025f;
+ public static final float COS_15_DEG = 0.965926f;
+
+ public static final int TEN_BITS = ~(~0 << 10);
+ public static final int TENTH_BIT = 1 << 10;
+ public static final int TEN_BITS_MAX = ~(~0 << 9);
+ public static final int TEN_BITS_MAX_UNSIGNED = ~(~0 << 10);
+ public static final int TWO_BITS = ~(~0 << 2);
+ public static final int SECOND_BIT = 1 << 2;
+ public static final int TWO_BITS_MAX = ~(~0 << 1);
+ public static final int TWO_BITS_MAX_UNSIGNED = ~(~0 << 2);
+
+ public static float roundIfZero(float x) {
+ return Math.abs(x) < ZERO_TOLERANCE_F ? 0.0f : x;
+ }
+
+ public static boolean equalsToZero(float x) {
+ return Math.abs(x) < ZERO_TOLERANCE_F;
+ }
+
+ public static boolean lessThanZero(float x) {
+ return (x < -ZERO_TOLERANCE_F);
+ }
+
+ public static boolean lessOrEqualsToZero(float x) {
+ return (x < ZERO_TOLERANCE_F);
+ }
+
+ public static boolean greaterThanZero(float x) {
+ return (x > ZERO_TOLERANCE_F);
+ }
+
+ public static boolean greaterOrEqualsToZero(float x) {
+ return (x > -ZERO_TOLERANCE_F);
+ }
+
+ public static boolean equalsToZero(float x, float epsilon) {
+ return Math.abs(x) < epsilon;
+ }
+
+ public static boolean equalsWithEpsilon(float x, float y) {
+ return Math.abs(x - y) < ZERO_TOLERANCE_F;
+ }
+
+ public static boolean equalsWithEpsilon(float x, float y, float epsilon) {
+ return Math.abs(x - y) < epsilon;
+ }
+
+ public static boolean lessWithEpsilon(float x, float y) {
+ return (x < y - ZERO_TOLERANCE_F);
+ }
+
+ public static boolean lessOrEqualsWithEpsilon(float x, float y) {
+ return (x < y + ZERO_TOLERANCE_F);
+ }
+
+ public static boolean lessWithEpsilon(float x, float y, float epsilon) {
+ return (x < y - epsilon);
+ }
+
+ public static boolean lessOrEqualsWithEpsilon(float x, float y, float epsilon) {
+ return (x < y + epsilon);
+ }
+
+ public static boolean greaterWithEpsilon(float x, float y) {
+ return (x > y + ZERO_TOLERANCE_F);
+ }
+
+ public static boolean greaterOrEqualsWithEpsilon(float x, float y) {
+ return (x > y - ZERO_TOLERANCE_F);
+ }
+
+ public static boolean greaterWithEpsilon(float x, float y, float epsilon) {
+ return (x > y + epsilon);
+ }
+
+ public static boolean greaterOrEqualsWithEpsilon(float x, float y, float epsilon) {
+ return (x > y - epsilon);
+ }
+
+ public static double roundIfZero(double x) {
+ return Math.abs(x) < ZERO_TOLERANCE_D ? 0.0d : x;
+ }
+
+ public static boolean equalsToZero(double x) {
+ return Math.abs(x) < ZERO_TOLERANCE_D;
+ }
+
+ public static boolean equalsWithEpsilon(double x, double y) {
+ return Math.abs(x - y) < ZERO_TOLERANCE_D;
+ }
+
+ public static boolean lessWithEpsilon(double x, double y) {
+ return (x < y - ZERO_TOLERANCE_D);
+ }
+
+ public static boolean lessOrEqualsWithEpsilon(double x, double y) {
+ return (x < y + ZERO_TOLERANCE_D);
+ }
+
+ public static boolean greaterWithEpsilon(double x, double y) {
+ return (x > y + ZERO_TOLERANCE_D);
+ }
+
+ public static boolean greaterOrEqualsWithEpsilon(double x, double y) {
+ return (x > y - ZERO_TOLERANCE_D);
+ }
+
+ public static float toDegrees(float angrad) {
+ return angrad * 180.0f / PI;
+ }
+
+ public static float toRad(float deg) {
+ return deg / 180.0f * PI;
+ }
+
+ public static boolean radEqual(float angle1, float angle2) {
+ float diff = clampRad(angle1 - angle2);
+ return Math.abs(diff) < ANGLE_EPSILON_RAD;
+ }
+
+ public static boolean degreesEqual(float angle1, float angle2) {
+ float diff = clampDegrees(angle1 - angle2);
+ return Math.abs(diff) < ANGLE_EPSILON;
+ }
+
+ /**
+ * @deprecated use {@link #normalizeRad(float)}
+ */
+ @Deprecated
+ public static float clampRad(float angle) {
+ return normalizeRad(angle);
+ }
+
+ public static float normalizeRad(float angle) {
+ return FastMath.normalize(angle, -FastMath.PI, FastMath.PI);
+ }
+
+ /**
+ * @deprecated use {@link #normalizeDegrees(float)}
+ */
+ @Deprecated
+ public static float clampDegrees(float angle) {
+ return normalizeDegrees(angle);
+ }
+
+ public static float normalizeDegrees(float angle) {
+ return FastMath.normalize(angle, -180f, 180f);
+ }
+
+ public static float animateEase(float t) {
+ // Special case of Bezier interpolation (p0 = p1 = 0, p2 = p3 = 1)
+ return (3.0f - 2.0f * t) * t * t;
+ }
+
+ public static float animateEaseIn(float t) {
+ return t * t;
+ }
+
+ /**
+ * Lineary remaps value from the source interval to the target interval.
+ * details
+ */
+ public static float mapValue(
+ float value,
+ float sourceStart,
+ float sourceEnd,
+ float targetStart,
+ float targetEnd
+ ) {
+ return targetStart
+ + (value - sourceStart) * (targetEnd - targetStart) / (sourceEnd - sourceStart);
+ }
+
+ /**
+ * Clamps the given value and remaps to the target interval.
+ *
+ * Note the source interval values should be sorted.
+ */
+ public static float mapValueWithClampBefore(
+ float value,
+ float sourceBottom,
+ float sourceTop,
+ float targetBottom,
+ float targetTop
+ ) {
+ return mapValue(
+ clamp(value, sourceBottom, sourceTop),
+ sourceBottom,
+ sourceTop,
+ targetBottom,
+ targetTop
+ );
+ }
+
+ /**
+ * Remaps the given value to the target interval and clamps.
+ *
+ * Note the target interval values should be sorted.
+ */
+ public static float mapValueWithClampAfter(
+ float value,
+ float sourceBottom,
+ float sourceTop,
+ float targetBottom,
+ float targetTop
+ ) {
+ return clamp(
+ mapValue(value, sourceBottom, sourceTop, targetBottom, targetTop),
+ targetBottom,
+ targetTop
+ );
+ }
+
+ public static float smoothstep(float edge0, float edge1, float x) {
+ // Scale, bias and saturate x to 0..1 range
+ x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+ // Evaluate polynomial
+ return x * x * (3f - 2f * x);
+ }
+
+ public static float smootherstep(float edge0, float edge1, float x) {
+ // Scale, and clamp x to 0..1 range
+ x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+ // Evaluate polynomial
+ return x * x * x * (x * (x * 6f - 15f) + 10f);
+ }
+
+ /**
+ * Applies linear contrast (with clamping).
+ *
+ * @param t - input value in range (0..1)
+ * @param k - contrast factor in range (-1..1):
+ *
+ *
1.0 - maximal contrast
+ *
0.0 - bypass (returns input value)
+ *
-1.0 - minimal contrast (returns 0.5f for any input)
+ *
+ * @return contrasted value in range (0..1)
+ */
+ public static float contrastLinear(float t, float k) {
+ float x = 2f * t - 1f; // -1..1
+ float gamma = (1f + k) / (1f - k);
+ float f = FastMath.clamp(gamma * x, -1f, 1f); // -1..1
+ return 0.5f * (f + 1f); // 0..1
+ }
+
+ /**
+ * Applies non-linear contrast by power function.
+ *
+ * @param t - input value in range (0..1)
+ * @param k - contrast factor in range (-1..1) exclusive:
+ *
+ *
0.999 - maximal contrast
+ *
0.0 - bypass (returns input value)
+ *
-0.999 - minimal contrast
+ *
+ * @return contrasted value in range (0..1)
+ */
+ public static float contrastPower(float t, float k) {
+ float x = 2f * t - 1f; // -1..1
+ float gamma = (1f - k) / (1f + k);
+ float f = FastMath.sign(x) * FastMath.pow(FastMath.abs(x), gamma); // -1..1
+ return 0.5f * (f + 1f); // 0..1
+ }
+
+ /**
+ * Applies non-linear contrast by square splines.
+ *
+ * @param t - input value in range (0..1)
+ * @param k - contrast factor in range (-1..1):
+ *
+ *
1.0 - maximal contrast
+ *
0.0 - bypass (returns input value)
+ *
-1.0 - minimal contrast
+ *
+ * @return contrasted value in range (0..1)
+ */
+ public static float contrastQuadricSpline(float t, float k) {
+ float x = 2f * t - 1f; // -1..1
+ float f = x * (1f + k * (1f - FastMath.abs(x))); // -1..1
+ return 0.5f * (f + 1f); // 0..1
+ }
+
+ /**
+ * Applies non-linear contrast by square splines inverted function.
+ *
+ * @param t - input value in range (0..1)
+ * @param k - contrast factor in range (-2..2):
+ *
+ *
2.0 - maximal contrast
+ *
0.0 - bypass (returns input value)
+ *
-2.0 - minimal contrast
+ *
+ * @return contrasted value in range (0..1)
+ */
+ public static float contrastInvertQuadricSpline(float t, float k) {
+ float x = 2f * t - 1f; // -1..1
+ float g;
+ if (k > 0) {
+ g = FastMath.sign(x) * FastMath.sqrt(FastMath.abs(x)) - 2f * x;
+ } else {
+ g = FastMath.sign(x) * (FastMath.sqrt(1f - FastMath.abs(x)) - 1f);
+ }
+ float f = (1f + k) * x + k * g; // -1..1
+ return 0.5f * (f + 1f); // 0..1
+ }
+
+ /**
+ * Applies non-linear contrast by cubic splines.
+ *
+ * @param t - input value in range (0..1)
+ * @param k - contrast factor in range (-1..1):
+ *
+ *
1.0 - maximal contrast
+ *
0.0 - bypass (returns input value)
+ *
-1.0 - minimal contrast
+ *
+ * @return contrasted value in range (0..1)
+ */
+ public static float contrastCubicSpline(float t, float k) {
+ float x = 2f * t - 1f; // -1..1
+ float f = x * (1f + FastMath.abs(k) * (x * x - 1f));
+ if (k < 0)
+ f -= x * 3f * k * (1f - FastMath.abs(x));
+ return 0.5f * (f + 1f); // 0..1
+ }
+
+ public static float fraction(float f) {
+ return f - (int) f;
+ }
+
+ public static double fraction(double d) {
+ return d - (long) d;
+ }
+
+ /**
+ * @deprecated Do not copy {@link Math} methods.
+ */
+ @Deprecated
+ public static float min(float a, float b) {
+ return a > b ? b : a;
+ }
+
+ public static float min(float a, float b, float c) {
+ return Math.min(Math.min(a, b), c);
+ }
+
+ public static float min(float a, float b, float c, float d) {
+ return Math.min(Math.min(a, b), Math.min(c, d));
+ }
+
+ /**
+ * @deprecated Do not copy {@link Math} methods.
+ */
+ @Deprecated
+ public static float max(float a, float b) {
+ return a > b ? a : b;
+ }
+
+ public static float max(float a, float b, float c) {
+ return Math.max(Math.max(a, b), c);
+ }
+
+ public static float max(float a, float b, float c, float d) {
+ return Math.max(Math.max(a, b), Math.max(c, d));
+ }
+
+ public static float cos(float value) {
+ return (float) Math.cos(value);
+ }
+
+ public static float sin(float value) {
+ return (float) Math.sin(value);
+ }
+
+ public static float ceil(float value) {
+ return (float) Math.ceil(value);
+ }
+
+ public static float floor(float value) {
+ return (float) Math.floor(value);
+ }
+
+ public static float pow(float value, float power) {
+ return (float) Math.pow(value, power);
+ }
+
+ /**
+ * @deprecated Do not copy {@link Math} methods.
+ */
+ @Deprecated
+ public static float abs(float value) {
+ return (float) Math.abs(value);
+ }
+
+ /**
+ * @deprecated Do not copy {@link Math} methods.
+ */
+ @Deprecated
+ public static float round(float value) {
+ return (float) Math.round(value);
+ }
+
+ public static float sqrt(float value) {
+ return (float) Math.sqrt(value);
+ }
+
+ public static float distance(float x0, float y0, float z0, float x1, float y1, float z1) {
+ return distance(x1 - x0, y1 - y0, z1 - z0);
+ }
+
+ public static float distance(float x, float y, float z) {
+ return sqrt(sqrDistance(x, y, z));
+ }
+
+ public static float sqrDistance(float x, float y, float z) {
+ return x * x + y * y + z * z;
+ }
+
+ public static float distance(float x, float y) {
+ return sqrt(sqrDistance(x, y));
+ }
+
+ public static float sqrDistance(float x, float y) {
+ return x * x + y * y;
+ }
+
+ public static float sqrDistance(Vector3f v, float x1, float y1, float z1) {
+ return sqrDistance(x1 - v.x, y1 - v.y, z1 - v.z);
+ }
+
+ public static float sqrDistance(float x0, float y0, float z0, float x1, float y1, float z1) {
+ return sqrDistance(x1 - x0, y1 - y0, z1 - z0);
+ }
+
+ public static float hypot(float x, float y) {
+ return FastMath.sqrt(x * x + y * y);
+ }
+
+ public static float hypot(float x, float y, float z) {
+ return FastMath.sqrt(x * x + y * y + z * z);
+ }
+
+ /**
+ * The same as FastMath.clamp
+ */
+ public static float clamp(float value, float min, float max) {
+ if (value <= min)
+ return min;
+ if (value >= max)
+ return max;
+ return value;
+ }
+
+ public static Vector3f int2101010RevToFloats(int packedValue, Vector3f store) {
+ if (store == null)
+ store = new Vector3f();
+ store.x = packedValue & TEN_BITS_MAX;
+ if ((packedValue & TENTH_BIT) != 0)
+ store.x *= -1;
+ store.y = (packedValue >>> 10) & TEN_BITS_MAX;
+ if ((packedValue & (TENTH_BIT << 10)) != 0)
+ store.y *= -1;
+ store.z = (packedValue >>> 20) & TEN_BITS_MAX;
+ if ((packedValue & (TENTH_BIT << 20)) != 0)
+ store.z *= -1;
+ return store;
+ }
+
+ public static int floatToInt210101Rev(Vector3f values) {
+ int store = 0;
+ store |= ((int) values.x) & TEN_BITS_MAX;
+ if (values.x < 0)
+ store |= TENTH_BIT;
+ store |= (((int) values.y) & TEN_BITS_MAX) << 10;
+ if (values.y < 0)
+ store |= TENTH_BIT << 10;
+ store |= (((int) values.z) & TEN_BITS_MAX) << 20;
+ if (values.z < 0)
+ store |= TENTH_BIT << 20;
+ return store;
+ }
+
+ public static int floatToInt210101RevNormalized(Vector3f values) {
+ int store = 0;
+ store |= ((int) (values.x * TEN_BITS)) & TEN_BITS_MAX;
+ if (values.x < 0)
+ store |= TENTH_BIT;
+ store |= (((int) (values.y * TEN_BITS)) & TEN_BITS_MAX) << 10;
+ if (values.y < 0)
+ store |= TENTH_BIT << 10;
+ store |= (((int) (values.z * TEN_BITS)) & TEN_BITS_MAX) << 20;
+ if (values.z < 0)
+ store |= TENTH_BIT << 20;
+ return store;
+ }
+
+ public static int floatToUnsignedInt210101Rev(Vector3f values) {
+ int store = 0;
+ store |= ((int) values.x) & TEN_BITS;
+ store |= (((int) values.y) & TEN_BITS) << 10;
+ store |= (((int) values.z) & TEN_BITS) << 20;
+ return store;
+ }
+
+ public static int floatToUnsignedInt210101RevNormalized(Vector3f values) {
+ int store = 0;
+ store |= ((int) (values.x * TEN_BITS)) & TEN_BITS;
+ store |= (((int) (values.y * TEN_BITS)) & TEN_BITS) << 10;
+ store |= (((int) (values.z * TEN_BITS)) & TEN_BITS) << 20;
+ return store;
+ }
+
+ public static int floatToInt210101Rev(float x, float y, float z) {
+ int store = 0;
+ store |= ((int) x) & TEN_BITS_MAX;
+ if (x < 0)
+ store |= TENTH_BIT;
+ store |= (((int) y) & TEN_BITS_MAX) << 10;
+ if (y < 0)
+ store |= TENTH_BIT << 10;
+ store |= (((int) z) & TEN_BITS_MAX) << 20;
+ if (z < 0)
+ store |= TENTH_BIT << 20;
+ return store;
+ }
+
+ public static int floatToUnsignedInt210101Rev(float x, float y, float z) {
+ int store = 0;
+ store |= ((int) x) & TEN_BITS;
+ store |= (((int) y) & TEN_BITS) << 10;
+ store |= (((int) z) & TEN_BITS) << 20;
+ return store;
+ }
+
+ public static Vector4f int2101010RevToFloats(int packedValue, Vector4f store) {
+ if (store == null)
+ store = new Vector4f();
+ store.x = packedValue & TEN_BITS_MAX;
+ if ((packedValue & TENTH_BIT) != 0)
+ store.x *= -1;
+ store.y = (packedValue >>> 10) & TEN_BITS_MAX;
+ if ((packedValue & (TENTH_BIT << 10)) != 0)
+ store.y *= -1;
+ store.z = (packedValue >>> 20) & TEN_BITS_MAX;
+ if ((packedValue & (TENTH_BIT << 20)) != 0)
+ store.z *= -1;
+ store.w = (packedValue >>> 30) & TWO_BITS_MAX;
+ if ((packedValue & (SECOND_BIT << 30)) != 0)
+ store.w *= -1;
+ return store;
+ }
+
+ public static int floatToInt210101Rev(Vector4f values) {
+ int store = 0;
+ store |= ((int) values.x) & TEN_BITS_MAX;
+ if (values.x < 0)
+ store |= TENTH_BIT;
+ store |= (((int) values.y) & TEN_BITS_MAX) << 10;
+ if (values.y < 0)
+ store |= TENTH_BIT << 10;
+ store |= (((int) values.z) & TEN_BITS_MAX) << 20;
+ if (values.z < 0)
+ store |= TENTH_BIT << 20;
+ store |= (((int) values.z) & TWO_BITS_MAX) << 30;
+ if (values.w < 0)
+ store |= SECOND_BIT << 30;
+ return store;
+ }
+
+ public static int floatToUnsignedInt210101Rev(Vector4f values) {
+ int store = 0;
+ store |= ((int) values.x) & TEN_BITS;
+ store |= (((int) values.y) & TEN_BITS) << 10;
+ store |= (((int) values.z) & TEN_BITS) << 20;
+ store |= (((int) values.z) & TWO_BITS) << 30;
+ return store;
+ }
+
+ public static Vector3f unsignedInt2101010RevToFloats(int packedValue, Vector3f store) {
+ if (store == null)
+ store = new Vector3f();
+ store.x = packedValue & TEN_BITS;
+ store.y = (packedValue >>> 10) & TEN_BITS;
+ store.z = (packedValue >>> 20) & TEN_BITS;
+ return store;
+ }
+
+ public static Vector4f unsignedInt2101010RevToFloats(int packedValue, Vector4f store) {
+ if (store == null)
+ store = new Vector4f();
+ store.x = packedValue & TEN_BITS;
+ store.y = (packedValue >>> 10) & TEN_BITS;
+ store.z = (packedValue >>> 20) & TEN_BITS;
+ store.w = (packedValue >>> 30) & TWO_BITS;
+ return store;
+ }
+
+ public static Vector3f int2101010RevNormalizedToFloats(int packedValue, Vector3f store) {
+ store = int2101010RevToFloats(packedValue, store);
+ store.x /= TEN_BITS_MAX;
+ store.y /= TEN_BITS_MAX;
+ store.z /= TEN_BITS_MAX;
+ return store;
+ }
+
+ public static Vector4f int2101010RevNormalizedToFloats(int packedValue, Vector4f store) {
+ store = int2101010RevToFloats(packedValue, store);
+ store.x /= TEN_BITS_MAX;
+ store.y /= TEN_BITS_MAX;
+ store.z /= TEN_BITS_MAX;
+ store.w /= TWO_BITS_MAX;
+ return store;
+ }
+
+ public static Vector3f unsignedInt2101010RevNormalizedToFloats(
+ int packedValue,
+ Vector3f store
+ ) {
+ store = unsignedInt2101010RevToFloats(packedValue, store);
+ store.x /= TEN_BITS;
+ store.y /= TEN_BITS;
+ store.z /= TEN_BITS;
+ return store;
+ }
+
+ public static Vector4f unsignedInt2101010RevNormalizedToFloats(
+ int packedValue,
+ Vector4f store
+ ) {
+ store = unsignedInt2101010RevToFloats(packedValue, store);
+ store.x /= TEN_BITS;
+ store.y /= TEN_BITS;
+ store.z /= TEN_BITS;
+ store.w /= TWO_BITS;
+ return store;
+ }
+}
diff --git a/java/io/eiren/math/Vector3d.java b/java/io/eiren/math/Vector3d.java
new file mode 100644
index 000000000..14a180692
--- /dev/null
+++ b/java/io/eiren/math/Vector3d.java
@@ -0,0 +1,257 @@
+package io.eiren.math;
+
+import com.jme3.math.Vector3f;
+
+
+public class Vector3d implements Cloneable {
+
+ public double x;
+ public double y;
+ public double z;
+
+ public Vector3d() {
+ }
+
+ public Vector3d(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public Vector3d(double x1, double y1, double z1, double x2, double y2, double z2) {
+ this.x = x2 - x1;
+ this.y = y2 - y1;
+ this.z = z2 - z1;
+ }
+
+ public Vector3d(Vector3f src) {
+ this(src.x, src.y, src.z);
+ }
+
+ public Vector3d set(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ public Vector3d set(Vector3d v) {
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ return this;
+ }
+
+ public Vector3d add(double addX, double addY, double addZ) {
+ return new Vector3d(this.x + addX, this.y + addY, this.z + addZ);
+ }
+
+ public Vector3d addLocal(Vector3d vec) {
+ return addLocal(vec.x, vec.y, vec.z);
+ }
+
+ public Vector3d addLocal(double addX, double addY, double addZ) {
+ x += addX;
+ y += addY;
+ z += addZ;
+ return this;
+ }
+
+ public Vector3d substract(double subX, double subY, double subZ) {
+ return new Vector3d(this.x - subX, this.y - subY, this.z - subZ);
+ }
+
+ public Vector3d substractLocal(Vector3d vec) {
+ if (null == vec) {
+ return null;
+ }
+ x -= vec.x;
+ y -= vec.y;
+ z -= vec.z;
+ return this;
+ }
+
+ public Vector3d substractLocal(double subX, double subY, double subZ) {
+ x -= subX;
+ y -= subY;
+ z -= subZ;
+ return this;
+ }
+
+ public Vector3d negate() {
+ return new Vector3d(-x, -y, -z);
+ }
+
+ public Vector3d negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ return this;
+ }
+
+ public Vector3d mult(double scalar) {
+ return new Vector3d(x * scalar, y * scalar, z * scalar);
+ }
+
+ public Vector3d multLocal(double scalar) {
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+ public Vector3d divide(double scalar) {
+ return new Vector3d(x / scalar, y / scalar, z / scalar);
+ }
+
+ public Vector3d divideLocal(double scalar) {
+ x /= scalar;
+ y /= scalar;
+ z /= scalar;
+ return this;
+ }
+
+ public double dot(Vector3d v) {
+ return x * v.x + y * v.y + z * v.z;
+ }
+
+ public double dot(double vx, double vy, double vz) {
+ return x * vx + y * vy + z * vz;
+ }
+
+ public Vector3d cross(Vector3d other, Vector3d result) {
+ if (result == null)
+ result = new Vector3d();
+ double resX = ((y * other.z) - (z * other.y));
+ double resY = ((z * other.x) - (x * other.z));
+ double resZ = ((x * other.y) - (y * other.x));
+ result.set(resX, resY, resZ);
+ return result;
+ }
+
+ @Override
+ public Vector3d clone() {
+ return new Vector3d(this.x, this.y, this.z);
+ }
+
+ public Vector3d normalize() {
+ double length = x * x + y * y + z * z;
+ if (length != 1.0 && length != 0.0) {
+ double invLength = 1.0 / Math.sqrt(length);
+ return mult(invLength);
+ }
+ return clone();
+ }
+
+ public Vector3d normalizeLocal() {
+ double length = x * x + y * y + z * z;
+ if (length != 1.0 && length != 0.0) {
+ length = Math.sqrt(length);
+ double invLength = 1.0 / length;
+ x *= invLength;
+ z *= invLength;
+ y *= invLength;
+ }
+ return this;
+ }
+
+ public Vector3f toVector3f() {
+ return new Vector3f((float) x, (float) y, (float) z);
+ }
+
+ public double length() {
+ return Math.sqrt(x * x + y * y + z * z);
+ }
+
+ public double lengthSquared() {
+ return x * x + y * y + z * z;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("Vector3D{")
+ .append(x)
+ .append(',')
+ .append(y)
+ .append(',')
+ .append(z)
+ .append('}')
+ .toString();
+ }
+
+ public void rotateAroundX(float f) {
+ double f1 = Math.cos(f);
+ double f2 = Math.sin(f);
+ double d = x;
+ double d1 = y * f1 + z * f2;
+ double d2 = z * f1 - y * f2;
+ x = (float) d;
+ y = (float) d1;
+ z = (float) d2;
+ }
+
+ public void rotateAroundY(float f) {
+ double f1 = Math.cos(f);
+ double f2 = Math.sin(f);
+ double d = x * f1 + z * f2;
+ double d1 = y;
+ double d2 = z * f1 - x * f2;
+ x = (float) d;
+ y = (float) d1;
+ z = (float) d2;
+ }
+
+ public double distanceTo(Vector3d vec3d) {
+ return Math.sqrt(squaredDistance(vec3d));
+ }
+
+ public double squaredDistance(Vector3d point) {
+ return squaredDistance(point.x, point.y, point.z);
+ }
+
+ public double squaredDistance(double toX, double toY, double toZ) {
+ return (this.x - toX) * (this.x - toX)
+ + (this.y - toY) * (this.y - toY)
+ + (this.z - toZ) * (this.z - toZ);
+ }
+
+ public Vector3d add(Vector3d dir) {
+ return add(dir.x, dir.y, dir.z);
+ }
+
+ public Vector3d substract(Vector3d dir) {
+ return substract(dir.x, dir.y, dir.z);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(x);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(y);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(z);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Vector3d other = (Vector3d) obj;
+ if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
+ return false;
+ if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
+ return false;
+ if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
+ return false;
+ return true;
+ }
+}
diff --git a/java/io/eiren/util/BufferedTimer.java b/java/io/eiren/util/BufferedTimer.java
new file mode 100644
index 000000000..4bba98941
--- /dev/null
+++ b/java/io/eiren/util/BufferedTimer.java
@@ -0,0 +1,130 @@
+package io.eiren.util;
+
+import java.beans.ConstructorProperties;
+
+import com.jme3.system.NanoTimer;
+
+
+/**
+ * This timer accumulate measured TPF and returns average/min/max FPS value
+ */
+public class BufferedTimer extends NanoTimer {
+
+ private final float measureInterval;
+ private float averageTpf;
+ private float averageFps;
+ private float averageFrameRenderTime;
+ private float sumFrameRenderTime;
+ private float sumTpf;
+ private float minFpsCurrent;
+ private float maxFpsCurrent;
+ private float maxFps;
+ private float minFps;
+ private int count;
+ private boolean measured = false;
+
+ /**
+ * Measure average tpf over the provided inverval in seconds
+ *
+ * @param measureInterval interval to measure averages over
+ */
+ public BufferedTimer(float measureInterval) {
+ averageFps = 0;
+ sumTpf = 0;
+ count = 0;
+ this.measureInterval = measureInterval;
+ }
+
+ public float getAverageFPS() {
+ return averageFps;
+ }
+
+ public float getMinFPS() {
+ return minFps;
+ }
+
+ public float getMaxFPS() {
+ return maxFps;
+ }
+
+ public void addRenderTime(float renderTime) {
+ sumFrameRenderTime += renderTime;
+ }
+
+ public float getAverageFrameRenderTime() {
+ return averageFrameRenderTime;
+ }
+
+ public boolean isMeasured() {
+ if (measured) {
+ measured = false;
+ return true;
+ }
+ return false;
+ }
+
+ public TimerSample getCurrentData() {
+ return new TimerSample(getFrameRate(), minFps, maxFps, averageFps);
+ }
+
+ @Override
+ public void update() {
+ super.update();
+ // Accumulate instant rate
+ sumTpf += getTimePerFrame();
+ float fps = getFrameRate();
+ if (fps < minFpsCurrent)
+ minFpsCurrent = fps;
+ if (fps > maxFpsCurrent)
+ maxFpsCurrent = fps;
+ ++count;
+ // Calculate results once per measure interval
+ if (!measured || sumTpf > measureInterval) {
+ // Average results
+ averageTpf = sumTpf / count;
+ averageFps = 1.0f / averageTpf;
+ averageFrameRenderTime = sumFrameRenderTime / count;
+ minFps = minFpsCurrent;
+ maxFps = maxFpsCurrent;
+ // Reset counter
+ sumTpf = 0;
+ sumFrameRenderTime = 0;
+ minFpsCurrent = Float.MAX_VALUE;
+ maxFpsCurrent = 0;
+ count = 0;
+ measured = true;
+ }
+ }
+
+ public static class TimerSample {
+
+ public float fps;
+ public float minFps;
+ public float maxFps;
+ public float averageFps;
+
+ @ConstructorProperties({ "fps", "minFps", "maxFps", "averageFps" })
+ public TimerSample(float fps, float minFps, float maxFps, float averageFps) {
+ this.fps = fps;
+ this.minFps = minFps;
+ this.maxFps = maxFps;
+ this.averageFps = averageFps;
+ }
+
+ public float getFps() {
+ return fps;
+ }
+
+ public float getMinFps() {
+ return minFps;
+ }
+
+ public float getMaxFps() {
+ return maxFps;
+ }
+
+ public float getAverageFps() {
+ return averageFps;
+ }
+ }
+}
diff --git a/java/io/eiren/util/MacOSX.java b/java/io/eiren/util/MacOSX.java
new file mode 100644
index 000000000..e9f027123
--- /dev/null
+++ b/java/io/eiren/util/MacOSX.java
@@ -0,0 +1,41 @@
+package io.eiren.util;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.lang.reflect.Method;
+import java.util.List;
+
+
+public class MacOSX {
+
+ public static void setIcons(List extends Image> icons) {
+ try {
+ Class> applicationClass = Class.forName("com.apple.eawt.Application");
+ Method m = applicationClass.getDeclaredMethod("getApplication");
+ Object application = m.invoke(null);
+ m = application.getClass().getDeclaredMethod("setDockIconImage", Image.class);
+ m.invoke(application, icons.get(icons.size() - 1));
+ } catch (Exception e) {}
+ }
+
+ public static void setTitle(String title) {
+ try {
+ Class> applicationClass = Class.forName("com.apple.eawt.Application");
+ Method m = applicationClass.getDeclaredMethod("getApplication");
+ Object application = m.invoke(null);
+ m = application.getClass().getDeclaredMethod("setDockIconImage", String.class);
+ m.invoke(application, title);
+ } catch (Exception e) {}
+ }
+
+ public static boolean hasRetinaDisplay() {
+ Object obj = Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor");
+ if (obj instanceof Float) {
+ Float f = (Float) obj;
+ int scale = f.intValue();
+ return (scale == 2); // 1 indicates a regular mac display.
+ }
+ return false;
+ }
+
+}
diff --git a/java/io/eiren/util/OperatingSystem.java b/java/io/eiren/util/OperatingSystem.java
new file mode 100644
index 000000000..7182e7df2
--- /dev/null
+++ b/java/io/eiren/util/OperatingSystem.java
@@ -0,0 +1,47 @@
+package io.eiren.util;
+
+import java.io.File;
+
+
+public enum OperatingSystem {
+
+ //@formatter:off
+ LINUX("linux", new String[]{"linux", "unix"}),
+ WINDOWS("windows", new String[]{"win"}),
+ OSX("osx", new String[]{"mac"}),
+ UNKNOWN("unknown", new String[0]);
+ //@fomatter: on
+
+ private final String[] aliases;
+ public final String name;
+ private static OperatingSystem currentPlatform;
+
+ private OperatingSystem(String name, String[] aliases) {
+ this.aliases = aliases;
+ this.name = name;
+ }
+
+ public static String getJavaExecutable(boolean forceConsole) {
+ String separator = System.getProperty("file.separator");
+ String path = System.getProperty("java.home") + separator + "bin" + separator;
+ if(getCurrentPlatform() == WINDOWS) {
+ if(!forceConsole && new File(path + "javaw.exe").isFile())
+ return path + "javaw.exe";
+ return path + "java.exe";
+ }
+ return path + "java";
+ }
+
+ public static OperatingSystem getCurrentPlatform() {
+ if(currentPlatform != null)
+ return currentPlatform;
+ String osName = System.getProperty("os.name").toLowerCase();
+ for(OperatingSystem os : values()) {
+ for(String alias : os.aliases) {
+ if(osName.contains(alias))
+ return currentPlatform = os;
+ }
+ }
+ return UNKNOWN;
+ }
+}
\ No newline at end of file
diff --git a/java/io/eiren/util/StringUtils.java b/java/io/eiren/util/StringUtils.java
new file mode 100644
index 000000000..7ee936964
--- /dev/null
+++ b/java/io/eiren/util/StringUtils.java
@@ -0,0 +1,35 @@
+package io.eiren.util;
+
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+
+
+public class StringUtils {
+
+ private static char DECIMAL_SEP;
+
+ public static char getDecimalSeparator() {
+ if (DECIMAL_SEP == '\u0000') {
+ final Locale l = Locale.getDefault(Locale.Category.FORMAT);
+ // Formatter.java always use "." in the Locale.US
+ DECIMAL_SEP = (l == null || l.equals(Locale.US)
+ ? '.'
+ : DecimalFormatSymbols.getInstance(l).getDecimalSeparator());
+ }
+ return DECIMAL_SEP;
+ }
+
+ public static String prettyNumber(float f) {
+ return prettyNumber(f, 4);
+ }
+
+ public static String prettyNumber(float f, int numDigits) {
+ String str = String.format("%." + numDigits + "f", f);
+ if (numDigits != 0)
+ str = org.apache.commons.lang3.StringUtils.stripEnd(str, "0");
+ char lastChar = str.charAt(str.length() - 1);
+ if (lastChar == getDecimalSeparator())
+ str = str.substring(0, str.length() - 1);
+ return str;
+ }
+}
diff --git a/java/io/eiren/util/Util.java b/java/io/eiren/util/Util.java
new file mode 100644
index 000000000..85fe39b57
--- /dev/null
+++ b/java/io/eiren/util/Util.java
@@ -0,0 +1,176 @@
+package io.eiren.util;
+
+import java.io.Closeable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+
+public class Util {
+
+ public static void close(Object r) {
+ try {
+ if (r != null) {
+ if (r instanceof Closeable)
+ ((Closeable) r).close();
+ else if (r instanceof AutoCloseable)
+ ((AutoCloseable) r).close();
+ }
+ } catch (Exception e) {}
+ }
+
+ public static void close(Object r1, Object r2) {
+ close(r1);
+ close(r2);
+ }
+
+ public static void close(Object... r) {
+ for (int i = 0; i < r.length; ++i)
+ try {
+ if (r[i] != null) {
+ if (r[i] instanceof Closeable)
+ ((Closeable) r[i]).close();
+ else if (r[i] instanceof AutoCloseable)
+ ((AutoCloseable) r[i]).close();
+ }
+ } catch (Exception e) {}
+ }
+
+ public static void close(AutoCloseable... r) {
+ for (int i = 0; i < r.length; ++i)
+ try {
+ if (r[i] != null)
+ r[i].close();
+ } catch (Exception e) {}
+ }
+
+ public static void close(Closeable... r) {
+ for (int i = 0; i < r.length; ++i)
+ try {
+ if (r[i] != null)
+ r[i].close();
+ } catch (Exception e) {}
+ }
+
+ /**
+ *
+ * Performs a deep toString of provided object. It shows content of arrays,
+ * collections and maps (trove not supported yet).
+ *
+ *
+ * Highly ineffective, use only for debug.
+ *
+ *
+ * @param object
+ * @return
+ */
+ public static String toString(Object object) {
+ if (object == null)
+ return "null";
+ StringBuilder buf = new StringBuilder();
+ elementToString(object, buf, new HashSet