aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/math
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/core/com/jme3/math')
-rw-r--r--engine/src/core/com/jme3/math/AbstractTriangle.java49
-rw-r--r--engine/src/core/com/jme3/math/ColorRGBA.java549
-rw-r--r--engine/src/core/com/jme3/math/CurveAndSurfaceMath.java133
-rw-r--r--engine/src/core/com/jme3/math/Eigen3f.java421
-rw-r--r--engine/src/core/com/jme3/math/FastMath.java987
-rw-r--r--engine/src/core/com/jme3/math/Line.java239
-rw-r--r--engine/src/core/com/jme3/math/LineSegment.java631
-rw-r--r--engine/src/core/com/jme3/math/Matrix3f.java1387
-rw-r--r--engine/src/core/com/jme3/math/Matrix4f.java2305
-rw-r--r--engine/src/core/com/jme3/math/Plane.java284
-rw-r--r--engine/src/core/com/jme3/math/Quaternion.java1345
-rw-r--r--engine/src/core/com/jme3/math/Ray.java521
-rw-r--r--engine/src/core/com/jme3/math/Rectangle.java197
-rw-r--r--engine/src/core/com/jme3/math/Ring.java233
-rw-r--r--engine/src/core/com/jme3/math/Spline.java447
-rw-r--r--engine/src/core/com/jme3/math/Transform.java318
-rw-r--r--engine/src/core/com/jme3/math/Triangle.java302
-rw-r--r--engine/src/core/com/jme3/math/Vector2f.java757
-rw-r--r--engine/src/core/com/jme3/math/Vector3f.java1061
-rw-r--r--engine/src/core/com/jme3/math/Vector4f.java1003
-rw-r--r--engine/src/core/com/jme3/math/package.html53
21 files changed, 13222 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/math/AbstractTriangle.java b/engine/src/core/com/jme3/math/AbstractTriangle.java
new file mode 100644
index 0000000..1ea6da5
--- /dev/null
+++ b/engine/src/core/com/jme3/math/AbstractTriangle.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResults;
+
+public abstract class AbstractTriangle implements Collidable {
+
+ public abstract Vector3f get1();
+ public abstract Vector3f get2();
+ public abstract Vector3f get3();
+ public abstract void set(Vector3f v1, Vector3f v2, Vector3f v3);
+
+ public int collideWith(Collidable other, CollisionResults results){
+ return other.collideWith(this, results);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/math/ColorRGBA.java b/engine/src/core/com/jme3/math/ColorRGBA.java
new file mode 100644
index 0000000..a22d453
--- /dev/null
+++ b/engine/src/core/com/jme3/math/ColorRGBA.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+/**
+ * <code>ColorRGBA</code> 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 Savable, 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.
+ */
+ public float r;
+ /**
+ * The green component of the color.
+ */
+ public float g;
+ /**
+ * the blue component of the color.
+ */
+ public float b;
+ /**
+ * the alpha component of the color. 0 is transparent and 1 is opaque
+ */
+ public float a;
+
+ /**
+ * Constructor instantiates a new <code>ColorRGBA</code> object. This
+ * color is the default "white" with all values 1.
+ *
+ */
+ public ColorRGBA() {
+ r = g = b = a = 1.0f;
+ }
+
+ /**
+ * Constructor instantiates a new <code>ColorRGBA</code> 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 color.
+ * @param b the blue component of this color.
+ * @param a the alpha component of this color.
+ */
+ 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 <code>ColorRGBA</code> object, based on
+ * a provided color.
+ * @param rgba the <code>ColorRGBA</code> object to copy.
+ */
+ public ColorRGBA(ColorRGBA rgba) {
+ this.a = rgba.a;
+ this.r = rgba.r;
+ this.g = rgba.g;
+ this.b = rgba.b;
+ }
+
+ /**
+ *
+ * <code>set</code> sets the RGBA values of this color. 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;
+ }
+
+ /**
+ * <code>set</code> sets the values of this color to those set by a parameter
+ * color.
+ *
+ * @param rgba ColorRGBA the color to set this color 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;
+ }
+
+ /**
+ * <code>clamp</code> 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;
+ }
+ }
+
+ /**
+ *
+ * <code>getColorArray</code> retrieves the color values of this object as
+ * a four element float array.
+ * @return the float array that contains the color elements.
+ */
+ public float[] getColorArray() {
+ return new float[]{r, g, b, a};
+ }
+
+ /**
+ * Stores the current r/g/b/a values into the tempf array. The tempf array must have a
+ * length of 4 or greater, or an array index out of bounds exception will be thrown.
+ * @param store The array of floats to store the values into.
+ * @return The float[] after storage.
+ */
+ public float[] getColorArray(float[] store) {
+ store[0] = r;
+ store[1] = g;
+ store[2] = b;
+ store[3] = a;
+ return store;
+ }
+
+ public float getAlpha() {
+ return a;
+ }
+
+ public float getRed() {
+ return r;
+ }
+
+ public float getBlue() {
+ return b;
+ }
+
+ public float getGreen() {
+ return g;
+ }
+
+ /**
+ * Sets this color 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 precentage
+ * 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 color 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;
+ }
+
+ /**
+ *
+ * <code>randomColor</code> is a utility method that generates a random
+ * color.
+ *
+ * @return a random color.
+ */
+ 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 color by the 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.
+ * @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 color 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.
+ * @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 color by the 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 scalar scalar to multiply with
+ * @return The new ColorRGBA. 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 color by the r/g/b/a of the given color and
+ * returns the result as a new ColorRGBA.
+ * @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);
+ }
+
+ /**
+ * Multiplies each r/g/b/a of this color by the 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.
+ * @return The new ColorRGBA. this*c
+ */
+ public ColorRGBA addLocal(ColorRGBA c) {
+ set(c.r + r, c.g + g, c.b + b, c.a + a);
+ return this;
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this color.
+ * The format of the string is:<br>
+ * <Class Name>: [R=RR.RRRR, G=GG.GGGG, B=BB.BBBB, A=AA.AAAA]
+ * @return the string representation of this color.
+ */
+ 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[] object.
+ *
+ * @param floats
+ * The float[] 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;
+ }
+
+ /**
+ * <code>equals</code> returns true if this color is logically equivalent
+ * to a given color. That is, if the values of the two colors are the same.
+ * False is returned otherwise.
+ * @param o the object to compare againts.
+ * @return true if the colors are equal, false otherwise.
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof ColorRGBA)) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ ColorRGBA comp = (ColorRGBA) o;
+ if (Float.compare(r, comp.r) != 0) {
+ return false;
+ }
+ if (Float.compare(g, comp.g) != 0) {
+ return false;
+ }
+ if (Float.compare(b, comp.b) != 0) {
+ return false;
+ }
+ if (Float.compare(a, comp.a) != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * <code>hashCode</code> returns a unique code for this color object based
+ * on it's values. If two colors are logically equivalent, they will return
+ * the same hash code value.
+ * @return the hash code value of this color.
+ */
+ 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;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(r, "r", 0);
+ capsule.write(g, "g", 0);
+ capsule.write(b, "b", 0);
+ capsule.write(a, "a", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ r = capsule.readFloat("r", 0);
+ g = capsule.readFloat("g", 0);
+ b = capsule.readFloat("b", 0);
+ a = capsule.readFloat("a", 0);
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 the current 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 current color definition.
+ */
+ public Vector3f toVector3f() {
+ return new Vector3f(r, g, b);
+ }
+
+ /**
+ * Transform the current 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 current color definition.
+ */
+ public Vector4f toVector4f() {
+ return new Vector4f(r, g, b, a);
+ }
+}
diff --git a/engine/src/core/com/jme3/math/CurveAndSurfaceMath.java b/engine/src/core/com/jme3/math/CurveAndSurfaceMath.java
new file mode 100644
index 0000000..079880f
--- /dev/null
+++ b/engine/src/core/com/jme3/math/CurveAndSurfaceMath.java
@@ -0,0 +1,133 @@
+package com.jme3.math;
+
+import com.jme3.math.Spline.SplineType;
+import java.util.List;
+
+/**
+ * This class offers methods to help with curves and surfaces calculations.
+ * @author Marcin Roguski (Kealthas)
+ */
+public class CurveAndSurfaceMath {
+ private static final float KNOTS_MINIMUM_DELTA = 0.0001f;
+
+ /**
+ * A private constructor is defined to avoid instatiation of this class.
+ */
+ private CurveAndSurfaceMath() {}
+
+ /**
+ * This method interpolates tha data for the nurbs curve.
+ * @param u
+ * the u value
+ * @param nurbSpline
+ * the nurbs spline definition
+ * @param store
+ * the resulting point in 3D space
+ */
+ public static void interpolateNurbs(float u, Spline nurbSpline, Vector3f store) {
+ if (nurbSpline.getType() != SplineType.Nurb) {
+ throw new IllegalArgumentException("Given spline is not of a NURB type!");
+ }
+ List<Vector3f> controlPoints = nurbSpline.getControlPoints();
+ float[] weights = nurbSpline.getWeights();
+ List<Float> knots = nurbSpline.getKnots();
+ int controlPointAmount = controlPoints.size();
+
+ store.set(Vector3f.ZERO);
+ float delimeter = 0;
+ for (int i = 0; i < controlPointAmount; ++i) {
+ float val = weights[i] * CurveAndSurfaceMath.computeBaseFunctionValue(i, nurbSpline.getBasisFunctionDegree(), u, knots);
+ store.addLocal(nurbSpline.getControlPoints().get(i)
+ .mult(val));
+ delimeter += val;
+ }
+ store.divideLocal(delimeter);
+ }
+
+ /**
+ * This method interpolates tha data for the nurbs surface.
+ *
+ * @param u
+ * the u value
+ * @param v
+ * the v value
+ * @param controlPoints
+ * the nurbs' control points
+ * @param knots
+ * the nurbs' knots
+ * @param basisUFunctionDegree
+ * the degree of basis U function
+ * @param basisVFunctionDegree
+ * the degree of basis V function
+ * @param store
+ * the resulting point in 3D space
+ */
+ public static void interpolate(float u, float v, List<List<Vector4f>> controlPoints, List<Float>[] knots,
+ int basisUFunctionDegree, int basisVFunctionDegree, Vector3f store) {
+ store.set(Vector3f.ZERO);
+ float delimeter = 0;
+ int vControlPointsAmount = controlPoints.size();
+ int uControlPointsAmount = controlPoints.get(0).size();
+ for (int i = 0; i < vControlPointsAmount; ++i) {
+ for (int j = 0; j < uControlPointsAmount; ++j) {
+ Vector4f controlPoint = controlPoints.get(i).get(j);
+ float val = controlPoint.w
+ * CurveAndSurfaceMath.computeBaseFunctionValue(i, basisVFunctionDegree, v, knots[1])
+ * CurveAndSurfaceMath.computeBaseFunctionValue(j, basisUFunctionDegree, u, knots[0]);
+ store.addLocal(controlPoint.x * val, controlPoint.y * val, controlPoint.z * val);
+ delimeter += val;
+ }
+ }
+ store.divideLocal(delimeter);
+ }
+
+ /**
+ * This method prepares the knots to be used. If the knots represent non-uniform B-splines (first and last knot values are being
+ * repeated) it leads to NaN results during calculations. This method adds a small number to each of such knots to avoid NaN's.
+ * @param knots
+ * the knots to be prepared to use
+ * @param basisFunctionDegree
+ * the degree of basis function
+ */
+ // TODO: improve this; constant delta may lead to errors if the difference between tha last repeated
+ // point and the following one is lower than it
+ public static void prepareNurbsKnots(List<Float> knots, int basisFunctionDegree) {
+ float delta = KNOTS_MINIMUM_DELTA;
+ float prevValue = knots.get(0).floatValue();
+ for(int i=1;i<knots.size();++i) {
+ float value = knots.get(i).floatValue();
+ if(value<=prevValue) {
+ value += delta;
+ knots.set(i, Float.valueOf(value));
+ delta += KNOTS_MINIMUM_DELTA;
+ } else {
+ delta = KNOTS_MINIMUM_DELTA;//reset the delta's value
+ }
+
+ prevValue = value;
+ }
+ }
+
+ /**
+ * This method computes the base function value for the NURB curve.
+ * @param i
+ * the knot index
+ * @param k
+ * the base function degree
+ * @param t
+ * the knot value
+ * @param knots
+ * the knots' values
+ * @return the base function value
+ */
+ private static float computeBaseFunctionValue(int i, int k, float t, List<Float> knots) {
+ if (k == 1) {
+ return knots.get(i) <= t && t < knots.get(i + 1) ? 1.0f : 0.0f;
+ } else {
+ return (t - knots.get(i)) / (knots.get(i + k - 1) - knots.get(i)) *
+ CurveAndSurfaceMath.computeBaseFunctionValue(i, k - 1, t, knots)
+ + (knots.get(i + k) - t) / (knots.get(i + k) - knots.get(i + 1)) *
+ CurveAndSurfaceMath.computeBaseFunctionValue(i + 1, k - 1, t, knots);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Eigen3f.java b/engine/src/core/com/jme3/math/Eigen3f.java
new file mode 100644
index 0000000..e356057
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Eigen3f.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class Eigen3f implements java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger.getLogger(Eigen3f.class
+ .getName());
+
+ float[] eigenValues = new float[3];
+ Vector3f[] eigenVectors = new Vector3f[3];
+
+ static final double ONE_THIRD_DOUBLE = 1.0 / 3.0;
+ static final double ROOT_THREE_DOUBLE = Math.sqrt(3.0);
+
+
+ public Eigen3f() {
+
+ }
+
+ public Eigen3f(Matrix3f data) {
+ calculateEigen(data);
+ }
+
+ public void calculateEigen(Matrix3f data) {
+ // prep work...
+ eigenVectors[0] = new Vector3f();
+ eigenVectors[1] = new Vector3f();
+ eigenVectors[2] = new Vector3f();
+
+ Matrix3f scaledData = new Matrix3f(data);
+ float maxMagnitude = scaleMatrix(scaledData);
+
+ // Compute the eigenvalues using double-precision arithmetic.
+ double roots[] = new double[3];
+ computeRoots(scaledData, roots);
+ eigenValues[0] = (float) roots[0];
+ eigenValues[1] = (float) roots[1];
+ eigenValues[2] = (float) roots[2];
+
+ float[] maxValues = new float[3];
+ Vector3f[] maxRows = new Vector3f[3];
+ maxRows[0] = new Vector3f();
+ maxRows[1] = new Vector3f();
+ maxRows[2] = new Vector3f();
+
+ for (int i = 0; i < 3; i++) {
+ Matrix3f tempMatrix = new Matrix3f(scaledData);
+ tempMatrix.m00 -= eigenValues[i];
+ tempMatrix.m11 -= eigenValues[i];
+ tempMatrix.m22 -= eigenValues[i];
+ float[] val = new float[1];
+ val[0] = maxValues[i];
+ if (!positiveRank(tempMatrix, val, maxRows[i])) {
+ // Rank was 0 (or very close to 0), so just return.
+ // Rescale back to the original size.
+ if (maxMagnitude > 1f) {
+ for (int j = 0; j < 3; j++) {
+ eigenValues[j] *= maxMagnitude;
+ }
+ }
+
+ eigenVectors[0].set(Vector3f.UNIT_X);
+ eigenVectors[1].set(Vector3f.UNIT_Y);
+ eigenVectors[2].set(Vector3f.UNIT_Z);
+ return;
+ }
+ maxValues[i] = val[0];
+ }
+
+ float maxCompare = maxValues[0];
+ int i = 0;
+ if (maxValues[1] > maxCompare) {
+ maxCompare = maxValues[1];
+ i = 1;
+ }
+ if (maxValues[2] > maxCompare) {
+ i = 2;
+ }
+
+ // use the largest row to compute and order the eigen vectors.
+ switch (i) {
+ case 0:
+ maxRows[0].normalizeLocal();
+ computeVectors(scaledData, maxRows[0], 1, 2, 0);
+ break;
+ case 1:
+ maxRows[1].normalizeLocal();
+ computeVectors(scaledData, maxRows[1], 2, 0, 1);
+ break;
+ case 2:
+ maxRows[2].normalizeLocal();
+ computeVectors(scaledData, maxRows[2], 0, 1, 2);
+ break;
+ }
+
+ // Rescale the values back to the original size.
+ if (maxMagnitude > 1f) {
+ for (i = 0; i < 3; i++) {
+ eigenValues[i] *= maxMagnitude;
+ }
+ }
+ }
+
+ /**
+ * Scale the matrix so its entries are in [-1,1]. The scaling is applied
+ * only when at least one matrix entry has magnitude larger than 1.
+ *
+ * @return the max magnitude in this matrix
+ */
+ private float scaleMatrix(Matrix3f mat) {
+
+ float max = FastMath.abs(mat.m00);
+ float abs = FastMath.abs(mat.m01);
+
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m02);
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m11);
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m12);
+ if (abs > max) {
+ max = abs;
+ }
+ abs = FastMath.abs(mat.m22);
+ if (abs > max) {
+ max = abs;
+ }
+
+ if (max > 1f) {
+ float fInvMax = 1f / max;
+ mat.multLocal(fInvMax);
+ }
+
+ return max;
+ }
+
+ /**
+ * Compute the eigenvectors of the given Matrix, using the
+ * @param mat
+ * @param vect
+ * @param index1
+ * @param index2
+ * @param index3
+ */
+ private void computeVectors(Matrix3f mat, Vector3f vect, int index1,
+ int index2, int index3) {
+ Vector3f vectorU = new Vector3f(), vectorV = new Vector3f();
+ Vector3f.generateComplementBasis(vectorU, vectorV, vect);
+
+ Vector3f tempVect = mat.mult(vectorU);
+ float p00 = eigenValues[index3] - vectorU.dot(tempVect);
+ float p01 = vectorV.dot(tempVect);
+ float p11 = eigenValues[index3] - vectorV.dot(mat.mult(vectorV));
+ float invLength;
+ float max = FastMath.abs(p00);
+ int row = 0;
+ float fAbs = FastMath.abs(p01);
+ if (fAbs > max) {
+ max = fAbs;
+ }
+ fAbs = FastMath.abs(p11);
+ if (fAbs > max) {
+ max = fAbs;
+ row = 1;
+ }
+
+ if (max >= FastMath.ZERO_TOLERANCE) {
+ if (row == 0) {
+ invLength = FastMath.invSqrt(p00 * p00 + p01 * p01);
+ p00 *= invLength;
+ p01 *= invLength;
+ vectorU.mult(p01, eigenVectors[index3])
+ .addLocal(vectorV.mult(p00));
+ } else {
+ invLength = FastMath.invSqrt(p11 * p11 + p01 * p01);
+ p11 *= invLength;
+ p01 *= invLength;
+ vectorU.mult(p11, eigenVectors[index3])
+ .addLocal(vectorV.mult(p01));
+ }
+ } else {
+ if (row == 0) {
+ eigenVectors[index3] = vectorV;
+ } else {
+ eigenVectors[index3] = vectorU;
+ }
+ }
+
+ Vector3f vectorS = vect.cross(eigenVectors[index3]);
+ mat.mult(vect, tempVect);
+ p00 = eigenValues[index1] - vect.dot(tempVect);
+ p01 = vectorS.dot(tempVect);
+ p11 = eigenValues[index1] - vectorS.dot(mat.mult(vectorS));
+ max = FastMath.abs(p00);
+ row = 0;
+ fAbs = FastMath.abs(p01);
+ if (fAbs > max) {
+ max = fAbs;
+ }
+ fAbs = FastMath.abs(p11);
+ if (fAbs > max) {
+ max = fAbs;
+ row = 1;
+ }
+
+ if (max >= FastMath.ZERO_TOLERANCE) {
+ if (row == 0) {
+ invLength = FastMath.invSqrt(p00 * p00 + p01 * p01);
+ p00 *= invLength;
+ p01 *= invLength;
+ eigenVectors[index1] = vect.mult(p01).add(vectorS.mult(p00));
+ } else {
+ invLength = FastMath.invSqrt(p11 * p11 + p01 * p01);
+ p11 *= invLength;
+ p01 *= invLength;
+ eigenVectors[index1] = vect.mult(p11).add(vectorS.mult(p01));
+ }
+ } else {
+ if (row == 0) {
+ eigenVectors[index1].set(vectorS);
+ } else {
+ eigenVectors[index1].set(vect);
+ }
+ }
+
+ eigenVectors[index3].cross(eigenVectors[index1], eigenVectors[index2]);
+ }
+
+ /**
+ * Check the rank of the given Matrix to determine if it is positive. While
+ * doing so, store the max magnitude entry in the given float store and the
+ * max row of the matrix in the Vector store.
+ *
+ * @param matrix
+ * the Matrix3f to analyze.
+ * @param maxMagnitudeStore
+ * a float array in which to store (in the 0th position) the max
+ * magnitude entry of the matrix.
+ * @param maxRowStore
+ * a Vector3f to store the values of the row of the matrix
+ * containing the max magnitude entry.
+ * @return true if the given matrix has a non 0 rank.
+ */
+ private boolean positiveRank(Matrix3f matrix, float[] maxMagnitudeStore, Vector3f maxRowStore) {
+ // Locate the maximum-magnitude entry of the matrix.
+ maxMagnitudeStore[0] = -1f;
+ int iRow, iCol, iMaxRow = -1;
+ for (iRow = 0; iRow < 3; iRow++) {
+ for (iCol = iRow; iCol < 3; iCol++) {
+ float fAbs = FastMath.abs(matrix.get(iRow, iCol));
+ if (fAbs > maxMagnitudeStore[0]) {
+ maxMagnitudeStore[0] = fAbs;
+ iMaxRow = iRow;
+ }
+ }
+ }
+
+ // Return the row containing the maximum, to be used for eigenvector
+ // construction.
+ maxRowStore.set(matrix.getRow(iMaxRow));
+
+ return maxMagnitudeStore[0] >= FastMath.ZERO_TOLERANCE;
+ }
+
+ /**
+ * Generate the base eigen values of the given matrix using double precision
+ * math.
+ *
+ * @param mat
+ * the Matrix3f to analyze.
+ * @param rootsStore
+ * a double array to store the results in. Must be at least
+ * length 3.
+ */
+ private void computeRoots(Matrix3f mat, double[] rootsStore) {
+ // Convert the unique matrix entries to double precision.
+ double a = mat.m00, b = mat.m01, c = mat.m02,
+ d = mat.m11, e = mat.m12,
+ f = mat.m22;
+
+ // The characteristic equation is x^3 - c2*x^2 + c1*x - c0 = 0. The
+ // eigenvalues are the roots to this equation, all guaranteed to be
+ // real-valued, because the matrix is symmetric.
+ double char0 = a * d * f + 2.0 * b * c * e - a
+ * e * e - d * c * c - f * b * b;
+
+ double char1 = a * d - b * b + a * f - c * c
+ + d * f - e * e;
+
+ double char2 = a + d + f;
+
+ // Construct the parameters used in classifying the roots of the
+ // equation and in solving the equation for the roots in closed form.
+ double char2Div3 = char2 * ONE_THIRD_DOUBLE;
+ double abcDiv3 = (char1 - char2 * char2Div3) * ONE_THIRD_DOUBLE;
+ if (abcDiv3 > 0.0) {
+ abcDiv3 = 0.0;
+ }
+
+ double mbDiv2 = 0.5 * (char0 + char2Div3 * (2.0 * char2Div3 * char2Div3 - char1));
+
+ double q = mbDiv2 * mbDiv2 + abcDiv3 * abcDiv3 * abcDiv3;
+ if (q > 0.0) {
+ q = 0.0;
+ }
+
+ // Compute the eigenvalues by solving for the roots of the polynomial.
+ double magnitude = Math.sqrt(-abcDiv3);
+ double angle = Math.atan2(Math.sqrt(-q), mbDiv2) * ONE_THIRD_DOUBLE;
+ double cos = Math.cos(angle);
+ double sin = Math.sin(angle);
+ double root0 = char2Div3 + 2.0 * magnitude * cos;
+ double root1 = char2Div3 - magnitude
+ * (cos + ROOT_THREE_DOUBLE * sin);
+ double root2 = char2Div3 - magnitude
+ * (cos - ROOT_THREE_DOUBLE * sin);
+
+ // Sort in increasing order.
+ if (root1 >= root0) {
+ rootsStore[0] = root0;
+ rootsStore[1] = root1;
+ } else {
+ rootsStore[0] = root1;
+ rootsStore[1] = root0;
+ }
+
+ if (root2 >= rootsStore[1]) {
+ rootsStore[2] = root2;
+ } else {
+ rootsStore[2] = rootsStore[1];
+ if (root2 >= rootsStore[0]) {
+ rootsStore[1] = root2;
+ } else {
+ rootsStore[1] = rootsStore[0];
+ rootsStore[0] = root2;
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ Matrix3f mat = new Matrix3f(2, 1, 1, 1, 2, 1, 1, 1, 2);
+ Eigen3f eigenSystem = new Eigen3f(mat);
+
+ logger.info("eigenvalues = ");
+ for (int i = 0; i < 3; i++)
+ logger.log(Level.INFO, "{0} ", eigenSystem.getEigenValue(i));
+
+ logger.info("eigenvectors = ");
+ for (int i = 0; i < 3; i++) {
+ Vector3f vector = eigenSystem.getEigenVector(i);
+ logger.info(vector.toString());
+ mat.setColumn(i, vector);
+ }
+ logger.info(mat.toString());
+
+ // eigenvalues =
+ // 1.000000 1.000000 4.000000
+ // eigenvectors =
+ // 0.411953 0.704955 0.577350
+ // 0.404533 -0.709239 0.577350
+ // -0.816485 0.004284 0.577350
+ }
+
+ public float getEigenValue(int i) {
+ return eigenValues[i];
+ }
+
+ public Vector3f getEigenVector(int i) {
+ return eigenVectors[i];
+ }
+
+ public float[] getEigenValues() {
+ return eigenValues;
+ }
+
+ public Vector3f[] getEigenVectors() {
+ return eigenVectors;
+ }
+}
diff --git a/engine/src/core/com/jme3/math/FastMath.java b/engine/src/core/com/jme3/math/FastMath.java
new file mode 100644
index 0000000..6043d57
--- /dev/null
+++ b/engine/src/core/com/jme3/math/FastMath.java
@@ -0,0 +1,987 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import java.util.Random;
+
+/**
+ * <code>FastMath</code> 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 an angle given in radians.<br>
+ * Special cases:
+ * <ul><li>If fValue is smaller than -1, then the result is PI.
+ * <li>If the argument is greater than 1, then the result is 0.</ul>
+ * @param fValue The angle, in radians.
+ * @return fValue's acos
+ * @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 an angle given in radians.<br>
+ * Special cases:
+ * <ul><li>If fValue is smaller than -1, then the result is -HALF_PI.
+ * <li>If the argument is greater than 1, then the result is HALF_PI.</ul>
+ * @param fValue The angle, in radians.
+ * @return fValue's asin
+ * @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.<br>
+ * @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);
+ }
+
+ /**
+ * Fast Trig functions for x86. This forces the trig functiosn to stay
+ * within the safe area on the x86 processor (-45 degrees to +45 degrees)
+ * The results may be very slightly off from what the Math and StrictMath
+ * trig functions give due to rounding in the angle reduction but it will be
+ * very very close.
+ *
+ * note: code from wiki posting on java.net by jeffpk
+ */
+ public static float reduceSinAngle(float radians) {
+ radians %= TWO_PI; // put us in -2PI to +2PI space
+ if (Math.abs(radians) > PI) { // put us in -PI to +PI space
+ radians = radians - (TWO_PI);
+ }
+ if (Math.abs(radians) > HALF_PI) {// put us in -PI/2 to +PI/2 space
+ radians = PI - radians;
+ }
+
+ return radians;
+ }
+
+ /**
+ * Returns sine of a value.
+ *
+ * note: code from wiki posting on java.net by jeffpk
+ *
+ * @param fValue
+ * The value to sine, in radians.
+ * @return The sine of fValue.
+ * @see java.lang.Math#sin(double)
+ */
+ public static float sin2(float fValue) {
+ fValue = reduceSinAngle(fValue); // limits angle to between -PI/2 and +PI/2
+ if (Math.abs(fValue) <= Math.PI / 4) {
+ return (float) Math.sin(fValue);
+ }
+
+ return (float) Math.cos(Math.PI / 2 - fValue);
+ }
+
+ /**
+ * Returns cos of a value.
+ *
+ * @param fValue
+ * The value to cosine, in radians.
+ * @return The cosine of fValue.
+ * @see java.lang.Math#cos(double)
+ */
+ public static float cos2(float fValue) {
+ return sin2(fValue + HALF_PI);
+ }
+
+ public static float cos(float v) {
+ return (float) Math.cos(v);
+ }
+
+ 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 <tt>0.0f</tt> (inclusive) to
+ * <tt>1.0f</tt> (exclusive).
+ */
+ public static float nextRandomFloat() {
+ return rand.nextFloat();
+ }
+
+ /**
+ * Returns a random float between min and max.
+ *
+ * @return A random int between <tt>min</tt> (inclusive) to
+ * <tt>max</tt> (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) {
+ 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) {
+ 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) {
+ 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) {
+ 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).
+ *
+ * <p>Source: <a href="http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf">
+ * http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf</a><br><strong>broken link</strong>
+ *
+ * @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/engine/src/core/com/jme3/math/Line.java b/engine/src/core/com/jme3/math/Line.java
new file mode 100644
index 0000000..a0fa79b
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Line.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * <code>Line</code> defines a line. Where a line is defined as infinite along
+ * two points. The two points of the line are defined as the origin and direction.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class Line implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f origin;
+ private Vector3f direction;
+
+ /**
+ * Constructor instantiates a new <code>Line</code> object. The origin and
+ * direction are set to defaults (0,0,0).
+ *
+ */
+ public Line() {
+ origin = new Vector3f();
+ direction = new Vector3f();
+ }
+
+ /**
+ * Constructor instantiates a new <code>Line</code> object. The origin
+ * and direction are set via the parameters.
+ * @param origin the origin of the line.
+ * @param direction the direction of the line.
+ */
+ public Line(Vector3f origin, Vector3f direction) {
+ this.origin = origin;
+ this.direction = direction;
+ }
+
+ /**
+ *
+ * <code>getOrigin</code> returns the origin of the line.
+ * @return the origin of the line.
+ */
+ public Vector3f getOrigin() {
+ return origin;
+ }
+
+ /**
+ *
+ * <code>setOrigin</code> sets the origin of the line.
+ * @param origin the origin of the line.
+ */
+ public void setOrigin(Vector3f origin) {
+ this.origin = origin;
+ }
+
+ /**
+ *
+ * <code>getDirection</code> returns the direction of the line.
+ * @return the direction of the line.
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ *
+ * <code>setDirection</code> sets the direction of the line.
+ * @param direction the direction of the line.
+ */
+ public void setDirection(Vector3f direction) {
+ this.direction = direction;
+ }
+
+ public float distanceSquared(Vector3f point) {
+ TempVars vars = TempVars.get();
+
+ Vector3f compVec1 = vars.vect1;
+ Vector3f compVec2 = vars.vect2;
+
+ point.subtract(origin, compVec1);
+ float lineParameter = direction.dot(compVec1);
+ origin.add(direction.mult(lineParameter, compVec2), compVec2);
+ compVec2.subtract(point, compVec1);
+ float len = compVec1.lengthSquared();
+ vars.release();
+ return len;
+ }
+
+ public float distance(Vector3f point) {
+ return FastMath.sqrt(distanceSquared(point));
+ }
+
+ public void orthogonalLineFit(FloatBuffer points) {
+ if (points == null) {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+
+ Vector3f compVec1 = vars.vect1;
+ Vector3f compVec2 = vars.vect2;
+ Matrix3f compMat1 = vars.tempMat3;
+ Eigen3f compEigen1 = vars.eigen;
+
+ points.rewind();
+
+ // compute average of points
+ int length = points.remaining() / 3;
+
+ BufferUtils.populateFromBuffer(origin, points, 0);
+ for (int i = 1; i < length; i++) {
+ BufferUtils.populateFromBuffer(compVec1, points, i);
+ origin.addLocal(compVec1);
+ }
+
+ origin.multLocal(1f / (float) length);
+
+ // compute sums of products
+ float sumXX = 0.0f, sumXY = 0.0f, sumXZ = 0.0f;
+ float sumYY = 0.0f, sumYZ = 0.0f, sumZZ = 0.0f;
+
+ points.rewind();
+ for (int i = 0; i < length; i++) {
+ BufferUtils.populateFromBuffer(compVec1, points, i);
+ compVec1.subtract(origin, compVec2);
+ sumXX += compVec2.x * compVec2.x;
+ sumXY += compVec2.x * compVec2.y;
+ sumXZ += compVec2.x * compVec2.z;
+ sumYY += compVec2.y * compVec2.y;
+ sumYZ += compVec2.y * compVec2.z;
+ sumZZ += compVec2.z * compVec2.z;
+ }
+
+ //find the smallest eigen vector for the direction vector
+ compMat1.m00 = sumYY + sumZZ;
+ compMat1.m01 = -sumXY;
+ compMat1.m02 = -sumXZ;
+ compMat1.m10 = -sumXY;
+ compMat1.m11 = sumXX + sumZZ;
+ compMat1.m12 = -sumYZ;
+ compMat1.m20 = -sumXZ;
+ compMat1.m21 = -sumYZ;
+ compMat1.m22 = sumXX + sumYY;
+
+ compEigen1.calculateEigen(compMat1);
+ direction = compEigen1.getEigenVector(0);
+
+ vars.release();
+ }
+
+ /**
+ *
+ * <code>random</code> determines a random point along the line.
+ * @return a random point on the line.
+ */
+ public Vector3f random() {
+ return random(null);
+ }
+
+ /**
+ * <code>random</code> determines a random point along the line.
+ *
+ * @param result Vector to store result in
+ * @return a random point on the line.
+ */
+ public Vector3f random(Vector3f result) {
+ if (result == null) {
+ result = new Vector3f();
+ }
+ float rand = (float) Math.random();
+
+ result.x = (origin.x * (1 - rand)) + (direction.x * rand);
+ result.y = (origin.y * (1 - rand)) + (direction.y * rand);
+ result.z = (origin.z * (1 - rand)) + (direction.z * rand);
+
+ return result;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(origin, "origin", Vector3f.ZERO);
+ capsule.write(direction, "direction", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
+ direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Line clone() {
+ try {
+ Line line = (Line) super.clone();
+ line.direction = direction.clone();
+ line.origin = origin.clone();
+ return line;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/LineSegment.java b/engine/src/core/com/jme3/math/LineSegment.java
new file mode 100644
index 0000000..c86619c
--- /dev/null
+++ b/engine/src/core/com/jme3/math/LineSegment.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * <p>LineSegment represents a segment in the space. This is a portion of a Line
+ * that has a limited start and end points.</p>
+ * <p>A LineSegment is defined by an origin, a direction and an extent (or length).
+ * Direction should be a normalized vector. It is not internally normalized.</p>
+ * <p>This class provides methods to calculate distances between LineSegments, Rays and Vectors.
+ * It is also possible to retrieve both end points of the segment {@link LineSegment#getPositiveEnd(Vector3f)}
+ * and {@link LineSegment#getNegativeEnd(Vector3f)}. There are also methods to check whether
+ * a point is within the segment bounds.</p>
+ *
+ * @see Ray
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class LineSegment implements Cloneable, Savable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f origin;
+ private Vector3f direction;
+ private float extent;
+
+ public LineSegment() {
+ origin = new Vector3f();
+ direction = new Vector3f();
+ }
+
+ public LineSegment(LineSegment ls) {
+ this.origin = new Vector3f(ls.getOrigin());
+ this.direction = new Vector3f(ls.getDirection());
+ this.extent = ls.getExtent();
+ }
+
+ /**
+ * <p>Creates a new LineSegment with the given origin, direction and extent.</p>
+ * <p>Note that the origin is not one of the ends of the LineSegment, but its center.</p>
+ */
+ public LineSegment(Vector3f origin, Vector3f direction, float extent) {
+ this.origin = origin;
+ this.direction = direction;
+ this.extent = extent;
+ }
+
+ /**
+ * <p>Creates a new LineSegment with a given origin and end. This constructor will calculate the
+ * center, the direction and the extent.</p>
+ */
+ public LineSegment(Vector3f start, Vector3f end) {
+ this.origin = new Vector3f(0.5f * (start.x + end.x), 0.5f * (start.y + end.y), 0.5f * (start.z + end.z));
+ this.direction = end.subtract(start);
+ this.extent = direction.length() * 0.5f;
+ direction.normalizeLocal();
+ }
+
+ public void set(LineSegment ls) {
+ this.origin = new Vector3f(ls.getOrigin());
+ this.direction = new Vector3f(ls.getDirection());
+ this.extent = ls.getExtent();
+ }
+
+ public float distance(Vector3f point) {
+ return FastMath.sqrt(distanceSquared(point));
+ }
+
+ public float distance(LineSegment ls) {
+ return FastMath.sqrt(distanceSquared(ls));
+ }
+
+ public float distance(Ray r) {
+ return FastMath.sqrt(distanceSquared(r));
+ }
+
+ public float distanceSquared(Vector3f point) {
+ TempVars vars = TempVars.get();
+ Vector3f compVec1 = vars.vect1;
+
+ point.subtract(origin, compVec1);
+ float segmentParameter = direction.dot(compVec1);
+
+ if (-extent < segmentParameter) {
+ if (segmentParameter < extent) {
+ origin.add(direction.mult(segmentParameter, compVec1),
+ compVec1);
+ } else {
+ origin.add(direction.mult(extent, compVec1), compVec1);
+ }
+ } else {
+ origin.subtract(direction.mult(extent, compVec1), compVec1);
+ }
+
+ compVec1.subtractLocal(point);
+ float len = compVec1.lengthSquared();
+ vars.release();
+ return len;
+ }
+
+ public float distanceSquared(LineSegment test) {
+ TempVars vars = TempVars.get();
+ Vector3f compVec1 = vars.vect1;
+
+ origin.subtract(test.getOrigin(), compVec1);
+ float negativeDirectionDot = -(direction.dot(test.getDirection()));
+ float diffThisDot = compVec1.dot(direction);
+ float diffTestDot = -(compVec1.dot(test.getDirection()));
+ float lengthOfDiff = compVec1.lengthSquared();
+ vars.release();
+ float determinant = FastMath.abs(1.0f - negativeDirectionDot
+ * negativeDirectionDot);
+ float s0, s1, squareDistance, extentDeterminant0, extentDeterminant1, tempS0, tempS1;
+
+ if (determinant >= FastMath.FLT_EPSILON) {
+ // segments are not parallel
+ s0 = negativeDirectionDot * diffTestDot - diffThisDot;
+ s1 = negativeDirectionDot * diffThisDot - diffTestDot;
+ extentDeterminant0 = extent * determinant;
+ extentDeterminant1 = test.getExtent() * determinant;
+
+ if (s0 >= -extentDeterminant0) {
+ if (s0 <= extentDeterminant0) {
+ if (s1 >= -extentDeterminant1) {
+ if (s1 <= extentDeterminant1) // region 0 (interior)
+ {
+ // minimum at two interior points of 3D lines
+ float inverseDeterminant = ((float) 1.0)
+ / determinant;
+ s0 *= inverseDeterminant;
+ s1 *= inverseDeterminant;
+ squareDistance = s0
+ * (s0 + negativeDirectionDot * s1 + (2.0f) * diffThisDot)
+ + s1
+ * (negativeDirectionDot * s0 + s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else // region 3 (side)
+ {
+ s1 = test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0)
+ + s1 * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0)
+ + s1 * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ }
+ }
+ } else // region 7 (side)
+ {
+ s1 = -test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ }
+ }
+ } else {
+ if (s1 >= -extentDeterminant1) {
+ if (s1 <= extentDeterminant1) // region 1 (side)
+ {
+ s0 = extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ } else // region 2 (corner)
+ {
+ s1 = test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0)
+ + s1 * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1
+ * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1
+ * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ } else // region 8 (corner)
+ {
+ s1 = -test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 < -extent) {
+ s0 = -extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 <= extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 > test.getExtent()) {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 >= -test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ }
+ } else {
+ if (s1 >= -extentDeterminant1) {
+ if (s1 <= extentDeterminant1) // region 5 (side)
+ {
+ s0 = -extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ } else // region 4 (corner)
+ {
+ s1 = test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 > extent) {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else if (tempS0 >= -extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot)
+ + lengthOfDiff;
+ } else {
+ s0 = -extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1)
+ + s0 * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ } else // region 6 (corner)
+ {
+ s1 = -test.getExtent();
+ tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
+ if (tempS0 > extent) {
+ s0 = extent;
+ squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
+ * (s1 + (2.0f) * diffTestDot) + lengthOfDiff;
+ } else if (tempS0 >= -extent) {
+ s0 = tempS0;
+ squareDistance = -s0 * s0 + s1
+ * (s1 + (2.0f) * diffTestDot) + lengthOfDiff;
+ } else {
+ s0 = -extent;
+ tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
+ if (tempS1 < -test.getExtent()) {
+ s1 = -test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else if (tempS1 <= test.getExtent()) {
+ s1 = tempS1;
+ squareDistance = -s1 * s1 + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ } else {
+ s1 = test.getExtent();
+ squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
+ * (s0 + (2.0f) * diffThisDot)
+ + lengthOfDiff;
+ }
+ }
+ }
+ }
+ } else {
+ // The segments are parallel. The average b0 term is designed to
+ // ensure symmetry of the function. That is, dist(seg0,seg1) and
+ // dist(seg1,seg0) should produce the same number.get
+ float extentSum = extent + test.getExtent();
+ float sign = (negativeDirectionDot > 0.0f ? -1.0f : 1.0f);
+ float averageB0 = (0.5f) * (diffThisDot - sign * diffTestDot);
+ float lambda = -averageB0;
+ if (lambda < -extentSum) {
+ lambda = -extentSum;
+ } else if (lambda > extentSum) {
+ lambda = extentSum;
+ }
+
+ squareDistance = lambda * (lambda + (2.0f) * averageB0)
+ + lengthOfDiff;
+ }
+
+ return FastMath.abs(squareDistance);
+ }
+
+ public float distanceSquared(Ray r) {
+ Vector3f kDiff = r.getOrigin().subtract(origin);
+ float fA01 = -r.getDirection().dot(direction);
+ float fB0 = kDiff.dot(r.getDirection());
+ float fB1 = -kDiff.dot(direction);
+ float fC = kDiff.lengthSquared();
+ float fDet = FastMath.abs(1.0f - fA01 * fA01);
+ float fS0, fS1, fSqrDist, fExtDet;
+
+ if (fDet >= FastMath.FLT_EPSILON) {
+ // The ray and segment are not parallel.
+ fS0 = fA01 * fB1 - fB0;
+ fS1 = fA01 * fB0 - fB1;
+ fExtDet = extent * fDet;
+
+ if (fS0 >= (float) 0.0) {
+ if (fS1 >= -fExtDet) {
+ if (fS1 <= fExtDet) // region 0
+ {
+ // minimum at interior points of ray and segment
+ float fInvDet = ((float) 1.0) / fDet;
+ fS0 *= fInvDet;
+ fS1 *= fInvDet;
+ fSqrDist = fS0
+ * (fS0 + fA01 * fS1 + ((float) 2.0) * fB0)
+ + fS1
+ * (fA01 * fS0 + fS1 + ((float) 2.0) * fB1) + fC;
+ } else // region 1
+ {
+ fS1 = extent;
+ fS0 = -(fA01 * fS1 + fB0);
+ if (fS0 > (float) 0.0) {
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ } else // region 5
+ {
+ fS1 = -extent;
+ fS0 = -(fA01 * fS1 + fB0);
+ if (fS0 > (float) 0.0) {
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ } else {
+ if (fS1 <= -fExtDet) // region 4
+ {
+ fS0 = -(-fA01 * extent + fB0);
+ if (fS0 > (float) 0.0) {
+ fS1 = -extent;
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fS1 = -fB1;
+ if (fS1 < -extent) {
+ fS1 = -extent;
+ } else if (fS1 > extent) {
+ fS1 = extent;
+ }
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ } else if (fS1 <= fExtDet) // region 3
+ {
+ fS0 = (float) 0.0;
+ fS1 = -fB1;
+ if (fS1 < -extent) {
+ fS1 = -extent;
+ } else if (fS1 > extent) {
+ fS1 = extent;
+ }
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else // region 2
+ {
+ fS0 = -(fA01 * extent + fB0);
+ if (fS0 > (float) 0.0) {
+ fS1 = extent;
+ fSqrDist = -fS0 * fS0 + fS1
+ * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fS1 = -fB1;
+ if (fS1 < -extent) {
+ fS1 = -extent;
+ } else if (fS1 > extent) {
+ fS1 = extent;
+ }
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ }
+ } else {
+ // ray and segment are parallel
+ if (fA01 > (float) 0.0) {
+ // opposite direction vectors
+ fS1 = -extent;
+ } else {
+ // same direction vectors
+ fS1 = extent;
+ }
+
+ fS0 = -(fA01 * fS1 + fB0);
+ if (fS0 > (float) 0.0) {
+ fSqrDist = -fS0 * fS0 + fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ } else {
+ fS0 = (float) 0.0;
+ fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
+ }
+ }
+ return FastMath.abs(fSqrDist);
+ }
+
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ public void setDirection(Vector3f direction) {
+ this.direction = direction;
+ }
+
+ public float getExtent() {
+ return extent;
+ }
+
+ public void setExtent(float extent) {
+ this.extent = extent;
+ }
+
+ public Vector3f getOrigin() {
+ return origin;
+ }
+
+ public void setOrigin(Vector3f origin) {
+ this.origin = origin;
+ }
+
+ // P+e*D
+ public Vector3f getPositiveEnd(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ return origin.add((direction.mult(extent, store)), store);
+ }
+
+ // P-e*D
+ public Vector3f getNegativeEnd(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ return origin.subtract((direction.mult(extent, store)), store);
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(origin, "origin", Vector3f.ZERO);
+ capsule.write(direction, "direction", Vector3f.ZERO);
+ capsule.write(extent, "extent", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
+ direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
+ extent = capsule.readFloat("extent", 0);
+ }
+
+ @Override
+ public LineSegment clone() {
+ try {
+ LineSegment segment = (LineSegment) super.clone();
+ segment.direction = direction.clone();
+ segment.origin = origin.clone();
+ return segment;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * <p>Evaluates whether a given point is contained within the axis aligned bounding box
+ * that contains this LineSegment.</p><p>This function is float error aware.</p>
+ */
+ public boolean isPointInsideBounds(Vector3f point) {
+ return isPointInsideBounds(point, Float.MIN_VALUE);
+ }
+
+ /**
+ * <p>Evaluates whether a given point is contained within the axis aligned bounding box
+ * that contains this LineSegment.</p><p>This function accepts an error parameter, which
+ * is added to the extent of the bounding box.</p>
+ */
+ public boolean isPointInsideBounds(Vector3f point, float error) {
+
+ if (FastMath.abs(point.x - origin.x) > FastMath.abs(direction.x * extent) + error) {
+ return false;
+ }
+ if (FastMath.abs(point.y - origin.y) > FastMath.abs(direction.y * extent) + error) {
+ return false;
+ }
+ if (FastMath.abs(point.z - origin.z) > FastMath.abs(direction.z * extent) + error) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Matrix3f.java b/engine/src/core/com/jme3/math/Matrix3f.java
new file mode 100644
index 0000000..96bf1b3
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Matrix3f.java
@@ -0,0 +1,1387 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.logging.Logger;
+
+/**
+ * <code>Matrix3f</code> 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 Savable, 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 <code>Matrix3f</code> 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 <code>Matrix3f</code> 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);
+ }
+
+ /**
+ * <code>copy</code> 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;
+ }
+
+ /**
+ * <code>get</code> retrieves a value from the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> 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.");
+ }
+
+ /**
+ * <code>get(float[])</code> 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().");
+ }
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three columns specified by the
+ * parameter. This column is returned as a <code>Vector3f</code> 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);
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three columns specified by the
+ * parameter. This column is returned as a <code>Vector3f</code> 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;
+ }
+
+ /**
+ * <code>getColumn</code> returns one of three rows as specified by the
+ * parameter. This row is returned as a <code>Vector3f</code> 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);
+ }
+
+ /**
+ * <code>getRow</code> returns one of three rows as specified by the
+ * parameter. This row is returned as a <code>Vector3f</code> 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;
+ }
+
+ /**
+ * <code>toFloatBuffer</code> returns a FloatBuffer object that contains
+ * the matrix data.
+ *
+ * @return matrix data as a FloatBuffer.
+ */
+ public FloatBuffer toFloatBuffer() {
+ FloatBuffer fb = BufferUtils.createFloatBuffer(9);
+
+ fb.put(m00).put(m01).put(m02);
+ fb.put(m10).put(m11).put(m12);
+ fb.put(m20).put(m21).put(m22);
+ fb.rewind();
+ return fb;
+ }
+
+ /**
+ * <code>fillFloatBuffer</code> 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;
+ }
+ }
+
+ /**
+ *
+ * <code>setColumn</code> 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;
+ }
+
+ /**
+ *
+ * <code>setRow</code> 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;
+ }
+
+ /**
+ * <code>set</code> places a given value into the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> 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.");
+ }
+
+ /**
+ *
+ * <code>set</code> 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;
+ }
+
+ /**
+ * <code>set</code> 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);
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+
+ /**
+ *
+ * <code>set</code> defines the values of the matrix based on a supplied
+ * <code>Quaternion</code>. 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);
+ }
+
+ /**
+ * <code>loadIdentity</code> 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);
+ }
+
+ /**
+ * <code>fromAngleAxis</code> 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);
+ }
+
+ /**
+ * <code>fromAngleNormalAxis</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> multiplies this matrix by a given
+ * <code>Vector3f</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> multiplies this matrix by a given
+ * <code>Vector3f</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>determinant</code> 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;
+ }
+
+ /**
+ * <code>transpose</code> <b>locally</b> 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();
+ }
+
+ /**
+ * <code>transposeNew</code> 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;
+ }
+
+ /**
+ * <code>toString</code> 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 <br>[<br>
+ * 1.0 0.0 0.0 <br>
+ * 0.0 1.0 0.0 <br>
+ * 0.0 0.0 1.0 <br>]<br>
+ *
+ * @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();
+ }
+
+ /**
+ *
+ * <code>hashCode</code> 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;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule cap = e.getCapsule(this);
+ cap.write(m00, "m00", 1);
+ cap.write(m01, "m01", 0);
+ cap.write(m02, "m02", 0);
+ cap.write(m10, "m10", 0);
+ cap.write(m11, "m11", 1);
+ cap.write(m12, "m12", 0);
+ cap.write(m20, "m20", 0);
+ cap.write(m21, "m21", 0);
+ cap.write(m22, "m22", 1);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule cap = e.getCapsule(this);
+ m00 = cap.readFloat("m00", 1);
+ m01 = cap.readFloat("m01", 0);
+ m02 = cap.readFloat("m02", 0);
+ m10 = cap.readFloat("m10", 0);
+ m11 = cap.readFloat("m11", 1);
+ m12 = cap.readFloat("m12", 0);
+ m20 = cap.readFloat("m20", 0);
+ m21 = cap.readFloat("m21", 0);
+ m22 = cap.readFloat("m22", 1);
+ }
+
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * <code>scale</code> 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/engine/src/core/com/jme3/math/Matrix4f.java b/engine/src/core/com/jme3/math/Matrix4f.java
new file mode 100644
index 0000000..8521eab
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Matrix4f.java
@@ -0,0 +1,2305 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.logging.Logger;
+
+/**
+ * <code>Matrix4f</code> 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 Savable, 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 <code>Matrix</code> 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 <code>Matrix</code> 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);
+ }
+
+ /**
+ * <code>copy</code> 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);
+ }
+
+ /**
+ * <code>get</code> 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);
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+ }
+
+ /**
+ * <code>get</code> retrieves a value from the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> 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.");
+ }
+
+ /**
+ * <code>getColumn</code> 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);
+ }
+
+ /**
+ * <code>getColumn</code> 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;
+ }
+
+ /**
+ *
+ * <code>setColumn</code> 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);
+ }
+ }
+
+ /**
+ * <code>set</code> places a given value into the matrix at the given
+ * position. If the position is invalid a <code>JmeException</code> 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.");
+ }
+
+ /**
+ * <code>set</code> 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];
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+
+ /**
+ * <code>set</code> 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);
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+
+ /**
+ * <code>transpose</code> 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;
+ }
+
+ /**
+ * <code>toFloatBuffer</code> returns a FloatBuffer object that contains
+ * the matrix data.
+ *
+ * @return matrix data as a FloatBuffer.
+ */
+ public FloatBuffer toFloatBuffer() {
+ return toFloatBuffer(false);
+ }
+
+ /**
+ * <code>toFloatBuffer</code> returns a FloatBuffer object that contains the
+ * matrix data.
+ *
+ * @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. The position is set to 0 for
+ * convenience.
+ */
+ public FloatBuffer toFloatBuffer(boolean columnMajor) {
+ FloatBuffer fb = BufferUtils.createFloatBuffer(16);
+ fillFloatBuffer(fb, columnMajor);
+ fb.rewind();
+ return fb;
+ }
+
+ /**
+ * <code>fillFloatBuffer</code> 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);
+ }
+
+ /**
+ * <code>fillFloatBuffer</code> 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;
+ }
+ }
+
+ /**
+ * <code>readFloatBuffer</code> 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);
+ }
+
+ /**
+ * <code>readFloatBuffer</code> 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;
+ }
+
+ /**
+ * <code>loadIdentity</code> 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);
+ }
+ }
+
+ /**
+ * <code>fromAngleAxis</code> 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);
+ }
+
+ /**
+ * <code>fromAngleNormalAxis</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> multiplies a <code>Vector4f</code> about a rotation
+ * matrix. The resulting vector is returned as a new <code>Vector4f</code>.
+ *
+ * @param vec
+ * vec to multiply against.
+ * @return the rotated vector.
+ */
+ public Vector4f mult(Vector4f vec) {
+ return mult(vec, null);
+ }
+
+ /**
+ * <code>mult</code> multiplies a <code>Vector4f</code> 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.info("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;
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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.info("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.z = m03 * vx + m13 * vy + m23 * vz + m33 * vw;
+
+ return store;
+ }
+
+ /**
+ * <code>multNormal</code> 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;
+ }
+
+ /**
+ * <code>multNormal</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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.info("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;
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>determinant</code> 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;
+ }
+
+ /**
+ * <code>add</code> 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;
+
+ }
+
+ 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;
+ }
+
+ /**
+ * <code>setTranslation</code> 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];
+ }
+
+ /**
+ * <code>setTranslation</code> 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;
+ }
+
+ /**
+ * <code>setTranslation</code> 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;
+ }
+
+ /**
+ * <code>setInverseTranslation</code> 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];
+ }
+
+ /**
+ * <code>angleRotation</code> 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 <code>Vector3f</code>
+ * 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;
+ }
+
+ /**
+ * <code>setRotationQuaternion</code> builds a rotation from a
+ * <code>Quaternion</code>.
+ *
+ * @param quat
+ * the quaternion to build the rotation from.
+ * @throws NullPointerException
+ * if quat is null.
+ */
+ public void setRotationQuaternion(Quaternion quat) {
+ quat.toRotationMatrix(this);
+ }
+
+ /**
+ * <code>setInverseRotationRadians</code> 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);
+ }
+
+ /**
+ * <code>setInverseRotationDegrees</code> 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);
+ }
+
+ /**
+ *
+ * <code>inverseTranslateVect</code> 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;
+ }
+
+ /**
+ *
+ * <code>inverseTranslateVect</code> 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;
+ }
+
+ /**
+ *
+ * <code>inverseTranslateVect</code> 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;
+ }
+
+ /**
+ *
+ * <code>inverseRotateVect</code> 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;
+ }
+
+ /**
+ * <code>toString</code> 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 <br>[<br>
+ * 1.0 0.0 0.0 0.0 <br>
+ * 0.0 1.0 0.0 0.0 <br>
+ * 0.0 0.0 1.0 0.0 <br>
+ * 0.0 0.0 0.0 1.0 <br>]<br>
+ *
+ * @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();
+ }
+
+ /**
+ *
+ * <code>hashCode</code> 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;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule cap = e.getCapsule(this);
+ cap.write(m00, "m00", 1);
+ cap.write(m01, "m01", 0);
+ cap.write(m02, "m02", 0);
+ cap.write(m03, "m03", 0);
+ cap.write(m10, "m10", 0);
+ cap.write(m11, "m11", 1);
+ cap.write(m12, "m12", 0);
+ cap.write(m13, "m13", 0);
+ cap.write(m20, "m20", 0);
+ cap.write(m21, "m21", 0);
+ cap.write(m22, "m22", 1);
+ cap.write(m23, "m23", 0);
+ cap.write(m30, "m30", 0);
+ cap.write(m31, "m31", 0);
+ cap.write(m32, "m32", 0);
+ cap.write(m33, "m33", 1);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule cap = e.getCapsule(this);
+ m00 = cap.readFloat("m00", 1);
+ m01 = cap.readFloat("m01", 0);
+ m02 = cap.readFloat("m02", 0);
+ m03 = cap.readFloat("m03", 0);
+ m10 = cap.readFloat("m10", 0);
+ m11 = cap.readFloat("m11", 1);
+ m12 = cap.readFloat("m12", 0);
+ m13 = cap.readFloat("m13", 0);
+ m20 = cap.readFloat("m20", 0);
+ m21 = cap.readFloat("m21", 0);
+ m22 = cap.readFloat("m22", 1);
+ m23 = cap.readFloat("m23", 0);
+ m30 = cap.readFloat("m30", 0);
+ m31 = cap.readFloat("m31", 0);
+ m32 = cap.readFloat("m32", 0);
+ m33 = cap.readFloat("m33", 1);
+ }
+
+ /**
+ * @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/engine/src/core/com/jme3/math/Plane.java b/engine/src/core/com/jme3/math/Plane.java
new file mode 100644
index 0000000..e14e645
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Plane.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * <code>Plane</code> defines a plane where Normal dot (x,y,z) = Constant.
+ * This provides methods for calculating a "distance" of a point from this
+ * plane. The distance is pseudo due to the fact that it can be negative if the
+ * point is on the non-normal side of the plane.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class Plane implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private static final Logger logger = Logger
+ .getLogger(Plane.class.getName());
+
+ public static enum Side {
+ None,
+ Positive,
+ Negative
+ }
+
+ /**
+ * Vector normal to the plane.
+ */
+ protected Vector3f normal = new Vector3f();
+
+ /**
+ * Constant of the plane. See formula in class definition.
+ */
+ protected float constant;
+
+ /**
+ * Constructor instantiates a new <code>Plane</code> object. This is the
+ * default object and contains a normal of (0,0,0) and a constant of 0.
+ */
+ public Plane() {
+ }
+
+ /**
+ * Constructor instantiates a new <code>Plane</code> object. The normal
+ * and constant values are set at creation.
+ *
+ * @param normal
+ * the normal of the plane.
+ * @param constant
+ * the constant of the plane.
+ */
+ public Plane(Vector3f normal, float constant) {
+ if (normal == null) {
+ throw new IllegalArgumentException("normal cannot be null");
+ }
+
+ this.normal.set(normal);
+ this.constant = constant;
+ }
+
+ /**
+ * <code>setNormal</code> sets the normal of the plane.
+ *
+ * @param normal
+ * the new normal of the plane.
+ */
+ public void setNormal(Vector3f normal) {
+ if (normal == null) {
+ throw new IllegalArgumentException("normal cannot be null");
+ }
+ this.normal.set(normal);
+ }
+
+ /**
+ * <code>setNormal</code> sets the normal of the plane.
+ *
+ */
+ public void setNormal(float x, float y, float z) {
+ this.normal.set(x,y,z);
+ }
+
+ /**
+ * <code>getNormal</code> retrieves the normal of the plane.
+ *
+ * @return the normal of the plane.
+ */
+ public Vector3f getNormal() {
+ return normal;
+ }
+
+ /**
+ * <code>setConstant</code> sets the constant value that helps define the
+ * plane.
+ *
+ * @param constant
+ * the new constant value.
+ */
+ public void setConstant(float constant) {
+ this.constant = constant;
+ }
+
+ /**
+ * <code>getConstant</code> returns the constant of the plane.
+ *
+ * @return the constant of the plane.
+ */
+ public float getConstant() {
+ return constant;
+ }
+
+ public Vector3f getClosestPoint(Vector3f point, Vector3f store){
+// float t = constant - normal.dot(point);
+// return store.set(normal).multLocal(t).addLocal(point);
+ float t = (constant - normal.dot(point)) / normal.dot(normal);
+ return store.set(normal).multLocal(t).addLocal(point);
+ }
+
+ public Vector3f getClosestPoint(Vector3f point){
+ return getClosestPoint(point, new Vector3f());
+ }
+
+ public Vector3f reflect(Vector3f point, Vector3f store){
+ if (store == null)
+ store = new Vector3f();
+
+ float d = pseudoDistance(point);
+ store.set(normal).negateLocal().multLocal(d * 2f);
+ store.addLocal(point);
+ return store;
+ }
+
+ /**
+ * <code>pseudoDistance</code> calculates the distance from this plane to
+ * a provided point. If the point is on the negative side of the plane the
+ * distance returned is negative, otherwise it is positive. If the point is
+ * on the plane, it is zero.
+ *
+ * @param point
+ * the point to check.
+ * @return the signed distance from the plane to a point.
+ */
+ public float pseudoDistance(Vector3f point) {
+ return normal.dot(point) - constant;
+ }
+
+ /**
+ * <code>whichSide</code> returns the side at which a point lies on the
+ * plane. The positive values returned are: NEGATIVE_SIDE, POSITIVE_SIDE and
+ * NO_SIDE.
+ *
+ * @param point
+ * the point to check.
+ * @return the side at which the point lies.
+ */
+ public Side whichSide(Vector3f point) {
+ float dis = pseudoDistance(point);
+ if (dis < 0) {
+ return Side.Negative;
+ } else if (dis > 0) {
+ return Side.Positive;
+ } else {
+ return Side.None;
+ }
+ }
+
+ public boolean isOnPlane(Vector3f point){
+ float dist = pseudoDistance(point);
+ if (dist < FastMath.FLT_EPSILON && dist > -FastMath.FLT_EPSILON)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Initialize this plane using the three points of the given triangle.
+ *
+ * @param t
+ * the triangle
+ */
+ public void setPlanePoints(AbstractTriangle t) {
+ setPlanePoints(t.get1(), t.get2(), t.get3());
+ }
+
+ /**
+ * Initialize this plane using a point of origin and a normal.
+ *
+ * @param origin
+ * @param normal
+ */
+ public void setOriginNormal(Vector3f origin, Vector3f normal){
+ this.normal.set(normal);
+ this.constant = normal.x * origin.x + normal.y * origin.y + normal.z * origin.z;
+ }
+
+ /**
+ * Initialize the Plane using the given 3 points as coplanar.
+ *
+ * @param v1
+ * the first point
+ * @param v2
+ * the second point
+ * @param v3
+ * the third point
+ */
+ public void setPlanePoints(Vector3f v1, Vector3f v2, Vector3f v3) {
+ normal.set(v2).subtractLocal(v1);
+ normal.crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z)
+ .normalizeLocal();
+ constant = normal.dot(v1);
+ }
+
+ /**
+ * <code>toString</code> returns a string thta represents the string
+ * representation of this plane. It represents the normal as a
+ * <code>Vector3f</code> object, so the format is the following:
+ * com.jme.math.Plane [Normal: org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY,
+ * Z=ZZ.ZZZZ] - Constant: CC.CCCCC]
+ *
+ * @return the string representation of this plane.
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [Normal: " + normal + " - Constant: "
+ + constant + "]";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(normal, "normal", Vector3f.ZERO);
+ capsule.write(constant, "constant", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ normal = (Vector3f) capsule.readSavable("normal", Vector3f.ZERO.clone());
+ constant = capsule.readFloat("constant", 0);
+ }
+
+ @Override
+ public Plane clone() {
+ try {
+ Plane p = (Plane) super.clone();
+ p.normal = normal.clone();
+ return p;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Quaternion.java b/engine/src/core/com/jme3/math/Quaternion.java
new file mode 100644
index 0000000..5a5a1c9
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Quaternion.java
@@ -0,0 +1,1345 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import com.jme3.util.TempVars;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.logging.Logger;
+
+/**
+ * <code>Quaternion</code> 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.
+ *
+ * <code>Quaternion</code> is defined by four floating point numbers: {x y z
+ * w}.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Quaternion implements Savable, 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);
+
+ static {
+ DIRECTION_Z.fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
+ }
+ protected float x, y, z, w;
+
+ /**
+ * Constructor instantiates a new <code>Quaternion</code> 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 <code>Quaternion</code> 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 <code>Quaternion</code> 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 <code>Quaternion</code> object to be equal to the
+ * passed <code>Quaternion</code> 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 <code>Quaternion</code> object from a
+ * collection of rotation angles.
+ *
+ * @param angles
+ * the angles of rotation (x, y, z) that will define the
+ * <code>Quaternion</code>.
+ */
+ public Quaternion(float[] angles) {
+ fromAngles(angles);
+ }
+
+ /**
+ * Constructor instantiates a new <code>Quaternion</code> 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 <code>Quaternion</code> 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;
+ }
+ }
+
+ /**
+ * <code>fromAngles</code> 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]);
+ }
+
+ /**
+ * <code>fromAngles</code> builds a Quaternion from the Euler rotation
+ * angles (y,r,p). Note that we are applying in order: roll, pitch, yaw but
+ * we've ordered them in x, y, and z for convenience.
+ * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm</a>
+ *
+ * @param yaw
+ * the Euler yaw of rotation (in radians). (aka Bank, often rot
+ * around x)
+ * @param roll
+ * the Euler roll of rotation (in radians). (aka Heading, often
+ * rot around y)
+ * @param pitch
+ * the Euler pitch of rotation (in radians). (aka Attitude, often
+ * rot around z)
+ */
+ public Quaternion fromAngles(float yaw, float roll, float pitch) {
+ float angle;
+ float sinRoll, sinPitch, sinYaw, cosRoll, cosPitch, cosYaw;
+ angle = pitch * 0.5f;
+ sinPitch = FastMath.sin(angle);
+ cosPitch = FastMath.cos(angle);
+ angle = roll * 0.5f;
+ sinRoll = FastMath.sin(angle);
+ cosRoll = FastMath.cos(angle);
+ angle = yaw * 0.5f;
+ sinYaw = FastMath.sin(angle);
+ cosYaw = FastMath.cos(angle);
+
+ // variables used to reduce multiplication calls.
+ float cosRollXcosPitch = cosRoll * cosPitch;
+ float sinRollXsinPitch = sinRoll * sinPitch;
+ float cosRollXsinPitch = cosRoll * sinPitch;
+ float sinRollXcosPitch = sinRoll * cosPitch;
+
+ w = (cosRollXcosPitch * cosYaw - sinRollXsinPitch * sinYaw);
+ x = (cosRollXcosPitch * sinYaw + sinRollXsinPitch * cosYaw);
+ y = (sinRollXcosPitch * cosYaw + cosRollXsinPitch * sinYaw);
+ z = (cosRollXsinPitch * cosYaw - sinRollXcosPitch * sinYaw);
+
+ normalize();
+ return this;
+ }
+
+ /**
+ * <code>toAngles</code> returns this quaternion converted to Euler
+ * rotation angles (yaw,roll,pitch).<br/>
+ * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm">http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm</a>
+ *
+ * @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); // roll or heading
+ angles[2] = FastMath.asin(2 * test / unit); // pitch or attitude
+ angles[0] = FastMath.atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + sqw); // yaw or bank
+ }
+ return angles;
+ }
+
+ /**
+ *
+ * <code>fromRotationMatrix</code> 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) {
+ // 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;
+ }
+
+ /**
+ * <code>toRotationMatrix</code> 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);
+ }
+
+ /**
+ * <code>toRotationMatrix</code> 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;
+ }
+
+ /**
+ * <code>toRotationMatrix</code> 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) {
+
+ 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;
+ }
+
+ /**
+ * <code>getRotationColumn</code> returns one of three columns specified
+ * by the parameter. This column is returned as a <code>Vector3f</code>
+ * 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);
+ }
+
+ /**
+ * <code>getRotationColumn</code> returns one of three columns specified
+ * by the parameter. This column is returned as a <code>Vector3f</code>
+ * 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;
+ }
+
+ /**
+ * <code>fromAngleAxis</code> 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;
+ }
+
+ /**
+ * <code>fromAngleNormalAxis</code> 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;
+ }
+
+ /**
+ * <code>toAngleAxis</code> sets a given angle and axis to that
+ * represented by the current quaternion. The values are stored as
+ * following: 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;
+ }
+
+ /**
+ * <code>slerp</code> sets this quaternion's value as an interpolation
+ * between two other 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) {
+ // Create a local quaternion to store the interpolated quaternion
+ if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w) {
+ this.set(q1);
+ return this;
+ }
+
+ float result = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z)
+ + (q1.w * q2.w);
+
+ if (result < 0.0f) {
+ // Negate the second quaternion and the result of the dot product
+ q2.x = -q2.x;
+ q2.y = -q2.y;
+ q2.z = -q2.z;
+ q2.w = -q2.w;
+ result = -result;
+ }
+
+ // Set the first and second scale for the interpolation
+ float scale0 = 1 - t;
+ float scale1 = t;
+
+ // Check if the angle between the 2 quaternions was big enough to
+ // warrant such calculations
+ if ((1 - result) > 0.1f) {// Get the angle between the 2 quaternions,
+ // and then store the sin() of that angle
+ float theta = FastMath.acos(result);
+ float invSinTheta = 1f / FastMath.sin(theta);
+
+ // Calculate the scale for q1 and q2, according to the angle and
+ // it's sine value
+ scale0 = FastMath.sin((1 - t) * theta) * invSinTheta;
+ scale1 = FastMath.sin((t * theta)) * invSinTheta;
+ }
+
+ // Calculate the x, y, z and w values for the quaternion by using a
+ // special
+ // form of linear interpolation for quaternions.
+ this.x = (scale0 * q1.x) + (scale1 * q2.x);
+ this.y = (scale0 * q1.y) + (scale1 * q2.y);
+ this.z = (scale0 * q1.z) + (scale1 * q2.z);
+ this.w = (scale0 * q1.w) + (scale1 * q2.w);
+
+ // Return the interpolated quaternion
+ return this;
+ }
+
+ /**
+ * Sets the values of this quaternion to the slerp from itself to q2 by
+ * changeAmnt
+ *
+ * @param q2
+ * Final interpolation value
+ * @param changeAmnt
+ * The amount diffrence
+ */
+ public void slerp(Quaternion q2, float changeAmnt) {
+ if (this.x == q2.x && this.y == q2.y && this.z == q2.z
+ && this.w == q2.w) {
+ return;
+ }
+
+ float result = (this.x * q2.x) + (this.y * q2.y) + (this.z * q2.z)
+ + (this.w * q2.w);
+
+ if (result < 0.0f) {
+ // Negate the second quaternion and the result of the dot product
+ q2.x = -q2.x;
+ q2.y = -q2.y;
+ q2.z = -q2.z;
+ q2.w = -q2.w;
+ result = -result;
+ }
+
+ // Set the first and second scale for the interpolation
+ float scale0 = 1 - changeAmnt;
+ float scale1 = changeAmnt;
+
+ // Check if the angle between the 2 quaternions was big enough to
+ // warrant such calculations
+ if ((1 - result) > 0.1f) {
+ // Get the angle between the 2 quaternions, and then store the sin()
+ // of that angle
+ float theta = FastMath.acos(result);
+ float invSinTheta = 1f / FastMath.sin(theta);
+
+ // Calculate the scale for q1 and q2, according to the angle and
+ // it's sine value
+ scale0 = FastMath.sin((1 - changeAmnt) * theta) * invSinTheta;
+ scale1 = FastMath.sin((changeAmnt * theta)) * invSinTheta;
+ }
+
+ // Calculate the x, y, z and w values for the quaternion by using a
+ // special
+ // form of linear interpolation for quaternions.
+ this.x = (scale0 * this.x) + (scale1 * q2.x);
+ this.y = (scale0 * this.y) + (scale1 * q2.y);
+ this.z = (scale0 * this.z) + (scale1 * q2.z);
+ this.w = (scale0 * this.w) + (scale1 * q2.w);
+ }
+
+ /**
+ * 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();
+ }
+
+ /**
+ * <code>add</code> 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);
+ }
+
+ /**
+ * <code>add</code> 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;
+ }
+
+ /**
+ * <code>subtract</code> 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);
+ }
+
+ /**
+ * <code>subtract</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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 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;
+ }
+
+ /**
+ * <code>apply</code> 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;
+ }
+
+ /**
+ *
+ * <code>fromAxes</code> creates a <code>Quaternion</code> 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]);
+ }
+
+ /**
+ *
+ * <code>fromAxes</code> creates a <code>Quaternion</code> 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);
+ }
+
+ /**
+ *
+ * <code>toAxes</code> 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]);
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>dot</code> 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;
+ }
+
+ /**
+ * <code>norm</code> 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;
+ }
+
+ /**
+ * <code>normalize</code> normalizes the current <code>Quaternion</code>
+ * @deprecated The naming of this method doesn't follow convention.
+ * Please use {@link Quaternion#normalizeLocal() } instead.
+ */
+ @Deprecated
+ public void normalize() {
+ float n = FastMath.invSqrt(norm());
+ x *= n;
+ y *= n;
+ z *= n;
+ w *= n;
+ }
+
+ /**
+ * <code>normalize</code> normalizes the current <code>Quaternion</code>
+ */
+ public void normalizeLocal() {
+ float n = FastMath.invSqrt(norm());
+ x *= n;
+ y *= n;
+ z *= n;
+ w *= n;
+ }
+
+ /**
+ * <code>inverse</code> 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;
+ }
+
+ /**
+ * <code>inverse</code> 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 norma 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;
+ }
+
+ /**
+ * <code>negate</code> inverts the values of the quaternion.
+ *
+ */
+ public void negate() {
+ x *= -1;
+ y *= -1;
+ z *= -1;
+ w *= -1;
+ }
+
+ /**
+ *
+ * <code>toString</code> creates the string representation of this
+ * <code>Quaternion</code>. The values of the quaternion are displace (x,
+ * y, z, w), in the following manner: <br>
+ * (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 + ")";
+ }
+
+ /**
+ * <code>equals</code> 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;
+ }
+
+ /**
+ *
+ * <code>hashCode</code> 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;
+
+ }
+
+ /**
+ * <code>readExternal</code> builds a quaternion from an
+ * <code>ObjectInput</code> object. <br>
+ * 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();
+ }
+
+ /**
+ * <code>writeExternal</code> writes this quaternion out to a
+ * <code>ObjectOutput</code> 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);
+ }
+
+ /**
+ * <code>lookAt</code> 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();
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule cap = e.getCapsule(this);
+ cap.write(x, "x", 0);
+ cap.write(y, "y", 0);
+ cap.write(z, "z", 0);
+ cap.write(w, "w", 1);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule cap = e.getCapsule(this);
+ x = cap.readFloat("x", 0);
+ y = cap.readFloat("y", 0);
+ z = cap.readFloat("z", 0);
+ w = cap.readFloat("w", 1);
+ }
+
+ /**
+ * @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
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Ray.java b/engine/src/core/com/jme3/math/Ray.java
new file mode 100644
index 0000000..f363613
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Ray.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.*;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+
+/**
+ * <code>Ray</code> defines a line segment which has an origin and a direction.
+ * That is, a point and an infinite ray is cast from this point. The ray is
+ * defined by the following equation: R(t) = origin + t*direction for t >= 0.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Ray implements Savable, Cloneable, Collidable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ /**
+ * The ray's begining point.
+ */
+ public Vector3f origin = new Vector3f();
+
+ /**
+ * The direction of the ray.
+ */
+ public Vector3f direction = new Vector3f(0, 0, 1);
+
+
+ public float limit = Float.POSITIVE_INFINITY;
+
+ /**
+ * Constructor instantiates a new <code>Ray</code> object. As default, the
+ * origin is (0,0,0) and the direction is (0,0,1).
+ *
+ */
+ public Ray() {
+ }
+
+ /**
+ * Constructor instantiates a new <code>Ray</code> object. The origin and
+ * direction are given.
+ * @param origin the origin of the ray.
+ * @param direction the direction the ray travels in.
+ */
+ public Ray(Vector3f origin, Vector3f direction) {
+ setOrigin(origin);
+ setDirection(direction);
+ }
+
+ /**
+ * <code>intersect</code> determines if the Ray intersects a triangle.
+ * @param t the Triangle to test against.
+ * @return true if the ray collides.
+ */
+// public boolean intersect(Triangle t) {
+// return intersect(t.get(0), t.get(1), t.get(2));
+// }
+ /**
+ * <code>intersect</code> determines if the Ray intersects a triangle
+ * defined by the specified points.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @return true if the ray collides.
+ */
+// public boolean intersect(Vector3f v0,Vector3f v1,Vector3f v2){
+// return intersectWhere(v0, v1, v2, null);
+// }
+ /**
+ * <code>intersectWhere</code> determines if the Ray intersects a triangle. It then
+ * stores the point of intersection in the given loc vector
+ * @param t the Triangle to test against.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides)
+ * @return true if the ray collides.
+ */
+ public boolean intersectWhere(Triangle t, Vector3f loc) {
+ return intersectWhere(t.get(0), t.get(1), t.get(2), loc);
+ }
+
+ /**
+ * <code>intersectWhere</code> determines if the Ray intersects a triangle
+ * defined by the specified points and if so it stores the point of
+ * intersection in the given loc vector.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) if null, only boolean is calculated.
+ * @return true if the ray collides.
+ */
+ public boolean intersectWhere(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f loc) {
+ return intersects(v0, v1, v2, loc, false, false);
+ }
+
+ /**
+ * <code>intersectWherePlanar</code> determines if the Ray intersects a
+ * triangle and if so it stores the point of
+ * intersection in the given loc vector as t, u, v where t is the distance
+ * from the origin to the point of intersection and u,v is the intersection
+ * point in terms of the triangle plane.
+ *
+ * @param t the Triangle to test against.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) as t, u, v
+ * @return true if the ray collides.
+ */
+ public boolean intersectWherePlanar(Triangle t, Vector3f loc) {
+ return intersectWherePlanar(t.get(0), t.get(1), t.get(2), loc);
+ }
+
+ /**
+ * <code>intersectWherePlanar</code> determines if the Ray intersects a
+ * triangle defined by the specified points and if so it stores the point of
+ * intersection in the given loc vector as t, u, v where t is the distance
+ * from the origin to the point of intersection and u,v is the intersection
+ * point in terms of the triangle plane.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) as t, u, v
+ * @return true if the ray collides.
+ */
+ public boolean intersectWherePlanar(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f loc) {
+ return intersects(v0, v1, v2, loc, true, false);
+ }
+
+ /**
+ * <code>intersects</code> does the actual intersection work.
+ *
+ * @param v0
+ * first point of the triangle.
+ * @param v1
+ * second point of the triangle.
+ * @param v2
+ * third point of the triangle.
+ * @param store
+ * storage vector - if null, no intersection is calc'd
+ * @param doPlanar
+ * true if we are calcing planar results.
+ * @param quad
+ * @return true if ray intersects triangle
+ */
+ private boolean intersects(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f store, boolean doPlanar, boolean quad) {
+ TempVars vars = TempVars.get();
+
+ Vector3f tempVa = vars.vect1,
+ tempVb = vars.vect2,
+ tempVc = vars.vect3,
+ tempVd = vars.vect4;
+
+ Vector3f diff = origin.subtract(v0, tempVa);
+ Vector3f edge1 = v1.subtract(v0, tempVb);
+ Vector3f edge2 = v2.subtract(v0, tempVc);
+ Vector3f norm = edge1.cross(edge2, tempVd);
+
+ float dirDotNorm = direction.dot(norm);
+ float sign;
+ if (dirDotNorm > FastMath.FLT_EPSILON) {
+ sign = 1;
+ } else if (dirDotNorm < -FastMath.FLT_EPSILON) {
+ sign = -1f;
+ dirDotNorm = -dirDotNorm;
+ } else {
+ // ray and triangle/quad are parallel
+ vars.release();
+ return false;
+ }
+
+ float dirDotDiffxEdge2 = sign * direction.dot(diff.cross(edge2, edge2));
+ if (dirDotDiffxEdge2 >= 0.0f) {
+ float dirDotEdge1xDiff = sign
+ * direction.dot(edge1.crossLocal(diff));
+
+ if (dirDotEdge1xDiff >= 0.0f) {
+ if (!quad ? dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm : dirDotEdge1xDiff <= dirDotNorm) {
+ float diffDotNorm = -sign * diff.dot(norm);
+ if (diffDotNorm >= 0.0f) {
+ // this method always returns
+ vars.release();
+
+ // ray intersects triangle
+ // if storage vector is null, just return true,
+ if (store == null) {
+ return true;
+ }
+
+ // else fill in.
+ float inv = 1f / dirDotNorm;
+ float t = diffDotNorm * inv;
+ if (!doPlanar) {
+ store.set(origin).addLocal(direction.x * t,
+ direction.y * t, direction.z * t);
+ } else {
+ // these weights can be used to determine
+ // interpolated values, such as texture coord.
+ // eg. texcoord s,t at intersection point:
+ // s = w0*s0 + w1*s1 + w2*s2;
+ // t = w0*t0 + w1*t1 + w2*t2;
+ float w1 = dirDotDiffxEdge2 * inv;
+ float w2 = dirDotEdge1xDiff * inv;
+ //float w0 = 1.0f - w1 - w2;
+ store.set(t, w1, w2);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ vars.release();
+ return false;
+ }
+
+ public float intersects(Vector3f v0, Vector3f v1, Vector3f v2) {
+ float edge1X = v1.x - v0.x;
+ float edge1Y = v1.y - v0.y;
+ float edge1Z = v1.z - v0.z;
+
+ float edge2X = v2.x - v0.x;
+ float edge2Y = v2.y - v0.y;
+ float edge2Z = v2.z - v0.z;
+
+ float normX = ((edge1Y * edge2Z) - (edge1Z * edge2Y));
+ float normY = ((edge1Z * edge2X) - (edge1X * edge2Z));
+ float normZ = ((edge1X * edge2Y) - (edge1Y * edge2X));
+
+ float dirDotNorm = direction.x * normX + direction.y * normY + direction.z * normZ;
+
+ float diffX = origin.x - v0.x;
+ float diffY = origin.y - v0.y;
+ float diffZ = origin.z - v0.z;
+
+ float sign;
+ if (dirDotNorm > FastMath.FLT_EPSILON) {
+ sign = 1;
+ } else if (dirDotNorm < -FastMath.FLT_EPSILON) {
+ sign = -1f;
+ dirDotNorm = -dirDotNorm;
+ } else {
+ // ray and triangle/quad are parallel
+ return Float.POSITIVE_INFINITY;
+ }
+
+ float diffEdge2X = ((diffY * edge2Z) - (diffZ * edge2Y));
+ float diffEdge2Y = ((diffZ * edge2X) - (diffX * edge2Z));
+ float diffEdge2Z = ((diffX * edge2Y) - (diffY * edge2X));
+
+ float dirDotDiffxEdge2 = sign * (direction.x * diffEdge2X
+ + direction.y * diffEdge2Y
+ + direction.z * diffEdge2Z);
+
+ if (dirDotDiffxEdge2 >= 0.0f) {
+ diffEdge2X = ((edge1Y * diffZ) - (edge1Z * diffY));
+ diffEdge2Y = ((edge1Z * diffX) - (edge1X * diffZ));
+ diffEdge2Z = ((edge1X * diffY) - (edge1Y * diffX));
+
+ float dirDotEdge1xDiff = sign * (direction.x * diffEdge2X
+ + direction.y * diffEdge2Y
+ + direction.z * diffEdge2Z);
+
+ if (dirDotEdge1xDiff >= 0.0f) {
+ if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) {
+ float diffDotNorm = -sign * (diffX * normX + diffY * normY + diffZ * normZ);
+ if (diffDotNorm >= 0.0f) {
+ // ray intersects triangle
+ // fill in.
+ float inv = 1f / dirDotNorm;
+ float t = diffDotNorm * inv;
+ return t;
+ }
+ }
+ }
+ }
+
+ return Float.POSITIVE_INFINITY;
+ }
+
+ /**
+ * <code>intersectWherePlanar</code> determines if the Ray intersects a
+ * quad defined by the specified points and if so it stores the point of
+ * intersection in the given loc vector as t, u, v where t is the distance
+ * from the origin to the point of intersection and u,v is the intersection
+ * point in terms of the quad plane.
+ * One edge of the quad is [v0,v1], another one [v0,v2]. The behaviour thus is like
+ * {@link #intersectWherePlanar(Vector3f, Vector3f, Vector3f, Vector3f)} except for
+ * the extended area, which is equivalent to the union of the triangles [v0,v1,v2]
+ * and [-v0+v1+v2,v1,v2].
+ *
+ * @param v0
+ * top left point of the quad.
+ * @param v1
+ * top right point of the quad.
+ * @param v2
+ * bottom left point of the quad.
+ * @param loc
+ * storage vector to save the collision point in (if the ray
+ * collides) as t, u, v
+ * @return true if the ray collides with the quad.
+ */
+ public boolean intersectWherePlanarQuad(Vector3f v0, Vector3f v1, Vector3f v2,
+ Vector3f loc) {
+ return intersects(v0, v1, v2, loc, true, true);
+ }
+
+ /**
+ *
+ * @param p
+ * @param loc
+ * @return true if the ray collides with the given Plane
+ */
+ public boolean intersectsWherePlane(Plane p, Vector3f loc) {
+ float denominator = p.getNormal().dot(direction);
+
+ if (denominator > -FastMath.FLT_EPSILON && denominator < FastMath.FLT_EPSILON) {
+ return false; // coplanar
+ }
+ float numerator = -(p.getNormal().dot(origin) - p.getConstant());
+ float ratio = numerator / denominator;
+
+ if (ratio < FastMath.FLT_EPSILON) {
+ return false; // intersects behind origin
+ }
+ loc.set(direction).multLocal(ratio).addLocal(origin);
+
+ return true;
+ }
+
+ public int collideWith(Collidable other, CollisionResults results) {
+ if (other instanceof BoundingVolume) {
+ BoundingVolume bv = (BoundingVolume) other;
+ return bv.collideWith(this, results);
+ } else if (other instanceof AbstractTriangle) {
+ AbstractTriangle tri = (AbstractTriangle) other;
+ float d = intersects(tri.get1(), tri.get2(), tri.get3());
+ if (Float.isInfinite(d) || Float.isNaN(d)) {
+ return 0;
+ }
+
+ Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin);
+ results.addCollision(new CollisionResult(point, d));
+ return 1;
+ } else {
+ throw new UnsupportedCollisionException();
+ }
+ }
+
+ public float distanceSquared(Vector3f point) {
+ TempVars vars = TempVars.get();
+
+ Vector3f tempVa = vars.vect1,
+ tempVb = vars.vect2;
+
+ point.subtract(origin, tempVa);
+ float rayParam = direction.dot(tempVa);
+ if (rayParam > 0) {
+ origin.add(direction.mult(rayParam, tempVb), tempVb);
+ } else {
+ tempVb.set(origin);
+ rayParam = 0.0f;
+ }
+
+ tempVb.subtract(point, tempVa);
+ float len = tempVa.lengthSquared();
+ vars.release();
+ return len;
+ }
+
+ /**
+ *
+ * <code>getOrigin</code> retrieves the origin point of the ray.
+ *
+ * @return the origin of the ray.
+ */
+ public Vector3f getOrigin() {
+ return origin;
+ }
+
+ /**
+ *
+ * <code>setOrigin</code> sets the origin of the ray.
+ * @param origin the origin of the ray.
+ */
+ public void setOrigin(Vector3f origin) {
+ this.origin.set(origin);
+ }
+
+ /**
+ * <code>getLimit</code> returns the limit of the ray, aka the length.
+ * If the limit is not infinity, then this ray is a line with length <code>
+ * limit</code>.
+ *
+ * @return the limit of the ray, aka the length.
+ */
+ public float getLimit() {
+ return limit;
+ }
+
+ /**
+ * <code>setLimit</code> sets the limit of the ray.
+ * @param limit the limit of the ray.
+ * @see Ray#getLimit()
+ */
+ public void setLimit(float limit) {
+ this.limit = limit;
+ }
+
+ /**
+ *
+ * <code>getDirection</code> retrieves the direction vector of the ray.
+ * @return the direction of the ray.
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ *
+ * <code>setDirection</code> sets the direction vector of the ray.
+ * @param direction the direction of the ray.
+ */
+ public void setDirection(Vector3f direction) {
+ assert direction.isUnitVector();
+ this.direction.set(direction);
+ }
+
+ /**
+ * Copies information from a source ray into this ray.
+ *
+ * @param source
+ * the ray to copy information from
+ */
+ public void set(Ray source) {
+ origin.set(source.getOrigin());
+ direction.set(source.getDirection());
+ }
+
+ public String toString() {
+ return getClass().getSimpleName() + " [Origin: " + origin + ", Direction: " + direction + "]";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(origin, "origin", Vector3f.ZERO);
+ capsule.write(direction, "direction", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
+ direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Ray clone() {
+ try {
+ Ray r = (Ray) super.clone();
+ r.direction = direction.clone();
+ r.origin = origin.clone();
+ return r;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Rectangle.java b/engine/src/core/com/jme3/math/Rectangle.java
new file mode 100644
index 0000000..310270c
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Rectangle.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+
+/**
+ *
+ * <code>Rectangle</code> defines a finite plane within three dimensional space
+ * that is specified via three points (A, B, C). These three points define a
+ * triangle with the forth point defining the rectangle ((B + C) - A.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+
+public final class Rectangle implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f a, b, c;
+
+ /**
+ * Constructor creates a new <code>Rectangle</code> with no defined corners.
+ * A, B, and C must be set to define a valid rectangle.
+ *
+ */
+ public Rectangle() {
+ a = new Vector3f();
+ b = new Vector3f();
+ c = new Vector3f();
+ }
+
+ /**
+ * Constructor creates a new <code>Rectangle</code> with defined A, B, and C
+ * points that define the area of the rectangle.
+ *
+ * @param a
+ * the first corner of the rectangle.
+ * @param b
+ * the second corner of the rectangle.
+ * @param c
+ * the third corner of the rectangle.
+ */
+ public Rectangle(Vector3f a, Vector3f b, Vector3f c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+
+ /**
+ * <code>getA</code> returns the first point of the rectangle.
+ *
+ * @return the first point of the rectangle.
+ */
+ public Vector3f getA() {
+ return a;
+ }
+
+ /**
+ * <code>setA</code> sets the first point of the rectangle.
+ *
+ * @param a
+ * the first point of the rectangle.
+ */
+ public void setA(Vector3f a) {
+ this.a = a;
+ }
+
+ /**
+ * <code>getB</code> returns the second point of the rectangle.
+ *
+ * @return the second point of the rectangle.
+ */
+ public Vector3f getB() {
+ return b;
+ }
+
+ /**
+ * <code>setB</code> sets the second point of the rectangle.
+ *
+ * @param b
+ * the second point of the rectangle.
+ */
+ public void setB(Vector3f b) {
+ this.b = b;
+ }
+
+ /**
+ * <code>getC</code> returns the third point of the rectangle.
+ *
+ * @return the third point of the rectangle.
+ */
+ public Vector3f getC() {
+ return c;
+ }
+
+ /**
+ * <code>setC</code> sets the third point of the rectangle.
+ *
+ * @param c
+ * the third point of the rectangle.
+ */
+ public void setC(Vector3f c) {
+ this.c = c;
+ }
+
+ /**
+ * <code>random</code> returns a random point within the plane defined by:
+ * A, B, C, and (B + C) - A.
+ *
+ * @return a random point within the rectangle.
+ */
+ public Vector3f random() {
+ return random(null);
+ }
+
+ /**
+ * <code>random</code> returns a random point within the plane defined by:
+ * A, B, C, and (B + C) - A.
+ *
+ * @param result
+ * Vector to store result in
+ * @return a random point within the rectangle.
+ */
+ public Vector3f random(Vector3f result) {
+ if (result == null) {
+ result = new Vector3f();
+ }
+
+ float s = FastMath.nextRandomFloat();
+ float t = FastMath.nextRandomFloat();
+
+ float aMod = 1.0f - s - t;
+ result.set(a.mult(aMod).addLocal(b.mult(s).addLocal(c.mult(t))));
+ return result;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(a, "a", Vector3f.ZERO);
+ capsule.write(b, "b", Vector3f.ZERO);
+ capsule.write(c, "c", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ a = (Vector3f) capsule.readSavable("a", Vector3f.ZERO.clone());
+ b = (Vector3f) capsule.readSavable("b", Vector3f.ZERO.clone());
+ c = (Vector3f) capsule.readSavable("c", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Rectangle clone() {
+ try {
+ Rectangle r = (Rectangle) super.clone();
+ r.a = a.clone();
+ r.b = b.clone();
+ r.c = c.clone();
+ return r;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Ring.java b/engine/src/core/com/jme3/math/Ring.java
new file mode 100644
index 0000000..20f1ea4
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Ring.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+
+/**
+ * <code>Ring</code> defines a flat ring or disk within three dimensional
+ * space that is specified via the ring's center point, an up vector, an inner
+ * radius, and an outer radius.
+ *
+ * @author Andrzej Kapolka
+ * @author Joshua Slack
+ */
+
+public final class Ring implements Savable, Cloneable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f center, up;
+ private float innerRadius, outerRadius;
+ private transient static Vector3f b1 = new Vector3f(), b2 = new Vector3f();
+
+ /**
+ * Constructor creates a new <code>Ring</code> lying on the XZ plane,
+ * centered at the origin, with an inner radius of zero and an outer radius
+ * of one (a unit disk).
+ */
+ public Ring() {
+ center = new Vector3f();
+ up = Vector3f.UNIT_Y.clone();
+ innerRadius = 0f;
+ outerRadius = 1f;
+ }
+
+ /**
+ * Constructor creates a new <code>Ring</code> with defined center point,
+ * up vector, and inner and outer radii.
+ *
+ * @param center
+ * the center of the ring.
+ * @param up
+ * the unit up vector defining the ring's orientation.
+ * @param innerRadius
+ * the ring's inner radius.
+ * @param outerRadius
+ * the ring's outer radius.
+ */
+ public Ring(Vector3f center, Vector3f up, float innerRadius,
+ float outerRadius) {
+ this.center = center;
+ this.up = up;
+ this.innerRadius = innerRadius;
+ this.outerRadius = outerRadius;
+ }
+
+ /**
+ * <code>getCenter</code> returns the center of the ring.
+ *
+ * @return the center of the ring.
+ */
+ public Vector3f getCenter() {
+ return center;
+ }
+
+ /**
+ * <code>setCenter</code> sets the center of the ring.
+ *
+ * @param center
+ * the center of the ring.
+ */
+ public void setCenter(Vector3f center) {
+ this.center = center;
+ }
+
+ /**
+ * <code>getUp</code> returns the ring's up vector.
+ *
+ * @return the ring's up vector.
+ */
+ public Vector3f getUp() {
+ return up;
+ }
+
+ /**
+ * <code>setUp</code> sets the ring's up vector.
+ *
+ * @param up
+ * the ring's up vector.
+ */
+ public void setUp(Vector3f up) {
+ this.up = up;
+ }
+
+ /**
+ * <code>getInnerRadius</code> returns the ring's inner radius.
+ *
+ * @return the ring's inner radius.
+ */
+ public float getInnerRadius() {
+ return innerRadius;
+ }
+
+ /**
+ * <code>setInnerRadius</code> sets the ring's inner radius.
+ *
+ * @param innerRadius
+ * the ring's inner radius.
+ */
+ public void setInnerRadius(float innerRadius) {
+ this.innerRadius = innerRadius;
+ }
+
+ /**
+ * <code>getOuterRadius</code> returns the ring's outer radius.
+ *
+ * @return the ring's outer radius.
+ */
+ public float getOuterRadius() {
+ return outerRadius;
+ }
+
+ /**
+ * <code>setOuterRadius</code> sets the ring's outer radius.
+ *
+ * @param outerRadius
+ * the ring's outer radius.
+ */
+ public void setOuterRadius(float outerRadius) {
+ this.outerRadius = outerRadius;
+ }
+
+ /**
+ *
+ * <code>random</code> returns a random point within the ring.
+ *
+ * @return a random point within the ring.
+ */
+ public Vector3f random() {
+ return random(null);
+ }
+
+ /**
+ *
+ * <code>random</code> returns a random point within the ring.
+ *
+ * @param result Vector to store result in
+ * @return a random point within the ring.
+ */
+ public Vector3f random(Vector3f result) {
+ if (result == null) {
+ result = new Vector3f();
+ }
+
+ // compute a random radius according to the ring area distribution
+ float inner2 = innerRadius * innerRadius, outer2 = outerRadius
+ * outerRadius, r = FastMath.sqrt(inner2
+ + FastMath.nextRandomFloat() * (outer2 - inner2)), theta = FastMath
+ .nextRandomFloat()
+ * FastMath.TWO_PI;
+ up.cross(Vector3f.UNIT_X, b1);
+ if (b1.lengthSquared() < FastMath.FLT_EPSILON) {
+ up.cross(Vector3f.UNIT_Y, b1);
+ }
+ b1.normalizeLocal();
+ up.cross(b1, b2);
+ result.set(b1).multLocal(r * FastMath.cos(theta)).addLocal(center);
+ result.scaleAdd(r * FastMath.sin(theta), b2, result);
+ return result;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(center, "center", Vector3f.ZERO);
+ capsule.write(up, "up", Vector3f.UNIT_Z);
+ capsule.write(innerRadius, "innerRadius", 0f);
+ capsule.write(outerRadius, "outerRadius", 1f);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ center = (Vector3f) capsule.readSavable("center",
+ Vector3f.ZERO.clone());
+ up = (Vector3f) capsule
+ .readSavable("up", Vector3f.UNIT_Z.clone());
+ innerRadius = capsule.readFloat("innerRadius", 0f);
+ outerRadius = capsule.readFloat("outerRadius", 1f);
+ }
+
+ @Override
+ public Ring clone() {
+ try {
+ Ring r = (Ring) super.clone();
+ r.center = center.clone();
+ r.up = up.clone();
+ return r;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/math/Spline.java b/engine/src/core/com/jme3/math/Spline.java
new file mode 100644
index 0000000..b28a797
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Spline.java
@@ -0,0 +1,447 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class Spline implements Savable {
+
+ public enum SplineType {
+ Linear,
+ CatmullRom,
+ Bezier,
+ Nurb
+ }
+
+ private List<Vector3f> controlPoints = new ArrayList<Vector3f>();
+ private List<Float> knots; //knots of NURBS spline
+ private float[] weights; //weights of NURBS spline
+ private int basisFunctionDegree; //degree of NURBS spline basis function (computed automatically)
+ private boolean cycle;
+ private List<Float> segmentsLength;
+ private float totalLength;
+ private List<Vector3f> CRcontrolPoints;
+ private float curveTension = 0.5f;
+ private SplineType type = SplineType.CatmullRom;
+
+ public Spline() {
+ }
+
+ /**
+ * Create a spline
+ * @param splineType the type of the spline @see {SplineType}
+ * @param controlPoints an array of vector to use as control points of the spline
+ * If the type of the curve is Bezier curve the control points should be provided
+ * in the appropriate way. Each point 'p' describing control position in the scene
+ * should be surrounded by two handler points. This applies to every point except
+ * for the border points of the curve, who should have only one handle point.
+ * The pattern should be as follows:
+ * P0 - H0 : H1 - P1 - H1 : ... : Hn - Pn
+ *
+ * n is the amount of 'P' - points.
+ * @param curveTension the tension of the spline
+ * @param cycle true if the spline cycle.
+ */
+ public Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle) {
+ if(splineType==SplineType.Nurb) {
+ throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
+ }
+ for (int i = 0; i < controlPoints.length; i++) {
+ Vector3f vector3f = controlPoints[i];
+ this.controlPoints.add(vector3f);
+ }
+ type = splineType;
+ this.curveTension = curveTension;
+ this.cycle = cycle;
+ this.computeTotalLentgh();
+ }
+
+ /**
+ * Create a spline
+ * @param splineType the type of the spline @see {SplineType}
+ * @param controlPoints a list of vector to use as control points of the spline
+ * If the type of the curve is Bezier curve the control points should be provided
+ * in the appropriate way. Each point 'p' describing control position in the scene
+ * should be surrounded by two handler points. This applies to every point except
+ * for the border points of the curve, who should have only one handle point.
+ * The pattern should be as follows:
+ * P0 - H0 : H1 - P1 - H1 : ... : Hn - Pn
+ *
+ * n is the amount of 'P' - points.
+ * @param curveTension the tension of the spline
+ * @param cycle true if the spline cycle.
+ */
+ public Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle) {
+ if(splineType==SplineType.Nurb) {
+ throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
+ }
+ type = splineType;
+ this.controlPoints.addAll(controlPoints);
+ this.curveTension = curveTension;
+ this.cycle = cycle;
+ this.computeTotalLentgh();
+ }
+
+ /**
+ * Create a NURBS spline. A spline type is automatically set to SplineType.Nurb.
+ * The cycle is set to <b>false</b> by default.
+ * @param controlPoints a list of vector to use as control points of the spline
+ * @param nurbKnots the nurb's spline knots
+ */
+ public Spline(List<Vector4f> controlPoints, List<Float> nurbKnots) {
+ //input data control
+ for(int i=0;i<nurbKnots.size()-1;++i) {
+ if(nurbKnots.get(i)>nurbKnots.get(i+1)) {
+ throw new IllegalArgumentException("The knots values cannot decrease!");
+ }
+ }
+
+ //storing the data
+ type = SplineType.Nurb;
+ this.weights = new float[controlPoints.size()];
+ this.knots = nurbKnots;
+ this.basisFunctionDegree = nurbKnots.size() - weights.length;
+ for(int i=0;i<controlPoints.size();++i) {
+ Vector4f controlPoint = controlPoints.get(i);
+ this.controlPoints.add(new Vector3f(controlPoint.x, controlPoint.y, controlPoint.z));
+ this.weights[i] = controlPoint.w;
+ }
+ CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree);
+ this.computeTotalLentgh();
+ }
+
+ private void initCatmullRomWayPoints(List<Vector3f> list) {
+ if (CRcontrolPoints == null) {
+ CRcontrolPoints = new ArrayList<Vector3f>();
+ } else {
+ CRcontrolPoints.clear();
+ }
+ int nb = list.size() - 1;
+
+ if (cycle) {
+ CRcontrolPoints.add(list.get(list.size() - 2));
+ } else {
+ CRcontrolPoints.add(list.get(0).subtract(list.get(1).subtract(list.get(0))));
+ }
+
+ for (Iterator<Vector3f> it = list.iterator(); it.hasNext();) {
+ Vector3f vector3f = it.next();
+ CRcontrolPoints.add(vector3f);
+ }
+ if (cycle) {
+ CRcontrolPoints.add(list.get(1));
+ } else {
+ CRcontrolPoints.add(list.get(nb).add(list.get(nb).subtract(list.get(nb - 1))));
+ }
+
+ }
+
+ /**
+ * Adds a controlPoint to the spline
+ * @param controlPoint a position in world space
+ */
+ public void addControlPoint(Vector3f controlPoint) {
+ if (controlPoints.size() > 2 && this.cycle) {
+ controlPoints.remove(controlPoints.size() - 1);
+ }
+ controlPoints.add(controlPoint);
+ if (controlPoints.size() >= 2 && this.cycle) {
+ controlPoints.add(controlPoints.get(0));
+ }
+ if (controlPoints.size() > 1) {
+ this.computeTotalLentgh();
+ }
+ }
+
+ /**
+ * remove the controlPoint from the spline
+ * @param controlPoint the controlPoint to remove
+ */
+ public void removeControlPoint(Vector3f controlPoint) {
+ controlPoints.remove(controlPoint);
+ if (controlPoints.size() > 1) {
+ this.computeTotalLentgh();
+ }
+ }
+
+ public void clearControlPoints(){
+ controlPoints.clear();
+ totalLength = 0;
+ }
+
+ /**
+ * This method computes the total length of the curve.
+ */
+ private void computeTotalLentgh() {
+ totalLength = 0;
+ float l = 0;
+ if (segmentsLength == null) {
+ segmentsLength = new ArrayList<Float>();
+ } else {
+ segmentsLength.clear();
+ }
+ if (type == SplineType.Linear) {
+ if (controlPoints.size() > 1) {
+ for (int i = 0; i < controlPoints.size() - 1; i++) {
+ l = controlPoints.get(i + 1).subtract(controlPoints.get(i)).length();
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ } else if(type == SplineType.Bezier) {
+ this.computeBezierLength();
+ } else if(type == SplineType.Nurb) {
+ this.computeNurbLength();
+ } else {
+ this.initCatmullRomWayPoints(controlPoints);
+ this.computeCatmulLength();
+ }
+ }
+
+ /**
+ * This method computes the Catmull Rom curve length.
+ */
+ private void computeCatmulLength() {
+ float l = 0;
+ if (controlPoints.size() > 1) {
+ for (int i = 0; i < controlPoints.size() - 1; i++) {
+ l = FastMath.getCatmullRomP1toP2Length(CRcontrolPoints.get(i),
+ CRcontrolPoints.get(i + 1), CRcontrolPoints.get(i + 2), CRcontrolPoints.get(i + 3), 0, 1, curveTension);
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ }
+
+ /**
+ * This method calculates the Bezier curve length.
+ */
+ private void computeBezierLength() {
+ float l = 0;
+ if (controlPoints.size() > 1) {
+ for (int i = 0; i < controlPoints.size() - 1; i+=3) {
+ l = FastMath.getBezierP1toP2Length(controlPoints.get(i),
+ controlPoints.get(i + 1), controlPoints.get(i + 2), controlPoints.get(i + 3));
+ segmentsLength.add(l);
+ totalLength += l;
+ }
+ }
+ }
+
+ /**
+ * This method calculates the NURB curve length.
+ */
+ private void computeNurbLength() {
+ //TODO: implement
+ }
+
+ /**
+ * Iterpolate a position on the spline
+ * @param value a value from 0 to 1 that represent the postion between the curent control point and the next one
+ * @param currentControlPoint the current control point
+ * @param store a vector to store the result (use null to create a new one that will be returned by the method)
+ * @return the position
+ */
+ public Vector3f interpolate(float value, int currentControlPoint, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ switch (type) {
+ case CatmullRom:
+ FastMath.interpolateCatmullRom(value, curveTension, CRcontrolPoints.get(currentControlPoint), CRcontrolPoints.get(currentControlPoint + 1), CRcontrolPoints.get(currentControlPoint + 2), CRcontrolPoints.get(currentControlPoint + 3), store);
+ break;
+ case Linear:
+ FastMath.interpolateLinear(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), store);
+ break;
+ case Bezier:
+ FastMath.interpolateBezier(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), controlPoints.get(currentControlPoint + 2), controlPoints.get(currentControlPoint + 3), store);
+ break;
+ case Nurb:
+ CurveAndSurfaceMath.interpolateNurbs(value, this, store);
+ break;
+ default:
+ break;
+ }
+ return store;
+ }
+
+ /**
+ * returns the curve tension
+ */
+ public float getCurveTension() {
+ return curveTension;
+ }
+
+ /**
+ * sets the curve tension
+ *
+ * @param curveTension the tension
+ */
+ public void setCurveTension(float curveTension) {
+ this.curveTension = curveTension;
+ if(type==SplineType.CatmullRom) {
+ this.computeTotalLentgh();
+ }
+ }
+
+ /**
+ * returns true if the spline cycle
+ */
+ public boolean isCycle() {
+ return cycle;
+ }
+
+ /**
+ * set to true to make the spline cycle
+ * @param cycle
+ */
+ public void setCycle(boolean cycle) {
+ if(type!=SplineType.Nurb) {
+ if (controlPoints.size() >= 2) {
+ if (this.cycle && !cycle) {
+ controlPoints.remove(controlPoints.size() - 1);
+ }
+ if (!this.cycle && cycle) {
+ controlPoints.add(controlPoints.get(0));
+ }
+ this.cycle = cycle;
+ this.computeTotalLentgh();
+ } else {
+ this.cycle = cycle;
+ }
+ }
+ }
+
+ /**
+ * return the total lenght of the spline
+ */
+ public float getTotalLength() {
+ return totalLength;
+ }
+
+ /**
+ * return the type of the spline
+ */
+ public SplineType getType() {
+ return type;
+ }
+
+ /**
+ * Sets the type of the spline
+ * @param type
+ */
+ public void setType(SplineType type) {
+ this.type = type;
+ this.computeTotalLentgh();
+ }
+
+ /**
+ * returns this spline control points
+ */
+ public List<Vector3f> getControlPoints() {
+ return controlPoints;
+ }
+
+ /**
+ * returns a list of float representing the segments lenght
+ */
+ public List<Float> getSegmentsLength() {
+ return segmentsLength;
+ }
+
+ //////////// NURBS getters /////////////////////
+
+ /**
+ * This method returns the minimum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
+ * type - NPE will be thrown.
+ * @return the minimum nurb curve knot value
+ */
+ public float getMinNurbKnot() {
+ return knots.get(basisFunctionDegree - 1);
+ }
+
+ /**
+ * This method returns the maximum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
+ * type - NPE will be thrown.
+ * @return the maximum nurb curve knot value
+ */
+ public float getMaxNurbKnot() {
+ return knots.get(weights.length);
+ }
+
+ /**
+ * This method returns NURBS' spline knots.
+ * @return NURBS' spline knots
+ */
+ public List<Float> getKnots() {
+ return knots;
+ }
+
+ /**
+ * This method returns NURBS' spline weights.
+ * @return NURBS' spline weights
+ */
+ public float[] getWeights() {
+ return weights;
+ }
+
+ /**
+ * This method returns NURBS' spline basis function degree.
+ * @return NURBS' spline basis function degree
+ */
+ public int getBasisFunctionDegree() {
+ return basisFunctionDegree;
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.writeSavableArrayList((ArrayList) controlPoints, "controlPoints", null);
+ oc.write(type, "type", SplineType.CatmullRom);
+ float list[] = new float[segmentsLength.size()];
+ for (int i = 0; i < segmentsLength.size(); i++) {
+ list[i] = segmentsLength.get(i);
+ }
+ oc.write(list, "segmentsLength", null);
+
+ oc.write(totalLength, "totalLength", 0);
+ oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null);
+ oc.write(curveTension, "curveTension", 0.5f);
+ oc.write(cycle, "cycle", false);
+ oc.writeSavableArrayList((ArrayList<Float>)knots, "knots", null);
+ oc.write(weights, "weights", null);
+ oc.write(basisFunctionDegree, "basisFunctionDegree", 0);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule in = im.getCapsule(this);
+
+ controlPoints = (ArrayList<Vector3f>) in.readSavableArrayList("wayPoints", null);
+ float list[] = in.readFloatArray("segmentsLength", null);
+ if (list != null) {
+ segmentsLength = new ArrayList<Float>();
+ for (int i = 0; i < list.length; i++) {
+ segmentsLength.add(new Float(list[i]));
+ }
+ }
+ type = in.readEnum("pathSplineType", SplineType.class, SplineType.CatmullRom);
+ totalLength = in.readFloat("totalLength", 0);
+ CRcontrolPoints = (ArrayList<Vector3f>) in.readSavableArrayList("CRControlPoints", null);
+ curveTension = in.readFloat("curveTension", 0.5f);
+ cycle = in.readBoolean("cycle", false);
+ knots = in.readSavableArrayList("knots", null);
+ weights = in.readFloatArray("weights", null);
+ basisFunctionDegree = in.readInt("basisFunctionDegree", 0);
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Transform.java b/engine/src/core/com/jme3/math/Transform.java
new file mode 100644
index 0000000..7ccd847
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Transform.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+
+/**
+ * Started Date: Jul 16, 2004<br><br>
+ * Represents a translation, rotation and scale in one object.
+ *
+ * @author Jack Lindamood
+ * @author Joshua Slack
+ */
+public final class Transform implements Savable, 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;
+ }
+
+ /**
+ * 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=1,1,1 scale=0,0,0 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;
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(rot, "rot", new Quaternion());
+ capsule.write(translation, "translation", Vector3f.ZERO);
+ capsule.write(scale, "scale", Vector3f.UNIT_XYZ);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+
+ rot = (Quaternion)capsule.readSavable("rot", new Quaternion());
+ translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO);
+ scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);
+ }
+
+ @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/engine/src/core/com/jme3/math/Triangle.java b/engine/src/core/com/jme3/math/Triangle.java
new file mode 100644
index 0000000..6faa53e
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Triangle.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.math;
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import java.io.IOException;
+
+/**
+ * <code>Triangle</code> defines an object for containing triangle information.
+ * The triangle is defined by a collection of three {@link Vector3f}
+ * objects.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public class Triangle extends AbstractTriangle implements Savable, java.io.Serializable {
+
+ static final long serialVersionUID = 1;
+
+ private Vector3f pointa = new Vector3f();
+ private Vector3f pointb = new Vector3f();
+ private Vector3f pointc = new Vector3f();
+ private transient Vector3f center;
+ private transient Vector3f normal;
+ private float projection;
+ private int index;
+
+ public Triangle() {
+ }
+
+ /**
+ * Constructor instantiates a new <Code>Triangle</code> object with the
+ * supplied vectors as the points. It is recommended that the vertices
+ * be supplied in a counter clockwise winding to support normals for a
+ * right handed coordinate system.
+ * @param p1 the first point of the triangle.
+ * @param p2 the second point of the triangle.
+ * @param p3 the third point of the triangle.
+ */
+ public Triangle(Vector3f p1, Vector3f p2, Vector3f p3) {
+ pointa.set(p1);
+ pointb.set(p2);
+ pointc.set(p3);
+ }
+
+ /**
+ *
+ * <code>get</code> retrieves a point on the triangle denoted by the index
+ * supplied.
+ * @param i the index of the point.
+ * @return the point.
+ */
+ public Vector3f get(int i) {
+ switch (i) {
+ case 0:
+ return pointa;
+ case 1:
+ return pointb;
+ case 2:
+ return pointc;
+ default:
+ return null;
+ }
+ }
+
+ public Vector3f get1() {
+ return pointa;
+ }
+
+ public Vector3f get2() {
+ return pointb;
+ }
+
+ public Vector3f get3() {
+ return pointc;
+ }
+
+ /**
+ *
+ * <code>set</code> sets one of the triangle's points to that specified as
+ * a parameter.
+ * @param i the index to place the point.
+ * @param point the point to set.
+ */
+ public void set(int i, Vector3f point) {
+ switch (i) {
+ case 0:
+ pointa.set(point);
+ break;
+ case 1:
+ pointb.set(point);
+ break;
+ case 2:
+ pointc.set(point);
+ break;
+ }
+ }
+
+ /**
+ *
+ * <code>set</code> sets one of the triangle's points to that specified as
+ * a parameter.
+ * @param i the index to place the point.
+ */
+ public void set(int i, float x, float y, float z) {
+ switch (i) {
+ case 0:
+ pointa.set(x, y, z);
+ break;
+ case 1:
+ pointb.set(x, y, z);
+ break;
+ case 2:
+ pointc.set(x, y, z);
+ break;
+ }
+ }
+
+ public void set1(Vector3f v) {
+ pointa.set(v);
+ }
+
+ public void set2(Vector3f v) {
+ pointb.set(v);
+ }
+
+ public void set3(Vector3f v) {
+ pointc.set(v);
+ }
+
+ public void set(Vector3f v1, Vector3f v2, Vector3f v3) {
+ pointa.set(v1);
+ pointb.set(v2);
+ pointc.set(v3);
+ }
+
+ /**
+ * calculateCenter finds the average point of the triangle.
+ *
+ */
+ public void calculateCenter() {
+ if (center == null) {
+ center = new Vector3f(pointa);
+ } else {
+ center.set(pointa);
+ }
+ center.addLocal(pointb).addLocal(pointc).multLocal(FastMath.ONE_THIRD);
+ }
+
+ /**
+ * calculateNormal generates the normal for this triangle
+ *
+ */
+ public void calculateNormal() {
+ if (normal == null) {
+ normal = new Vector3f(pointb);
+ } else {
+ normal.set(pointb);
+ }
+ normal.subtractLocal(pointa).crossLocal(pointc.x - pointa.x, pointc.y - pointa.y, pointc.z - pointa.z);
+ normal.normalizeLocal();
+ }
+
+ /**
+ * obtains the center point of this triangle (average of the three triangles)
+ * @return the center point.
+ */
+ public Vector3f getCenter() {
+ if (center == null) {
+ calculateCenter();
+ }
+ return center;
+ }
+
+ /**
+ * sets the center point of this triangle (average of the three triangles)
+ * @param center the center point.
+ */
+ public void setCenter(Vector3f center) {
+ this.center = center;
+ }
+
+ /**
+ * obtains the unit length normal vector of this triangle, if set or
+ * calculated
+ *
+ * @return the normal vector
+ */
+ public Vector3f getNormal() {
+ if (normal == null) {
+ calculateNormal();
+ }
+ return normal;
+ }
+
+ /**
+ * sets the normal vector of this triangle (to conform, must be unit length)
+ * @param normal the normal vector.
+ */
+ public void setNormal(Vector3f normal) {
+ this.normal = normal;
+ }
+
+ /**
+ * obtains the projection of the vertices relative to the line origin.
+ * @return the projection of the triangle.
+ */
+ public float getProjection() {
+ return this.projection;
+ }
+
+ /**
+ * sets the projection of the vertices relative to the line origin.
+ * @param projection the projection of the triangle.
+ */
+ public void setProjection(float projection) {
+ this.projection = projection;
+ }
+
+ /**
+ * obtains an index that this triangle represents if it is contained in a OBBTree.
+ * @return the index in an OBBtree
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * sets an index that this triangle represents if it is contained in a OBBTree.
+ * @param index the index in an OBBtree
+ */
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public static Vector3f computeTriangleNormal(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f store) {
+ if (store == null) {
+ store = new Vector3f(v2);
+ } else {
+ store.set(v2);
+ }
+
+ store.subtractLocal(v1).crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z);
+ return store.normalizeLocal();
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ e.getCapsule(this).write(pointa, "pointa", Vector3f.ZERO);
+ e.getCapsule(this).write(pointb, "pointb", Vector3f.ZERO);
+ e.getCapsule(this).write(pointc, "pointc", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ pointa = (Vector3f) e.getCapsule(this).readSavable("pointa", Vector3f.ZERO.clone());
+ pointb = (Vector3f) e.getCapsule(this).readSavable("pointb", Vector3f.ZERO.clone());
+ pointc = (Vector3f) e.getCapsule(this).readSavable("pointc", Vector3f.ZERO.clone());
+ }
+
+ @Override
+ public Triangle clone() {
+ try {
+ Triangle t = (Triangle) super.clone();
+ t.pointa = pointa.clone();
+ t.pointb = pointb.clone();
+ t.pointc = pointc.clone();
+ return t;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/math/Vector2f.java b/engine/src/core/com/jme3/math/Vector2f.java
new file mode 100644
index 0000000..c2d8c6f
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Vector2f.java
@@ -0,0 +1,757 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.logging.Logger;
+
+/**
+ * <code>Vector2f</code> defines a Vector for a two float value vector.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ */
+public final class Vector2f implements Savable, 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;
+ }
+
+ /**
+ * <code>add</code> 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);
+ }
+
+ /**
+ * <code>addLocal</code> 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;
+ }
+
+ /**
+ * <code>addLocal</code> 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;
+ }
+
+ /**
+ * <code>add</code> adds this vector by <code>vec</code> and stores the
+ * result in <code>result</code>.
+ *
+ * @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;
+ }
+
+ /**
+ * <code>dot</code> 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;
+ }
+
+ /**
+ * <code>cross</code> 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;
+ }
+
+ /**
+ * <code>length</code> calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * <code>lengthSquared</code> 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;
+ }
+
+ /**
+ * <code>distanceSquared</code> 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);
+ }
+
+ /**
+ * <code>distanceSquared</code> 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);
+ }
+
+ /**
+ * <code>distance</code> 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));
+ }
+
+ /**
+ * <code>mult</code> 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);
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>divide</code> 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 <code>Vector</code>.
+ */
+ public Vector2f divide(float scalar) {
+ return new Vector2f(x / scalar, y / scalar);
+ }
+
+ /**
+ * <code>divideLocal</code> 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;
+ }
+
+ /**
+ * <code>negate</code> 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);
+ }
+
+ /**
+ * <code>negateLocal</code> negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector2f negateLocal() {
+ x = -x;
+ y = -y;
+ return this;
+ }
+
+ /**
+ * <code>subtract</code> 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);
+ }
+
+ /**
+ * <code>subtract</code> 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;
+ }
+
+ /**
+ * <code>subtract</code> 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);
+ }
+
+ /**
+ * <code>subtractLocal</code> 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;
+ }
+
+ /**
+ * <code>subtractLocal</code> 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;
+ }
+
+ /**
+ * <code>normalize</code> 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);
+ }
+
+ /**
+ * <code>normalizeLocal</code> 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);
+ }
+
+ /**
+ * <code>smallestAngleBetween</code> 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;
+ }
+
+ /**
+ * <code>angleBetween</code> 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;
+ }
+ /**
+ * <code>getAngle</code> returns (in radians) the angle represented by
+ * this Vector2f as expressed by a conversion from rectangular coordinates (<code>x</code>,&nbsp;<code>y</code>)
+ * to polar coordinates (r,&nbsp;<i>theta</i>).
+ *
+ * @return the angle in radians. [-pi, pi)
+ */
+ public float getAngle() {
+ return FastMath.atan2(y, x);
+ }
+
+ /**
+ * <code>zero</code> resets this vector's data to zero internally.
+ */
+ public Vector2f zero() {
+ x = y = 0;
+ return this;
+ }
+
+ /**
+ * <code>hashCode</code> 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.
+ */
+ 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
+ */
+ 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;
+ }
+
+ /**
+ * <code>toString</code> 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.
+ */
+ 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 write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(x, "x", 0);
+ capsule.write(y, "y", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ x = capsule.readFloat("x", 0);
+ y = capsule.readFloat("y", 0);
+ }
+
+ 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/engine/src/core/com/jme3/math/Vector3f.java b/engine/src/core/com/jme3/math/Vector3f.java
new file mode 100644
index 0000000..9f6f851
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Vector3f.java
@@ -0,0 +1,1061 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/*
+ * -- Added *Local methods to cut down on object creation - JS
+ */
+
+/**
+ * <code>Vector3f</code> defines a Vector for a three float value tuple.
+ * <code>Vector3f</code> 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 Savable, 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_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);
+
+
+ /**
+ * 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 <code>Vector3f</code> with default
+ * values of (0,0,0).
+ *
+ */
+ public Vector3f() {
+ x = y = z = 0;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Vector3f</code> 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 <code>Vector3f</code> that is a copy
+ * of the provided vector
+ * @param copy The Vector3f to copy
+ */
+ public Vector3f(Vector3f copy) {
+ this.set(copy);
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+
+ /**
+ *
+ * <code>add</code> 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);
+ }
+
+ /**
+ *
+ * <code>add</code> 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;
+ }
+
+ /**
+ * <code>addLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>add</code> 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);
+ }
+
+ /**
+ * <code>addLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> 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;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> 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;
+ }
+
+ /**
+ *
+ * <code>dot</code> 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;
+ }
+
+ /**
+ * <code>cross</code> 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);
+ }
+
+ /**
+ * <code>cross</code> calculates the cross product of this vector with a
+ * parameter vector v. The result is stored in <code>result</code>
+ *
+ * @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);
+ }
+
+ /**
+ * <code>cross</code> calculates the cross product of this vector with a
+ * parameter vector v. The result is stored in <code>result</code>
+ *
+ * @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;
+ }
+
+ /**
+ * <code>crossLocal</code> 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);
+ }
+
+ /**
+ * <code>crossLocal</code> 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;
+ }
+
+ 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);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * <code>length</code> calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * <code>lengthSquared</code> 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;
+ }
+
+ /**
+ * <code>distanceSquared</code> 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);
+ }
+
+ /**
+ * <code>distance</code> 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));
+ }
+
+ /**
+ *
+ * <code>mult</code> 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);
+ }
+
+ /**
+ *
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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);
+ }
+
+ /**
+ * <code>multLocal</code> 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);
+ }
+
+
+ /**
+ * <code>divide</code> 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 <code>Vector</code>.
+ */
+ public Vector3f divide(float scalar) {
+ scalar = 1f/scalar;
+ return new Vector3f(x * scalar, y * scalar, z * scalar);
+ }
+
+ /**
+ * <code>divideLocal</code> 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;
+ }
+
+
+ /**
+ * <code>divide</code> 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 <code>Vector</code>.
+ */
+ public Vector3f divide(Vector3f scalar) {
+ return new Vector3f(x / scalar.x, y / scalar.y, z / scalar.z);
+ }
+
+ /**
+ * <code>divideLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>negate</code> 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);
+ }
+
+ /**
+ *
+ * <code>negateLocal</code> negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector3f negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ return this;
+ }
+
+ /**
+ *
+ * <code>subtract</code> 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);
+ }
+
+ /**
+ * <code>subtractLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>subtract</code>
+ *
+ * @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;
+ }
+
+ /**
+ *
+ * <code>subtract</code> 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);
+ }
+
+ /**
+ * <code>subtractLocal</code> 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;
+ }
+
+ /**
+ * <code>normalize</code> returns the unit vector of this vector.
+ *
+ * @return unit vector of this vector.
+ */
+ public Vector3f normalize() {
+// float length = length();
+// if (length != 0) {
+// return divide(length);
+// }
+//
+// return divide(1);
+ 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();
+ }
+
+ /**
+ * <code>normalizeLocal</code> 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;
+ }
+
+ /**
+ * <code>maxLocal</code> computes the maximum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void maxLocal(Vector3f other){
+ x = other.x > x ? other.x : x;
+ y = other.y > y ? other.y : y;
+ z = other.z > z ? other.z : z;
+ }
+
+ /**
+ * <code>minLocal</code> computes the minimum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void minLocal(Vector3f other){
+ x = other.x < x ? other.x : x;
+ y = other.y < y ? other.y : y;
+ z = other.z < z ? other.z : z;
+ }
+
+ /**
+ * <code>zero</code> resets this vector's data to zero internally.
+ */
+ public Vector3f zero() {
+ x = y = z = 0;
+ return this;
+ }
+
+ /**
+ * <code>angleBetween</code> 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
+ */
+ 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;
+ }
+
+ /**
+ * <code>hashCode</code> 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.
+ */
+ 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;
+ }
+
+ /**
+ * <code>toString</code> 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.
+ */
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ")";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(x, "x", 0);
+ capsule.write(y, "y", 0);
+ capsule.write(z, "z", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ x = capsule.readFloat("x", 0);
+ y = capsule.readFloat("y", 0);
+ z = capsule.readFloat("z", 0);
+ }
+
+ 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");
+ }
+
+}
diff --git a/engine/src/core/com/jme3/math/Vector4f.java b/engine/src/core/com/jme3/math/Vector4f.java
new file mode 100644
index 0000000..d6e7ad7
--- /dev/null
+++ b/engine/src/core/com/jme3/math/Vector4f.java
@@ -0,0 +1,1003 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.math;
+
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * <code>Vector4f</code> defines a Vector for a four float value tuple.
+ * <code>Vector4f</code> 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 Savable, 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 <code>Vector3f</code> with default
+ * values of (0,0,0).
+ *
+ */
+ public Vector4f() {
+ x = y = z = w = 0;
+ }
+
+ /**
+ * Constructor instantiates a new <code>Vector4f</code> 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 <code>Vector3f</code> that is a copy
+ * of the provided vector
+ * @param copy The Vector3f to copy
+ */
+ public Vector4f(Vector4f copy) {
+ this.set(copy);
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+
+ /**
+ * <code>set</code> 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;
+ }
+
+ /**
+ *
+ * <code>add</code> 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);
+ }
+
+ /**
+ *
+ * <code>add</code> 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;
+ }
+
+ /**
+ * <code>addLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>add</code> 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);
+ }
+
+ /**
+ * <code>addLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> 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;
+ }
+
+ /**
+ *
+ * <code>scaleAdd</code> 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;
+ }
+
+ /**
+ *
+ * <code>dot</code> 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;
+ }
+
+ /**
+ * <code>length</code> calculates the magnitude of this vector.
+ *
+ * @return the length or magnitude of the vector.
+ */
+ public float length() {
+ return FastMath.sqrt(lengthSquared());
+ }
+
+ /**
+ * <code>lengthSquared</code> 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;
+ }
+
+ /**
+ * <code>distanceSquared</code> 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);
+ }
+
+ /**
+ * <code>distance</code> 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));
+ }
+
+ /**
+ *
+ * <code>mult</code> 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);
+ }
+
+ /**
+ *
+ * <code>mult</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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;
+ }
+
+ /**
+ * <code>multLocal</code> 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);
+ }
+
+ /**
+ * <code>multLocal</code> 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);
+ }
+
+ /**
+ * <code>divide</code> 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 <code>Vector</code>.
+ */
+ public Vector4f divide(float scalar) {
+ scalar = 1f/scalar;
+ return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
+ }
+
+ /**
+ * <code>divideLocal</code> 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;
+ }
+
+ /**
+ * <code>divide</code> 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 <code>Vector</code>.
+ */
+ public Vector4f divide(Vector4f scalar) {
+ return new Vector4f(x / scalar.x, y / scalar.y, z / scalar.z, w / scalar.w);
+ }
+
+ /**
+ * <code>divideLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>negate</code> 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);
+ }
+
+ /**
+ *
+ * <code>negateLocal</code> negates the internal values of this vector.
+ *
+ * @return this.
+ */
+ public Vector4f negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ w = -w;
+ return this;
+ }
+
+ /**
+ *
+ * <code>subtract</code> 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);
+ }
+
+ /**
+ * <code>subtractLocal</code> 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;
+ }
+
+ /**
+ *
+ * <code>subtract</code>
+ *
+ * @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;
+ }
+
+ /**
+ *
+ * <code>subtract</code> 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);
+ }
+
+ /**
+ * <code>subtractLocal</code> 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;
+ }
+
+ /**
+ * <code>normalize</code> 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();
+ }
+
+ /**
+ * <code>normalizeLocal</code> 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;
+ }
+
+ /**
+ * <code>maxLocal</code> computes the maximum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void 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;
+ }
+
+ /**
+ * <code>minLocal</code> computes the minimum value for each
+ * component in this and <code>other</code> vector. The result is stored
+ * in this vector.
+ * @param other
+ */
+ public void 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;
+ }
+
+ /**
+ * <code>zero</code> resets this vector's data to zero internally.
+ */
+ public Vector4f zero() {
+ x = y = z = w = 0;
+ return this;
+ }
+
+ /**
+ * <code>angleBetween</code> 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
+ */
+ 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;
+ }
+
+ /**
+ * <code>hashCode</code> 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.
+ */
+ 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;
+ }
+
+ /**
+ * <code>toString</code> 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.
+ */
+ public String toString() {
+ return "(" + x + ", " + y + ", " + z + ", " + w + ")";
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(x, "x", 0);
+ capsule.write(y, "y", 0);
+ capsule.write(z, "z", 0);
+ capsule.write(w, "w", 0);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ InputCapsule capsule = e.getCapsule(this);
+ x = capsule.readFloat("x", 0);
+ y = capsule.readFloat("y", 0);
+ z = capsule.readFloat("z", 0);
+ w = capsule.readFloat("w", 0);
+ }
+
+ 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");
+ }
+
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/math/package.html b/engine/src/core/com/jme3/math/package.html
new file mode 100644
index 0000000..64da8aa
--- /dev/null
+++ b/engine/src/core/com/jme3/math/package.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.math</code> package provides mathematic data structures
+and utilities which are used by the rest of the engine.
+The math package provides the following classes:<br>
+<h3>General purpose vectors</h3>
+<ul>
+ <li>{@link com.jme3.math.Vector2f} - 2D general purpose vector</li>
+ <li>{@link com.jme3.math.Vector3f} - 3D general purpose vector</li>
+ <li>{@link com.jme3.math.Vector4f} - 4D general purpose vector</li>
+</ul>
+<h3>Special purpose vectors</h3>
+<ul>
+ <li>{@link com.jme3.math.ColorRGBA} - Floating-point RGB color with alpha</li>
+ <li>{@link com.jme3.math.Quaternion} - Specialized 4D data structure to represent rotation</li>
+</ul>
+<h3>Matrices</h3>
+<ul>
+ <li>{@link com.jme3.math.Matrix3f} - 3x3 matrix, usually used to represent rotation</li>
+ <li>{@link com.jme3.math.Matrix4f} - 4x4 matrix, used as an efficient transform representation</li>
+</ul>
+<h3>Shapes</h3>
+<ul>
+ <li>{@link com.jme3.math.AbstractTriangle} - Abstract triangle. Data to be provided by implementation</li>
+ <li>{@link com.jme3.math.Triangle} - Concrete implementation of AbstractTriangle with center and normal vectors</li>
+ <li>{@link com.jme3.math.Line} - Infinite 3D line</li>
+ <li>{@link com.jme3.math.LineSegment} - 3D line with start and end point</li>
+ <li>{@link com.jme3.math.Plane} - 3D plane</li>
+ <li>{@link com.jme3.math.Ray} - 3D ray</li>
+ <li>{@link com.jme3.math.Rectangle} - 3D rectangle</li>
+ <li>{@link com.jme3.math.Ring} - 3D ring</li>
+</ul>
+<h3>Curves</h3>
+<ul>
+ <li>{@link com.jme3.math.Spline} - 3D curve defined by control points and a function</li>
+</ul>
+<h3>Utility classes</h3>
+<ul>
+ <li>{@link com.jme3.math.Transform} - Representation of a transform with translation, rotation, and scale</li>
+ <li>{@link com.jme3.math.FastMath} - Contains static methods for floating-point math</li>
+ <li>{@link com.jme3.math.CurveAndSurfaceMath} - Contains static methods specific to curve and surface math</li>
+ <li>{@link com.jme3.math.Eigen3f} - Provides computation of eigenvectors given a matrix</li>
+</ul>
+
+</body>
+</html>