From 98ffe52c7747c521b8d57ef7a239efd62550a16c Mon Sep 17 00:00:00 2001 From: "sgold@sonic.net" <> Date: Sun, 25 May 2014 08:54:49 -0700 Subject: [PATCH] correct core comments and standardize line endings as discussed at http://hub.jmonkeyengine.org/forum/topic/typos-in-com-jme3-mathrendererpost/ --- .../main/java/com/jme3/math/ColorRGBA.java | 1190 +++++----- .../com/jme3/math/CurveAndSurfaceMath.java | 329 +-- .../src/main/java/com/jme3/math/FastMath.java | 1919 +++++++++-------- .../src/main/java/com/jme3/post/Filter.java | 912 ++++---- .../main/java/com/jme3/renderer/Camera.java | 16 +- .../main/java/com/jme3/renderer/Renderer.java | 2 +- 6 files changed, 2185 insertions(+), 2183 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java b/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java index 347bec609..6b02289e4 100644 --- a/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java +++ b/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java @@ -1,595 +1,595 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.math; - -import com.jme3.export.*; -import java.io.IOException; - -/** - * ColorRGBA defines a color made from a collection of red, green - * and blue values. An alpha value determines is transparency. All values must - * be between 0 and 1. If any value is set higher or lower than these - * constraints they are clamped to the min or max. That is, if a value smaller - * than zero is set the value clamps to zero. If a value higher than 1 is - * passed, that value is clamped to 1. However, because the attributes r, g, b, - * a are public for efficiency reasons, they can be directly modified with - * invalid values. The client should take care when directly addressing the - * values. A call to clamp will assure that the values are within the - * constraints. - * - * @author Mark Powell - * @version $Id: ColorRGBA.java,v 1.29 2007/09/09 18:25:14 irrisor Exp $ - */ -public final class ColorRGBA implements 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. 0 is none and 1 is maximum red. - */ - public float r; - /** - * The green component of the color. 0 is none and 1 is maximum green. - */ - public float g; - /** - * The blue component of the color. 0 is none and 1 is maximum blue. - */ - public float b; - /** - * The alpha component of the color. 0 is transparent and 1 is opaque. - */ - public float a; - - /** - * Constructor instantiates a new ColorRGBA object. This - * color is the default "white" with all values 1. - */ - public ColorRGBA() { - r = g = b = a = 1.0f; - } - - /** - * Constructor instantiates a new ColorRGBA object. The - * values are defined as passed parameters. These values are then clamped - * to insure that they are between 0 and 1. - * @param r The red component of this color. - * @param g The green component of this ColorRGBA. - * @param b The blue component of this ColorRGBA. - * @param a The alpha component of this ColorRGBA. - */ - public ColorRGBA(float r, float g, float b, float a) { - this.r = r; - this.g = g; - this.b = b; - this.a = a; - } - - /** - * Copy constructor creates a new ColorRGBA object, based on - * a provided color. - * @param rgba The ColorRGBA object to copy. - */ - public ColorRGBA(ColorRGBA rgba) { - this.a = rgba.a; - this.r = rgba.r; - this.g = rgba.g; - this.b = rgba.b; - } - - /** - * set sets the RGBA values of this ColorRGBA. The - * values are then clamped to insure that they are between 0 and 1. - * - * @param r The red component of this color. - * @param g The green component of this color. - * @param b The blue component of this color. - * @param a The alpha component of this color. - * @return this - */ - public ColorRGBA set(float r, float g, float b, float a) { - this.r = r; - this.g = g; - this.b = b; - this.a = a; - return this; - } - - /** - * set sets the values of this ColorRGBA to those - * set by a parameter color. - * - * @param rgba The color to set this ColorRGBA to. - * @return this - */ - public ColorRGBA set(ColorRGBA rgba) { - if (rgba == null) { - r = 0; - g = 0; - b = 0; - a = 0; - } else { - r = rgba.r; - g = rgba.g; - b = rgba.b; - a = rgba.a; - } - return this; - } - - /** - * clamp insures that all values are between 0 and 1. If any - * are less than 0 they are set to zero. If any are more than 1 they are - * set to one. - */ - public void clamp() { - if (r < 0) { - r = 0; - } else if (r > 1) { - r = 1; - } - - if (g < 0) { - g = 0; - } else if (g > 1) { - g = 1; - } - - if (b < 0) { - b = 0; - } else if (b > 1) { - b = 1; - } - - if (a < 0) { - a = 0; - } else if (a > 1) { - a = 1; - } - } - - /** - * getColorArray retrieves the color values of this - * ColorRGBA as a four element float array in the - * order: r,g,b,a. - * @return The float array that contains the color components. - */ - public float[] getColorArray() { - return new float[]{r, g, b, a}; - } - - /** - * Stores the current r,g,b,a values into the given array. The given array must have a - * length of 4 or greater, or an array index out of bounds exception will be thrown. - * @param store The float array to store the values into. - * @return The float array after storage. - */ - public float[] getColorArray(float[] store) { - store[0] = r; - store[1] = g; - store[2] = b; - store[3] = a; - return store; - } - - /** - * Retrieves the alpha component value of this ColorRGBA. - * @return The alpha component value. - */ - public float getAlpha() { - return a; - } - - /** - * Retrieves the red component value of this ColorRGBA. - * @return The red component value. - */ - public float getRed() { - return r; - } - - /** - * Retrieves the blue component value of this ColorRGBA. - * @return The blue component value. - */ - public float getBlue() { - return b; - } - - /** - * Retrieves the green component value of this ColorRGBA. - * @return The green component value. - */ - public float getGreen() { - return g; - } - - /** - * Sets this ColorRGBA to the interpolation by changeAmnt from - * this to the finalColor: - * this=(1-changeAmnt)*this + changeAmnt * finalColor - * @param finalColor The final color to interpolate towards. - * @param changeAmnt An amount between 0.0 - 1.0 representing a percentage - * change from this towards finalColor. - */ - public void interpolate(ColorRGBA finalColor, float changeAmnt) { - this.r = (1 - changeAmnt) * this.r + changeAmnt * finalColor.r; - this.g = (1 - changeAmnt) * this.g + changeAmnt * finalColor.g; - this.b = (1 - changeAmnt) * this.b + changeAmnt * finalColor.b; - this.a = (1 - changeAmnt) * this.a + changeAmnt * finalColor.a; - } - - /** - * Sets this ColorRGBA to the interpolation by changeAmnt from - * beginColor to finalColor: - * this=(1-changeAmnt)*beginColor + changeAmnt * finalColor - * @param beginColor The begining color (changeAmnt=0). - * @param finalColor The final color to interpolate towards (changeAmnt=1). - * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage - * change from beginColor towards finalColor. - */ - public void interpolate(ColorRGBA beginColor, ColorRGBA finalColor, float changeAmnt) { - this.r = (1 - changeAmnt) * beginColor.r + changeAmnt * finalColor.r; - this.g = (1 - changeAmnt) * beginColor.g + changeAmnt * finalColor.g; - this.b = (1 - changeAmnt) * beginColor.b + changeAmnt * finalColor.b; - this.a = (1 - changeAmnt) * beginColor.a + changeAmnt * finalColor.a; - } - - /** - * randomColor is a utility method that generates a random - * opaque color. - * @return a random ColorRGBA with an alpha set to 1. - */ - public static ColorRGBA randomColor() { - ColorRGBA rVal = new ColorRGBA(0, 0, 0, 1); - rVal.r = FastMath.nextRandomFloat(); - rVal.g = FastMath.nextRandomFloat(); - rVal.b = FastMath.nextRandomFloat(); - return rVal; - } - - /** - * Multiplies each r,g,b,a of this ColorRGBA by the corresponding - * r,g,b,a of the given color and returns the result as a new ColorRGBA. - * Used as a way of combining colors and lights. - * @param c The color to multiply by. - * @return The new ColorRGBA. this*c - */ - public ColorRGBA mult(ColorRGBA c) { - return new ColorRGBA(c.r * r, c.g * g, c.b * b, c.a * a); - } - - /** - * Multiplies each r,g,b,a of this ColorRGBA by the given scalar and - * returns the result as a new ColorRGBA. - * Used as a way of making colors dimmer or brighter. - * @param scalar The scalar to multiply by. - * @return The new ColorRGBA. this*scalar - */ - public ColorRGBA mult(float scalar) { - return new ColorRGBA(scalar * r, scalar * g, scalar * b, scalar * a); - } - - /** - * Multiplies each r,g,b,a of this ColorRGBA by the given scalar and - * returns the result (this). - * Used as a way of making colors dimmer or brighter. - * @param scalar The scalar to multiply by. - * @return this*c - */ - public ColorRGBA multLocal(float scalar) { - this.r *= scalar; - this.g *= scalar; - this.b *= scalar; - this.a *= scalar; - return this; - } - - /** - * Adds each r,g,b,a of this ColorRGBA by the corresponding - * r,g,b,a of the given color and returns the result as a new ColorRGBA. - * Used as a way of combining colors and lights. - * @param c The color to add. - * @return The new ColorRGBA. this+c - */ - public ColorRGBA add(ColorRGBA c) { - return new ColorRGBA(c.r + r, c.g + g, c.b + b, c.a + a); - } - - /** - * Adds each r,g,b,a of this ColorRGBA by the r,g,b,a the given - * color and returns the result (this). - * Used as a way of combining colors and lights. - * @param c The color to add. - * @return this+c - */ - public ColorRGBA addLocal(ColorRGBA c) { - set(c.r + r, c.g + g, c.b + b, c.a + a); - return this; - } - - /** - * toString returns the string representation of this ColorRGBA. - * The format of the string is:
- * : [R=RR.RRRR, G=GG.GGGG, B=BB.BBBB, A=AA.AAAA] - * @return The string representation of this ColorRGBA. - */ - @Override - public String toString() { - return "Color[" + r + ", " + g + ", " + b + ", " + a + "]"; - } - - @Override - public ColorRGBA clone() { - try { - return (ColorRGBA) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(); // can not happen - } - } - - /** - * Saves this ColorRGBA into the given float array. - * @param floats The float array to take this ColorRGBA. - * If null, a new float[4] is created. - * @return The array, with r,g,b,a float values in that order. - */ - public float[] toArray(float[] floats) { - if (floats == null) { - floats = new float[4]; - } - floats[0] = r; - floats[1] = g; - floats[2] = b; - floats[3] = a; - return floats; - } - - /** - * equals returns true if this ColorRGBA is logically equivalent - * to a given color. That is, if all the components of the two colors are the same. - * False is returned otherwise. - * @param o The object to compare against. - * @return true if the colors are equal, false otherwise. - */ - @Override - public boolean equals(Object o) { - if (!(o instanceof ColorRGBA)) { - return false; - } - - if (this == o) { - return true; - } - - ColorRGBA comp = (ColorRGBA) o; - if (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; - } - - /** - * hashCode returns a unique code for this ColorRGBA based - * on its values. If two colors are logically equivalent, they will return - * the same hash code value. - * @return The hash code value of this ColorRGBA. - */ - @Override - public int hashCode() { - int hash = 37; - hash += 37 * hash + Float.floatToIntBits(r); - hash += 37 * hash + Float.floatToIntBits(g); - hash += 37 * hash + Float.floatToIntBits(b); - hash += 37 * hash + Float.floatToIntBits(a); - return hash; - } - - 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); - } - /** - * Retrieves the component values of this ColorRGBA as - * a four element byte array in the order: r,g,b,a. - * @return the byte array that contains the color components. - */ - public byte[] asBytesRGBA() { - byte[] store = new byte[4]; - store[0] = (byte) ((int) (r * 255) & 0xFF); - store[1] = (byte) ((int) (g * 255) & 0xFF); - store[2] = (byte) ((int) (b * 255) & 0xFF); - store[3] = (byte) ((int) (a * 255) & 0xFF); - return store; - } - - /** - * Retrieves the component values of this ColorRGBA as an - * int in a,r,g,b order. - * Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue. - * @return The integer representation of this ColorRGBA in a,r,g,b order. - */ - public int asIntARGB() { - int argb = (((int) (a * 255) & 0xFF) << 24) - | (((int) (r * 255) & 0xFF) << 16) - | (((int) (g * 255) & 0xFF) << 8) - | (((int) (b * 255) & 0xFF)); - return argb; - } - - /** - * Retrieves the component values of this ColorRGBA as an - * int in r,g,b,a order. - * Bits 24-31 are red, 16-23 are green, 8-15 are blue, 0-7 are alpha. - * @return The integer representation of this ColorRGBA in r,g,b,a order. - */ - public int asIntRGBA() { - int rgba = (((int) (r * 255) & 0xFF) << 24) - | (((int) (g * 255) & 0xFF) << 16) - | (((int) (b * 255) & 0xFF) << 8) - | (((int) (a * 255) & 0xFF)); - return rgba; - } - /** - * Retrieves the component values of this ColorRGBA as an - * int in a,b,g,r order. - * Bits 24-31 are alpha, 16-23 are blue, 8-15 are green, 0-7 are red. - * @return The integer representation of this ColorRGBA in a,b,g,r order. - */ - public int asIntABGR() { - int abgr = (((int) (a * 255) & 0xFF) << 24) - | (((int) (b * 255) & 0xFF) << 16) - | (((int) (g * 255) & 0xFF) << 8) - | (((int) (r * 255) & 0xFF)); - return abgr; - } - /** - * Sets the component values of this ColorRGBA with the given - * combined ARGB int. - * Bits 24-31 are alpha, bits 16-23 are red, bits 8-15 are green, bits 0-7 are blue. - * @param color The integer ARGB value used to set this ColorRGBA. - */ - public void fromIntARGB(int color) { - a = ((byte) (color >> 24) & 0xFF) / 255f; - r = ((byte) (color >> 16) & 0xFF) / 255f; - g = ((byte) (color >> 8) & 0xFF) / 255f; - b = ((byte) (color) & 0xFF) / 255f; - } - /** - * Sets the RGBA values of this ColorRGBA with the given combined RGBA value - * Bits 24-31 are red, bits 16-23 are green, bits 8-15 are blue, bits 0-7 are alpha. - * @param color The integer RGBA value used to set this object. - */ - public void fromIntRGBA(int color) { - r = ((byte) (color >> 24) & 0xFF) / 255f; - g = ((byte) (color >> 16) & 0xFF) / 255f; - b = ((byte) (color >> 8) & 0xFF) / 255f; - a = ((byte) (color) & 0xFF) / 255f; - } - - /** - * Transform this ColorRGBA to a Vector3f using - * x = r, y = g, z = b. The Alpha value is not used. - * This method is useful to use for shaders assignment. - * @return A Vector3f containing the RGB value of this ColorRGBA. - */ - public Vector3f toVector3f() { - return new Vector3f(r, g, b); - } - - /** - * Transform this ColorRGBA to a Vector4f using - * x = r, y = g, z = b, w = a. - * This method is useful to use for shaders assignment. - * @return A Vector4f containing the RGBA value of this ColorRGBA. - */ - public Vector4f toVector4f() { - return new Vector4f(r, g, b, a); - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.math; + +import com.jme3.export.*; +import java.io.IOException; + +/** + * ColorRGBA defines a color made from a collection of red, green + * and blue values. An alpha value determines is transparency. All values must + * be between 0 and 1. If any value is set higher or lower than these + * constraints they are clamped to the min or max. That is, if a value smaller + * than zero is set the value clamps to zero. If a value higher than 1 is + * passed, that value is clamped to 1. However, because the attributes r, g, b, + * a are public for efficiency reasons, they can be directly modified with + * invalid values. The client should take care when directly addressing the + * values. A call to clamp will assure that the values are within the + * constraints. + * + * @author Mark Powell + * @version $Id: ColorRGBA.java,v 1.29 2007/09/09 18:25:14 irrisor Exp $ + */ +public final class ColorRGBA implements 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. 0 is none and 1 is maximum red. + */ + public float r; + /** + * The green component of the color. 0 is none and 1 is maximum green. + */ + public float g; + /** + * The blue component of the color. 0 is none and 1 is maximum blue. + */ + public float b; + /** + * The alpha component of the color. 0 is transparent and 1 is opaque. + */ + public float a; + + /** + * Constructor instantiates a new ColorRGBA object. This + * color is the default "white" with all values 1. + */ + public ColorRGBA() { + r = g = b = a = 1.0f; + } + + /** + * Constructor instantiates a new ColorRGBA object. The + * values are defined as passed parameters. These values are then clamped + * to insure that they are between 0 and 1. + * @param r The red component of this color. + * @param g The green component of this ColorRGBA. + * @param b The blue component of this ColorRGBA. + * @param a The alpha component of this ColorRGBA. + */ + public ColorRGBA(float r, float g, float b, float a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + /** + * Copy constructor creates a new ColorRGBA object, based on + * a provided color. + * @param rgba The ColorRGBA object to copy. + */ + public ColorRGBA(ColorRGBA rgba) { + this.a = rgba.a; + this.r = rgba.r; + this.g = rgba.g; + this.b = rgba.b; + } + + /** + * set sets the RGBA values of this ColorRGBA. The + * values are then clamped to insure that they are between 0 and 1. + * + * @param r The red component of this color. + * @param g The green component of this color. + * @param b The blue component of this color. + * @param a The alpha component of this color. + * @return this + */ + public ColorRGBA set(float r, float g, float b, float a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + return this; + } + + /** + * set sets the values of this ColorRGBA to those + * set by a parameter color. + * + * @param rgba The color to set this ColorRGBA to. + * @return this + */ + public ColorRGBA set(ColorRGBA rgba) { + if (rgba == null) { + r = 0; + g = 0; + b = 0; + a = 0; + } else { + r = rgba.r; + g = rgba.g; + b = rgba.b; + a = rgba.a; + } + return this; + } + + /** + * clamp insures that all values are between 0 and 1. If any + * are less than 0 they are set to zero. If any are more than 1 they are + * set to one. + */ + public void clamp() { + if (r < 0) { + r = 0; + } else if (r > 1) { + r = 1; + } + + if (g < 0) { + g = 0; + } else if (g > 1) { + g = 1; + } + + if (b < 0) { + b = 0; + } else if (b > 1) { + b = 1; + } + + if (a < 0) { + a = 0; + } else if (a > 1) { + a = 1; + } + } + + /** + * getColorArray retrieves the color values of this + * ColorRGBA as a four element float array in the + * order: r,g,b,a. + * @return The float array that contains the color components. + */ + public float[] getColorArray() { + return new float[]{r, g, b, a}; + } + + /** + * Stores the current r,g,b,a values into the given array. The given array must have a + * length of 4 or greater, or an array index out of bounds exception will be thrown. + * @param store The float array to store the values into. + * @return The float array after storage. + */ + public float[] getColorArray(float[] store) { + store[0] = r; + store[1] = g; + store[2] = b; + store[3] = a; + return store; + } + + /** + * Retrieves the alpha component value of this ColorRGBA. + * @return The alpha component value. + */ + public float getAlpha() { + return a; + } + + /** + * Retrieves the red component value of this ColorRGBA. + * @return The red component value. + */ + public float getRed() { + return r; + } + + /** + * Retrieves the blue component value of this ColorRGBA. + * @return The blue component value. + */ + public float getBlue() { + return b; + } + + /** + * Retrieves the green component value of this ColorRGBA. + * @return The green component value. + */ + public float getGreen() { + return g; + } + + /** + * Sets this ColorRGBA to the interpolation by changeAmnt from + * this to the finalColor: + * this=(1-changeAmnt)*this + changeAmnt * finalColor + * @param finalColor The final color to interpolate towards. + * @param changeAmnt An amount between 0.0 - 1.0 representing a percentage + * change from this towards finalColor. + */ + public void interpolate(ColorRGBA finalColor, float changeAmnt) { + this.r = (1 - changeAmnt) * this.r + changeAmnt * finalColor.r; + this.g = (1 - changeAmnt) * this.g + changeAmnt * finalColor.g; + this.b = (1 - changeAmnt) * this.b + changeAmnt * finalColor.b; + this.a = (1 - changeAmnt) * this.a + changeAmnt * finalColor.a; + } + + /** + * Sets this ColorRGBA to the interpolation by changeAmnt from + * beginColor to finalColor: + * this=(1-changeAmnt)*beginColor + changeAmnt * finalColor + * @param beginColor The beginning color (changeAmnt=0). + * @param finalColor The final color to interpolate towards (changeAmnt=1). + * @param changeAmnt An amount between 0.0 - 1.0 representing a percentage + * change from beginColor towards finalColor. + */ + public void interpolate(ColorRGBA beginColor, ColorRGBA finalColor, float changeAmnt) { + this.r = (1 - changeAmnt) * beginColor.r + changeAmnt * finalColor.r; + this.g = (1 - changeAmnt) * beginColor.g + changeAmnt * finalColor.g; + this.b = (1 - changeAmnt) * beginColor.b + changeAmnt * finalColor.b; + this.a = (1 - changeAmnt) * beginColor.a + changeAmnt * finalColor.a; + } + + /** + * randomColor is a utility method that generates a random + * opaque color. + * @return a random ColorRGBA with an alpha set to 1. + */ + public static ColorRGBA randomColor() { + ColorRGBA rVal = new ColorRGBA(0, 0, 0, 1); + rVal.r = FastMath.nextRandomFloat(); + rVal.g = FastMath.nextRandomFloat(); + rVal.b = FastMath.nextRandomFloat(); + return rVal; + } + + /** + * Multiplies each r,g,b,a of this ColorRGBA by the corresponding + * r,g,b,a of the given color and returns the result as a new ColorRGBA. + * Used as a way of combining colors and lights. + * @param c The color to multiply by. + * @return The new ColorRGBA. this*c + */ + public ColorRGBA mult(ColorRGBA c) { + return new ColorRGBA(c.r * r, c.g * g, c.b * b, c.a * a); + } + + /** + * Multiplies each r,g,b,a of this ColorRGBA by the given scalar and + * returns the result as a new ColorRGBA. + * Used as a way of making colors dimmer or brighter. + * @param scalar The scalar to multiply by. + * @return The new ColorRGBA. this*scalar + */ + public ColorRGBA mult(float scalar) { + return new ColorRGBA(scalar * r, scalar * g, scalar * b, scalar * a); + } + + /** + * Multiplies each r,g,b,a of this ColorRGBA by the given scalar and + * returns the result (this). + * Used as a way of making colors dimmer or brighter. + * @param scalar The scalar to multiply by. + * @return this*c + */ + public ColorRGBA multLocal(float scalar) { + this.r *= scalar; + this.g *= scalar; + this.b *= scalar; + this.a *= scalar; + return this; + } + + /** + * Adds each r,g,b,a of this ColorRGBA by the corresponding + * r,g,b,a of the given color and returns the result as a new ColorRGBA. + * Used as a way of combining colors and lights. + * @param c The color to add. + * @return The new ColorRGBA. this+c + */ + public ColorRGBA add(ColorRGBA c) { + return new ColorRGBA(c.r + r, c.g + g, c.b + b, c.a + a); + } + + /** + * Adds each r,g,b,a of this ColorRGBA by the r,g,b,a the given + * color and returns the result (this). + * Used as a way of combining colors and lights. + * @param c The color to add. + * @return this+c + */ + public ColorRGBA addLocal(ColorRGBA c) { + set(c.r + r, c.g + g, c.b + b, c.a + a); + return this; + } + + /** + * toString returns the string representation of this ColorRGBA. + * The format of the string is:
+ * : [R=RR.RRRR, G=GG.GGGG, B=BB.BBBB, A=AA.AAAA] + * @return The string representation of this ColorRGBA. + */ + @Override + public String toString() { + return "Color[" + r + ", " + g + ", " + b + ", " + a + "]"; + } + + @Override + public ColorRGBA clone() { + try { + return (ColorRGBA) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); // can not happen + } + } + + /** + * Saves this ColorRGBA into the given float array. + * @param floats The float array to take this ColorRGBA. + * If null, a new float[4] is created. + * @return The array, with r,g,b,a float values in that order. + */ + public float[] toArray(float[] floats) { + if (floats == null) { + floats = new float[4]; + } + floats[0] = r; + floats[1] = g; + floats[2] = b; + floats[3] = a; + return floats; + } + + /** + * equals returns true if this ColorRGBA is logically equivalent + * to a given color. That is, if all the components of the two colors are the same. + * False is returned otherwise. + * @param o The object to compare against. + * @return true if the colors are equal, false otherwise. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof ColorRGBA)) { + return false; + } + + if (this == o) { + return true; + } + + ColorRGBA comp = (ColorRGBA) o; + if (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; + } + + /** + * hashCode returns a unique code for this ColorRGBA based + * on its values. If two colors are logically equivalent, they will return + * the same hash code value. + * @return The hash code value of this ColorRGBA. + */ + @Override + public int hashCode() { + int hash = 37; + hash += 37 * hash + Float.floatToIntBits(r); + hash += 37 * hash + Float.floatToIntBits(g); + hash += 37 * hash + Float.floatToIntBits(b); + hash += 37 * hash + Float.floatToIntBits(a); + return hash; + } + + 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); + } + /** + * Retrieves the component values of this ColorRGBA as + * a four element byte array in the order: r,g,b,a. + * @return the byte array that contains the color components. + */ + public byte[] asBytesRGBA() { + byte[] store = new byte[4]; + store[0] = (byte) ((int) (r * 255) & 0xFF); + store[1] = (byte) ((int) (g * 255) & 0xFF); + store[2] = (byte) ((int) (b * 255) & 0xFF); + store[3] = (byte) ((int) (a * 255) & 0xFF); + return store; + } + + /** + * Retrieves the component values of this ColorRGBA as an + * int in a,r,g,b order. + * Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue. + * @return The integer representation of this ColorRGBA in a,r,g,b order. + */ + public int asIntARGB() { + int argb = (((int) (a * 255) & 0xFF) << 24) + | (((int) (r * 255) & 0xFF) << 16) + | (((int) (g * 255) & 0xFF) << 8) + | (((int) (b * 255) & 0xFF)); + return argb; + } + + /** + * Retrieves the component values of this ColorRGBA as an + * int in r,g,b,a order. + * Bits 24-31 are red, 16-23 are green, 8-15 are blue, 0-7 are alpha. + * @return The integer representation of this ColorRGBA in r,g,b,a order. + */ + public int asIntRGBA() { + int rgba = (((int) (r * 255) & 0xFF) << 24) + | (((int) (g * 255) & 0xFF) << 16) + | (((int) (b * 255) & 0xFF) << 8) + | (((int) (a * 255) & 0xFF)); + return rgba; + } + /** + * Retrieves the component values of this ColorRGBA as an + * int in a,b,g,r order. + * Bits 24-31 are alpha, 16-23 are blue, 8-15 are green, 0-7 are red. + * @return The integer representation of this ColorRGBA in a,b,g,r order. + */ + public int asIntABGR() { + int abgr = (((int) (a * 255) & 0xFF) << 24) + | (((int) (b * 255) & 0xFF) << 16) + | (((int) (g * 255) & 0xFF) << 8) + | (((int) (r * 255) & 0xFF)); + return abgr; + } + /** + * Sets the component values of this ColorRGBA with the given + * combined ARGB int. + * Bits 24-31 are alpha, bits 16-23 are red, bits 8-15 are green, bits 0-7 are blue. + * @param color The integer ARGB value used to set this ColorRGBA. + */ + public void fromIntARGB(int color) { + a = ((byte) (color >> 24) & 0xFF) / 255f; + r = ((byte) (color >> 16) & 0xFF) / 255f; + g = ((byte) (color >> 8) & 0xFF) / 255f; + b = ((byte) (color) & 0xFF) / 255f; + } + /** + * Sets the RGBA values of this ColorRGBA with the given combined RGBA value + * Bits 24-31 are red, bits 16-23 are green, bits 8-15 are blue, bits 0-7 are alpha. + * @param color The integer RGBA value used to set this object. + */ + public void fromIntRGBA(int color) { + r = ((byte) (color >> 24) & 0xFF) / 255f; + g = ((byte) (color >> 16) & 0xFF) / 255f; + b = ((byte) (color >> 8) & 0xFF) / 255f; + a = ((byte) (color) & 0xFF) / 255f; + } + + /** + * Transform this ColorRGBA to a Vector3f using + * x = r, y = g, z = b. The Alpha value is not used. + * This method is useful to use for shaders assignment. + * @return A Vector3f containing the RGB value of this ColorRGBA. + */ + public Vector3f toVector3f() { + return new Vector3f(r, g, b); + } + + /** + * Transform this ColorRGBA to a Vector4f using + * x = r, y = g, z = b, w = a. + * This method is useful to use for shaders assignment. + * @return A Vector4f containing the RGBA value of this ColorRGBA. + */ + public Vector4f toVector4f() { + return new Vector4f(r, g, b, a); + } +} diff --git a/jme3-core/src/main/java/com/jme3/math/CurveAndSurfaceMath.java b/jme3-core/src/main/java/com/jme3/math/CurveAndSurfaceMath.java index 82226e34c..522821e03 100644 --- a/jme3-core/src/main/java/com/jme3/math/CurveAndSurfaceMath.java +++ b/jme3-core/src/main/java/com/jme3/math/CurveAndSurfaceMath.java @@ -1,164 +1,165 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.math; - -import 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 controlPoints = nurbSpline.getControlPoints(); - float[] weights = nurbSpline.getWeights(); - List 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> controlPoints, List[] 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 knots, int basisFunctionDegree) { - float delta = KNOTS_MINIMUM_DELTA; - float prevValue = knots.get(0).floatValue(); - for(int i=1;i 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); - } - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.math; + +import 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 instantiation of this + * class. + */ + private CurveAndSurfaceMath() {} + + /** + * This method interpolates the 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 controlPoints = nurbSpline.getControlPoints(); + float[] weights = nurbSpline.getWeights(); + List 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 the 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> controlPoints, List[] 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 knots, int basisFunctionDegree) { + float delta = KNOTS_MINIMUM_DELTA; + float prevValue = knots.get(0).floatValue(); + for(int i=1;i 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/jme3-core/src/main/java/com/jme3/math/FastMath.java b/jme3-core/src/main/java/com/jme3/math/FastMath.java index 2165f62fc..5e230dabb 100644 --- a/jme3-core/src/main/java/com/jme3/math/FastMath.java +++ b/jme3-core/src/main/java/com/jme3/math/FastMath.java @@ -1,959 +1,960 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.math; - -import java.util.Random; - -/** - * FastMath provides 'fast' math approximations and float equivalents of Math - * functions. These are all used as static values and functions. - * - * @author Various - * @version $Id: FastMath.java,v 1.45 2007/08/26 08:44:20 irrisor Exp $ - */ -final public class FastMath { - - private FastMath() { - } - /** A "close to zero" double epsilon value for use*/ - public static final double DBL_EPSILON = 2.220446049250313E-16d; - /** A "close to zero" float epsilon value for use*/ - public static final float FLT_EPSILON = 1.1920928955078125E-7f; - /** A "close to zero" float epsilon value for use*/ - public static final float ZERO_TOLERANCE = 0.0001f; - public static final float ONE_THIRD = 1f / 3f; - /** The value PI as a float. (180 degrees) */ - public static final float PI = (float) Math.PI; - /** The value 2PI as a float. (360 degrees) */ - public static final float TWO_PI = 2.0f * PI; - /** The value PI/2 as a float. (90 degrees) */ - public static final float HALF_PI = 0.5f * PI; - /** The value PI/4 as a float. (45 degrees) */ - public static final float QUARTER_PI = 0.25f * PI; - /** The value 1/PI as a float. */ - public static final float INV_PI = 1.0f / PI; - /** The value 1/(2PI) as a float. */ - public static final float INV_TWO_PI = 1.0f / TWO_PI; - /** A value to multiply a degree value by, to convert it to radians. */ - public static final float DEG_TO_RAD = PI / 180.0f; - /** A value to multiply a radian value by, to convert it to degrees. */ - public static final float RAD_TO_DEG = 180.0f / PI; - /** A precreated random object for random numbers. */ - public static final Random rand = new Random(System.currentTimeMillis()); - - /** - * Returns true if the number is a power of 2 (2,4,8,16...) - * - * A good implementation found on the Java boards. note: a number is a power - * of two if and only if it is the smallest number with that number of - * significant bits. Therefore, if you subtract 1, you know that the new - * number will have fewer bits, so ANDing the original number with anything - * less than it will give 0. - * - * @param number - * The number to test. - * @return True if it is a power of two. - */ - public static boolean isPowerOfTwo(int number) { - return (number > 0) && (number & (number - 1)) == 0; - } - - public static int nearestPowerOfTwo(int number) { - return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); - } - - /** - * Linear interpolation from startValue to endValue by the given percent. - * Basically: ((1 - percent) * startValue) + (percent * endValue) - * - * @param scale - * scale value to use. if 1, use endValue, if 0, use startValue. - * @param startValue - * Begining value. 0% of f - * @param endValue - * ending value. 100% of f - * @return The interpolated value between startValue and endValue. - */ - public static float interpolateLinear(float scale, float startValue, float endValue) { - if (startValue == endValue) { - return startValue; - } - if (scale <= 0f) { - return startValue; - } - if (scale >= 1f) { - return endValue; - } - return ((1f - scale) * startValue) + (scale * endValue); - } - - /** - * Linear interpolation from startValue to endValue by the given percent. - * Basically: ((1 - percent) * startValue) + (percent * endValue) - * - * @param scale - * scale value to use. if 1, use endValue, if 0, use startValue. - * @param startValue - * Begining value. 0% of f - * @param endValue - * ending value. 100% of f - * @param store a vector3f to store the result - * @return The interpolated value between startValue and endValue. - */ - public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) { - if (store == null) { - store = new Vector3f(); - } - store.x = interpolateLinear(scale, startValue.x, endValue.x); - store.y = interpolateLinear(scale, startValue.y, endValue.y); - store.z = interpolateLinear(scale, startValue.z, endValue.z); - return store; - } - - /** - * Linear interpolation from startValue to endValue by the given percent. - * Basically: ((1 - percent) * startValue) + (percent * endValue) - * - * @param scale - * scale value to use. if 1, use endValue, if 0, use startValue. - * @param startValue - * Begining value. 0% of f - * @param endValue - * ending value. 100% of f - * @return The interpolated value between startValue and endValue. - */ - public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue) { - return interpolateLinear(scale, startValue, endValue, null); - } - - /** - * Linear extrapolation from startValue to endValue by the given scale. - * if scale is between 0 and 1 this method returns the same result as interpolateLinear - * if the scale is over 1 the value is linearly extrapolated. - * Note that the end value is the value for a scale of 1. - * @param scale the scale for extrapolation - * @param startValue the starting value (scale = 0) - * @param endValue the end value (scale = 1) - * @return an extrapolation for the given parameters - */ - public static float extrapolateLinear(float scale, float startValue, float endValue) { -// if (scale <= 0f) { -// return startValue; -// } - return ((1f - scale) * startValue) + (scale * endValue); - } - - /** - * Linear extrapolation from startValue to endValue by the given scale. - * if scale is between 0 and 1 this method returns the same result as interpolateLinear - * if the scale is over 1 the value is linearly extrapolated. - * Note that the end value is the value for a scale of 1. - * @param scale the scale for extrapolation - * @param startValue the starting value (scale = 0) - * @param endValue the end value (scale = 1) - * @param store an initialized vector to store the return value - * @return an extrapolation for the given parameters - */ - public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) { - if (store == null) { - store = new Vector3f(); - } -// if (scale <= 1f) { -// return interpolateLinear(scale, startValue, endValue, store); -// } - store.x = extrapolateLinear(scale, startValue.x, endValue.x); - store.y = extrapolateLinear(scale, startValue.y, endValue.y); - store.z = extrapolateLinear(scale, startValue.z, endValue.z); - return store; - } - - /** - * Linear extrapolation from startValue to endValue by the given scale. - * if scale is between 0 and 1 this method returns the same result as interpolateLinear - * if the scale is over 1 the value is linearly extrapolated. - * Note that the end value is the value for a scale of 1. - * @param scale the scale for extrapolation - * @param startValue the starting value (scale = 0) - * @param endValue the end value (scale = 1) - * @return an extrapolation for the given parameters - */ - public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue) { - return extrapolateLinear(scale, startValue, endValue, null); - } - - /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation. - * here is the interpolation matrix - * m = [ 0.0 1.0 0.0 0.0 ] - * [-T 0.0 T 0.0 ] - * [ 2T T-3 3-2T -T ] - * [-T 2-T T-2 T ] - * where T is the curve tension - * the result is a value between p1 and p2, t=0 for p1, t=1 for p2 - * @param u value from 0 to 1 - * @param T The tension of the curve - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @return catmull-Rom interpolation - */ - public static float interpolateCatmullRom(float u, float T, float p0, float p1, float p2, float p3) { - float c1, c2, c3, c4; - c1 = p1; - c2 = -1.0f * T * p0 + T * p2; - c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3; - c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3; - - return (float) (((c4 * u + c3) * u + c2) * u + c1); - } - - /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation. - * here is the interpolation matrix - * m = [ 0.0 1.0 0.0 0.0 ] - * [-T 0.0 T 0.0 ] - * [ 2T T-3 3-2T -T ] - * [-T 2-T T-2 T ] - * where T is the tension of the curve - * the result is a value between p1 and p2, t=0 for p1, t=1 for p2 - * @param u value from 0 to 1 - * @param T The tension of the curve - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @param store a Vector3f to store the result - * @return catmull-Rom interpolation - */ - public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) { - if (store == null) { - store = new Vector3f(); - } - store.x = interpolateCatmullRom(u, T, p0.x, p1.x, p2.x, p3.x); - store.y = interpolateCatmullRom(u, T, p0.y, p1.y, p2.y, p3.y); - store.z = interpolateCatmullRom(u, T, p0.z, p1.z, p2.z, p3.z); - return store; - } - - /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation. - * here is the interpolation matrix - * m = [ 0.0 1.0 0.0 0.0 ] - * [-T 0.0 T 0.0 ] - * [ 2T T-3 3-2T -T ] - * [-T 2-T T-2 T ] - * where T is the tension of the curve - * the result is a value between p1 and p2, t=0 for p1, t=1 for p2 - * @param u value from 0 to 1 - * @param T The tension of the curve - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @return catmull-Rom interpolation - */ - public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { - return interpolateCatmullRom(u, T, p0, p1, p2, p3, null); - } - - /**Interpolate a spline between at least 4 control points following the Bezier equation. - * here is the interpolation matrix - * m = [ -1.0 3.0 -3.0 1.0 ] - * [ 3.0 -6.0 3.0 0.0 ] - * [ -3.0 3.0 0.0 0.0 ] - * [ 1.0 0.0 0.0 0.0 ] - * where T is the curve tension - * the result is a value between p1 and p3, t=0 for p1, t=1 for p3 - * @param u value from 0 to 1 - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @return Bezier interpolation - */ - public static float interpolateBezier(float u, float p0, float p1, float p2, float p3) { - float oneMinusU = 1.0f - u; - float oneMinusU2 = oneMinusU * oneMinusU; - float u2 = u * u; - return p0 * oneMinusU2 * oneMinusU - + 3.0f * p1 * u * oneMinusU2 - + 3.0f * p2 * u2 * oneMinusU - + p3 * u2 * u; - } - - /**Interpolate a spline between at least 4 control points following the Bezier equation. - * here is the interpolation matrix - * m = [ -1.0 3.0 -3.0 1.0 ] - * [ 3.0 -6.0 3.0 0.0 ] - * [ -3.0 3.0 0.0 0.0 ] - * [ 1.0 0.0 0.0 0.0 ] - * where T is the tension of the curve - * the result is a value between p1 and p3, t=0 for p1, t=1 for p3 - * @param u value from 0 to 1 - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @param store a Vector3f to store the result - * @return Bezier interpolation - */ - public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) { - if (store == null) { - store = new Vector3f(); - } - store.x = interpolateBezier(u, p0.x, p1.x, p2.x, p3.x); - store.y = interpolateBezier(u, p0.y, p1.y, p2.y, p3.y); - store.z = interpolateBezier(u, p0.z, p1.z, p2.z, p3.z); - return store; - } - - /**Interpolate a spline between at least 4 control points following the Bezier equation. - * here is the interpolation matrix - * m = [ -1.0 3.0 -3.0 1.0 ] - * [ 3.0 -6.0 3.0 0.0 ] - * [ -3.0 3.0 0.0 0.0 ] - * [ 1.0 0.0 0.0 0.0 ] - * where T is the tension of the curve - * the result is a value between p1 and p3, t=0 for p1, t=1 for p3 - * @param u value from 0 to 1 - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @return Bezier interpolation - */ - public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { - return interpolateBezier(u, p0, p1, p2, p3, null); - } - - /** - * Compute the lenght on a catmull rom spline between control point 1 and 2 - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @param startRange the starting range on the segment (use 0) - * @param endRange the end range on the segment (use 1) - * @param curveTension the curve tension - * @return the length of the segment - */ - public static float getCatmullRomP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, float startRange, float endRange, float curveTension) { - - float epsilon = 0.001f; - float middleValue = (startRange + endRange) * 0.5f; - Vector3f start = p1.clone(); - if (startRange != 0) { - FastMath.interpolateCatmullRom(startRange, curveTension, p0, p1, p2, p3, start); - } - Vector3f end = p2.clone(); - if (endRange != 1) { - FastMath.interpolateCatmullRom(endRange, curveTension, p0, p1, p2, p3, end); - } - Vector3f middle = FastMath.interpolateCatmullRom(middleValue, curveTension, p0, p1, p2, p3); - float l = end.subtract(start).length(); - float l1 = middle.subtract(start).length(); - float l2 = end.subtract(middle).length(); - float len = l1 + l2; - if (l + epsilon < len) { - l1 = getCatmullRomP1toP2Length(p0, p1, p2, p3, startRange, middleValue, curveTension); - l2 = getCatmullRomP1toP2Length(p0, p1, p2, p3, middleValue, endRange, curveTension); - } - l = l1 + l2; - return l; - } - - /** - * Compute the lenght on a bezier spline between control point 1 and 2 - * @param p0 control point 0 - * @param p1 control point 1 - * @param p2 control point 2 - * @param p3 control point 3 - * @return the length of the segment - */ - public static float getBezierP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { - float delta = 0.02f, t = 0.0f, result = 0.0f; - Vector3f v1 = p0.clone(), v2 = new Vector3f(); - while (t <= 1.0f) { - FastMath.interpolateBezier(t, p0, p1, p2, p3, v2); - result += v1.subtractLocal(v2).length(); - v1.set(v2); - t += delta; - } - return result; - } - - /** - * Returns the arc cosine of a value.
- * Special cases: - *
  • If fValue is smaller than -1, then the result is PI. - *
  • If the argument is greater than 1, then the result is 0.
- * @param fValue The value to arc cosine. - * @return The angle, in radians. - * @see java.lang.Math#acos(double) - */ - public static float acos(float fValue) { - if (-1.0f < fValue) { - if (fValue < 1.0f) { - return (float) Math.acos(fValue); - } - - return 0.0f; - } - - return PI; - } - - /** - * Returns the arc sine of a value.
- * Special cases: - *
  • If fValue is smaller than -1, then the result is -HALF_PI. - *
  • If the argument is greater than 1, then the result is HALF_PI.
- * @param fValue The value to arc sine. - * @return the angle in radians. - * @see java.lang.Math#asin(double) - */ - public static float asin(float fValue) { - if (-1.0f < fValue) { - if (fValue < 1.0f) { - return (float) Math.asin(fValue); - } - - return HALF_PI; - } - - return -HALF_PI; - } - - /** - * Returns the arc tangent of an angle given in radians.
- * @param fValue The angle, in radians. - * @return fValue's atan - * @see java.lang.Math#atan(double) - */ - public static float atan(float fValue) { - return (float) Math.atan(fValue); - } - - /** - * A direct call to Math.atan2. - * @param fY - * @param fX - * @return Math.atan2(fY,fX) - * @see java.lang.Math#atan2(double, double) - */ - public static float atan2(float fY, float fX) { - return (float) Math.atan2(fY, fX); - } - - /** - * Rounds a fValue up. A call to Math.ceil - * @param fValue The value. - * @return The fValue rounded up - * @see java.lang.Math#ceil(double) - */ - public static float ceil(float fValue) { - return (float) Math.ceil(fValue); - } - - /** - * Returns cosine of an angle. Direct call to java.lang.Math - * @see Math#cos(double) - * @param v The angle to cosine. - * @return the cosine of the angle. - */ - public static float cos(float v) { - return (float) Math.cos(v); - } - - /** - * Returns the sine of an angle. Direct call to java.lang.Math - * @see Math#sin(double) - * @param v The angle to sine. - * @return the sine of the angle. - */ - public static float sin(float v) { - return (float) Math.sin(v); - } - - /** - * Returns E^fValue - * @param fValue Value to raise to a power. - * @return The value E^fValue - * @see java.lang.Math#exp(double) - */ - public static float exp(float fValue) { - return (float) Math.exp(fValue); - } - - /** - * Returns Absolute value of a float. - * @param fValue The value to abs. - * @return The abs of the value. - * @see java.lang.Math#abs(float) - */ - public static float abs(float fValue) { - if (fValue < 0) { - return -fValue; - } - return fValue; - } - - /** - * Returns a number rounded down. - * @param fValue The value to round - * @return The given number rounded down - * @see java.lang.Math#floor(double) - */ - public static float floor(float fValue) { - return (float) Math.floor(fValue); - } - - /** - * Returns 1/sqrt(fValue) - * @param fValue The value to process. - * @return 1/sqrt(fValue) - * @see java.lang.Math#sqrt(double) - */ - public static float invSqrt(float fValue) { - return (float) (1.0f / Math.sqrt(fValue)); - } - - public static float fastInvSqrt(float x) { - float xhalf = 0.5f * x; - int i = Float.floatToIntBits(x); // get bits for floating value - i = 0x5f375a86 - (i >> 1); // gives initial guess y0 - x = Float.intBitsToFloat(i); // convert bits back to float - x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy - return x; - } - - /** - * Returns the log base E of a value. - * @param fValue The value to log. - * @return The log of fValue base E - * @see java.lang.Math#log(double) - */ - public static float log(float fValue) { - return (float) Math.log(fValue); - } - - /** - * Returns the logarithm of value with given base, calculated as log(value)/log(base), - * so that pow(base, return)==value (contributed by vear) - * @param value The value to log. - * @param base Base of logarithm. - * @return The logarithm of value with given base - */ - public static float log(float value, float base) { - return (float) (Math.log(value) / Math.log(base)); - } - - /** - * Returns a number raised to an exponent power. fBase^fExponent - * @param fBase The base value (IE 2) - * @param fExponent The exponent value (IE 3) - * @return base raised to exponent (IE 8) - * @see java.lang.Math#pow(double, double) - */ - public static float pow(float fBase, float fExponent) { - return (float) Math.pow(fBase, fExponent); - } - - /** - * Returns the value squared. fValue ^ 2 - * @param fValue The vaule to square. - * @return The square of the given value. - */ - public static float sqr(float fValue) { - return fValue * fValue; - } - - /** - * Returns the square root of a given value. - * @param fValue The value to sqrt. - * @return The square root of the given value. - * @see java.lang.Math#sqrt(double) - */ - public static float sqrt(float fValue) { - return (float) Math.sqrt(fValue); - } - - /** - * Returns the tangent of a value. If USE_FAST_TRIG is enabled, an approximate value - * is returned. Otherwise, a direct value is used. - * @param fValue The value to tangent, in radians. - * @return The tangent of fValue. - * @see java.lang.Math#tan(double) - */ - public static float tan(float fValue) { - return (float) Math.tan(fValue); - } - - /** - * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise - * @param iValue The integer to examine. - * @return The integer's sign. - */ - public static int sign(int iValue) { - if (iValue > 0) { - return 1; - } - if (iValue < 0) { - return -1; - } - return 0; - } - - /** - * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise - * @param fValue The float to examine. - * @return The float's sign. - */ - public static float sign(float fValue) { - return Math.signum(fValue); - } - - /** - * Given 3 points in a 2d plane, this function computes if the points going from A-B-C - * are moving counter clock wise. - * @param p0 Point 0. - * @param p1 Point 1. - * @param p2 Point 2. - * @return 1 If they are CCW, -1 if they are not CCW, 0 if p2 is between p0 and p1. - */ - public static int counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) { - float dx1, dx2, dy1, dy2; - dx1 = p1.x - p0.x; - dy1 = p1.y - p0.y; - dx2 = p2.x - p0.x; - dy2 = p2.y - p0.y; - if (dx1 * dy2 > dy1 * dx2) { - return 1; - } - if (dx1 * dy2 < dy1 * dx2) { - return -1; - } - if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0)) { - return -1; - } - if ((dx1 * dx1 + dy1 * dy1) < (dx2 * dx2 + dy2 * dy2)) { - return 1; - } - return 0; - } - - /** - * Test if a point is inside a triangle. 1 if the point is on the ccw side, - * -1 if the point is on the cw side, and 0 if it is on neither. - * @param t0 First point of the triangle. - * @param t1 Second point of the triangle. - * @param t2 Third point of the triangle. - * @param p The point to test. - * @return Value 1 or -1 if inside triangle, 0 otherwise. - */ - public static int pointInsideTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p) { - int val1 = counterClockwise(t0, t1, p); - if (val1 == 0) { - return 1; - } - int val2 = counterClockwise(t1, t2, p); - if (val2 == 0) { - return 1; - } - if (val2 != val1) { - return 0; - } - int val3 = counterClockwise(t2, t0, p); - if (val3 == 0) { - return 1; - } - if (val3 != val1) { - return 0; - } - return val3; - } - - /** - * A method that computes normal for a triangle defined by three vertices. - * @param v1 first vertex - * @param v2 second vertex - * @param v3 third vertex - * @return a normal for the face - */ - public static Vector3f computeNormal(Vector3f v1, Vector3f v2, Vector3f v3) { - Vector3f a1 = v1.subtract(v2); - Vector3f a2 = v3.subtract(v2); - return a2.crossLocal(a1).normalizeLocal(); - } - - /** - * Returns the determinant of a 4x4 matrix. - */ - public static float determinant(double m00, double m01, double m02, - double m03, double m10, double m11, double m12, double m13, - double m20, double m21, double m22, double m23, double m30, - double m31, double m32, double m33) { - - double det01 = m20 * m31 - m21 * m30; - double det02 = m20 * m32 - m22 * m30; - double det03 = m20 * m33 - m23 * m30; - double det12 = m21 * m32 - m22 * m31; - double det13 = m21 * m33 - m23 * m31; - double det23 = m22 * m33 - m23 * m32; - return (float) (m00 * (m11 * det23 - m12 * det13 + m13 * det12) - m01 - * (m10 * det23 - m12 * det03 + m13 * det02) + m02 - * (m10 * det13 - m11 * det03 + m13 * det01) - m03 - * (m10 * det12 - m11 * det02 + m12 * det01)); - } - - /** - * Returns a random float between 0 and 1. - * - * @return A random float between 0.0f (inclusive) to - * 1.0f (exclusive). - */ - public static float nextRandomFloat() { - return rand.nextFloat(); - } - - /** - * Returns a random integer between min and max. - * - * @return A random int between min (inclusive) to - * max (inclusive). - */ - public static int nextRandomInt(int min, int max) { - return (int) (nextRandomFloat() * (max - min + 1)) + min; - } - - public static int nextRandomInt() { - return rand.nextInt(); - } - - /** - * Converts a point from Spherical coordinates to Cartesian (using positive - * Y as up) and stores the results in the store var. - */ - public static Vector3f sphericalToCartesian(Vector3f sphereCoords, - Vector3f store) { - if (store == null) { - store = new Vector3f(); - } - store.y = sphereCoords.x * FastMath.sin(sphereCoords.z); - float a = sphereCoords.x * FastMath.cos(sphereCoords.z); - store.x = a * FastMath.cos(sphereCoords.y); - store.z = a * FastMath.sin(sphereCoords.y); - - return store; - } - - /** - * Converts a point from Cartesian coordinates (using positive Y as up) to - * Spherical and stores the results in the store var. (Radius, Azimuth, - * Polar) - */ - public static Vector3f cartesianToSpherical(Vector3f cartCoords, - Vector3f store) { - if (store == null) { - store = new Vector3f(); - } - float x = cartCoords.x; - if (x == 0) { - x = FastMath.FLT_EPSILON; - } - store.x = FastMath.sqrt((x * x) - + (cartCoords.y * cartCoords.y) - + (cartCoords.z * cartCoords.z)); - store.y = FastMath.atan(cartCoords.z / x); - if (x < 0) { - store.y += FastMath.PI; - } - store.z = FastMath.asin(cartCoords.y / store.x); - return store; - } - - /** - * Converts a point from Spherical coordinates to Cartesian (using positive - * Z as up) and stores the results in the store var. - */ - public static Vector3f sphericalToCartesianZ(Vector3f sphereCoords, - Vector3f store) { - if (store == null) { - store = new Vector3f(); - } - store.z = sphereCoords.x * FastMath.sin(sphereCoords.z); - float a = sphereCoords.x * FastMath.cos(sphereCoords.z); - store.x = a * FastMath.cos(sphereCoords.y); - store.y = a * FastMath.sin(sphereCoords.y); - - return store; - } - - /** - * Converts a point from Cartesian coordinates (using positive Z as up) to - * Spherical and stores the results in the store var. (Radius, Azimuth, - * Polar) - */ - public static Vector3f cartesianZToSpherical(Vector3f cartCoords, - Vector3f store) { - if (store == null) { - store = new Vector3f(); - } - float x = cartCoords.x; - if (x == 0) { - x = FastMath.FLT_EPSILON; - } - store.x = FastMath.sqrt((x * x) - + (cartCoords.y * cartCoords.y) - + (cartCoords.z * cartCoords.z)); - store.z = FastMath.atan(cartCoords.z / x); - if (x < 0) { - store.z += FastMath.PI; - } - store.y = FastMath.asin(cartCoords.y / store.x); - return store; - } - - /** - * Takes an value and expresses it in terms of min to max. - * - * @param val - - * the angle to normalize (in radians) - * @return the normalized angle (also in radians) - */ - public static float normalize(float val, float min, float max) { - if (Float.isInfinite(val) || Float.isNaN(val)) { - return 0f; - } - float range = max - min; - while (val > max) { - val -= range; - } - while (val < min) { - val += range; - } - return val; - } - - /** - * @param x - * the value whose sign is to be adjusted. - * @param y - * the value whose sign is to be used. - * @return x with its sign changed to match the sign of y. - */ - public static float copysign(float x, float y) { - if (y >= 0 && x <= -0) { - return -x; - } else if (y < 0 && x >= 0) { - return -x; - } else { - return x; - } - } - - /** - * Take a float input and clamp it between min and max. - * - * @param input - * @param min - * @param max - * @return clamped input - */ - public static float clamp(float input, float min, float max) { - return (input < min) ? min : (input > max) ? max : input; - } - - /** - * Clamps the given float to be between 0 and 1. - * - * @param input - * @return input clamped between 0 and 1. - */ - public static float saturate(float input) { - return clamp(input, 0f, 1f); - } - - /** - * Converts a single precision (32 bit) floating point value - * into half precision (16 bit). - * - *

Source: - * http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
broken link - * - * @param half The half floating point value as a short. - * @return floating point value of the half. - */ - public static float convertHalfToFloat(short half) { - switch ((int) half) { - case 0x0000: - return 0f; - case 0x8000: - return -0f; - case 0x7c00: - return Float.POSITIVE_INFINITY; - case 0xfc00: - return Float.NEGATIVE_INFINITY; - // TODO: Support for NaN? - default: - return Float.intBitsToFloat(((half & 0x8000) << 16) - | (((half & 0x7c00) + 0x1C000) << 13) - | ((half & 0x03FF) << 13)); - } - } - - public static short convertFloatToHalf(float flt) { - if (Float.isNaN(flt)) { - throw new UnsupportedOperationException("NaN to half conversion not supported!"); - } else if (flt == Float.POSITIVE_INFINITY) { - return (short) 0x7c00; - } else if (flt == Float.NEGATIVE_INFINITY) { - return (short) 0xfc00; - } else if (flt == 0f) { - return (short) 0x0000; - } else if (flt == -0f) { - return (short) 0x8000; - } else if (flt > 65504f) { - // max value supported by half float - return 0x7bff; - } else if (flt < -65504f) { - return (short) (0x7bff | 0x8000); - } else if (flt > 0f && flt < 5.96046E-8f) { - return 0x0001; - } else if (flt < 0f && flt > -5.96046E-8f) { - return (short) 0x8001; - } - - int f = Float.floatToIntBits(flt); - return (short) (((f >> 16) & 0x8000) - | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) - | ((f >> 13) & 0x03ff)); - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.math; + +import java.util.Random; + +/** + * FastMath provides 'fast' math approximations and float equivalents of Math + * functions. These are all used as static values and functions. + * + * @author Various + * @version $Id: FastMath.java,v 1.45 2007/08/26 08:44:20 irrisor Exp $ + */ +final public class FastMath { + + private FastMath() { + } + /** A "close to zero" double epsilon value for use*/ + public static final double DBL_EPSILON = 2.220446049250313E-16d; + /** A "close to zero" float epsilon value for use*/ + public static final float FLT_EPSILON = 1.1920928955078125E-7f; + /** A "close to zero" float epsilon value for use*/ + public static final float ZERO_TOLERANCE = 0.0001f; + public static final float ONE_THIRD = 1f / 3f; + /** The value PI as a float. (180 degrees) */ + public static final float PI = (float) Math.PI; + /** The value 2PI as a float. (360 degrees) */ + public static final float TWO_PI = 2.0f * PI; + /** The value PI/2 as a float. (90 degrees) */ + public static final float HALF_PI = 0.5f * PI; + /** The value PI/4 as a float. (45 degrees) */ + public static final float QUARTER_PI = 0.25f * PI; + /** The value 1/PI as a float. */ + public static final float INV_PI = 1.0f / PI; + /** The value 1/(2PI) as a float. */ + public static final float INV_TWO_PI = 1.0f / TWO_PI; + /** A value to multiply a degree value by, to convert it to radians. */ + public static final float DEG_TO_RAD = PI / 180.0f; + /** A value to multiply a radian value by, to convert it to degrees. */ + public static final float RAD_TO_DEG = 180.0f / PI; + /** A precreated random object for random numbers. */ + public static final Random rand = new Random(System.currentTimeMillis()); + + /** + * Returns true if the number is a power of 2 (2,4,8,16...) + * + * A good implementation found on the Java boards. note: a number is a power + * of two if and only if it is the smallest number with that number of + * significant bits. Therefore, if you subtract 1, you know that the new + * number will have fewer bits, so ANDing the original number with anything + * less than it will give 0. + * + * @param number + * The number to test. + * @return True if it is a power of two. + */ + public static boolean isPowerOfTwo(int number) { + return (number > 0) && (number & (number - 1)) == 0; + } + + public static int nearestPowerOfTwo(int number) { + return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); + } + + /** + * Linear interpolation from startValue to endValue by the given percent. + * Basically: ((1 - percent) * startValue) + (percent * endValue) + * + * @param scale + * scale value to use. if 1, use endValue, if 0, use startValue. + * @param startValue + * Beginning 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 + * Beginning 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 + * Beginning 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 using 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 length of a Catmull–Rom spline between control points 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 length on a Bezier spline between control points 1 and 2. + * @param p0 control point 0 + * @param p1 control point 1 + * @param p2 control point 2 + * @param p3 control point 3 + * @return the length of the segment + */ + public static float getBezierP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { + float delta = 0.02f, t = 0.0f, result = 0.0f; + Vector3f v1 = p0.clone(), v2 = new Vector3f(); + while (t <= 1.0f) { + FastMath.interpolateBezier(t, p0, p1, p2, p3, v2); + result += v1.subtractLocal(v2).length(); + v1.set(v2); + t += delta; + } + return result; + } + + /** + * Returns the arc cosine of a value.
+ * Special cases: + *

  • If fValue is smaller than -1, then the result is PI. + *
  • If the argument is greater than 1, then the result is 0.
+ * @param fValue The value to arc cosine. + * @return The angle, in radians. + * @see java.lang.Math#acos(double) + */ + public static float acos(float fValue) { + if (-1.0f < fValue) { + if (fValue < 1.0f) { + return (float) Math.acos(fValue); + } + + return 0.0f; + } + + return PI; + } + + /** + * Returns the arc sine of a value.
+ * Special cases: + *
  • If fValue is smaller than -1, then the result is -HALF_PI. + *
  • If the argument is greater than 1, then the result is HALF_PI.
+ * @param fValue The value to arc sine. + * @return the angle in radians. + * @see java.lang.Math#asin(double) + */ + public static float asin(float fValue) { + if (-1.0f < fValue) { + if (fValue < 1.0f) { + return (float) Math.asin(fValue); + } + + return HALF_PI; + } + + return -HALF_PI; + } + + /** + * Returns the arc tangent of an angle given in radians.
+ * @param fValue The angle, in radians. + * @return fValue's atan + * @see java.lang.Math#atan(double) + */ + public static float atan(float fValue) { + return (float) Math.atan(fValue); + } + + /** + * A direct call to Math.atan2. + * @param fY + * @param fX + * @return Math.atan2(fY,fX) + * @see java.lang.Math#atan2(double, double) + */ + public static float atan2(float fY, float fX) { + return (float) Math.atan2(fY, fX); + } + + /** + * Rounds a fValue up. A call to Math.ceil + * @param fValue The value. + * @return The fValue rounded up + * @see java.lang.Math#ceil(double) + */ + public static float ceil(float fValue) { + return (float) Math.ceil(fValue); + } + + /** + * Returns cosine of an angle. Direct call to java.lang.Math + * @see Math#cos(double) + * @param v The angle to cosine. + * @return the cosine of the angle. + */ + public static float cos(float v) { + return (float) Math.cos(v); + } + + /** + * Returns the sine of an angle. Direct call to java.lang.Math + * @see Math#sin(double) + * @param v The angle to sine. + * @return the sine of the angle. + */ + public static float sin(float v) { + return (float) Math.sin(v); + } + + /** + * Returns E^fValue + * @param fValue Value to raise to a power. + * @return The value E^fValue + * @see java.lang.Math#exp(double) + */ + public static float exp(float fValue) { + return (float) Math.exp(fValue); + } + + /** + * Returns Absolute value of a float. + * @param fValue The value to abs. + * @return The abs of the value. + * @see java.lang.Math#abs(float) + */ + public static float abs(float fValue) { + if (fValue < 0) { + return -fValue; + } + return fValue; + } + + /** + * Returns a number rounded down. + * @param fValue The value to round + * @return The given number rounded down + * @see java.lang.Math#floor(double) + */ + public static float floor(float fValue) { + return (float) Math.floor(fValue); + } + + /** + * Returns 1/sqrt(fValue) + * @param fValue The value to process. + * @return 1/sqrt(fValue) + * @see java.lang.Math#sqrt(double) + */ + public static float invSqrt(float fValue) { + return (float) (1.0f / Math.sqrt(fValue)); + } + + public static float fastInvSqrt(float x) { + float xhalf = 0.5f * x; + int i = Float.floatToIntBits(x); // get bits for floating value + i = 0x5f375a86 - (i >> 1); // gives initial guess y0 + x = Float.intBitsToFloat(i); // convert bits back to float + x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy + return x; + } + + /** + * Returns the log base E of a value. + * @param fValue The value to log. + * @return The log of fValue base E + * @see java.lang.Math#log(double) + */ + public static float log(float fValue) { + return (float) Math.log(fValue); + } + + /** + * Returns the logarithm of value with given base, calculated as log(value)/log(base), + * so that pow(base, return)==value (contributed by vear) + * @param value The value to log. + * @param base Base of logarithm. + * @return The logarithm of value with given base + */ + public static float log(float value, float base) { + return (float) (Math.log(value) / Math.log(base)); + } + + /** + * Returns a number raised to an exponent power. fBase^fExponent + * @param fBase The base value (IE 2) + * @param fExponent The exponent value (IE 3) + * @return base raised to exponent (IE 8) + * @see java.lang.Math#pow(double, double) + */ + public static float pow(float fBase, float fExponent) { + return (float) Math.pow(fBase, fExponent); + } + + /** + * Returns the value squared. fValue ^ 2 + * @param fValue The value to square. + * @return The square of the given value. + */ + public static float sqr(float fValue) { + return fValue * fValue; + } + + /** + * Returns the square root of a given value. + * @param fValue The value to sqrt. + * @return The square root of the given value. + * @see java.lang.Math#sqrt(double) + */ + public static float sqrt(float fValue) { + return (float) Math.sqrt(fValue); + } + + /** + * Returns the tangent of a value. If USE_FAST_TRIG is enabled, an approximate value + * is returned. Otherwise, a direct value is used. + * @param fValue The value to tangent, in radians. + * @return The tangent of fValue. + * @see java.lang.Math#tan(double) + */ + public static float tan(float fValue) { + return (float) Math.tan(fValue); + } + + /** + * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise + * @param iValue The integer to examine. + * @return The integer's sign. + */ + public static int sign(int iValue) { + if (iValue > 0) { + return 1; + } + if (iValue < 0) { + return -1; + } + return 0; + } + + /** + * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise + * @param fValue The float to examine. + * @return The float's sign. + */ + public static float sign(float fValue) { + return Math.signum(fValue); + } + + /** + * Given 3 points in a 2d plane, this function computes if the points going from A-B-C + * are moving counter clock wise. + * @param p0 Point 0. + * @param p1 Point 1. + * @param p2 Point 2. + * @return 1 If they are CCW, -1 if they are not CCW, 0 if p2 is between p0 and p1. + */ + public static int counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) { + float dx1, dx2, dy1, dy2; + dx1 = p1.x - p0.x; + dy1 = p1.y - p0.y; + dx2 = p2.x - p0.x; + dy2 = p2.y - p0.y; + if (dx1 * dy2 > dy1 * dx2) { + return 1; + } + if (dx1 * dy2 < dy1 * dx2) { + return -1; + } + if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0)) { + return -1; + } + if ((dx1 * dx1 + dy1 * dy1) < (dx2 * dx2 + dy2 * dy2)) { + return 1; + } + return 0; + } + + /** + * Test if a point is inside a triangle. 1 if the point is on the ccw side, + * -1 if the point is on the cw side, and 0 if it is on neither. + * @param t0 First point of the triangle. + * @param t1 Second point of the triangle. + * @param t2 Third point of the triangle. + * @param p The point to test. + * @return Value 1 or -1 if inside triangle, 0 otherwise. + */ + public static int pointInsideTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p) { + int val1 = counterClockwise(t0, t1, p); + if (val1 == 0) { + return 1; + } + int val2 = counterClockwise(t1, t2, p); + if (val2 == 0) { + return 1; + } + if (val2 != val1) { + return 0; + } + int val3 = counterClockwise(t2, t0, p); + if (val3 == 0) { + return 1; + } + if (val3 != val1) { + return 0; + } + return val3; + } + + /** + * A method that computes normal for a triangle defined by three vertices. + * @param v1 first vertex + * @param v2 second vertex + * @param v3 third vertex + * @return a normal for the face + */ + public static Vector3f computeNormal(Vector3f v1, Vector3f v2, Vector3f v3) { + Vector3f a1 = v1.subtract(v2); + Vector3f a2 = v3.subtract(v2); + return a2.crossLocal(a1).normalizeLocal(); + } + + /** + * Returns the determinant of a 4x4 matrix. + */ + public static float determinant(double m00, double m01, double m02, + double m03, double m10, double m11, double m12, double m13, + double m20, double m21, double m22, double m23, double m30, + double m31, double m32, double m33) { + + double det01 = m20 * m31 - m21 * m30; + double det02 = m20 * m32 - m22 * m30; + double det03 = m20 * m33 - m23 * m30; + double det12 = m21 * m32 - m22 * m31; + double det13 = m21 * m33 - m23 * m31; + double det23 = m22 * m33 - m23 * m32; + return (float) (m00 * (m11 * det23 - m12 * det13 + m13 * det12) - m01 + * (m10 * det23 - m12 * det03 + m13 * det02) + m02 + * (m10 * det13 - m11 * det03 + m13 * det01) - m03 + * (m10 * det12 - m11 * det02 + m12 * det01)); + } + + /** + * Returns a random float between 0 and 1. + * + * @return A random float between 0.0f (inclusive) to + * 1.0f (exclusive). + */ + public static float nextRandomFloat() { + return rand.nextFloat(); + } + + /** + * Returns a random integer between min and max. + * + * @return A random int between min (inclusive) to + * max (inclusive). + */ + public static int nextRandomInt(int min, int max) { + return (int) (nextRandomFloat() * (max - min + 1)) + min; + } + + public static int nextRandomInt() { + return rand.nextInt(); + } + + /** + * Converts a point from Spherical coordinates to Cartesian (using positive + * Y as up) and stores the results in the store var. + */ + public static Vector3f sphericalToCartesian(Vector3f sphereCoords, + Vector3f store) { + if (store == null) { + store = new Vector3f(); + } + store.y = sphereCoords.x * FastMath.sin(sphereCoords.z); + float a = sphereCoords.x * FastMath.cos(sphereCoords.z); + store.x = a * FastMath.cos(sphereCoords.y); + store.z = a * FastMath.sin(sphereCoords.y); + + return store; + } + + /** + * Converts a point from Cartesian coordinates (using positive Y as up) to + * Spherical and stores the results in the store var. (Radius, Azimuth, + * Polar) + */ + public static Vector3f cartesianToSpherical(Vector3f cartCoords, + Vector3f store) { + if (store == null) { + store = new Vector3f(); + } + float x = cartCoords.x; + if (x == 0) { + x = FastMath.FLT_EPSILON; + } + store.x = FastMath.sqrt((x * x) + + (cartCoords.y * cartCoords.y) + + (cartCoords.z * cartCoords.z)); + store.y = FastMath.atan(cartCoords.z / x); + if (x < 0) { + store.y += FastMath.PI; + } + store.z = FastMath.asin(cartCoords.y / store.x); + return store; + } + + /** + * Converts a point from Spherical coordinates to Cartesian (using positive + * Z as up) and stores the results in the store var. + */ + public static Vector3f sphericalToCartesianZ(Vector3f sphereCoords, + Vector3f store) { + if (store == null) { + store = new Vector3f(); + } + store.z = sphereCoords.x * FastMath.sin(sphereCoords.z); + float a = sphereCoords.x * FastMath.cos(sphereCoords.z); + store.x = a * FastMath.cos(sphereCoords.y); + store.y = a * FastMath.sin(sphereCoords.y); + + return store; + } + + /** + * Converts a point from Cartesian coordinates (using positive Z as up) to + * Spherical and stores the results in the store var. (Radius, Azimuth, + * Polar) + */ + public static Vector3f cartesianZToSpherical(Vector3f cartCoords, + Vector3f store) { + if (store == null) { + store = new Vector3f(); + } + float x = cartCoords.x; + if (x == 0) { + x = FastMath.FLT_EPSILON; + } + store.x = FastMath.sqrt((x * x) + + (cartCoords.y * cartCoords.y) + + (cartCoords.z * cartCoords.z)); + store.z = FastMath.atan(cartCoords.z / x); + if (x < 0) { + store.z += FastMath.PI; + } + store.y = FastMath.asin(cartCoords.y / store.x); + return store; + } + + /** + * Takes an value and expresses it in terms of min to max. + * + * @param val - + * the angle to normalize (in radians) + * @return the normalized angle (also in radians) + */ + public static float normalize(float val, float min, float max) { + if (Float.isInfinite(val) || Float.isNaN(val)) { + return 0f; + } + float range = max - min; + while (val > max) { + val -= range; + } + while (val < min) { + val += range; + } + return val; + } + + /** + * @param x + * the value whose sign is to be adjusted. + * @param y + * the value whose sign is to be used. + * @return x with its sign changed to match the sign of y. + */ + public static float copysign(float x, float y) { + if (y >= 0 && x <= -0) { + return -x; + } else if (y < 0 && x >= 0) { + return -x; + } else { + return x; + } + } + + /** + * Take a float input and clamp it between min and max. + * + * @param input + * @param min + * @param max + * @return clamped input + */ + public static float clamp(float input, float min, float max) { + return (input < min) ? min : (input > max) ? max : input; + } + + /** + * Clamps the given float to be between 0 and 1. + * + * @param input + * @return input clamped between 0 and 1. + */ + public static float saturate(float input) { + return clamp(input, 0f, 1f); + } + + /** + * Converts a single precision (32 bit) floating point value + * into half precision (16 bit). + * + *

Source: + * http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
broken link + * + * @param half The half floating point value as a short. + * @return floating point value of the half. + */ + public static float convertHalfToFloat(short half) { + switch ((int) half) { + case 0x0000: + return 0f; + case 0x8000: + return -0f; + case 0x7c00: + return Float.POSITIVE_INFINITY; + case 0xfc00: + return Float.NEGATIVE_INFINITY; + // TODO: Support for NaN? + default: + return Float.intBitsToFloat(((half & 0x8000) << 16) + | (((half & 0x7c00) + 0x1C000) << 13) + | ((half & 0x03FF) << 13)); + } + } + + public static short convertFloatToHalf(float flt) { + if (Float.isNaN(flt)) { + throw new UnsupportedOperationException("NaN to half conversion not supported!"); + } else if (flt == Float.POSITIVE_INFINITY) { + return (short) 0x7c00; + } else if (flt == Float.NEGATIVE_INFINITY) { + return (short) 0xfc00; + } else if (flt == 0f) { + return (short) 0x0000; + } else if (flt == -0f) { + return (short) 0x8000; + } else if (flt > 65504f) { + // max value supported by half float + return 0x7bff; + } else if (flt < -65504f) { + return (short) (0x7bff | 0x8000); + } else if (flt > 0f && flt < 5.96046E-8f) { + return 0x0001; + } else if (flt < 0f && flt > -5.96046E-8f) { + return (short) 0x8001; + } + + int f = Float.floatToIntBits(flt); + return (short) (((f >> 16) & 0x8000) + | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) + | ((f >> 13) & 0x03ff)); + } +} diff --git a/jme3-core/src/main/java/com/jme3/post/Filter.java b/jme3-core/src/main/java/com/jme3/post/Filter.java index a7cb93d37..a15374590 100644 --- a/jme3-core/src/main/java/com/jme3/post/Filter.java +++ b/jme3-core/src/main/java/com/jme3/post/Filter.java @@ -1,456 +1,456 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.post; - -import com.jme3.asset.AssetManager; -import com.jme3.export.*; -import com.jme3.material.Material; -import com.jme3.renderer.Caps; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.Renderer; -import com.jme3.renderer.ViewPort; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture2D; -import java.io.IOException; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -/** - * Filters are 2D effects applied to the rendered scene.
- * The filter is fed with the rendered scene image rendered in an offscreen frame buffer.
- * This texture is applied on a fullscreen quad, with a special material.
- * This material uses a shader that aplly the desired effect to the scene texture.
- *
- * This class is abstract, any Filter must extend it.
- * Any filter holds a frameBuffer and a texture
- * The getMaterial must return a Material that use a GLSL shader immplementing the desired effect
- * - * @author Rémy Bouquet aka Nehon - */ -public abstract class Filter implements Savable { - - - private String name; - protected Pass defaultPass; - protected List postRenderPasses; - protected Material material; - protected boolean enabled = true; - protected FilterPostProcessor processor; - - public Filter(String name) { - this.name = name; - } - - /** - * Inner class Pass - * Pass are like filters in filters. - * Some filters will need multiple passes before the final render - */ - public class Pass { - - protected FrameBuffer renderFrameBuffer; - protected Texture2D renderedTexture; - protected Texture2D depthTexture; - protected Material passMaterial; - - /** - * init the pass called internally - * @param renderer - * @param width - * @param height - * @param textureFormat - * @param depthBufferFormat - * @param numSamples - */ - public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples, boolean renderDepth) { - Collection caps = renderer.getCaps(); - if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample) && caps.contains(Caps.OpenGL31)) { - renderFrameBuffer = new FrameBuffer(width, height, numSamples); - renderedTexture = new Texture2D(width, height, numSamples, textureFormat); - renderFrameBuffer.setDepthBuffer(depthBufferFormat); - if (renderDepth) { - depthTexture = new Texture2D(width, height, numSamples, depthBufferFormat); - renderFrameBuffer.setDepthTexture(depthTexture); - } - } else { - renderFrameBuffer = new FrameBuffer(width, height, 1); - renderedTexture = new Texture2D(width, height, textureFormat); - renderFrameBuffer.setDepthBuffer(depthBufferFormat); - if (renderDepth) { - depthTexture = new Texture2D(width, height, depthBufferFormat); - renderFrameBuffer.setDepthTexture(depthTexture); - } - } - - renderFrameBuffer.setColorTexture(renderedTexture); - - - } - - /** - * init the pass called internally - * @param renderer - * @param width - * @param height - * @param textureFormat - * @param depthBufferFormat - */ - public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat) { - init(renderer, width, height, textureFormat, depthBufferFormat, 1); - } - - public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples) { - init(renderer, width, height, textureFormat, depthBufferFormat, numSamples, false); - } - - /** - * init the pass called internally - * @param renderer - * @param width - * @param height - * @param textureFormat - * @param depthBufferFormat - * @param numSample - * @param material - */ - public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSample, Material material) { - init(renderer, width, height, textureFormat, depthBufferFormat, numSample); - passMaterial = material; - } - - public boolean requiresSceneAsTexture() { - return false; - } - - public boolean requiresDepthAsTexture() { - return false; - } - - public void beforeRender() { - } - - public FrameBuffer getRenderFrameBuffer() { - return renderFrameBuffer; - } - - public void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { - this.renderFrameBuffer = renderFrameBuffer; - } - - public Texture2D getDepthTexture() { - return depthTexture; - } - - public Texture2D getRenderedTexture() { - return renderedTexture; - } - - public void setRenderedTexture(Texture2D renderedTexture) { - this.renderedTexture = renderedTexture; - } - - public Material getPassMaterial() { - return passMaterial; - } - - public void setPassMaterial(Material passMaterial) { - this.passMaterial = passMaterial; - } - - public void cleanup(Renderer r) { - renderFrameBuffer.dispose(); - renderedTexture.getImage().dispose(); - if(depthTexture!=null){ - depthTexture.getImage().dispose(); - } - } - } - - /** - * returns the default pass texture format - * @return - */ - protected Format getDefaultPassTextureFormat() { - return Format.RGBA8; - } - - /** - * returns the default pass depth format - * @return - */ - protected Format getDefaultPassDepthFormat() { - return Format.Depth; - } - - /** - * contruct a Filter - */ - protected Filter() { - this("filter"); - } - - /** - * - * initialize this filter - * use InitFilter for overriding filter initialization - * @param manager the assetManager - * @param renderManager the renderManager - * @param vp the viewport - * @param w the width - * @param h the height - */ - protected final void init(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { - // cleanup(renderManager.getRenderer()); - defaultPass = new Pass(); - defaultPass.init(renderManager.getRenderer(), w, h, getDefaultPassTextureFormat(), getDefaultPassDepthFormat()); - initFilter(manager, renderManager, vp, w, h); - } - - /** - * cleanup this filter - * @param r - */ - protected final void cleanup(Renderer r) { - processor = null; - if (defaultPass != null) { - defaultPass.cleanup(r); - } - if (postRenderPasses != null) { - for (Iterator it = postRenderPasses.iterator(); it.hasNext();) { - Pass pass = it.next(); - pass.cleanup(r); - } - } - cleanUpFilter(r); - } - - /** - * Initialization of sub classes filters - * This method is called once when the filter is added to the FilterPostProcessor - * It should contain Material initializations and extra passes initialization - * @param manager the assetManager - * @param renderManager the renderManager - * @param vp the viewPort where this filter is rendered - * @param w the width of the filter - * @param h the height of the filter - */ - protected abstract void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h); - - /** - * override this method if you have some cleanup to do - * @param r the renderer - */ - protected void cleanUpFilter(Renderer r) { - } - - /** - * Must return the material used for this filter. - * this method is called every frame. - * - * @return the material used for this filter. - */ - protected abstract Material getMaterial(); - - /** - * Override if you want to do something special with the depth texture; - * @param depthTexture - */ - protected void setDepthTexture(Texture depthTexture){ - getMaterial().setTexture("DepthTexture", depthTexture); - } - - /** - * Override this method if you want to make a pre pass, before the actual rendering of the frame - * @param queue - */ - protected void postQueue(RenderQueue queue) { - } - - /** - * Override this method if you want to modify parameters according to tpf before the rendering of the frame. - * This is usefull for animated filters - * Also it can be the place to render pre passes - * @param tpf the time used to render the previous frame - */ - protected void preFrame(float tpf) { - } - - /** - * Override this method if you want to make a pass just after the frame has been rendered and just before the filter rendering - * @param renderManager - * @param viewPort - * @param prevFilterBuffer - * @param sceneBuffer - */ - protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { - } - - /** - * Override this method if you want to save extra properties when the filter is saved else only basic properties of the filter will be saved - * This method should always begin by super.write(ex); - * @param ex - * @throws IOException - */ - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.write(name, "name", ""); - oc.write(enabled, "enabled", true); - } - - /** - * Override this method if you want to load extra properties when the filter - * is loaded else only basic properties of the filter will be loaded - * This method should always begin by super.read(im); - */ - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - name = ic.readString("name", ""); - enabled = ic.readBoolean("enabled", true); - } - - /** - * returns the name of the filter - * @return the Filter's name - */ - public String getName() { - return name; - } - - /** - * Sets the name of the filter - * @param name - */ - public void setName(String name) { - this.name = name; - } - - /** - * returns the default pass frame buffer - * @return - */ - protected FrameBuffer getRenderFrameBuffer() { - return defaultPass.renderFrameBuffer; - } - - /** - * sets the default pas frame buffer - * @param renderFrameBuffer - */ - protected void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { - this.defaultPass.renderFrameBuffer = renderFrameBuffer; - } - - /** - * returns the rendered texture of this filter - * @return - */ - protected Texture2D getRenderedTexture() { - return defaultPass.renderedTexture; - } - - /** - * sets the rendered texture of this filter - * @param renderedTexture - */ - protected void setRenderedTexture(Texture2D renderedTexture) { - this.defaultPass.renderedTexture = renderedTexture; - } - - /** - * Override this method and return true if your Filter needs the depth texture - * - * @return true if your Filter need the depth texture - */ - protected boolean isRequiresDepthTexture() { - return false; - } - - /** - * Override this method and return false if your Filter does not need the scene texture - * - * @return false if your Filter does not need the scene texture - */ - protected boolean isRequiresSceneTexture() { - return true; - } - - /** - * returns the list of the postRender passes - * @return - */ - protected List getPostRenderPasses() { - return postRenderPasses; - } - - /** - * Enable or disable this filter - * @param enabled true to enable - */ - public void setEnabled(boolean enabled) { - if (processor != null) { - processor.setFilterState(this, enabled); - } else { - this.enabled = enabled; - } - } - - /** - * returns ttrue if the filter is enabled - * @return enabled - */ - public boolean isEnabled() { - return enabled; - } - - /** - * sets a reference to the FilterPostProcessor ti which this filter is attached - * @param proc - */ - protected void setProcessor(FilterPostProcessor proc) { - processor = proc; - } - - /** - * This method is called right after the filter has been rendered to the - * framebuffer. - * Note that buffer will be null if the filter is the last one in the stack - * and has been rendered to screen - * @param r the renderer - * @param buffer the framebuffer on hich the filtre has been rendered. - */ - protected void postFilter(Renderer r, FrameBuffer buffer){ - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.post; + +import com.jme3.asset.AssetManager; +import com.jme3.export.*; +import com.jme3.material.Material; +import com.jme3.renderer.Caps; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Image.Format; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture2D; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * Filters are 2D effects applied to the rendered scene.
+ * The filter is fed with the rendered scene image rendered in an offscreen frame buffer.
+ * This texture is applied on a full-screen quad with a special material.
+ * This material uses a shader that applies the desired effect to the scene texture.
+ *
+ * This class is abstract, any Filter must extend it.
+ * Any filter holds a frameBuffer and a texture
+ * The getMaterial must return a Material that use a GLSL shader implementing the desired effect
+ * + * @author Rémy Bouquet aka Nehon + */ +public abstract class Filter implements Savable { + + + private String name; + protected Pass defaultPass; + protected List postRenderPasses; + protected Material material; + protected boolean enabled = true; + protected FilterPostProcessor processor; + + public Filter(String name) { + this.name = name; + } + + /** + * Inner class Pass + * Pass are like filters in filters. + * Some filters will need multiple passes before the final render + */ + public class Pass { + + protected FrameBuffer renderFrameBuffer; + protected Texture2D renderedTexture; + protected Texture2D depthTexture; + protected Material passMaterial; + + /** + * init the pass called internally + * @param renderer + * @param width + * @param height + * @param textureFormat + * @param depthBufferFormat + * @param numSamples + */ + public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples, boolean renderDepth) { + Collection caps = renderer.getCaps(); + if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample) && caps.contains(Caps.OpenGL31)) { + renderFrameBuffer = new FrameBuffer(width, height, numSamples); + renderedTexture = new Texture2D(width, height, numSamples, textureFormat); + renderFrameBuffer.setDepthBuffer(depthBufferFormat); + if (renderDepth) { + depthTexture = new Texture2D(width, height, numSamples, depthBufferFormat); + renderFrameBuffer.setDepthTexture(depthTexture); + } + } else { + renderFrameBuffer = new FrameBuffer(width, height, 1); + renderedTexture = new Texture2D(width, height, textureFormat); + renderFrameBuffer.setDepthBuffer(depthBufferFormat); + if (renderDepth) { + depthTexture = new Texture2D(width, height, depthBufferFormat); + renderFrameBuffer.setDepthTexture(depthTexture); + } + } + + renderFrameBuffer.setColorTexture(renderedTexture); + + + } + + /** + * init the pass called internally + * @param renderer + * @param width + * @param height + * @param textureFormat + * @param depthBufferFormat + */ + public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat) { + init(renderer, width, height, textureFormat, depthBufferFormat, 1); + } + + public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples) { + init(renderer, width, height, textureFormat, depthBufferFormat, numSamples, false); + } + + /** + * init the pass called internally + * @param renderer + * @param width + * @param height + * @param textureFormat + * @param depthBufferFormat + * @param numSample + * @param material + */ + public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSample, Material material) { + init(renderer, width, height, textureFormat, depthBufferFormat, numSample); + passMaterial = material; + } + + public boolean requiresSceneAsTexture() { + return false; + } + + public boolean requiresDepthAsTexture() { + return false; + } + + public void beforeRender() { + } + + public FrameBuffer getRenderFrameBuffer() { + return renderFrameBuffer; + } + + public void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { + this.renderFrameBuffer = renderFrameBuffer; + } + + public Texture2D getDepthTexture() { + return depthTexture; + } + + public Texture2D getRenderedTexture() { + return renderedTexture; + } + + public void setRenderedTexture(Texture2D renderedTexture) { + this.renderedTexture = renderedTexture; + } + + public Material getPassMaterial() { + return passMaterial; + } + + public void setPassMaterial(Material passMaterial) { + this.passMaterial = passMaterial; + } + + public void cleanup(Renderer r) { + renderFrameBuffer.dispose(); + renderedTexture.getImage().dispose(); + if(depthTexture!=null){ + depthTexture.getImage().dispose(); + } + } + } + + /** + * returns the default pass texture format + * @return + */ + protected Format getDefaultPassTextureFormat() { + return Format.RGBA8; + } + + /** + * returns the default pass depth format + * @return + */ + protected Format getDefaultPassDepthFormat() { + return Format.Depth; + } + + /** + * construct a Filter + */ + protected Filter() { + this("filter"); + } + + /** + * + * initialize this filter + * use InitFilter for overriding filter initialization + * @param manager the assetManager + * @param renderManager the renderManager + * @param vp the viewport + * @param w the width + * @param h the height + */ + protected final void init(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { + // cleanup(renderManager.getRenderer()); + defaultPass = new Pass(); + defaultPass.init(renderManager.getRenderer(), w, h, getDefaultPassTextureFormat(), getDefaultPassDepthFormat()); + initFilter(manager, renderManager, vp, w, h); + } + + /** + * cleanup this filter + * @param r + */ + protected final void cleanup(Renderer r) { + processor = null; + if (defaultPass != null) { + defaultPass.cleanup(r); + } + if (postRenderPasses != null) { + for (Iterator it = postRenderPasses.iterator(); it.hasNext();) { + Pass pass = it.next(); + pass.cleanup(r); + } + } + cleanUpFilter(r); + } + + /** + * Initialization of sub classes filters + * This method is called once when the filter is added to the FilterPostProcessor + * It should contain Material initializations and extra passes initialization + * @param manager the assetManager + * @param renderManager the renderManager + * @param vp the viewPort where this filter is rendered + * @param w the width of the filter + * @param h the height of the filter + */ + protected abstract void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h); + + /** + * override this method if you have some cleanup to do + * @param r the renderer + */ + protected void cleanUpFilter(Renderer r) { + } + + /** + * Must return the material used for this filter. + * this method is called every frame. + * + * @return the material used for this filter. + */ + protected abstract Material getMaterial(); + + /** + * Override if you want to do something special with the depth texture; + * @param depthTexture + */ + protected void setDepthTexture(Texture depthTexture){ + getMaterial().setTexture("DepthTexture", depthTexture); + } + + /** + * Override this method if you want to make a pre pass, before the actual rendering of the frame + * @param queue + */ + protected void postQueue(RenderQueue queue) { + } + + /** + * Override this method if you want to modify parameters according to tpf before the rendering of the frame. + * This is useful for animated filters + * Also it can be the place to render pre passes + * @param tpf the time used to render the previous frame + */ + protected void preFrame(float tpf) { + } + + /** + * Override this method if you want to make a pass just after the frame has been rendered and just before the filter rendering + * @param renderManager + * @param viewPort + * @param prevFilterBuffer + * @param sceneBuffer + */ + protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + } + + /** + * Override this method if you want to save extra properties when the filter is saved else only basic properties of the filter will be saved + * This method should always begin by super.write(ex); + * @param ex + * @throws IOException + */ + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(name, "name", ""); + oc.write(enabled, "enabled", true); + } + + /** + * Override this method if you want to load extra properties when the filter + * is loaded else only basic properties of the filter will be loaded + * This method should always begin by super.read(im); + */ + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + name = ic.readString("name", ""); + enabled = ic.readBoolean("enabled", true); + } + + /** + * returns the name of the filter + * @return the Filter's name + */ + public String getName() { + return name; + } + + /** + * Sets the name of the filter + * @param name + */ + public void setName(String name) { + this.name = name; + } + + /** + * returns the default pass frame buffer + * @return + */ + protected FrameBuffer getRenderFrameBuffer() { + return defaultPass.renderFrameBuffer; + } + + /** + * sets the default pas frame buffer + * @param renderFrameBuffer + */ + protected void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { + this.defaultPass.renderFrameBuffer = renderFrameBuffer; + } + + /** + * returns the rendered texture of this filter + * @return + */ + protected Texture2D getRenderedTexture() { + return defaultPass.renderedTexture; + } + + /** + * sets the rendered texture of this filter + * @param renderedTexture + */ + protected void setRenderedTexture(Texture2D renderedTexture) { + this.defaultPass.renderedTexture = renderedTexture; + } + + /** + * Override this method and return true if your Filter needs the depth texture + * + * @return true if your Filter need the depth texture + */ + protected boolean isRequiresDepthTexture() { + return false; + } + + /** + * Override this method and return false if your Filter does not need the scene texture + * + * @return false if your Filter does not need the scene texture + */ + protected boolean isRequiresSceneTexture() { + return true; + } + + /** + * returns the list of the postRender passes + * @return + */ + protected List getPostRenderPasses() { + return postRenderPasses; + } + + /** + * Enable or disable this filter + * @param enabled true to enable + */ + public void setEnabled(boolean enabled) { + if (processor != null) { + processor.setFilterState(this, enabled); + } else { + this.enabled = enabled; + } + } + + /** + * returns true if the filter is enabled + * @return enabled + */ + public boolean isEnabled() { + return enabled; + } + + /** + * sets a reference to the FilterPostProcessor ti which this filter is attached + * @param proc + */ + protected void setProcessor(FilterPostProcessor proc) { + processor = proc; + } + + /** + * This method is called right after the filter has been rendered to the + * framebuffer. + * Note that buffer will be null if the filter is the last one in the stack + * and has been rendered to screen + * @param r the renderer + * @param buffer the framebuffer on which the filter has been rendered. + */ + protected void postFilter(Renderer r, FrameBuffer buffer){ + } +} diff --git a/jme3-core/src/main/java/com/jme3/renderer/Camera.java b/jme3-core/src/main/java/com/jme3/renderer/Camera.java index 2cbf811c3..d3cd9f4a4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Camera.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Camera.java @@ -287,8 +287,8 @@ public class Camera implements Savable, Cloneable { } /** - * This method copise the settings of the given camera. - * + * This method copies the settings of the given camera. + * * @param cam * the camera we copy the settings from */ @@ -368,8 +368,8 @@ public class Camera implements Savable, Cloneable { /** * Sets a clipPlane for this camera. - * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane - * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel + * The clipPlane is used to recompute the + * projectionMatrix using the plane as the near plane * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel * more info here *

    *
  • http://www.terathon.com/code/oblique.html @@ -668,8 +668,8 @@ public class Camera implements Savable, Cloneable { } /** - * setRotation sets the orientation of this camera. - * This will be equivelant to setting each of the axes: + * setRotation sets the orientation of this camera. This will + * be equivalent to setting each of the axes: *
    * cam.setLeft(rotation.getRotationColumn(0));
    * cam.setUp(rotation.getRotationColumn(1));
    @@ -803,7 +803,7 @@ public class Camera implements Savable, Cloneable { } /** - * lookAt is a convienence method for auto-setting the frame + * lookAt is a convenience method for auto-setting the frame * based on a world position the user desires the camera to look at. It * repoints the camera towards the given position using the difference * between the position and the current camera location as a direction @@ -996,7 +996,7 @@ public class Camera implements Savable, Cloneable { /** * contains tests a bounding volume against the planes of the - * camera's frustum. The frustums planes are set such that the normals all + * camera's frustum. The frustum's planes are set such that the normals all * face in towards the viewable scene. Therefore, if the bounding volume is * on the negative side of the plane is can be culled out. * diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index cf3640dda..c2d2ec6c7 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -269,7 +269,7 @@ public interface Renderer { /** * Renders count meshes, with the geometry data supplied. * The shader which is currently set with setShader is - * responsible for transforming the input verticies into clip space + * responsible for transforming the input vertices into clip space * and shading it based on the given vertex attributes. * The int variable gl_InstanceID can be used to access the current * instance of the mesh being rendered inside the vertex shader.