Ver código fonte

Curve loading upgrade.
Bugfixes:
- curves bevels are now loaded properly (previously only curves in XZ plane were loaded the way they should)

Features:
- bezier triple radius is now loaded and used
- loaded mesh can be either smooth or solid depending on the user's setting

Improvements:
- decreased memory usage for curves with bevel applied
- reduced CPU usage by simplifying the computations

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9872 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

Kae..pl 13 anos atrás
pai
commit
7ce4477b52

+ 16 - 2
engine/src/blender/com/jme3/scene/plugins/blender/curves/BezierCurve.java

@@ -9,7 +9,7 @@ import java.util.List;
 /**
  * A class that helps to calculate the bezier curves calues. It uses doubles for performing calculations to minimize
  * floating point operations errors.
- * @author Marcin Roguski
+ * @author Marcin Roguski (Kaelthas)
  */
 public class BezierCurve {
 
@@ -25,6 +25,8 @@ public class BezierCurve {
     private int dimension;
     /** A table of the bezier points. */
     private float[][][] bezierPoints;
+    /** Array that stores a radius for each bezier triple. */
+    private float[] radiuses;
 
     @SuppressWarnings("unchecked")
     public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {
@@ -37,6 +39,7 @@ public class BezierCurve {
         //the second index points to a table od three points of a bezier triple (handle, point, handle)
         //the third index specifies the coordinates of the specific point in a bezier triple
         bezierPoints = new float[bezTriples.size()][3][dimension];
+        radiuses = new float[bezTriples.size()];
         int i = 0, j, k;
         for (Structure bezTriple : bezTriples) {
             DynamicArray<Number> vec = (DynamicArray<Number>) bezTriple.getFieldValue("vec");
@@ -45,7 +48,7 @@ public class BezierCurve {
                     bezierPoints[i][j][k] = vec.get(j, k).floatValue();
                 }
             }
-            ++i;
+            radiuses[i++] = ((Number)bezTriple.getFieldValue("radius")).floatValue();
         }
     }
 
@@ -93,6 +96,17 @@ public class BezierCurve {
         return type;
     }
 
+	/**
+	 * The method returns the radius for the required bezier triple.
+	 * 
+	 * @param bezierTripleIndex
+	 *            index of the bezier triple
+	 * @return radius of the required bezier triple
+	 */
+	public float getRadius(int bezierTripleIndex) {
+		return radiuses[bezierTripleIndex];
+	}
+    
     /**
      * This method returns a list of control points for this curve.
      * @return a list of control points for this curve.

+ 388 - 225
engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java

@@ -32,12 +32,12 @@
 package com.jme3.scene.plugins.blender.curves;
 
 import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.TreeMap;
 import java.util.logging.Logger;
 
 import com.jme3.material.Material;
@@ -69,11 +69,12 @@ import com.jme3.util.BufferUtils;
 
 /**
  * A class that is used in mesh calculations.
- * @author Marcin Roguski
+ * 
+ * @author Marcin Roguski (Kaelthas)
  */
 public class CurvesHelper extends AbstractBlenderHelper {
-
     private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName());
+    
     /** Minimum basis U function degree for NURBS curves and surfaces. */
     protected int minimumBasisUFunctionDegree = 4;
     /** Minimum basis V function degree for NURBS curves and surfaces. */
@@ -113,7 +114,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
         if (isBack) {
             LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face
         }
-
+        
         //reading nurbs (and sorting them by material)
         List<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(blenderContext);
         Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();
@@ -155,44 +156,43 @@ public class CurvesHelper extends AbstractBlenderHelper {
                 float handlerLength = bevelDepth / 2.0f;
 
                 List<Vector3f> conrtolPoints = new ArrayList<Vector3f>(extrude > 0.0f ? 19 : 13);
-                conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));
-                conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength + extrude, 0));
-
-                conrtolPoints.add(new Vector3f(-handlerLength, bevelDepth + extrude, 0));
-                conrtolPoints.add(new Vector3f(0, bevelDepth + extrude, 0));
-                conrtolPoints.add(new Vector3f(handlerLength, bevelDepth + extrude, 0));
-
-                conrtolPoints.add(new Vector3f(bevelDepth, extrude + handlerLength, 0));
-                conrtolPoints.add(new Vector3f(bevelDepth, extrude, 0));
-                conrtolPoints.add(new Vector3f(bevelDepth, extrude - handlerLength, 0));
-
                 if (extrude > 0.0f) {
-                    conrtolPoints.add(new Vector3f(bevelDepth, -extrude + handlerLength, 0));
-                    conrtolPoints.add(new Vector3f(bevelDepth, -extrude, 0));
-                    conrtolPoints.add(new Vector3f(bevelDepth, -extrude - handlerLength, 0));
+                	conrtolPoints.add(new Vector3f(-bevelDepth, 0, extrude));
+                	conrtolPoints.add(new Vector3f(-bevelDepth, 0, -handlerLength + extrude));
+                    conrtolPoints.add(new Vector3f(-bevelDepth, 0, handlerLength - extrude));
                 }
