Basic support for NURBS curves and surfaces added.
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7089 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
b641b5670c
commit
a9a7d2d4de
127
engine/src/core/com/jme3/math/CurveAndSurfaceMath.java
Normal file
127
engine/src/core/com/jme3/math/CurveAndSurfaceMath.java
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package com.jme3.math;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jme3.math.Spline.SplineType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class offers methods to help with curves and surfaces calculations.
|
||||||
|
* @author Marcin Roguski (Kealthas)
|
||||||
|
*/
|
||||||
|
public class CurveAndSurfaceMath {
|
||||||
|
private static final float KNOTS_MINIMUM_DELTA = 0.0001f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A private constructor is defined to avoid instatiation of this class.
|
||||||
|
*/
|
||||||
|
private CurveAndSurfaceMath() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method interpolates tha data for the nurbs curve.
|
||||||
|
* @param u
|
||||||
|
* the u value
|
||||||
|
* @param nurbSpline
|
||||||
|
* the nurbs spline definition
|
||||||
|
* @param store
|
||||||
|
* the resulting point in 3D space
|
||||||
|
*/
|
||||||
|
public static void interpolateNurbs(float u, Spline nurbSpline, Vector3f store) {
|
||||||
|
if (nurbSpline.getType() != SplineType.Nurb) {
|
||||||
|
throw new IllegalArgumentException("Given spline is not of a NURB type!");
|
||||||
|
}
|
||||||
|
List<Vector3f> controlPoints = nurbSpline.getControlPoints();
|
||||||
|
float[] weights = nurbSpline.getWeights();
|
||||||
|
List<Float> knots = nurbSpline.getKnots();
|
||||||
|
int controlPointAmount = controlPoints.size();
|
||||||
|
|
||||||
|
store.set(Vector3f.ZERO);
|
||||||
|
float delimeter = 0;
|
||||||
|
for (int i = 0; i < controlPointAmount; ++i) {
|
||||||
|
float val = weights[i] * CurveAndSurfaceMath.computeBaseFunctionValue(i, nurbSpline.getBasisFunctionDegree(), u, knots);
|
||||||
|
store.addLocal(nurbSpline.getControlPoints().get(i)
|
||||||
|
.mult(val));
|
||||||
|
delimeter += val;
|
||||||
|
}
|
||||||
|
store.divideLocal(delimeter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method interpolates tha data for the nurbs surface.
|
||||||
|
* @param u
|
||||||
|
* the u value
|
||||||
|
* @param v
|
||||||
|
* the v value
|
||||||
|
* @param controlPoints
|
||||||
|
* the nurbs' control points
|
||||||
|
* @param knots
|
||||||
|
* the nurbs' knots
|
||||||
|
* @param store
|
||||||
|
* the resulting point in 3D space
|
||||||
|
*/
|
||||||
|
public static void interpolate(float u, float v, List<List<Vector4f>> controlPoints, List<Float>[] knots, Vector3f store) {
|
||||||
|
store.set(Vector3f.ZERO);
|
||||||
|
float delimeter = 0;
|
||||||
|
int vControlPointsAmount = controlPoints.size();
|
||||||
|
int uControlPointsAmount = controlPoints.get(0).size();
|
||||||
|
int basisUFunctionDegree = knots[0].size() - controlPoints.get(0).size();
|
||||||
|
int basisVFunctionDegree = knots[1]==null ? 0 : knots[1].size() - controlPoints.size();
|
||||||
|
for (int i = 0; i < vControlPointsAmount; ++i) {
|
||||||
|
for (int j = 0; j < uControlPointsAmount; ++j) {
|
||||||
|
Vector4f controlPoint = controlPoints.get(j).get(i);
|
||||||
|
float val = controlPoint.w
|
||||||
|
* CurveAndSurfaceMath.computeBaseFunctionValue(i, basisVFunctionDegree, v, knots[1])
|
||||||
|
* CurveAndSurfaceMath.computeBaseFunctionValue(j, basisUFunctionDegree, u, knots[0]);
|
||||||
|
store.addLocal(controlPoint.x * val, controlPoint.y * val, controlPoint.z * val);
|
||||||
|
delimeter += val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store.divideLocal(delimeter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method prepares the knots to be used. If the knots represent non-uniform B-splines (first and last knot values are being
|
||||||
|
* repeated) it leads to NaN results during calculations. This method adds a small number to each of such knots to avoid NaN's.
|
||||||
|
* @param knots
|
||||||
|
* the knots to be prepared to use
|
||||||
|
* @param basisFunctionDegree
|
||||||
|
* the degree of basis function
|
||||||
|
*/
|
||||||
|
// TODO: improve this; constant delta may lead to errors if the difference between tha last repeated
|
||||||
|
// point and the following one is lower than it
|
||||||
|
public static void prepareNurbsKnots(List<Float> knots, int basisFunctionDegree) {
|
||||||
|
float delta = KNOTS_MINIMUM_DELTA;
|
||||||
|
for (int i = 1; i < basisFunctionDegree && knots.get(i).equals(knots.get(0)); ++i) {
|
||||||
|
knots.set(i, Float.valueOf(knots.get(i).floatValue() + delta));
|
||||||
|
delta += KNOTS_MINIMUM_DELTA;
|
||||||
|
}
|
||||||
|
float lastKnot = knots.get(knots.size() - 1);
|
||||||
|
delta = KNOTS_MINIMUM_DELTA;
|
||||||
|
for (int i = knots.size() - basisFunctionDegree + 1; i < knots.size() && knots.get(i).equals(lastKnot); ++i) {
|
||||||
|
knots.set(i, Float.valueOf(knots.get(i).floatValue() + delta));
|
||||||
|
delta += KNOTS_MINIMUM_DELTA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method computes the base function value for the NURB curve.
|
||||||
|
* @param i
|
||||||
|
* the knot index
|
||||||
|
* @param k
|
||||||
|
* the base function degree
|
||||||
|
* @param t
|
||||||
|
* the knot value
|
||||||
|
* @param knots
|
||||||
|
* the knots' values
|
||||||
|
* @return the base function value
|
||||||
|
*/
|
||||||
|
private static float computeBaseFunctionValue(int i, int k, float t, List<Float> knots) {
|
||||||
|
if (k == 1) {
|
||||||
|
return knots.get(i) <= t && t < knots.get(i + 1) ? 1.0f : 0.0f;
|
||||||
|
} else {
|
||||||
|
return (t - knots.get(i)) / (knots.get(i + k - 1) - knots.get(i)) *
|
||||||
|
CurveAndSurfaceMath.computeBaseFunctionValue(i, k - 1, t, knots)
|
||||||
|
+ (knots.get(i + k) - t) / (knots.get(i + k) - knots.get(i + 1)) *
|
||||||
|
CurveAndSurfaceMath.computeBaseFunctionValue(i + 1, k - 1, t, knots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -698,6 +698,19 @@ final public class FastMath {
|
|||||||
}
|
}
|
||||||
return val3;
|
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.
|
* Returns the determinant of a 4x4 matrix.
|
||||||
|
@ -4,15 +4,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.math;
|
package com.jme3.math;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.export.InputCapsule;
|
||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -24,10 +25,14 @@ public class Spline implements Savable {
|
|||||||
|
|
||||||
Linear,
|
Linear,
|
||||||
CatmullRom,
|
CatmullRom,
|
||||||
Bezier
|
Bezier,
|
||||||
|
Nurb
|
||||||
}
|
}
|
||||||
private List<Vector3f> controlPoints = new ArrayList<Vector3f>();
|
private List<Vector3f> controlPoints = new ArrayList<Vector3f>();
|
||||||
private boolean cycle = false;
|
private List<Float> knots; //knots of NURBS spline
|
||||||
|
private float[] weights; //weights of NURBS spline
|
||||||
|
private int basisFunctionDegree; //degree of NURBS spline basis function (computed automatically)
|
||||||
|
private boolean cycle;
|
||||||
private List<Float> segmentsLength;
|
private List<Float> segmentsLength;
|
||||||
private float totalLength;
|
private float totalLength;
|
||||||
private List<Vector3f> CRcontrolPoints;
|
private List<Vector3f> CRcontrolPoints;
|
||||||
@ -53,6 +58,9 @@ public class Spline implements Savable {
|
|||||||
* @param cycle true if the spline cycle.
|
* @param cycle true if the spline cycle.
|
||||||
*/
|
*/
|
||||||
public Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle) {
|
public Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle) {
|
||||||
|
if(splineType==SplineType.Nurb) {
|
||||||
|
throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
|
||||||
|
}
|
||||||
for (int i = 0; i < controlPoints.length; i++) {
|
for (int i = 0; i < controlPoints.length; i++) {
|
||||||
Vector3f vector3f = controlPoints[i];
|
Vector3f vector3f = controlPoints[i];
|
||||||
this.controlPoints.add(vector3f);
|
this.controlPoints.add(vector3f);
|
||||||
@ -60,7 +68,7 @@ public class Spline implements Savable {
|
|||||||
type = splineType;
|
type = splineType;
|
||||||
this.curveTension = curveTension;
|
this.curveTension = curveTension;
|
||||||
this.cycle = cycle;
|
this.cycle = cycle;
|
||||||
computeTotalLentgh();
|
this.computeTotalLentgh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,11 +87,42 @@ public class Spline implements Savable {
|
|||||||
* @param cycle true if the spline cycle.
|
* @param cycle true if the spline cycle.
|
||||||
*/
|
*/
|
||||||
public Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle) {
|
public Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle) {
|
||||||
|
if(splineType==SplineType.Nurb) {
|
||||||
|
throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
|
||||||
|
}
|
||||||
type = splineType;
|
type = splineType;
|
||||||
this.controlPoints.addAll(controlPoints);
|
this.controlPoints.addAll(controlPoints);
|
||||||
this.curveTension = curveTension;
|
this.curveTension = curveTension;
|
||||||
this.cycle = cycle;
|
this.cycle = cycle;
|
||||||
computeTotalLentgh();
|
this.computeTotalLentgh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a NURBS spline. A spline type is automatically set to SplineType.Nurb.
|
||||||
|
* The cycle is set to <b>false</b> by default.
|
||||||
|
* @param controlPoints a list of vector to use as control points of the spline
|
||||||
|
* @param nurbKnots the nurb's spline knots
|
||||||
|
*/
|
||||||
|
public Spline(List<Vector4f> controlPoints, List<Float> nurbKnots) {
|
||||||
|
//input data control
|
||||||
|
for(int i=0;i<nurbKnots.size()-1;++i) {
|
||||||
|
if(nurbKnots.get(i)>nurbKnots.get(i+1)) {
|
||||||
|
throw new IllegalArgumentException("The knots values cannot decrease!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//storing the data
|
||||||
|
type = SplineType.Nurb;
|
||||||
|
this.weights = new float[controlPoints.size()];
|
||||||
|
this.knots = nurbKnots;
|
||||||
|
this.basisFunctionDegree = nurbKnots.size() - weights.length;
|
||||||
|
for(int i=0;i<controlPoints.size();++i) {
|
||||||
|
Vector4f controlPoint = controlPoints.get(i);
|
||||||
|
this.controlPoints.add(new Vector3f(controlPoint.x, controlPoint.y, controlPoint.z));
|
||||||
|
this.weights[i] = controlPoint.w;
|
||||||
|
}
|
||||||
|
CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree);
|
||||||
|
this.computeTotalLentgh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initCatmullRomWayPoints(List<Vector3f> list) {
|
private void initCatmullRomWayPoints(List<Vector3f> list) {
|
||||||
@ -125,7 +164,7 @@ public class Spline implements Savable {
|
|||||||
controlPoints.add(controlPoints.get(0));
|
controlPoints.add(controlPoints.get(0));
|
||||||
}
|
}
|
||||||
if (controlPoints.size() > 1) {
|
if (controlPoints.size() > 1) {
|
||||||
computeTotalLentgh();
|
this.computeTotalLentgh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,10 +175,13 @@ public class Spline implements Savable {
|
|||||||
public void removeControlPoint(Vector3f controlPoint) {
|
public void removeControlPoint(Vector3f controlPoint) {
|
||||||
controlPoints.remove(controlPoint);
|
controlPoints.remove(controlPoint);
|
||||||
if (controlPoints.size() > 1) {
|
if (controlPoints.size() > 1) {
|
||||||
computeTotalLentgh();
|
this.computeTotalLentgh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method computes the total length of the curve.
|
||||||
|
*/
|
||||||
private void computeTotalLentgh() {
|
private void computeTotalLentgh() {
|
||||||
totalLength = 0;
|
totalLength = 0;
|
||||||
float l = 0;
|
float l = 0;
|
||||||
@ -158,12 +200,17 @@ public class Spline implements Savable {
|
|||||||
}
|
}
|
||||||
} else if(type == SplineType.Bezier) {
|
} else if(type == SplineType.Bezier) {
|
||||||
this.computeBezierLength();
|
this.computeBezierLength();
|
||||||
|
} else if(type == SplineType.Nurb) {
|
||||||
|
this.computeNurbLength();
|
||||||
} else {
|
} else {
|
||||||
initCatmullRomWayPoints(controlPoints);
|
this.initCatmullRomWayPoints(controlPoints);
|
||||||
computeCatmulLength();
|
this.computeCatmulLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method computes the Catmull Rom curve length.
|
||||||
|
*/
|
||||||
private void computeCatmulLength() {
|
private void computeCatmulLength() {
|
||||||
float l = 0;
|
float l = 0;
|
||||||
if (controlPoints.size() > 1) {
|
if (controlPoints.size() > 1) {
|
||||||
@ -190,6 +237,13 @@ public class Spline implements Savable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method calculates the NURB curve length.
|
||||||
|
*/
|
||||||
|
private void computeNurbLength() {
|
||||||
|
//TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterpolate a position on the spline
|
* Iterpolate a position on the spline
|
||||||
@ -211,6 +265,9 @@ public class Spline implements Savable {
|
|||||||
break;
|
break;
|
||||||
case Bezier:
|
case Bezier:
|
||||||
FastMath.interpolateBezier(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), controlPoints.get(currentControlPoint + 2), controlPoints.get(currentControlPoint + 3), store);
|
FastMath.interpolateBezier(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), controlPoints.get(currentControlPoint + 2), controlPoints.get(currentControlPoint + 3), store);
|
||||||
|
case Nurb:
|
||||||
|
CurveAndSurfaceMath.interpolateNurbs(value, this, store);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -232,7 +289,9 @@ public class Spline implements Savable {
|
|||||||
*/
|
*/
|
||||||
public void setCurveTension(float curveTension) {
|
public void setCurveTension(float curveTension) {
|
||||||
this.curveTension = curveTension;
|
this.curveTension = curveTension;
|
||||||
computeTotalLentgh();
|
if(type==SplineType.CatmullRom) {
|
||||||
|
this.computeTotalLentgh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,18 +307,20 @@ public class Spline implements Savable {
|
|||||||
* @param cycle
|
* @param cycle
|
||||||
*/
|
*/
|
||||||
public void setCycle(boolean cycle) {
|
public void setCycle(boolean cycle) {
|
||||||
if (controlPoints.size() >= 2) {
|
if(type!=SplineType.Nurb) {
|
||||||
if (this.cycle && !cycle) {
|
if (controlPoints.size() >= 2) {
|
||||||
controlPoints.remove(controlPoints.size() - 1);
|
if (this.cycle && !cycle) {
|
||||||
}
|
controlPoints.remove(controlPoints.size() - 1);
|
||||||
if (!this.cycle && cycle) {
|
}
|
||||||
controlPoints.add(controlPoints.get(0));
|
if (!this.cycle && cycle) {
|
||||||
}
|
controlPoints.add(controlPoints.get(0));
|
||||||
this.cycle = cycle;
|
}
|
||||||
computeTotalLentgh();
|
this.cycle = cycle;
|
||||||
} else {
|
this.computeTotalLentgh();
|
||||||
this.cycle = cycle;
|
} else {
|
||||||
}
|
this.cycle = cycle;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,7 +345,7 @@ public class Spline implements Savable {
|
|||||||
*/
|
*/
|
||||||
public void setType(SplineType type) {
|
public void setType(SplineType type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
computeTotalLentgh();
|
this.computeTotalLentgh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,6 +363,50 @@ public class Spline implements Savable {
|
|||||||
public List<Float> getSegmentsLength() {
|
public List<Float> getSegmentsLength() {
|
||||||
return segmentsLength;
|
return segmentsLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////// NURBS getters /////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the minimum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
|
||||||
|
* type - NPE will be thrown.
|
||||||
|
* @return the minimum nurb curve knot value
|
||||||
|
*/
|
||||||
|
public float getMinNurbKnot() {
|
||||||
|
return knots.get(basisFunctionDegree - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the maximum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
|
||||||
|
* type - NPE will be thrown.
|
||||||
|
* @return the maximum nurb curve knot value
|
||||||
|
*/
|
||||||
|
public float getMaxNurbKnot() {
|
||||||
|
return knots.get(weights.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns NURBS' spline knots.
|
||||||
|
* @return NURBS' spline knots
|
||||||
|
*/
|
||||||
|
public List<Float> getKnots() {
|
||||||
|
return knots;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns NURBS' spline weights.
|
||||||
|
* @return NURBS' spline weights
|
||||||
|
*/
|
||||||
|
public float[] getWeights() {
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns NURBS' spline basis function degree.
|
||||||
|
* @return NURBS' spline basis function degree
|
||||||
|
*/
|
||||||
|
public int getBasisFunctionDegree() {
|
||||||
|
return basisFunctionDegree;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
@ -318,6 +423,9 @@ public class Spline implements Savable {
|
|||||||
oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null);
|
oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null);
|
||||||
oc.write(curveTension, "curveTension", 0.5f);
|
oc.write(curveTension, "curveTension", 0.5f);
|
||||||
oc.write(cycle, "cycle", false);
|
oc.write(cycle, "cycle", false);
|
||||||
|
oc.writeSavableArrayList((ArrayList<Float>)knots, "knots", null);
|
||||||
|
oc.write(weights, "weights", null);
|
||||||
|
oc.write(basisFunctionDegree, "basisFunctionDegree", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -337,5 +445,8 @@ public class Spline implements Savable {
|
|||||||
CRcontrolPoints = (ArrayList<Vector3f>) in.readSavableArrayList("CRControlPoints", null);
|
CRcontrolPoints = (ArrayList<Vector3f>) in.readSavableArrayList("CRControlPoints", null);
|
||||||
curveTension = in.readFloat("curveTension", 0.5f);
|
curveTension = in.readFloat("curveTension", 0.5f);
|
||||||
cycle = in.readBoolean("cycle", false);
|
cycle = in.readBoolean("cycle", false);
|
||||||
|
knots = in.readSavableArrayList("knots", null);
|
||||||
|
weights = in.readFloatArray("weights", null);
|
||||||
|
basisFunctionDegree = in.readInt("basisFunctionDegree", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,9 @@ public class Curve extends Mesh {
|
|||||||
case Bezier:
|
case Bezier:
|
||||||
this.createBezierMesh(nbSubSegments);
|
this.createBezierMesh(nbSubSegments);
|
||||||
break;
|
break;
|
||||||
|
case Nurb:
|
||||||
|
this.createNurbMesh(nbSubSegments);
|
||||||
|
break;
|
||||||
case Linear:
|
case Linear:
|
||||||
default:
|
default:
|
||||||
this.createLinearMesh();
|
this.createLinearMesh();
|
||||||
@ -80,8 +83,8 @@ public class Curve extends Mesh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createCatmullRomMesh(int nbSubSegments) {
|
private void createCatmullRomMesh(int nbSubSegments) {
|
||||||
float[] array = new float[(((spline.getControlPoints().size() - 1) * nbSubSegments) + 1) * 3];
|
float[] array = new float[((spline.getControlPoints().size() - 1) * nbSubSegments + 1) * 3];
|
||||||
short[] indices = new short[((spline.getControlPoints().size() - 1) * nbSubSegments) * 2];
|
short[] indices = new short[(spline.getControlPoints().size() - 1) * nbSubSegments * 2];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int cptCP = 0;
|
int cptCP = 0;
|
||||||
for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
|
for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
|
||||||
@ -108,7 +111,7 @@ public class Curve extends Mesh {
|
|||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
int k = 0;
|
int k = 0;
|
||||||
for (int j = 0; j < ((spline.getControlPoints().size() - 1) * nbSubSegments); j++) {
|
for (int j = 0; j < (spline.getControlPoints().size() - 1) * nbSubSegments; j++) {
|
||||||
k = j;
|
k = j;
|
||||||
indices[i] = (short) k;
|
indices[i] = (short) k;
|
||||||
i++;
|
i++;
|
||||||
@ -117,11 +120,11 @@ public class Curve extends Mesh {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(Mesh.Mode.Lines);
|
this.setMode(Mesh.Mode.Lines);
|
||||||
setBuffer(VertexBuffer.Type.Position, 3, array);
|
this.setBuffer(VertexBuffer.Type.Position, 3, array);
|
||||||
setBuffer(VertexBuffer.Type.Index, ((spline.getControlPoints().size() - 1) * nbSubSegments) * 2, indices);
|
this.setBuffer(VertexBuffer.Type.Index, (spline.getControlPoints().size() - 1) * nbSubSegments * 2, indices);
|
||||||
updateBound();
|
this.updateBound();
|
||||||
updateCounts();
|
this.updateCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,6 +178,43 @@ public class Curve extends Mesh {
|
|||||||
this.updateBound();
|
this.updateBound();
|
||||||
this.updateCounts();
|
this.updateCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates the Nurb path for this curve.
|
||||||
|
* @param nbSubSegments
|
||||||
|
* amount of subsegments between position control points
|
||||||
|
*/
|
||||||
|
private void createNurbMesh(int nbSubSegments) {
|
||||||
|
float minKnot = spline.getMinNurbKnot();
|
||||||
|
float maxKnot = spline.getMaxNurbKnot();
|
||||||
|
float deltaU = (maxKnot - minKnot)/nbSubSegments;
|
||||||
|
|
||||||
|
float[] array = new float[(nbSubSegments + 1) * 3];
|
||||||
|
|
||||||
|
float u = minKnot;
|
||||||
|
Vector3f interpolationResult = new Vector3f();
|
||||||
|
for(int i=0;i<array.length;i+=3) {
|
||||||
|
spline.interpolate(u, 0, interpolationResult);
|
||||||
|
array[i] = interpolationResult.x;
|
||||||
|
array[i + 1] = interpolationResult.y;
|
||||||
|
array[i + 2] = interpolationResult.z;
|
||||||
|
u += deltaU;
|
||||||
|
}
|
||||||
|
|
||||||
|
//calculating indexes
|
||||||
|
int i = 0;
|
||||||
|
short[] indices = new short[nbSubSegments << 1];
|
||||||
|
for (int j = 0; j < nbSubSegments; ++j) {
|
||||||
|
indices[i++] = (short) j;
|
||||||
|
indices[i++] = (short) (j + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setMode(Mesh.Mode.Lines);
|
||||||
|
this.setBuffer(VertexBuffer.Type.Position, 3, array);
|
||||||
|
this.setBuffer(VertexBuffer.Type.Index, 2, indices);
|
||||||
|
this.updateBound();
|
||||||
|
this.updateCounts();
|
||||||
|
}
|
||||||
|
|
||||||
private void createLinearMesh() {
|
private void createLinearMesh() {
|
||||||
float[] array = new float[spline.getControlPoints().size() * 3];
|
float[] array = new float[spline.getControlPoints().size() * 3];
|
||||||
@ -202,11 +242,11 @@ public class Curve extends Mesh {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(Mesh.Mode.Lines);
|
this.setMode(Mesh.Mode.Lines);
|
||||||
setBuffer(VertexBuffer.Type.Position, 3, array);
|
this.setBuffer(VertexBuffer.Type.Position, 3, array);
|
||||||
setBuffer(VertexBuffer.Type.Index, (spline.getControlPoints().size() - 1) * 2, indices);
|
this.setBuffer(VertexBuffer.Type.Index, (spline.getControlPoints().size() - 1) * 2, indices);
|
||||||
updateBound();
|
this.updateBound();
|
||||||
updateCounts();
|
this.updateCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
277
engine/src/core/com/jme3/scene/shape/Surface.java
Normal file
277
engine/src/core/com/jme3/scene/shape/Surface.java
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
package com.jme3.scene.shape;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.CurveAndSurfaceMath;
|
||||||
|
import com.jme3.math.Spline.SplineType;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.math.Vector4f;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a surface described by knots, weights and control points.
|
||||||
|
* Currently the following types are supported:
|
||||||
|
* a) NURBS
|
||||||
|
* @author Marcin Roguski (Kealthas)
|
||||||
|
*/
|
||||||
|
public class Surface extends Mesh {
|
||||||
|
private SplineType type; //the type of the surface
|
||||||
|
private List<List<Vector4f>> controlPoints; //space control points and their weights
|
||||||
|
private List<Float>[] knots; //knots of the surface
|
||||||
|
private int basisUFunctionDegree; //the degree of basis U function (computed automatically)
|
||||||
|
private int basisVFunctionDegree; //the degree of basis V function (computed automatically)
|
||||||
|
private int uSegments; //the amount of U segments
|
||||||
|
private int vSegments; //the amount of V segments
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Constructs required surface.
|
||||||
|
* @param controlPoints space control points
|
||||||
|
* @param nurbKnots knots of the surface
|
||||||
|
* @param uSegments the amount of U segments
|
||||||
|
* @param vSegments the amount of V segments
|
||||||
|
*/
|
||||||
|
private Surface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots, int uSegments, int vSegments) {
|
||||||
|
this.validateInputData(controlPoints, nurbKnots, uSegments, vSegments);
|
||||||
|
this.type = SplineType.Nurb;
|
||||||
|
this.uSegments = uSegments;
|
||||||
|
this.vSegments = vSegments;
|
||||||
|
this.controlPoints = controlPoints;
|
||||||
|
this.knots = nurbKnots;
|
||||||
|
this.basisUFunctionDegree = nurbKnots[0].size() - controlPoints.get(0).size();
|
||||||
|
CurveAndSurfaceMath.prepareNurbsKnots(nurbKnots[0], basisUFunctionDegree);
|
||||||
|
if(nurbKnots[1]!=null) {
|
||||||
|
this.basisVFunctionDegree = nurbKnots[1].size() - controlPoints.size();
|
||||||
|
CurveAndSurfaceMath.prepareNurbsKnots(nurbKnots[1], basisVFunctionDegree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.buildSurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates a NURBS surface.
|
||||||
|
* @param controlPoints space control points
|
||||||
|
* @param nurbKnots knots of the surface
|
||||||
|
* @param uSegments the amount of U segments
|
||||||
|
* @param vSegments the amount of V segments
|
||||||
|
* @return an instance of NURBS surface
|
||||||
|
*/
|
||||||
|
public static final Surface createNurbsSurface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots, int uSegments, int vSegments) {
|
||||||
|
Surface result = new Surface(controlPoints, nurbKnots, uSegments, vSegments);
|
||||||
|
result.type = SplineType.Nurb;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates the surface.
|
||||||
|
*/
|
||||||
|
private void buildSurface() {
|
||||||
|
boolean smooth = true;//TODO: take smoothing into consideration
|
||||||
|
float minUKnot = this.getMinUNurbKnot();
|
||||||
|
float maxUKnot = this.getMaxUNurbKnot();
|
||||||
|
float deltaU = (maxUKnot - minUKnot)/uSegments;
|
||||||
|
|
||||||
|
float minVKnot = this.getMinVNurbKnot();
|
||||||
|
float maxVKnot = this.getMaxVNurbKnot();
|
||||||
|
float deltaV = (maxVKnot - minVKnot)/vSegments;
|
||||||
|
|
||||||
|
Vector3f[] vertices = new Vector3f[(uSegments + 1) * (vSegments + 1)];
|
||||||
|
|
||||||
|
float u = minUKnot, v = minVKnot;
|
||||||
|
int arrayIndex = 0;
|
||||||
|
|
||||||
|
for(int i=0;i<=vSegments; ++i) {
|
||||||
|
for(int j=0;j<=uSegments; ++j) {
|
||||||
|
Vector3f interpolationResult = new Vector3f();
|
||||||
|
CurveAndSurfaceMath.interpolate(u, v, controlPoints, knots, interpolationResult);
|
||||||
|
vertices[arrayIndex++] = interpolationResult;
|
||||||
|
u += deltaU;
|
||||||
|
}
|
||||||
|
u = minUKnot;
|
||||||
|
v += deltaV;
|
||||||
|
}
|
||||||
|
|
||||||
|
//adding indexes
|
||||||
|
int uVerticesAmount = uSegments + 1;
|
||||||
|
int[] indices = new int[uSegments * vSegments * 6];
|
||||||
|
arrayIndex = 0;
|
||||||
|
for(int i=0;i<vSegments; ++i) {
|
||||||
|
for(int j=0;j<uSegments; ++j) {
|
||||||
|
indices[arrayIndex++] = j + i*uVerticesAmount;
|
||||||
|
indices[arrayIndex++] = j + i*uVerticesAmount + 1;
|
||||||
|
indices[arrayIndex++] = j + i*uVerticesAmount + uVerticesAmount;
|
||||||
|
indices[arrayIndex++] = j + i*uVerticesAmount + 1;
|
||||||
|
indices[arrayIndex++] = j + i*uVerticesAmount + uVerticesAmount + 1;
|
||||||
|
indices[arrayIndex++] = j + i*uVerticesAmount + uVerticesAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//normalMap merges normals of faces that will be rendered smooth
|
||||||
|
Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(vertices.length);
|
||||||
|
for(int i=0;i<indices.length;i+=3) {
|
||||||
|
Vector3f n = FastMath.computeNormal(vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]);
|
||||||
|
this.addNormal(n, normalMap, smooth, vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]);
|
||||||
|
}
|
||||||
|
//preparing normal list (the order of normals must match the order of vertices)
|
||||||
|
float[] normals = new float[vertices.length * 3];
|
||||||
|
arrayIndex = 0;
|
||||||
|
for(int i=0;i<vertices.length;++i) {
|
||||||
|
Vector3f n = normalMap.get(vertices[i]);
|
||||||
|
normals[arrayIndex++] = n.x;
|
||||||
|
normals[arrayIndex++] = n.y;
|
||||||
|
normals[arrayIndex++] = n.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
|
||||||
|
this.setBuffer(VertexBuffer.Type.Index, 3, indices);
|
||||||
|
this.setBuffer(VertexBuffer.Type.Normal, 3, normals);
|
||||||
|
this.updateBound();
|
||||||
|
this.updateCounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<List<Vector4f>> getControlPoints() {
|
||||||
|
return controlPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the amount of U control points.
|
||||||
|
* @return the amount of U control points
|
||||||
|
*/
|
||||||
|
public int getUControlPointsAmount() {
|
||||||
|
return controlPoints.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the amount of V control points.
|
||||||
|
* @return the amount of V control points
|
||||||
|
*/
|
||||||
|
public int getVControlPointsAmount() {
|
||||||
|
return controlPoints.get(0)==null ? 0 : controlPoints.get(0).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the degree of basis U function.
|
||||||
|
* @return the degree of basis U function
|
||||||
|
*/
|
||||||
|
public int getBasisUFunctionDegree() {
|
||||||
|
return basisUFunctionDegree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the degree of basis V function.
|
||||||
|
* @return the degree of basis V function
|
||||||
|
*/
|
||||||
|
public int getBasisVFunctionDegree() {
|
||||||
|
return basisVFunctionDegree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the knots for specified dimension (U knots - value: '0',
|
||||||
|
* V knots - value: '1').
|
||||||
|
* @param dim an integer specifying if the U or V knots are required
|
||||||
|
* @return an array of knots
|
||||||
|
*/
|
||||||
|
public List<Float> getKnots(int dim) {
|
||||||
|
return knots[dim];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the type of the surface.
|
||||||
|
* @return the type of the surface
|
||||||
|
*/
|
||||||
|
public SplineType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the minimum nurb curve U knot value.
|
||||||
|
* @return the minimum nurb curve knot value
|
||||||
|
*/
|
||||||
|
private float getMinUNurbKnot() {
|
||||||
|
return knots[0].get(basisUFunctionDegree - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the maximum nurb curve U knot value.
|
||||||
|
* @return the maximum nurb curve knot value
|
||||||
|
*/
|
||||||
|
private float getMaxUNurbKnot() {
|
||||||
|
return knots[0].get(controlPoints.get(0).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the minimum nurb curve U knot value.
|
||||||
|
* @return the minimum nurb curve knot value
|
||||||
|
*/
|
||||||
|
private float getMinVNurbKnot() {
|
||||||
|
return knots[1].get(basisVFunctionDegree - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the maximum nurb curve U knot value.
|
||||||
|
* @return the maximum nurb curve knot value
|
||||||
|
*/
|
||||||
|
private float getMaxVNurbKnot() {
|
||||||
|
return knots[1].get(controlPoints.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
|
||||||
|
* @param normalToAdd
|
||||||
|
* a normal to be added
|
||||||
|
* @param normalMap
|
||||||
|
* merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
|
||||||
|
* @param smooth
|
||||||
|
* the variable that indicates wheather to merge normals (creating the smooth mesh) or not
|
||||||
|
* @param vertices
|
||||||
|
* a list of vertices read from the blender file
|
||||||
|
*/
|
||||||
|
private void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
|
||||||
|
for(Vector3f v : vertices) {
|
||||||
|
Vector3f n = normalMap.get(v);
|
||||||
|
if(!smooth || n == null) {
|
||||||
|
normalMap.put(v, normalToAdd.clone());
|
||||||
|
} else {
|
||||||
|
n.addLocal(normalToAdd).normalizeLocal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method validates the input data. It throws {@link IllegalArgumentException} if
|
||||||
|
* the data is invalid.
|
||||||
|
* @param controlPoints space control points
|
||||||
|
* @param nurbKnots knots of the surface
|
||||||
|
* @param uSegments the amount of U segments
|
||||||
|
* @param vSegments the amount of V segments
|
||||||
|
*/
|
||||||
|
private void validateInputData(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots,
|
||||||
|
int uSegments, int vSegments) {
|
||||||
|
int uPointsAmount = controlPoints.get(0).size();
|
||||||
|
for(int i=1;i<controlPoints.size();++i) {
|
||||||
|
if(controlPoints.get(i).size()!=uPointsAmount) {
|
||||||
|
throw new IllegalArgumentException("The amount of 'U' control points is invalid!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(uSegments<=0) {
|
||||||
|
throw new IllegalArgumentException("U segments amount should be positive!");
|
||||||
|
}
|
||||||
|
if(vSegments<0) {
|
||||||
|
throw new IllegalArgumentException("V segments amount cannot be negative!");
|
||||||
|
}
|
||||||
|
if (nurbKnots.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Nurb surface should have two rows of knots!");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < nurbKnots.length; ++i) {
|
||||||
|
for (int j = 0; j < nurbKnots[i].size() - 1; ++j) {
|
||||||
|
if (nurbKnots[i].get(j) > nurbKnots[i].get(j+1)) {
|
||||||
|
throw new IllegalArgumentException("The knots' values cannot decrease!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user