1package com.jme3.scene.plugins.blender.curves;
2
3import com.jme3.material.Material;
4import com.jme3.material.RenderState.FaceCullMode;
5import com.jme3.math.Spline.SplineType;
6import com.jme3.math.*;
7import com.jme3.scene.Geometry;
8import com.jme3.scene.Mesh;
9import com.jme3.scene.VertexBuffer.Type;
10import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
11import com.jme3.scene.plugins.blender.BlenderContext;
12import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
13import com.jme3.scene.plugins.blender.file.*;
14import com.jme3.scene.plugins.blender.materials.MaterialHelper;
15import com.jme3.scene.plugins.blender.meshes.MeshHelper;
16import com.jme3.scene.plugins.blender.objects.Properties;
17import com.jme3.scene.shape.Curve;
18import com.jme3.scene.shape.Surface;
19import com.jme3.util.BufferUtils;
20import java.nio.FloatBuffer;
21import java.nio.IntBuffer;
22import java.util.ArrayList;
23import java.util.HashMap;
24import java.util.List;
25import java.util.Map;
26import java.util.Map.Entry;
27import java.util.logging.Logger;
28
29/**
30 * A class that is used in mesh calculations.
31 * @author Marcin Roguski
32 */
33public 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}