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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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