-
-                conrtolPoints.add(new Vector3f(handlerLength, -bevelDepth - extrude, 0));
-                conrtolPoints.add(new Vector3f(0, -bevelDepth - extrude, 0));
-                conrtolPoints.add(new Vector3f(-handlerLength, -bevelDepth - extrude, 0));
-
-                conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength - extrude, 0));
-                conrtolPoints.add(new Vector3f(-bevelDepth, -extrude, 0));
-
+                
+                conrtolPoints.add(new Vector3f(-bevelDepth, 0, -extrude));
+                conrtolPoints.add(new Vector3f(-bevelDepth, 0, -handlerLength - extrude));
+                
+                conrtolPoints.add(new Vector3f(-handlerLength, 0, -bevelDepth - extrude));
+                conrtolPoints.add(new Vector3f(0, 0, -bevelDepth - extrude));
+                conrtolPoints.add(new Vector3f(handlerLength, 0, -bevelDepth - extrude));
+                
                 if (extrude > 0.0f) {
-                    conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength - extrude, 0));
-
-                    conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength + extrude, 0));
-                    conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));
+                	conrtolPoints.add(new Vector3f(bevelDepth, 0, -extrude - handlerLength));
+                	conrtolPoints.add(new Vector3f(bevelDepth, 0, -extrude));
+                    conrtolPoints.add(new Vector3f(bevelDepth, 0, -extrude + handlerLength));
                 }
-
+                
+                conrtolPoints.add(new Vector3f(bevelDepth, 0, extrude - handlerLength));
+                conrtolPoints.add(new Vector3f(bevelDepth, 0, extrude));
+                conrtolPoints.add(new Vector3f(bevelDepth, 0, extrude + handlerLength));
+                
+                conrtolPoints.add(new Vector3f(handlerLength, 0, bevelDepth + extrude));
+                conrtolPoints.add(new Vector3f(0, 0, bevelDepth + extrude));
+                conrtolPoints.add(new Vector3f(-handlerLength, 0, bevelDepth + extrude));
+                
+                conrtolPoints.add(new Vector3f(-bevelDepth, 0, handlerLength + extrude));
+                conrtolPoints.add(new Vector3f(-bevelDepth, 0, extrude));
+                
                 Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false);
                 Curve bevelCurve = new Curve(bevelSpline, bevResol);
                 bevelObject = new ArrayList<Geometry>(1);
                 bevelObject.add(new Geometry("", bevelCurve));
             } else if (extrude > 0.0f) {
                 Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[]{
-                            new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0)
+                		new Vector3f(0, 0, -extrude), new Vector3f(0, 0, extrude)
                         }, 1, false);
                 Curve bevelCurve = new Curve(bevelSpline, bevResol);
                 bevelObject = new ArrayList<Geometry>(1);
@@ -201,7 +201,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
         }
 
         //getting taper object
-        Curve taperObject = null;
+        Spline taperObject = null;
         Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj");
         if (bevelObject != null && pTaperObject.isNotNull()) {
             Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("data");
@@ -262,7 +262,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
      * @throws BlenderFileException
      *             an exception is thrown when there are problems with the blender file
      */
-    protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
+    protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Spline taperObject,
             BlenderContext blenderContext) throws BlenderFileException {
         Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
         List<Geometry> result = new ArrayList<Geometry>();
@@ -274,6 +274,23 @@ public class CurvesHelper extends AbstractBlenderHelper {
             //creating the curve object
             BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(blenderContext.getInputStream()), 3);
             List<Vector3f> controlPoints = bezierCurve.getControlPoints();
+            if(fixUpAxis) {
+            	for(Vector3f v : controlPoints) {
+            		float y = v.y;
+            		v.y = v.z;
+            		v.z = -y;
+            	}
+            }
+            
+			if (bevelObject != null && taperObject == null) {// create taper object using the scales of the bezier triple
+				int triplesCount = controlPoints.size() / 3;
+				List<Vector3f> taperControlPoints = new ArrayList<Vector3f>(triplesCount);
+				for (int i = 0; i < triplesCount; ++i) {
+					taperControlPoints.add(new Vector3f(controlPoints.get(i * 3 + 1).x, bezierCurve.getRadius(i), 0));
+				}
+				taperObject = new Spline(SplineType.Linear, taperControlPoints, 0, false);
+			}
+
             if (cyclic) {
                 //copy the first three points at the end
                 for (int i = 0; i < 3; ++i) {
@@ -292,7 +309,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
                 result.add(curveGeometry);
                 //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added
             } else {//creating curve with bevel and taper shape
-                result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, blenderContext);
+                result = this.applyBevelAndTaper(curve, bevelObject, taperObject,  smooth, blenderContext);
             }
         }
         return result;
