• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.jme3.scene.shape;
2 
3 import com.jme3.math.CurveAndSurfaceMath;
4 import com.jme3.math.FastMath;
5 import com.jme3.math.Spline.SplineType;
6 import com.jme3.math.Vector3f;
7 import com.jme3.math.Vector4f;
8 import com.jme3.scene.Mesh;
9 import com.jme3.scene.VertexBuffer;
10 import com.jme3.util.BufferUtils;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14 
15 /**
16  * This class represents a surface described by knots, weights and control points.
17  * Currently the following types are supported:
18  * a) NURBS
19  * @author Marcin Roguski (Kealthas)
20  */
21 public class Surface extends Mesh {
22 
23     private SplineType type;						//the type of the surface
24     private List<List<Vector4f>> controlPoints;		//space control points and their weights
25     private List<Float>[] knots;					//knots of the surface
26     private int basisUFunctionDegree;				//the degree of basis U function
27     private int basisVFunctionDegree;				//the degree of basis V function
28     private int uSegments;							//the amount of U segments
29     private int vSegments;							//the amount of V segments
30 
31     /**
32      * Constructor. Constructs required surface.
33      * @param controlPoints space control points
34      * @param nurbKnots knots of the surface
35      * @param uSegments the amount of U segments
36      * @param vSegments the amount of V segments
37      * @param basisUFunctionDegree the degree of basis U function
38      * @param basisVFunctionDegree the degree of basis V function
39      */
Surface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots, int uSegments, int vSegments, int basisUFunctionDegree, int basisVFunctionDegree)40     private Surface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots,
41             int uSegments, int vSegments, int basisUFunctionDegree, int basisVFunctionDegree) {
42         this.validateInputData(controlPoints, nurbKnots, uSegments, vSegments);
43         this.type = SplineType.Nurb;
44         this.uSegments = uSegments;
45         this.vSegments = vSegments;
46         this.controlPoints = controlPoints;
47         this.knots = nurbKnots;
48         this.basisUFunctionDegree = basisUFunctionDegree;
49         CurveAndSurfaceMath.prepareNurbsKnots(nurbKnots[0], basisUFunctionDegree);
50         if (nurbKnots[1] != null) {
51             this.basisVFunctionDegree = basisVFunctionDegree;
52             CurveAndSurfaceMath.prepareNurbsKnots(nurbKnots[1], basisVFunctionDegree);
53         }
54 
55         this.buildSurface();
56     }
57 
58     /**
59      * This method creates a NURBS surface.
60      * @param controlPoints space control points
61      * @param nurbKnots knots of the surface
62      * @param uSegments the amount of U segments
63      * @param vSegments the amount of V segments
64      * @param basisUFunctionDegree the degree of basis U function
65      * @param basisVFunctionDegree the degree of basis V function
66      * @return an instance of NURBS surface
67      */
createNurbsSurface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots, int uSegments, int vSegments, int basisUFunctionDegree, int basisVFunctionDegree)68     public static final Surface createNurbsSurface(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots,
69             int uSegments, int vSegments, int basisUFunctionDegree, int basisVFunctionDegree) {
70         Surface result = new Surface(controlPoints, nurbKnots, uSegments, vSegments, basisUFunctionDegree, basisVFunctionDegree);
71         result.type = SplineType.Nurb;
72         return result;
73     }
74 
75     /**
76      * This method creates the surface.
77      */
buildSurface()78     private void buildSurface() {
79         boolean smooth = true;//TODO: take smoothing into consideration
80         float minUKnot = this.getMinUNurbKnot();
81         float maxUKnot = this.getMaxUNurbKnot();
82         float deltaU = (maxUKnot - minUKnot) / uSegments;
83 
84         float minVKnot = this.getMinVNurbKnot();
85         float maxVKnot = this.getMaxVNurbKnot();
86         float deltaV = (maxVKnot - minVKnot) / vSegments;
87 
88         Vector3f[] vertices = new Vector3f[(uSegments + 1) * (vSegments + 1)];
89 
90         float u = minUKnot, v = minVKnot;
91         int arrayIndex = 0;
92 
93         for (int i = 0; i <= vSegments; ++i) {
94             for (int j = 0; j <= uSegments; ++j) {
95                 Vector3f interpolationResult = new Vector3f();
96                 CurveAndSurfaceMath.interpolate(u, v, controlPoints, knots, basisUFunctionDegree, basisVFunctionDegree, interpolationResult);
97                 vertices[arrayIndex++] = interpolationResult;
98                 u += deltaU;
99             }
100             u = minUKnot;
101             v += deltaV;
102         }
103 
104         //adding indexes
105         int uVerticesAmount = uSegments + 1;
106         int[] indices = new int[uSegments * vSegments * 6];
107         arrayIndex = 0;
108         for (int i = 0; i < vSegments; ++i) {
109             for (int j = 0; j < uSegments; ++j) {
110                 indices[arrayIndex++] = j + i * uVerticesAmount;
111                 indices[arrayIndex++] = j + i * uVerticesAmount + 1;
112                 indices[arrayIndex++] = j + i * uVerticesAmount + uVerticesAmount;
113                 indices[arrayIndex++] = j + i * uVerticesAmount + 1;
114                 indices[arrayIndex++] = j + i * uVerticesAmount + uVerticesAmount + 1;
115                 indices[arrayIndex++] = j + i * uVerticesAmount + uVerticesAmount;
116             }
117         }
118 
119         //normalMap merges normals of faces that will be rendered smooth
120         Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(vertices.length);
121         for (int i = 0; i < indices.length; i += 3) {
122             Vector3f n = FastMath.computeNormal(vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]);
123             this.addNormal(n, normalMap, smooth, vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]);
124         }
125         //preparing normal list (the order of normals must match the order of vertices)
126         float[] normals = new float[vertices.length * 3];
127         arrayIndex = 0;
128         for (int i = 0; i < vertices.length; ++i) {
129             Vector3f n = normalMap.get(vertices[i]);
130             normals[arrayIndex++] = n.x;
131             normals[arrayIndex++] = n.y;
132             normals[arrayIndex++] = n.z;
133         }
134 
135         this.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
136         this.setBuffer(VertexBuffer.Type.Index, 3, indices);
137         this.setBuffer(VertexBuffer.Type.Normal, 3, normals);
138         this.updateBound();
139         this.updateCounts();
140     }
141 
getControlPoints()142     public List<List<Vector4f>> getControlPoints() {
143         return controlPoints;
144     }
145 
146     /**
147      * This method returns the amount of U control points.
148      * @return the amount of U control points
149      */
getUControlPointsAmount()150     public int getUControlPointsAmount() {
151         return controlPoints.size();
152     }
153 
154     /**
155      * This method returns the amount of V control points.
156      * @return the amount of V control points
157      */
getVControlPointsAmount()158     public int getVControlPointsAmount() {
159         return controlPoints.get(0) == null ? 0 : controlPoints.get(0).size();
160     }
161 
162     /**
163      * This method returns the degree of basis U function.
164      * @return the degree of basis U function
165      */
getBasisUFunctionDegree()166     public int getBasisUFunctionDegree() {
167         return basisUFunctionDegree;
168     }
169 
170     /**
171      * This method returns the degree of basis V function.
172      * @return the degree of basis V function
173      */
getBasisVFunctionDegree()174     public int getBasisVFunctionDegree() {
175         return basisVFunctionDegree;
176     }
177 
178     /**
179      * This method returns the knots for specified dimension (U knots - value: '0',
180      * V knots - value: '1').
181      * @param dim an integer specifying if the U or V knots are required
182      * @return an array of knots
183      */
getKnots(int dim)184     public List<Float> getKnots(int dim) {
185         return knots[dim];
186     }
187 
188     /**
189      * This method returns the type of the surface.
190      * @return the type of the surface
191      */
getType()192     public SplineType getType() {
193         return type;
194     }
195 
196     /**
197      * This method returns the minimum nurb curve U knot value.
198      * @return the minimum nurb curve knot value
199      */
getMinUNurbKnot()200     private float getMinUNurbKnot() {
201         return knots[0].get(basisUFunctionDegree - 1);
202     }
203 
204     /**
205      * This method returns the maximum nurb curve U knot value.
206      * @return the maximum nurb curve knot value
207      */
getMaxUNurbKnot()208     private float getMaxUNurbKnot() {
209         return knots[0].get(knots[0].size() - basisUFunctionDegree);
210     }
211 
212     /**
213      * This method returns the minimum nurb curve U knot value.
214      * @return the minimum nurb curve knot value
215      */
getMinVNurbKnot()216     private float getMinVNurbKnot() {
217         return knots[1].get(basisVFunctionDegree - 1);
218     }
219 
220     /**
221      * This method returns the maximum nurb curve U knot value.
222      * @return the maximum nurb curve knot value
223      */
getMaxVNurbKnot()224     private float getMaxVNurbKnot() {
225         return knots[1].get(knots[1].size() - basisVFunctionDegree);
226     }
227 
228     /**
229      * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
230      * @param normalToAdd
231      *            a normal to be added
232      * @param normalMap
233      *            merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
234      * @param smooth
235      *            the variable that indicates wheather to merge normals (creating the smooth mesh) or not
236      * @param vertices
237      *            a list of vertices read from the blender file
238      */
addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices)239     private void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
240         for (Vector3f v : vertices) {
241             Vector3f n = normalMap.get(v);
242             if (!smooth || n == null) {
243                 normalMap.put(v, normalToAdd.clone());
244             } else {
245                 n.addLocal(normalToAdd).normalizeLocal();
246             }
247         }
248     }
249 
250     /**
251      * This method validates the input data. It throws {@link IllegalArgumentException} if
252      * the data is invalid.
253      * @param controlPoints space control points
254      * @param nurbKnots knots of the surface
255      * @param uSegments the amount of U segments
256      * @param vSegments the amount of V segments
257      */
validateInputData(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots, int uSegments, int vSegments)258     private void validateInputData(List<List<Vector4f>> controlPoints, List<Float>[] nurbKnots,
259             int uSegments, int vSegments) {
260         int uPointsAmount = controlPoints.get(0).size();
261         for (int i = 1; i < controlPoints.size(); ++i) {
262             if (controlPoints.get(i).size() != uPointsAmount) {
263                 throw new IllegalArgumentException("The amount of 'U' control points is invalid!");
264             }
265         }
266         if (uSegments <= 0) {
267             throw new IllegalArgumentException("U segments amount should be positive!");
268         }
269         if (vSegments < 0) {
270             throw new IllegalArgumentException("V segments amount cannot be negative!");
271         }
272         if (nurbKnots.length != 2) {
273             throw new IllegalArgumentException("Nurb surface should have two rows of knots!");
274         }
275         for (int i = 0; i < nurbKnots.length; ++i) {
276             for (int j = 0; j < nurbKnots[i].size() - 1; ++j) {
277                 if (nurbKnots[i].get(j) > nurbKnots[i].get(j + 1)) {
278                     throw new IllegalArgumentException("The knots' values cannot decrease!");
279                 }
280             }
281         }
282     }
283 }
284