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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -4,15 +4,16 @@
|
||||
*/
|
||||
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.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
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,
|
||||
CatmullRom,
|
||||
Bezier
|
||||
Bezier,
|
||||
Nurb
|
||||
}
|
||||
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 float totalLength;
|
||||
private List<Vector3f> CRcontrolPoints;
|
||||
@ -53,6 +58,9 @@ public class Spline implements Savable {
|
||||
* @param cycle true if the spline cycle.
|
||||
*/
|
||||
public Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle) {
|
||||
if(splineType==SplineType.Nurb) {
|
||||
throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
|
||||
}
|
||||
for (int i = 0; i < controlPoints.length; i++) {
|
||||
Vector3f vector3f = controlPoints[i];
|
||||
this.controlPoints.add(vector3f);
|
||||
@ -60,7 +68,7 @@ public class Spline implements Savable {
|
||||
type = splineType;
|
||||
this.curveTension = curveTension;
|
||||
this.cycle = cycle;
|
||||
computeTotalLentgh();
|
||||
this.computeTotalLentgh();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,11 +87,42 @@ public class Spline implements Savable {
|
||||
* @param cycle true if the spline cycle.
|
||||
*/
|
||||
public Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle) {
|
||||
if(splineType==SplineType.Nurb) {
|
||||
throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
|
||||
}
|
||||
type = splineType;
|
||||
this.controlPoints.addAll(controlPoints);
|
||||
this.curveTension = curveTension;
|
||||
this.cycle = cycle;
|
||||
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) {
|
||||
@ -125,7 +164,7 @@ public class Spline implements Savable {
|
||||
controlPoints.add(controlPoints.get(0));
|
||||
}
|
||||
if (controlPoints.size() > 1) {
|
||||
computeTotalLentgh();
|
||||
this.computeTotalLentgh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,10 +175,13 @@ public class Spline implements Savable {
|
||||
public void removeControlPoint(Vector3f controlPoint) {
|
||||
controlPoints.remove(controlPoint);
|
||||
if (controlPoints.size() > 1) {
|
||||
computeTotalLentgh();
|
||||
this.computeTotalLentgh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method computes the total length of the curve.
|
||||
*/
|
||||
private void computeTotalLentgh() {
|
||||
totalLength = 0;
|
||||
float l = 0;
|
||||
@ -158,12 +200,17 @@ public class Spline implements Savable {
|
||||
}
|
||||
} else if(type == SplineType.Bezier) {
|
||||
this.computeBezierLength();
|
||||
} else if(type == SplineType.Nurb) {
|
||||
this.computeNurbLength();
|
||||
} else {
|
||||
initCatmullRomWayPoints(controlPoints);
|
||||
computeCatmulLength();
|
||||
this.initCatmullRomWayPoints(controlPoints);
|
||||
this.computeCatmulLength();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method computes the Catmull Rom curve length.
|
||||
*/
|
||||
private void computeCatmulLength() {
|
||||
float l = 0;
|
||||
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
|
||||
@ -211,6 +265,9 @@ public class Spline implements Savable {
|
||||
break;
|
||||
case Bezier:
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@ -232,7 +289,9 @@ public class Spline implements Savable {
|
||||
*/
|
||||
public void setCurveTension(float curveTension) {
|
||||
this.curveTension = curveTension;
|
||||
computeTotalLentgh();
|
||||
if(type==SplineType.CatmullRom) {
|
||||
this.computeTotalLentgh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,18 +307,20 @@ public class Spline implements Savable {
|
||||
* @param cycle
|
||||
*/
|
||||
public void setCycle(boolean cycle) {
|
||||
if (controlPoints.size() >= 2) {
|
||||
if (this.cycle && !cycle) {
|
||||
controlPoints.remove(controlPoints.size() - 1);
|
||||
}
|
||||
if (!this.cycle && cycle) {
|
||||
controlPoints.add(controlPoints.get(0));
|
||||
}
|
||||
this.cycle = cycle;
|
||||
computeTotalLentgh();
|
||||
} else {
|
||||
this.cycle = cycle;
|
||||
}
|
||||
if(type!=SplineType.Nurb) {
|
||||
if (controlPoints.size() >= 2) {
|
||||
if (this.cycle && !cycle) {
|
||||
controlPoints.remove(controlPoints.size() - 1);
|
||||
}
|
||||
if (!this.cycle && cycle) {
|
||||
controlPoints.add(controlPoints.get(0));
|
||||
}
|
||||
this.cycle = cycle;
|
||||
this.computeTotalLentgh();
|
||||
} else {
|
||||
this.cycle = cycle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,7 +345,7 @@ public class Spline implements Savable {
|
||||
*/
|
||||
public void setType(SplineType type) {
|
||||
this.type = type;
|
||||
computeTotalLentgh();
|
||||
this.computeTotalLentgh();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,6 +363,50 @@ public class Spline implements Savable {
|
||||
public List<Float> getSegmentsLength() {
|
||||
return segmentsLength;
|
||||
}
|
||||
|
||||
//////////// NURBS getters /////////////////////
|
||||
|
||||
/**
|
||||
* This method returns the minimum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
|
||||
* type - NPE will be thrown.
|
||||
* @return the minimum nurb curve knot value
|
||||
*/
|
||||
public float getMinNurbKnot() {
|
||||
return knots.get(basisFunctionDegree - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the maximum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb
|
||||
* type - NPE will be thrown.
|
||||
* @return the maximum nurb curve knot value
|
||||
*/
|
||||
public float getMaxNurbKnot() {
|
||||
return knots.get(weights.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns NURBS' spline knots.
|
||||
* @return NURBS' spline knots
|
||||
*/
|
||||
public List<Float> getKnots() {
|
||||
return knots;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns NURBS' spline weights.
|
||||
* @return NURBS' spline weights
|
||||
*/
|
||||
public float[] getWeights() {
|
||||
return weights;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns NURBS' spline basis function degree.
|
||||
* @return NURBS' spline basis function degree
|
||||
*/
|
||||
public int getBasisFunctionDegree() {
|
||||
return basisFunctionDegree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
@ -318,6 +423,9 @@ public class Spline implements Savable {
|
||||
oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null);
|
||||
oc.write(curveTension, "curveTension", 0.5f);
|
||||
oc.write(cycle, "cycle", false);
|
||||
oc.writeSavableArrayList((ArrayList<Float>)knots, "knots", null);
|
||||
oc.write(weights, "weights", null);
|
||||
oc.write(basisFunctionDegree, "basisFunctionDegree", 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -337,5 +445,8 @@ public class Spline implements Savable {
|
||||
CRcontrolPoints = (ArrayList<Vector3f>) in.readSavableArrayList("CRControlPoints", null);
|
||||
curveTension = in.readFloat("curveTension", 0.5f);
|
||||
cycle = in.readBoolean("cycle", false);
|
||||
knots = in.readSavableArrayList("knots", null);
|
||||
weights = in.readFloatArray("weights", null);
|
||||
basisFunctionDegree = in.readInt("basisFunctionDegree", 0);
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,9 @@ public class Curve extends Mesh {
|
||||
case Bezier:
|
||||
this.createBezierMesh(nbSubSegments);
|
||||
break;
|
||||
case Nurb:
|
||||
this.createNurbMesh(nbSubSegments);
|
||||
break;
|
||||
case Linear:
|
||||
default:
|
||||
this.createLinearMesh();
|
||||
@ -80,8 +83,8 @@ public class Curve extends Mesh {
|
||||
}
|
||||
|
||||
private void createCatmullRomMesh(int nbSubSegments) {
|
||||
float[] array = new float[(((spline.getControlPoints().size() - 1) * nbSubSegments) + 1) * 3];
|
||||
short[] indices = new short[((spline.getControlPoints().size() - 1) * nbSubSegments) * 2];
|
||||
float[] array = new float[((spline.getControlPoints().size() - 1) * nbSubSegments + 1) * 3];
|
||||
short[] indices = new short[(spline.getControlPoints().size() - 1) * nbSubSegments * 2];
|
||||
int i = 0;
|
||||
int cptCP = 0;
|
||||
for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
|
||||
@ -108,7 +111,7 @@ public class Curve extends Mesh {
|
||||
|
||||
i = 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;
|
||||
indices[i] = (short) k;
|
||||
i++;
|
||||
@ -117,11 +120,11 @@ public class Curve extends Mesh {
|
||||
i++;
|
||||
}
|
||||
|
||||
setMode(Mesh.Mode.Lines);
|
||||
setBuffer(VertexBuffer.Type.Position, 3, array);
|
||||
setBuffer(VertexBuffer.Type.Index, ((spline.getControlPoints().size() - 1) * nbSubSegments) * 2, indices);
|
||||
updateBound();
|
||||
updateCounts();
|
||||
this.setMode(Mesh.Mode.Lines);
|
||||
this.setBuffer(VertexBuffer.Type.Position, 3, array);
|
||||
this.setBuffer(VertexBuffer.Type.Index, (spline.getControlPoints().size() - 1) * nbSubSegments * 2, indices);
|
||||
this.updateBound();
|
||||
this.updateCounts();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,6 +178,43 @@ public class Curve extends Mesh {
|
||||
this.updateBound();
|
||||
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() {
|
||||
float[] array = new float[spline.getControlPoints().size() * 3];
|
||||
@ -202,11 +242,11 @@ public class Curve extends Mesh {
|
||||
}
|
||||
}
|
||||
|
||||
setMode(Mesh.Mode.Lines);
|
||||
setBuffer(VertexBuffer.Type.Position, 3, array);
|
||||
setBuffer(VertexBuffer.Type.Index, (spline.getControlPoints().size() - 1) * 2, indices);
|
||||
updateBound();
|
||||
updateCounts();
|
||||
this.setMode(Mesh.Mode.Lines);
|
||||
this.setBuffer(VertexBuffer.Type.Position, 3, array);
|
||||
this.setBuffer(VertexBuffer.Type.Index, (spline.getControlPoints().size() - 1) * 2, indices);
|
||||
this.updateBound();
|
||||
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