Home | History | Annotate | Download | only in curves
      1 package com.jme3.scene.plugins.blender.curves;
      2 
      3 import com.jme3.material.Material;
      4 import com.jme3.material.RenderState.FaceCullMode;
      5 import com.jme3.math.Spline.SplineType;
      6 import com.jme3.math.*;
      7 import com.jme3.scene.Geometry;
      8 import com.jme3.scene.Mesh;
      9 import com.jme3.scene.VertexBuffer.Type;
     10 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
     11 import com.jme3.scene.plugins.blender.BlenderContext;
     12 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
     13 import com.jme3.scene.plugins.blender.file.*;
     14 import com.jme3.scene.plugins.blender.materials.MaterialHelper;
     15 import com.jme3.scene.plugins.blender.meshes.MeshHelper;
     16 import com.jme3.scene.plugins.blender.objects.Properties;
     17 import com.jme3.scene.shape.Curve;
     18 import com.jme3.scene.shape.Surface;
     19 import com.jme3.util.BufferUtils;
     20 import java.nio.FloatBuffer;
     21 import java.nio.IntBuffer;
     22 import java.util.ArrayList;
     23 import java.util.HashMap;
     24 import java.util.List;
     25 import java.util.Map;
     26 import java.util.Map.Entry;
     27 import java.util.logging.Logger;
     28 
     29 /**
     30  * A class that is used in mesh calculations.
     31  * @author Marcin Roguski
     32  */
     33 public class CurvesHelper extends AbstractBlenderHelper {
     34 
     35     private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName());
     36     /** Minimum basis U function degree for NURBS curves and surfaces. */
     37     protected int minimumBasisUFunctionDegree = 4;
     38     /** Minimum basis V function degree for NURBS curves and surfaces. */
     39     protected int minimumBasisVFunctionDegree = 4;
     40 
     41     /**
     42      * This constructor parses the given blender version and stores the result. Some functionalities may differ in
     43      * different blender versions.
     44      * @param blenderVersion
     45      *        the version read from the blend file
     46      * @param fixUpAxis
     47      *        a variable that indicates if the Y asxis is the UP axis or not
     48      */
     49     public CurvesHelper(String blenderVersion, boolean fixUpAxis) {
     50         super(blenderVersion, fixUpAxis);
     51     }
     52 
     53     /**
     54      * This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object
     55      * can have several separate curves.
     56      * @param curveStructure
     57      *            the curve structure
     58      * @param blenderContext
     59      *            the blender context
     60      * @return a list of geometries repreenting a single curve object
     61      * @throws BlenderFileException
     62      */
     63     public List<Geometry> toCurve(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException {
     64         String name = curveStructure.getName();
     65         int flag = ((Number) curveStructure.getFieldValue("flag")).intValue();
     66         boolean is3D = (flag & 0x01) != 0;
     67         boolean isFront = (flag & 0x02) != 0 && !is3D;
     68         boolean isBack = (flag & 0x04) != 0 && !is3D;
     69         if (isFront) {
     70             LOGGER.warning("No front face in curve implemented yet!");//TODO: implement front face
     71         }
     72         if (isBack) {
     73             LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face
     74         }
     75 
     76         //reading nurbs (and sorting them by material)
     77         List<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(blenderContext);
     78         Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();
     79         for (Structure nurb : nurbStructures) {
     80             Number matNumber = (Number) nurb.getFieldValue("mat_nr");
     81             List<Structure> nurbList = nurbs.get(matNumber);
     82             if (nurbList == null) {
     83                 nurbList = new ArrayList<Structure>();
     84                 nurbs.put(matNumber, nurbList);
     85             }
     86             nurbList.add(nurb);
     87         }
     88 
     89         //getting materials
     90         MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
     91         Material[] materials = materialHelper.getMaterials(curveStructure, blenderContext);
     92         if (materials == null) {
     93             materials = new Material[]{blenderContext.getDefaultMaterial().clone()};
     94         }
     95         for (Material material : materials) {
     96             material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
     97         }
     98 
     99         //getting or creating bevel object
    100         List<Geometry> bevelObject = null;
    101         Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj");
    102         if (pBevelObject.isNotNull()) {
    103             Pointer pBevelStructure = (Pointer) pBevelObject.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("data");
    104             Structure bevelStructure = pBevelStructure.fetchData(blenderContext.getInputStream()).get(0);
    105             bevelObject = this.toCurve(bevelStructure, blenderContext);
    106         } else {
    107             int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue();
    108             float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue();
    109             float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue();
    110             if (bevelDepth > 0.0f) {
    111                 float handlerLength = bevelDepth / 2.0f;
    112 
    113                 List<Vector3f> conrtolPoints = new ArrayList<Vector3f>(extrude > 0.0f ? 19 : 13);
    114                 conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));
    115                 conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength + extrude, 0));
    116 
    117                 conrtolPoints.add(new Vector3f(-handlerLength, bevelDepth + extrude, 0));
    118                 conrtolPoints.add(new Vector3f(0, bevelDepth + extrude, 0));
    119                 conrtolPoints.add(new Vector3f(handlerLength, bevelDepth + extrude, 0));
    120 
    121                 conrtolPoints.add(new Vector3f(bevelDepth, extrude + handlerLength, 0));
    122                 conrtolPoints.add(new Vector3f(bevelDepth, extrude, 0));
    123                 conrtolPoints.add(new Vector3f(bevelDepth, extrude - handlerLength, 0));
    124 
    125                 if (extrude > 0.0f) {
    126                     conrtolPoints.add(new Vector3f(bevelDepth, -extrude + handlerLength, 0));
    127                     conrtolPoints.add(new Vector3f(bevelDepth, -extrude, 0));
    128                     conrtolPoints.add(new Vector3f(bevelDepth, -extrude - handlerLength, 0));
    129                 }
    130 
    131                 conrtolPoints.add(new Vector3f(handlerLength, -bevelDepth - extrude, 0));
    132                 conrtolPoints.add(new Vector3f(0, -bevelDepth - extrude, 0));
    133                 conrtolPoints.add(new Vector3f(-handlerLength, -bevelDepth - extrude, 0));
    134 
    135                 conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength - extrude, 0));
    136                 conrtolPoints.add(new Vector3f(-bevelDepth, -extrude, 0));
    137 
    138                 if (extrude > 0.0f) {
    139                     conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength - extrude, 0));
    140 
    141                     conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength + extrude, 0));
    142                     conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0));
    143                 }
    144 
    145                 Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false);
    146                 Curve bevelCurve = new Curve(bevelSpline, bevResol);
    147                 bevelObject = new ArrayList<Geometry>(1);
    148                 bevelObject.add(new Geometry("", bevelCurve));
    149             } else if (extrude > 0.0f) {
    150                 Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[]{
    151                             new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0)
    152                         }, 1, false);
    153                 Curve bevelCurve = new Curve(bevelSpline, bevResol);
    154                 bevelObject = new ArrayList<Geometry>(1);
    155                 bevelObject.add(new Geometry("", bevelCurve));
    156             }
    157         }
    158 
    159         //getting taper object
    160         Curve taperObject = null;
    161         Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj");
    162         if (bevelObject != null && pTaperObject.isNotNull()) {
    163             Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("data");
    164             Structure taperStructure = pTaperStructure.fetchData(blenderContext.getInputStream()).get(0);
    165             taperObject = this.loadTaperObject(taperStructure, blenderContext);
    166         }
    167 
    168         Vector3f loc = this.getLoc(curveStructure);
    169         //creating the result curves
    170         List<Geometry> result = new ArrayList<Geometry>(nurbs.size());
    171         for (Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {
    172             for (Structure nurb : nurbEntry.getValue()) {
    173                 int type = ((Number) nurb.getFieldValue("type")).intValue();
    174                 List<Geometry> nurbGeoms = null;
    175                 if ((type & 0x01) != 0) {//Bezier curve
    176                     nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, blenderContext);
    177                 } else if ((type & 0x04) != 0) {//NURBS
    178                     nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, blenderContext);
    179                 }
    180                 if (nurbGeoms != null) {//setting the name and assigning materials
    181                     for (Geometry nurbGeom : nurbGeoms) {
    182                         nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]);
    183                         nurbGeom.setName(name);
    184                         result.add(nurbGeom);
    185                     }
    186                 }
    187             }
    188         }
    189 
    190         //reading custom properties
    191 		Properties properties = this.loadProperties(curveStructure, blenderContext);
    192 		if(properties != null && properties.getValue() != null) {
    193 			for(Geometry geom : result) {
    194 				geom.setUserData("properties", properties);
    195 			}
    196 		}
    197 
    198         return result;
    199     }
    200 
    201     /**
    202      * This method loads the bezier curve.
    203      * @param loc
    204      *            the translation of the curve
    205      * @param nurb
    206      *            the nurb structure
    207      * @param bevelObject
    208      *            the bevel object
    209      * @param taperObject
    210      *            the taper object
    211      * @param blenderContext
    212      *            the blender context
    213      * @return a list of geometries representing the curves
    214      * @throws BlenderFileException
    215      *             an exception is thrown when there are problems with the blender file
    216      */
    217     protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
    218             BlenderContext blenderContext) throws BlenderFileException {
    219         Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
    220         List<Geometry> result = new ArrayList<Geometry>();
    221         if (pBezierTriple.isNotNull()) {
    222             boolean smooth = (((Number) nurb.getFlatFieldValue("flag")).intValue() & 0x01) != 0;
    223             int resolution = ((Number) nurb.getFieldValue("resolu")).intValue();
    224             boolean cyclic = (((Number) nurb.getFieldValue("flagu")).intValue() & 0x01) != 0;
    225 
    226             //creating the curve object
    227             BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(blenderContext.getInputStream()), 3);
    228             List<Vector3f> controlPoints = bezierCurve.getControlPoints();
    229             if (cyclic) {
    230                 //copy the first three points at the end
    231                 for (int i = 0; i < 3; ++i) {
    232                     controlPoints.add(controlPoints.get(i));
    233                 }
    234             }
    235             //removing the first and last handles
    236             controlPoints.remove(0);
    237             controlPoints.remove(controlPoints.size() - 1);
    238 
    239             //creating curve
    240             Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);
    241             Curve curve = new Curve(spline, resolution);
    242             if (bevelObject == null) {//creating a normal curve
    243                 Geometry curveGeometry = new Geometry(null, curve);
    244                 result.add(curveGeometry);
    245                 //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added
    246             } else {//creating curve with bevel and taper shape
    247                 result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, blenderContext);
    248             }
    249         }
    250         return result;
    251     }
    252 
    253     /**
    254      * This method loads the NURBS curve or surface.
    255      * @param loc
    256      *            object's location
    257      * @param nurb
    258      *            the NURBS data structure
    259      * @param bevelObject
    260      *            the bevel object to be applied
    261      * @param taperObject
    262      *            the taper object to be applied
    263      * @param blenderContext
    264      *            the blender context
    265      * @return a list of geometries that represents the loaded NURBS curve or surface
    266      * @throws BlenderFileException
    267      *             an exception is throw when problems with blender loaded data occurs
    268      */
    269     @SuppressWarnings("unchecked")
    270     protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject,
    271             BlenderContext blenderContext) throws BlenderFileException {
    272         //loading the knots
    273         List<Float>[] knots = new List[2];
    274         Pointer[] pKnots = new Pointer[]{(Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv")};
    275         for (int i = 0; i < knots.length; ++i) {
    276             if (pKnots[i].isNotNull()) {
    277                 FileBlockHeader fileBlockHeader = blenderContext.getFileBlock(pKnots[i].getOldMemoryAddress());
    278                 BlenderInputStream blenderInputStream = blenderContext.getInputStream();
    279                 blenderInputStream.setPosition(fileBlockHeader.getBlockPosition());
    280                 int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4;
    281                 knots[i] = new ArrayList<Float>(knotsAmount);
    282                 for (int j = 0; j < knotsAmount; ++j) {
    283                     knots[i].add(Float.valueOf(blenderInputStream.readFloat()));
    284                 }
    285             }
    286         }
    287 
    288         //loading the flags and orders (basis functions degrees)
    289         int flagU = ((Number) nurb.getFieldValue("flagu")).intValue();
    290         int flagV = ((Number) nurb.getFieldValue("flagv")).intValue();
    291         int orderU = ((Number) nurb.getFieldValue("orderu")).intValue();
    292         int orderV = ((Number) nurb.getFieldValue("orderv")).intValue();
    293 
    294         //loading control points and their weights
    295         int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue();
    296         int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue();
    297         List<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData(blenderContext.getInputStream());
    298         List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);
    299         for (int i = 0; i < pntsV; ++i) {
    300             List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);
    301             for (int j = 0; j < pntsU; ++j) {
    302                 DynamicArray<Float> vec = (DynamicArray<Float>) bPoints.get(j + i * pntsU).getFieldValue("vec");
    303                 if (fixUpAxis) {
    304                     uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue()));
    305                 } else {
    306                     uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue()));
    307                 }
    308             }
    309             if ((flagU & 0x01) != 0) {
    310                 for (int k = 0; k < orderU - 1; ++k) {
    311                     uControlPoints.add(uControlPoints.get(k));
    312                 }
    313             }
    314             controlPoints.add(uControlPoints);
    315         }
    316         if ((flagV & 0x01) != 0) {
    317             for (int k = 0; k < orderV - 1; ++k) {
    318                 controlPoints.add(controlPoints.get(k));
    319             }
    320         }
    321 
    322         int resolu = ((Number) nurb.getFieldValue("resolu")).intValue() + 1;
    323         List<Geometry> result;
    324         if (knots[1] == null) {//creating the curve
    325             Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]);
    326             Curve nurbCurve = new Curve(nurbSpline, resolu);
    327             if (bevelObject != null) {
    328                 result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, blenderContext);//TODO: smooth
    329             } else {
    330                 result = new ArrayList<Geometry>(1);
    331                 Geometry nurbGeometry = new Geometry("", nurbCurve);
    332                 result.add(nurbGeometry);
    333             }
    334         } else {//creating the nurb surface
    335             int resolv = ((Number) nurb.getFieldValue("resolv")).intValue() + 1;
    336             Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV);
    337             Geometry nurbGeometry = new Geometry("", nurbSurface);
    338             result = new ArrayList<Geometry>(1);
    339             result.add(nurbGeometry);
    340         }
    341         return result;
    342     }
    343 
    344     /**
    345      * This method returns the taper scale that should be applied to the object.
    346      * @param taperPoints
    347      *            the taper points
    348      * @param taperLength
    349      *            the taper curve length
    350      * @param percent
    351      *            the percent of way along the whole taper curve
    352      * @param store
    353      *            the vector where the result will be stored
    354      */
    355     protected float getTaperScale(float[] taperPoints, float taperLength, float percent) {
    356         float length = taperLength * percent;
    357         float currentLength = 0;
    358         Vector3f p = new Vector3f();
    359         int i;
    360         for (i = 0; i < taperPoints.length - 6 && currentLength < length; i += 3) {
    361             p.set(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]);
    362             p.subtractLocal(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]);
    363             currentLength += p.length();
    364         }
    365         currentLength -= p.length();
    366         float leftLength = length - currentLength;
    367         float percentOnSegment = p.length() == 0 ? 0 : leftLength / p.length();
    368         Vector3f store = FastMath.interpolateLinear(percentOnSegment,
    369                 new Vector3f(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]),
    370                 new Vector3f(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]));
    371         return store.y;
    372     }
    373 
    374     /**
    375      * This method applies bevel and taper objects to the curve.
    376      * @param curve
    377      *            the curve we apply the objects to
    378      * @param bevelObject
    379      *            the bevel object
    380      * @param taperObject
    381      *            the taper object
    382      * @param smooth
    383      * 			  the smooth flag
    384      * @param blenderContext
    385      *            the blender context
    386      * @return a list of geometries representing the beveled and/or tapered curve
    387      */
    388     protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject,
    389             boolean smooth, BlenderContext blenderContext) {
    390         float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position));
    391         MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
    392         float curveLength = curve.getLength();
    393         //TODO: use the smooth var
    394 
    395         //taper data
    396         float[] taperPoints = null;
    397         float taperLength = 0;
    398         if (taperObject != null) {
    399             taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position));
    400             taperLength = taperObject.getLength();
    401         }
    402 
    403         //several objects can be allocated only once
    404         Vector3f p = new Vector3f();
    405         Vector3f z = new Vector3f(0, 0, 1);
    406         Vector3f negativeY = new Vector3f(0, -1, 0);
    407         Matrix4f m = new Matrix4f();
    408         float lengthAlongCurve = 0, taperScale = 1.0f;
    409         Quaternion planeRotation = new Quaternion();
    410         Quaternion zRotation = new Quaternion();
    411         float[] temp = new float[]{0, 0, 0, 1};
    412         Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//normalMap merges normals of faces that will be rendered smooth
    413 
    414         FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()];
    415         FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()];
    416         IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()];
    417         for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
    418             Mesh mesh = bevelObject.get(geomIndex).getMesh();
    419             FloatBuffer positions = mesh.getFloatBuffer(Type.Position);
    420             float[] vertices = BufferUtils.getFloatArray(positions);
    421 
    422             for (int i = 0; i < curvePoints.length; i += 3) {
    423                 p.set(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]);
    424                 Vector3f v;
    425                 if (i == 0) {
    426                     v = new Vector3f(curvePoints[3] - p.x, curvePoints[4] - p.y, curvePoints[5] - p.z);
    427                 } else if (i + 3 >= curvePoints.length) {
    428                     v = new Vector3f(p.x - curvePoints[i - 3], p.y - curvePoints[i - 2], p.z - curvePoints[i - 1]);
    429                     lengthAlongCurve += v.length();
    430                 } else {
    431                     v = new Vector3f(curvePoints[i + 3] - curvePoints[i - 3],
    432                             curvePoints[i + 4] - curvePoints[i - 2],
    433                             curvePoints[i + 5] - curvePoints[i - 1]);
    434                     lengthAlongCurve += new Vector3f(curvePoints[i + 3] - p.x, curvePoints[i + 4] - p.y, curvePoints[i + 5] - p.z).length();
    435                 }
    436                 v.normalizeLocal();
    437 
    438                 float angle = FastMath.acos(v.dot(z));
    439                 v.crossLocal(z).normalizeLocal();//v is the rotation axis now
    440                 planeRotation.fromAngleAxis(angle, v);
    441 
    442                 Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal();
    443                 float zAxisRotationAngle = FastMath.acos(negativeY.dot(v));
    444                 zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector);
    445 
    446                 //point transformation matrix
    447                 if (taperPoints != null) {
    448                     taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength);
    449                 }
    450                 m.set(Matrix4f.IDENTITY);
    451                 m.setRotationQuaternion(planeRotation.multLocal(zRotation));
    452                 m.setTranslation(p);
    453 
    454                 //these vertices need to be thrown on XY plane
    455                 //and moved to the origin of [p1.x, p1.y] on the plane
    456                 Vector3f[] verts = new Vector3f[vertices.length / 3];
    457                 for (int j = 0; j < verts.length; ++j) {
    458                     temp[0] = vertices[j * 3] * taperScale;
    459                     temp[1] = vertices[j * 3 + 1] * taperScale;
    460                     temp[2] = 0;
    461                     m.mult(temp);//the result is stored in the array
    462                     if (fixUpAxis) {//TODO: not the other way ???
    463                         verts[j] = new Vector3f(temp[0], temp[1], temp[2]);
    464                     } else {
    465                         verts[j] = new Vector3f(temp[0], temp[2], -temp[1]);
    466                     }
    467                 }
    468                 if (vertexBuffers[geomIndex] == null) {
    469                     vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(verts.length * curvePoints.length);
    470                 }
    471                 FloatBuffer buffer = BufferUtils.createFloatBuffer(verts);
    472                 vertexBuffers[geomIndex].put(buffer);
    473 
    474                 //adding indexes
    475                 IntBuffer indexBuffer = indexBuffers[geomIndex];
    476                 if (indexBuffer == null) {
    477                     //the amount of faces in the final mesh is the amount of edges in the bevel curve
    478                     //(which is less by 1 than its number of vertices)
    479                     //multiplied by 2 (because each edge has two faces assigned on both sides)
    480                     //and multiplied by the amount of bevel curve repeats which is equal to the amount of vertices on the target curve
    481                     //finally we need to subtract the bevel edges amount 2 times because the border edges have only one face attached
    482                     //and at last multiply everything by 3 because each face needs 3 indexes to be described
    483                     int bevelCurveEdgesAmount = verts.length - 1;
    484                     indexBuffer = BufferUtils.createIntBuffer(((bevelCurveEdgesAmount << 1) * curvePoints.length - bevelCurveEdgesAmount << 1) * 3);
    485                     indexBuffers[geomIndex] = indexBuffer;
    486                 }
    487                 int pointOffset = i / 3 * verts.length;
    488                 if (i + 3 < curvePoints.length) {
    489                     for (int index = 0; index < verts.length - 1; ++index) {
    490                         indexBuffer.put(index + pointOffset);
    491                         indexBuffer.put(index + pointOffset + 1);
    492                         indexBuffer.put(verts.length + index + pointOffset);
    493                         indexBuffer.put(verts.length + index + pointOffset);
    494                         indexBuffer.put(index + pointOffset + 1);
    495                         indexBuffer.put(verts.length + index + pointOffset + 1);
    496                     }
    497                 }
    498             }
    499         }
    500 
    501         //calculating the normals
    502         for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
    503             Vector3f[] allVerts = BufferUtils.getVector3Array(vertexBuffers[geomIndex]);
    504             int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]);
    505             for (int i = 0; i < allIndices.length - 3; i += 3) {
    506                 Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
    507                 meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
    508             }
    509             if (normalBuffers[geomIndex] == null) {
    510                 normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3);
    511             }
    512             for (Vector3f v : allVerts) {
    513                 Vector3f n = normalMap.get(v);
    514                 normalBuffers[geomIndex].put(n.x);
    515                 normalBuffers[geomIndex].put(n.y);
    516                 normalBuffers[geomIndex].put(n.z);
    517             }
    518         }
    519 
    520         List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length);
    521         Float oneReferenceToCurveLength = new Float(curveLength);//its important for array modifier to use one reference here
    522         for (int i = 0; i < vertexBuffers.length; ++i) {
    523             Mesh mesh = new Mesh();
    524             mesh.setBuffer(Type.Position, 3, vertexBuffers[i]);
    525             mesh.setBuffer(Type.Index, 3, indexBuffers[i]);
    526             mesh.setBuffer(Type.Normal, 3, normalBuffers[i]);
    527             Geometry g = new Geometry("g" + i, mesh);
    528             g.setUserData("curveLength", oneReferenceToCurveLength);
    529             g.updateModelBound();
    530             result.add(g);
    531         }
    532 
    533         return result;
    534     }
    535 
    536     /**
    537      * This method loads the taper object.
    538      * @param taperStructure
    539      *            the taper structure
    540      * @param blenderContext
    541      *            the blender context
    542      * @return the taper object
    543      * @throws BlenderFileException
    544      */
    545     protected Curve loadTaperObject(Structure taperStructure, BlenderContext blenderContext) throws BlenderFileException {
    546         //reading nurbs
    547         List<Structure> nurbStructures = ((Structure) taperStructure.getFieldValue("nurb")).evaluateListBase(blenderContext);
    548         for (Structure nurb : nurbStructures) {
    549             Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
    550             if (pBezierTriple.isNotNull()) {
    551                 //creating the curve object
    552                 BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(blenderContext.getInputStream()), 3);
    553                 List<Vector3f> controlPoints = bezierCurve.getControlPoints();
    554                 //removing the first and last handles
    555                 controlPoints.remove(0);
    556                 controlPoints.remove(controlPoints.size() - 1);
    557 
    558                 //return the first taper curve that has more than 3 control points
    559                 if (controlPoints.size() > 3) {
    560                     Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);
    561                     int resolution = ((Number) taperStructure.getFieldValue("resolu")).intValue();
    562                     return new Curve(spline, resolution);
    563                 }
    564             }
    565         }
    566         return null;
    567     }
    568 
    569     /**
    570      * This method returns the translation of the curve. The UP axis is taken into account here.
    571      * @param curveStructure
    572      *            the curve structure
    573      * @return curve translation
    574      */
    575     @SuppressWarnings("unchecked")
    576     protected Vector3f getLoc(Structure curveStructure) {
    577         DynamicArray<Number> locArray = (DynamicArray<Number>) curveStructure.getFieldValue("loc");
    578         if (fixUpAxis) {
    579             return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue());
    580         } else {
    581             return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue());
    582         }
    583     }
    584 
    585     @Override
    586     public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
    587     	return true;
    588     }
    589 }