@@ -315,7 +332,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
      *             an exception is throw when problems with blender loaded data occurs
      */
     @SuppressWarnings("unchecked")
-    protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
+    protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Spline taperObject,
             BlenderContext blenderContext) throws BlenderFileException {
         //loading the knots
         List<Float>[] knots = new List[2];
@@ -388,34 +405,41 @@ public class CurvesHelper extends AbstractBlenderHelper {
         }
         return result;
     }
-
-    /**
-     * This method returns the taper scale that should be applied to the object.
-     * @param taperPoints
-     *            the taper points
-     * @param taperLength
-     *            the taper curve length
-     * @param percent
-     *            the percent of way along the whole taper curve
-     */
-    protected float getTaperScale(float[] taperPoints, float taperLength, float percent) {
-        float length = taperLength * percent;
-        float currentLength = 0;
-        Vector3f p = new Vector3f();
-        int i;
-        for (i = 0; i < taperPoints.length - 6 && currentLength < length; i += 3) {
-            p.set(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]);
-            p.subtractLocal(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]);
-            currentLength += p.length();
-        }
-        currentLength -= p.length();
-        float leftLength = length - currentLength;
-        float percentOnSegment = p.length() == 0 ? 0 : leftLength / p.length();
-        Vector3f store = FastMath.interpolateLinear(percentOnSegment,
-                new Vector3f(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]),
-                new Vector3f(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]));
-        return store.y;
-    }
+    
+	/**
+	 * The method computes the taper scale on the given point on the curve.
+	 * 
+	 * @param taper
+	 *            the taper object that defines the scale
+	 * @param percent
+	 *            the percent of the 'road' along the curve
+	 * @return scale on the pointed place along the curve
+	 */
+	protected float getTaperScale(Spline taper, float percent) {
+		percent = FastMath.clamp(percent, 0, 1);
+		List<Float> segmentLengths = taper.getSegmentsLength();
+		float percentLength = taper.getTotalLength() * percent;
+		float partLength = 0;
+		int i;
+		for (i = 0; i < segmentLengths.size(); ++i) {
+			partLength += segmentLengths.get(i);
+			if (partLength > percentLength) {
+				partLength -= segmentLengths.get(i);
+				percentLength -= partLength;
+				percent = percentLength / segmentLengths.get(i);
+				break;
+			}
+		}
+		// do not cross the line :)
+		if (percent >= 1) {
+			percent = 1;
+			--i;
+		}
+		if (taper.getType() == SplineType.Bezier) {
+			i *= 3;
+		}
+		return taper.interpolate(percent, i, null).y;
+	}
 
     /**
      * This method applies bevel and taper objects to the curve.
@@ -431,186 +455,325 @@ public class CurvesHelper extends AbstractBlenderHelper {
      *            the blender context
      * @return a list of geometries representing the beveled and/or tapered curve
      */
-    protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject,
+    protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Spline taperObject,
             boolean smooth, BlenderContext blenderContext) {
-        float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position));
+    	Vector3f[] curvePoints = BufferUtils.getVector3Array(curve.getFloatBuffer(Type.Position));
         float curveLength = curve.getLength();
-        //TODO: use the smooth var
-
-        //taper data
-        float[] taperPoints = null;
-        float taperLength = 0;
-        if (taperObject != null) {
-            taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position));
-            taperLength = taperObject.getLength();
-        }
-
-        //several objects can be allocated only once
-        Vector3f p = new Vector3f();
-        Vector3f z = new Vector3f(0, 0, 1);
-        Vector3f negativeY = new Vector3f(0, -1, 0);
-        Matrix4f m = new Matrix4f();
-        float lengthAlongCurve = 0, taperScale = 1.0f;
-        Quaternion planeRotation = new Quaternion();
-        Quaternion zRotation = new Quaternion();
-        float[] temp = new float[]{0, 0, 0, 1};
-        Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//normalMap merges normals of faces that will be rendered smooth
-
+        
         FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()];
         FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()];
