Home | History | Annotate | Download | only in shape
      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