Basic support for NURBS curves and surfaces added.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7089 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Kae..pl 14 years ago
parent b641b5670c
commit a9a7d2d4de
  1. 127
      engine/src/core/com/jme3/math/CurveAndSurfaceMath.java
  2. 13
      engine/src/core/com/jme3/math/FastMath.java
  3. 163
      engine/src/core/com/jme3/math/Spline.java
  4. 66
      engine/src/core/com/jme3/scene/shape/Curve.java
  5. 277
      engine/src/core/com/jme3/scene/shape/Surface.java

@ -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();
}
/**

@ -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…
Cancel
Save