-        IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()];
-        for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
-            Mesh mesh = bevelObject.get(geomIndex).getMesh();
-            FloatBuffer positions = mesh.getFloatBuffer(Type.Position);
-            float[] vertices = BufferUtils.getFloatArray(positions);
-
-            for (int i = 0; i < curvePoints.length; i += 3) {
-                p.set(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]);
-                Vector3f v;
-                if (i == 0) {
-                    v = new Vector3f(curvePoints[3] - p.x, curvePoints[4] - p.y, curvePoints[5] - p.z);
-                } else if (i + 3 >= curvePoints.length) {
-                    v = new Vector3f(p.x - curvePoints[i - 3], p.y - curvePoints[i - 2], p.z - curvePoints[i - 1]);
-                    lengthAlongCurve += v.length();
-                } else {
-                    v = new Vector3f(curvePoints[i + 3] - curvePoints[i - 3],
-                            curvePoints[i + 4] - curvePoints[i - 2],
-                            curvePoints[i + 5] - curvePoints[i - 1]);
-                    lengthAlongCurve += new Vector3f(curvePoints[i + 3] - p.x, curvePoints[i + 4] - p.y, curvePoints[i + 5] - p.z).length();
-                }
-                v.normalizeLocal();
-
-                float angle = FastMath.acos(v.dot(z));
-                v.crossLocal(z).normalizeLocal();//v is the rotation axis now
-                planeRotation.fromAngleAxis(angle, v);
-
-                Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal();
-                float zAxisRotationAngle = FastMath.acos(negativeY.dot(v));
-                zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector);
-
-                //point transformation matrix
-                if (taperPoints != null) {
-                    taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength);
-                }
-                m.set(Matrix4f.IDENTITY);
-                m.setRotationQuaternion(planeRotation.multLocal(zRotation));
-                m.setTranslation(p);
-
-                //these vertices need to be thrown on XY plane
-                //and moved to the origin of [p1.x, p1.y] on the plane
-                Vector3f[] verts = new Vector3f[vertices.length / 3];
-                for (int j = 0; j < verts.length; ++j) {
-                    temp[0] = vertices[j * 3] * taperScale;
-                    temp[1] = vertices[j * 3 + 1] * taperScale;
-                    temp[2] = 0;
-                    m.mult(temp);//the result is stored in the array
-                    if (fixUpAxis) {//TODO: not the other way ???
-                        verts[j] = new Vector3f(temp[0], temp[1], temp[2]);
-                    } else {
-                        verts[j] = new Vector3f(temp[0], temp[2], -temp[1]);
-                    }
-                }
-                if (vertexBuffers[geomIndex] == null) {
-                    vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(verts.length * curvePoints.length);
-                }
-                FloatBuffer buffer = BufferUtils.createFloatBuffer(verts);
-                vertexBuffers[geomIndex].put(buffer);
-
-                //adding indexes
-                IntBuffer indexBuffer = indexBuffers[geomIndex];
-                if (indexBuffer == null) {
-                    //the amount of faces in the final mesh is the amount of edges in the bevel curve
-                    //(which is less by 1 than its number of vertices)
-                    //multiplied by 2 (because each edge has two faces assigned on both sides)
-                    //and multiplied by the amount of bevel curve repeats which is equal to the amount of vertices on the target curve
-                    //finally we need to subtract the bevel edges amount 2 times because the border edges have only one face attached
-                    //and at last multiply everything by 3 because each face needs 3 indexes to be described
-                    int bevelCurveEdgesAmount = verts.length - 1;
-                    indexBuffer = BufferUtils.createIntBuffer(((bevelCurveEdgesAmount << 1) * curvePoints.length - bevelCurveEdgesAmount << 1) * 3);
-                    indexBuffers[geomIndex] = indexBuffer;
-                }
-                int pointOffset = i / 3 * verts.length;
-                if (i + 3 < curvePoints.length) {
-                    for (int index = 0; index < verts.length - 1; ++index) {
-                        indexBuffer.put(index + pointOffset);
-                        indexBuffer.put(index + pointOffset + 1);
-                        indexBuffer.put(verts.length + index + pointOffset);
-                        indexBuffer.put(verts.length + index + pointOffset);
-                        indexBuffer.put(index + pointOffset + 1);
-                        indexBuffer.put(verts.length + index + pointOffset + 1);
-                    }
-                }
-            }
-        }
-
-        //calculating the normals
+        IndexBuffer[] indexBuffers = new IndexBuffer[bevelObject.size()];
         for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
