浏览代码

Basic support for NURBS curves and surfaces added.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7089 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 14 年之前
父节点
当前提交
a9a7d2d4de

+ 127 - 0
engine/src/core/com/jme3/math/CurveAndSurfaceMath.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);
+		}
+	}
+}

+ 13 - 0
engine/src/core/com/jme3/math/FastMath.java

@@ -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.

+ 137 - 26
engine/src/core/com/jme3/math/Spline.java

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

+ 53 - 13
engine/src/core/com/jme3/scene/shape/Curve.java

@@ -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 - 0
engine/src/core/com/jme3/scene/shape/Surface.java

@@ -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!");
+				}
+			}
+		}
+	}
+}