-            Vector3f[] allVerts = BufferUtils.getVector3Array(vertexBuffers[geomIndex]);
-            int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]);
-            for (int i = 0; i < allIndices.length - 3; i += 3) {
-                Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
-                this.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
-            }
-            if (normalBuffers[geomIndex] == null) {
-                normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3);
-            }
-            for (Vector3f v : allVerts) {
-                Vector3f n = normalMap.get(v);
-                normalBuffers[geomIndex].put(n.x);
-                normalBuffers[geomIndex].put(n.y);
-                normalBuffers[geomIndex].put(n.z);
+        	Mesh mesh = bevelObject.get(geomIndex).getMesh();
+            Vector3f[] positions = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position));
+            Vector3f[] bevelPoints = this.transformToFirstLineOfBevelPoints(positions, curvePoints[0], curvePoints[1]);
+            
+            List<Vector3f[]> bevels = new ArrayList<Vector3f[]>(curvePoints.length);
+            bevels.add(bevelPoints);
+            
+            vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(bevelPoints.length * 3 * curvePoints.length * (smooth ? 1 : 6));
+			for (int i = 1; i < curvePoints.length - 1; ++i) {
+            	bevelPoints = this.transformBevel(bevelPoints, curvePoints[i - 1], curvePoints[i], curvePoints[i + 1]);
+            	bevels.add(bevelPoints);
             }
+			bevelPoints = this.transformBevel(bevelPoints, curvePoints[curvePoints.length - 2], curvePoints[curvePoints.length - 1], null);
+			bevels.add(bevelPoints);
+			
+			//apply scales to the bevels
+			float lengthAlongCurve = 0;
+			for(int i=0;i<curvePoints.length; ++i) {
+				if(i > 0) {
+					lengthAlongCurve += curvePoints[i].subtract(curvePoints[i - 1]).length();
+				}
+				float taperScale = this.getTaperScale(taperObject, i == 0 ? 0 : lengthAlongCurve / curveLength);
+				this.applyScale(bevels.get(i), curvePoints[i], taperScale);
+			}
+			
+			if(smooth) {//add everything to the buffer
+				for(Vector3f[] bevel : bevels) {
+					for(Vector3f d : bevel) {
+						vertexBuffers[geomIndex].put(d.x);
+						vertexBuffers[geomIndex].put(d.y);
+						vertexBuffers[geomIndex].put(d.z);
+			    	}
+				}
+			} else {//add vertices to the buffer duplicating them so that every vertex belongs only to a single triangle
+				for (int i = 0; i < curvePoints.length - 1; ++i) {
+	    			for (int j = 0; j < bevelPoints.length - 1; ++j) {
+	    				//first triangle
+	    				vertexBuffers[geomIndex].put(bevels.get(i)[j].x);
+						vertexBuffers[geomIndex].put(bevels.get(i)[j].y);
+						vertexBuffers[geomIndex].put(bevels.get(i)[j].z);
+						vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].x);
+						vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].y);
+						vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].z);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].x);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].y);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].z);
+						
+						//second triangle
+						vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].x);
+						vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].y);
+						vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].z);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].x);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].y);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].z);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].x);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].y);
+						vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].z);
+	    			}
+	    		}
+			}
+			
+			indexBuffers[geomIndex] = this.generateIndexes(bevelPoints.length, curvePoints.length, smooth);
+			normalBuffers[geomIndex] = this.generateNormals(indexBuffers[geomIndex], vertexBuffers[geomIndex], smooth);
         }
-
+        
+        //creating and returning the result
         List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length);
         Float oneReferenceToCurveLength = new Float(curveLength);//its important for array modifier to use one reference here
         for (int i = 0; i < vertexBuffers.length; ++i) {
             Mesh mesh = new Mesh();
             mesh.setBuffer(Type.Position, 3, vertexBuffers[i]);
-            mesh.setBuffer(Type.Index, 3, indexBuffers[i]);
+            if(indexBuffers[i].getIntBuffer() != null) {
+            	mesh.setBuffer(Type.Index, 3, indexBuffers[i].getIntBuffer());
+            } else {
+            	mesh.setBuffer(Type.Index, 3, indexBuffers[i].getShortBuffer());
+            }
             mesh.setBuffer(Type.Normal, 3, normalBuffers[i]);
             Geometry g = new Geometry("g" + i, mesh);
             g.setUserData("curveLength", oneReferenceToCurveLength);
             g.updateModelBound();
             result.add(g);
         }
-
         return result;
     }
+    
+	/**
+	 * the method applies scale for the given bevel points. The points table is
+	 * being modified so expect ypur result there.
+	 * 
+	 * @param points
+	 *            the bevel points
+	 * @param centerPoint
+	 *            the center point of the bevel
+	 * @param scale
+	 *            the scale to be applied
+	 */
+	private void applyScale(Vector3f[] points, Vector3f centerPoint, float scale) {
+		Vector3f taperScaleVector = new Vector3f();
+		for (Vector3f p : points) {
+			taperScaleVector.set(centerPoint).subtractLocal(p).multLocal(1 - scale);
+			p.addLocal(taperScaleVector);
+		}
+	}
+    
+	/**
+	 * The method generates normal buffer for the created mesh of the curve.
+	 * 
+	 * @param indexes
+	 *            the indexes of the mesh points
+	 * @param points
+	 *            the mesh's points
+	 * @param smooth
+	 *            the flag indicating if the result is to be smooth or solid
+	 * @return normals buffer for the mesh
+	 */
+	private FloatBuffer generateNormals(IndexBuffer indexes, FloatBuffer points, boolean smooth) {
+		Map<Integer, Vector3f> normalMap = new TreeMap<Integer, Vector3f>();
+		Vector3f[] allVerts = BufferUtils.getVector3Array(points);
+
+		for (int i = 0; i < indexes.limit(); i += 3) {
+			int index1 = indexes.get(i);
+			int index2 = indexes.get(i + 1);
+			int index3 = indexes.get(i + 2);
+
+			Vector3f n = FastMath.computeNormal(allVerts[index1], allVerts[index2], allVerts[index3]);
+			this.addNormal(n, normalMap, smooth, index1, index2, index3);
+		}
 
-    /**
-     * 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);
+		FloatBuffer normals = BufferUtils.createFloatBuffer(normalMap.size() * 3);
+		for (Entry<Integer, Vector3f> entry : normalMap.entrySet()) {
+			normals.put(entry.getValue().x);
+			normals.put(entry.getValue().y);
+			normals.put(entry.getValue().z);
+		}
+		return normals;
+	}
+    
+	/**
+	 * The amount of faces in the final mesh is the amount of edges in the bevel
+	 * curve (which is less by 1 than its number of vertices) multiplied by 2
+	 * (because each edge has two faces assigned on both sides) and multiplied
+	 * by the amount of bevel curve repeats which is equal to the amount of
+	 * vertices on the target curve finally we need to subtract the bevel edges
+	 * amount 2 times because the border edges have only one face attached and
+	 * at last multiply everything by 3 because each face needs 3 indexes to be
+	 * described
+	 * 
+	 * @param bevelShapeVertexCount
+	 *            amount of points in bevel shape
+	 * @param bevelRepeats
+	 *            amount of bevel shapes along the curve
+	 * @param smooth
+	 *            the smooth flag
+	 * @return index buffer for the mesh
+	 */
+    private IndexBuffer generateIndexes(int bevelShapeVertexCount, int bevelRepeats, boolean smooth) {
+    	if(smooth) {
+    		int indexBufferSize = (bevelRepeats - 1) * (bevelShapeVertexCount - 1) * 6;
+    		IndexBuffer result = new IndexBuffer(indexBufferSize);
+            
+    		for (int i = 0; i < bevelRepeats - 1; ++i) {
+    			for (int j = 0; j < bevelShapeVertexCount - 1; ++j) {
+    				result.put(i * bevelShapeVertexCount + j);
+    				result.put(i * bevelShapeVertexCount + j + 1);
+    				result.put((i + 1) * bevelShapeVertexCount + j);
+    				
+    				result.put(i * bevelShapeVertexCount + j + 1);
+    				result.put((i + 1) * bevelShapeVertexCount + j + 1);
+    				result.put((i + 1) * bevelShapeVertexCount + j);
+    			}
+    		}
+            return result;
+    	} else {
+    		//every pair of bevel vertices belongs to two triangles
+    		//we have the same amount of pairs as the amount of vertices in bevel
+    		//so the amount of triangles is: bevelShapeVertexCount * 2 * (bevelRepeats - 1)
+    		//and this gives the amount of vertices in non smooth shape as below ...
+    		int indexBufferSize = bevelShapeVertexCount * bevelRepeats * 6;//6 = 2 * 3 where 2 is stated above and 3 is the count of vertices for each triangle
+    		IndexBuffer result = new IndexBuffer(indexBufferSize);
+    		for (int i = 0; i < indexBufferSize; ++i) {
+    			result.put(i);
+    		}
+            return result;
+    	}
+    }
+    
+	/**
+	 * The method transforms the bevel along the curve.
+	 * 
+	 * @param bevel
+	 *            the bevel to be transformed
+	 * @param prevPos
+	 *            previous curve point
+	 * @param currPos
+	 *            current curve point (here the center of the new bevel will be
+	 *            set)
+	 * @param nextPos
+	 *            next curve point
+	 * @return points of transformed bevel
+	 */
+    private Vector3f[] transformBevel(Vector3f[] bevel, Vector3f prevPos, Vector3f currPos, Vector3f nextPos) {
+    	bevel = bevel.clone();
+    	
+    	//currPos and directionVector define the line in 3D space
+    	Vector3f directionVector = prevPos != null ? currPos.subtract(prevPos) : nextPos.subtract(currPos);
+    	directionVector.normalizeLocal();
+    	
+    	//plane is described by equation: Ax + By + Cz + D = 0 where planeNormal = [A, B, C] and D = -(Ax + By + Cz)
+    	Vector3f planeNormal = null;
+    	if(prevPos != null) {
+    		planeNormal = currPos.subtract(prevPos).normalizeLocal();
+    		if(nextPos != null) {
+    			planeNormal.addLocal(nextPos.subtract(currPos).normalizeLocal()).normalizeLocal();
+    		}
+    	} else {
+    		planeNormal = nextPos.subtract(currPos).normalizeLocal();
+    	}
+    	float D = -planeNormal.dot(currPos);//D = -(Ax + By + Cz)
+    	
+    	//now we need to compute paralell cast of each bevel point on the plane, the leading line is already known
+    	//parametric equation of a line: x = px + vx * t; y = py + vy * t; z = pz + vz * t
+    	//where p = currPos and v = directionVector
+    	//using x, y and z in plane equation we get value of 't' that will allow us to compute the point where plane and line cross
+    	float temp = planeNormal.dot(directionVector);
+    	for(int i=0;i<bevel.length;++i) {
+    		float t = -(planeNormal.dot(bevel[i]) + D) / temp;
+    		if (fixUpAxis) {
+    			bevel[i] = new Vector3f(bevel[i].x + directionVector.x * t, bevel[i].y + directionVector.y * t, bevel[i].z + directionVector.z * t);
+            } else {
+            	bevel[i] = new Vector3f(bevel[i].x + directionVector.x * t, -bevel[i].z + directionVector.z * t, bevel[i].y + directionVector.y * t);
+            }
+    	}
+    	return bevel;
+    }
+    
+	/**
+	 * This method transforms the first line of the bevel points positioning it
+	 * on the first point of the curve.
+	 * 
+	 * @param startingLinePoints
+	 *            the vbevel shape points
+	 * @param firstCurvePoint
+	 *            the first curve's point
+	 * @param secondCurvePoint
+	 *            the second curve's point
+	 * @return points of transformed bevel
+	 */
+    private Vector3f[] transformToFirstLineOfBevelPoints(Vector3f[] startingLinePoints, Vector3f firstCurvePoint, Vector3f secondCurvePoint) {
+    	Vector3f planeNormal = secondCurvePoint.subtract(firstCurvePoint).normalizeLocal();
+    	
+    	float angle = FastMath.acos(planeNormal.dot(Vector3f.UNIT_Y));
+    	planeNormal.crossLocal(Vector3f.UNIT_Y).normalizeLocal();//planeNormal is the rotation axis now
+    	Quaternion pointRotation = new Quaternion();
+    	pointRotation.fromAngleAxis(angle, planeNormal);
+
+        Matrix4f m = new Matrix4f();
+        m.setRotationQuaternion(pointRotation);
+        m.setTranslation(firstCurvePoint);
+
+		float[] temp = new float[] { 0, 0, 0, 1 };
+        Vector3f[] verts = new Vector3f[startingLinePoints.length];
+        for (int j = 0; j < verts.length; ++j) {
+            temp[0] = startingLinePoints[j].x;
+            temp[1] = startingLinePoints[j].y;
+            temp[2] = startingLinePoints[j].z;
+            temp = m.mult(temp);//the result is stored in the array
+            if (fixUpAxis) {
+            	verts[j] = new Vector3f(temp[0], -temp[2], temp[1]);
+            } else {
+            	verts[j] = new Vector3f(temp[0], temp[1], temp[2]);
+            }
+        }
+    	return verts;
+    }
+    
+	/**
+	 * The method adds a normal to the given map. Depending in the smooth factor
+	 * it is either merged with the revious normal or not.
+	 * 
+	 * @param normalToAdd
+	 *            the normal vector to be added
+	 * @param normalMap
+	 *            the normal map where we add vectors
+	 * @param smooth
+	 *            the smooth flag
+	 * @param indexes
+	 *            the indexes of the normals
+	 */
+    private void addNormal(Vector3f normalToAdd, Map<Integer, Vector3f> normalMap, boolean smooth, int... indexes) {
+        for (int index : indexes) {
+            Vector3f n = normalMap.get(index);
             if (!smooth || n == null) {
-                normalMap.put(v, normalToAdd.clone());
+                normalMap.put(index, normalToAdd.clone());
             } else {
                 n.addLocal(normalToAdd).normalizeLocal();
             }
         }
     }
     
-    /**
-     * This method loads the taper object.
-     * @param taperStructure
-     *            the taper structure
-     * @param blenderContext
-     *            the blender context
-     * @return the taper object
-     * @throws BlenderFileException
-     */
-    protected Curve loadTaperObject(Structure taperStructure, BlenderContext blenderContext) throws BlenderFileException {
+	/**
+	 * This method loads the taper object.
+	 * 
+	 * @param taperStructure
+	 *            the taper structure
+	 * @param blenderContext
+	 *            the blender context
+	 * @return the taper object
+	 * @throws BlenderFileException
+	 */
+    protected Spline loadTaperObject(Structure taperStructure, BlenderContext blenderContext) throws BlenderFileException {
         //reading nurbs
         List<Structure> nurbStructures = ((Structure) taperStructure.getFieldValue("nurb")).evaluateListBase(blenderContext);
         for (Structure nurb : nurbStructures) {
@@ -625,21 +788,21 @@ public class CurvesHelper extends AbstractBlenderHelper {
 
                 //return the first taper curve that has more than 3 control points
                 if (controlPoints.size() > 3) {
-                    Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);
-                    int resolution = ((Number) taperStructure.getFieldValue("resolu")).intValue();
-                    return new Curve(spline, resolution);
+                    return new Spline(SplineType.Bezier, controlPoints, 0, false);
                 }
             }
         }
         return null;
     }
 
-    /**
-     * This method returns the translation of the curve. The UP axis is taken into account here.
-     * @param curveStructure
-     *            the curve structure
-     * @return curve translation
-     */
+	/**
+	 * This method returns the translation of the curve. The UP axis is taken
+	 * into account here.
+	 * 
+	 * @param curveStructure
+	 *            the curve structure
+	 * @return curve translation
+	 */
     @SuppressWarnings("unchecked")
     protected Vector3f getLoc(Structure curveStructure) {
         DynamicArray<Number> locArray = (DynamicArray<Number>) curveStructure.getFieldValue("loc");

+ 86 - 0
engine/src/blender/com/jme3/scene/plugins/blender/curves/IndexBuffer.java

@@ -0,0 +1,86 @@
+package com.jme3.scene.plugins.blender.curves;
+
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+import com.jme3.util.BufferUtils;
+
+/**
+ * A simple helping class to create index buffer. Depending on the size of the
+ * buffer either ShorBuffer or IntBuffer is used to save memory.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class IndexBuffer {
+	/** The buffer used for larger objects. */
+	private IntBuffer intBuffer;
+	/** The buffer used for smaller objects. */
+	private ShortBuffer shortBuffer;
+
+	/**
+	 * Creates the buffer depending on the given size.
+	 * 
+	 * @param bufferSize
+	 *            the size of the buffer
+	 */
+	public IndexBuffer(int bufferSize) {
+		if (bufferSize < Short.MAX_VALUE) {
+			shortBuffer = BufferUtils.createShortBuffer(bufferSize);
+		} else {
+			intBuffer = BufferUtils.createIntBuffer(bufferSize);
+		}
+	}
+
+	/**
+	 * Puts a value to the created buffer.
+	 * 
+	 * @param value
+	 *            the value to be put to the buffer
+	 */
+	public void put(int value) {
+		if (intBuffer != null) {
+			intBuffer.put(value);
+		} else {
+			shortBuffer.put((short) value);
+		}
+	}
+
+	/**
+	 * Returns the value on the given index. Take in mind that onlye <b>int</b>
+	 * is returned, no matter if short or int buffer is used.
+	 * 
+	 * @param index
+	 *            the index of the value
+	 * @return the value from the buffer
+	 */
+	public int get(int index) {
+		if (intBuffer != null) {
+			return intBuffer.get(index);
+		}
+		return shortBuffer.get(index);
+	}
+
+	/**
+	 * @return the limit of the buffer
+	 */
+	public int limit() {
+		if (intBuffer != null) {
+			return intBuffer.limit();
+		}
+		return shortBuffer.limit();
+	}
+
+	/**
+	 * @return integer buffer
+	 */
+	public IntBuffer getIntBuffer() {
+		return intBuffer;
+	}
+
+	/**
+	 * @return short buffer
+	 */
+	public ShortBuffer getShortBuffer() {
+		return shortBuffer;
+	}
+}