1/*
2 * Copyright (c) 2009-2010 jMonkeyEngine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32package com.jme3.scene.plugins.blender.meshes;
33
34import java.nio.ByteBuffer;
35import java.util.ArrayList;
36import java.util.HashMap;
37import java.util.LinkedList;
38import java.util.List;
39import java.util.Map;
40import java.util.Map.Entry;
41
42import com.jme3.asset.BlenderKey.FeaturesToLoad;
43import com.jme3.material.Material;
44import com.jme3.math.FastMath;
45import com.jme3.math.Vector2f;
46import com.jme3.math.Vector3f;
47import com.jme3.renderer.queue.RenderQueue.Bucket;
48import com.jme3.scene.Geometry;
49import com.jme3.scene.Mesh;
50import com.jme3.scene.VertexBuffer;
51import com.jme3.scene.VertexBuffer.Format;
52import com.jme3.scene.VertexBuffer.Type;
53import com.jme3.scene.VertexBuffer.Usage;
54import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
55import com.jme3.scene.plugins.blender.BlenderContext;
56import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
57import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
58import com.jme3.scene.plugins.blender.file.DynamicArray;
59import com.jme3.scene.plugins.blender.file.Pointer;
60import com.jme3.scene.plugins.blender.file.Structure;
61import com.jme3.scene.plugins.blender.materials.MaterialContext;
62import com.jme3.scene.plugins.blender.materials.MaterialHelper;
63import com.jme3.scene.plugins.blender.objects.Properties;
64import com.jme3.scene.plugins.blender.textures.TextureHelper;
65import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator;
66import com.jme3.texture.Texture;
67import com.jme3.util.BufferUtils;
68
69/**
70 * A class that is used in mesh calculations.
71 *
72 * @author Marcin Roguski (Kaelthas)
73 */
74public class MeshHelper extends AbstractBlenderHelper {
75
76    /**
77     * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
78     * versions.
79     *
80     * @param blenderVersion
81     *            the version read from the blend file
82     * @param fixUpAxis
83     *        a variable that indicates if the Y asxis is the UP axis or not
84     */
85    public MeshHelper(String blenderVersion, boolean fixUpAxis) {
86        super(blenderVersion,fixUpAxis);
87    }
88
89    /**
90     * This method reads converts the given structure into mesh. The given structure needs to be filled with the appropriate data.
91     *
92     * @param structure
93     *            the structure we read the mesh from
94     * @return the mesh feature
95     * @throws BlenderFileException
96     */
97    @SuppressWarnings("unchecked")
98    public List<Geometry> toMesh(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
99        List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(),
100                LoadedFeatureDataType.LOADED_FEATURE);
101        if (geometries != null) {
102            List<Geometry> copiedGeometries = new ArrayList<Geometry>(geometries.size());
103            for (Geometry geometry : geometries) {
104                copiedGeometries.add(geometry.clone());
105            }
106            return copiedGeometries;
107        }
108
109        // helpers
110        TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
111
112        // reading mesh data
113        String name = structure.getName();
114        MeshContext meshContext = new MeshContext();
115
116        // reading vertices
117        Vector3f[] vertices = this.getVertices(structure, blenderContext);
118        int verticesAmount = vertices.length;
119
120        // vertices Colors
121        List<byte[]> verticesColors = this.getVerticesColors(structure, blenderContext);
122
123        // reading faces
124        // the following map sorts faces by material number (because in jme Mesh can have only one material)
125        Map<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();
126        Pointer pMFace = (Pointer) structure.getFieldValue("mface");
127        List<Structure> mFaces = null;
128        if (pMFace.isNotNull()) {
129            mFaces = pMFace.fetchData(blenderContext.getInputStream());
130            if (mFaces == null || mFaces.size() == 0) {
131                return new ArrayList<Geometry>(0);
132            }
133        } else{
134        	mFaces = new ArrayList<Structure>(0);
135        }
136
137        Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
138        List<Vector2f> uvCoordinates = null;
139        List<Structure> mtFaces = null;
140
141        if (pMTFace.isNotNull()) {
142            mtFaces = pMTFace.fetchData(blenderContext.getInputStream());
143            int facesAmount = ((Number) structure.getFieldValue("totface")).intValue();
144            if (mtFaces.size() != facesAmount) {
145                throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");
146            }
147            uvCoordinates = new ArrayList<Vector2f>();
148        }
149
150        // normalMap merges normals of faces that will be rendered smooth
151        Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);
152
153        List<Vector3f> normalList = new ArrayList<Vector3f>();
154        List<Vector3f> vertexList = new ArrayList<Vector3f>();
155        // indicates if the material with the specified number should have a texture attached
156        Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();
157        // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
158        // positions (it simply tells which vertex is referenced where in the result list)
159        Map<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAmount);
160        int vertexColorIndex = 0;
161        for (int i = 0; i < mFaces.size(); ++i) {
162            Structure mFace = mFaces.get(i);
163            boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
164            DynamicArray<Number> uvs = null;
165            boolean materialWithoutTextures = false;
166            Pointer pImage = null;
167            if (mtFaces != null) {
168                Structure mtFace = mtFaces.get(i);
169                pImage = (Pointer) mtFace.getFieldValue("tpage");
170                materialWithoutTextures = pImage.isNull();
171                // uvs always must be added wheater we have texture or not
172                uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
173                uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
174                uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
175                uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
176            }
177            int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
178            Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);
179            List<Integer> indexList = meshesMap.get(materialNumber);
180            if (indexList == null) {
181                indexList = new ArrayList<Integer>();
182                meshesMap.put(materialNumber, indexList);
183            }
184
185            // attaching image to texture (face can have UV's and image whlie its material may have no texture attached)
186            if (pImage != null && pImage.isNotNull() && !materialNumberToTexture.containsKey(materialNumber)) {
187                Texture texture = textureHelper.getTextureFromImage(pImage.fetchData(blenderContext.getInputStream()).get(0),
188                        blenderContext);
189                if (texture != null) {
190                    materialNumberToTexture.put(materialNumber, texture);
191                }
192            }
193
194            int v1 = ((Number) mFace.getFieldValue("v1")).intValue();
195            int v2 = ((Number) mFace.getFieldValue("v2")).intValue();
196            int v3 = ((Number) mFace.getFieldValue("v3")).intValue();
197            int v4 = ((Number) mFace.getFieldValue("v4")).intValue();
198
199            Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);
200            this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);
201            normalList.add(normalMap.get(vertices[v1]));
202            normalList.add(normalMap.get(vertices[v2]));
203            normalList.add(normalMap.get(vertices[v3]));
204
205            this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
206            indexList.add(vertexList.size());
207            vertexList.add(vertices[v1]);
208
209            this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap);
210            indexList.add(vertexList.size());
211            vertexList.add(vertices[v2]);
212
213            this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
214            indexList.add(vertexList.size());
215            vertexList.add(vertices[v3]);
216
217            if (v4 > 0) {
218                if (uvs != null) {
219                    uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
220                    uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
221                    uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
222                }
223                this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
224                indexList.add(vertexList.size());
225                vertexList.add(vertices[v1]);
226
227                this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
228                indexList.add(vertexList.size());
229                vertexList.add(vertices[v3]);
230
231                this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap);
232                indexList.add(vertexList.size());
233                vertexList.add(vertices[v4]);
234
235                this.addNormal(n, normalMap, smooth, vertices[v4]);
236                normalList.add(normalMap.get(vertices[v1]));
237                normalList.add(normalMap.get(vertices[v3]));
238                normalList.add(normalMap.get(vertices[v4]));
239
240                if (verticesColors != null) {
241                    verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex));
242                    verticesColors.add(vertexColorIndex + 4, verticesColors.get(vertexColorIndex + 2));
243                }
244                vertexColorIndex += 6;
245            } else {
246                if (verticesColors != null) {
247                    verticesColors.remove(vertexColorIndex + 3);
248                    vertexColorIndex += 3;
249                }
250            }
251        }
252        meshContext.setVertexList(vertexList);
253        meshContext.setVertexReferenceMap(vertexReferenceMap);
254
255        Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
256
257        // reading vertices groups (from the parent)
258        Structure parent = blenderContext.peekParent();
259        Structure defbase = (Structure) parent.getFieldValue("defbase");
260        List<Structure> defs = defbase.evaluateListBase(blenderContext);
261        String[] verticesGroups = new String[defs.size()];
262        int defIndex = 0;
263        for (Structure def : defs) {
264            verticesGroups[defIndex++] = def.getFieldValue("name").toString();
265        }
266
267        // reading materials
268        MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
269        Material[] materials = null;
270        Material[] nonTexturedMaterials = null;
271        if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
272            materials = materialHelper.getMaterials(structure, blenderContext);
273            nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed
274        }
275
276        // creating the result meshes
277        geometries = new ArrayList<Geometry>(meshesMap.size());
278
279        VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
280        verticesBuffer.setupData(Usage.Stream, 3, Format.Float,
281                BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()])));
282
283        // initial vertex position (used with animation)
284        VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);
285        verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));
286
287        VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
288        normalsBuffer.setupData(Usage.Stream, 3, Format.Float, BufferUtils.createFloatBuffer(normals));
289
290        // initial normals position (used with animation)
291        VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
292        normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
293
294        VertexBuffer uvCoordsBuffer = null;
295        if (uvCoordinates != null) {
296            uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
297            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
298                    BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
299        }
300
301        //reading custom properties
302        Properties properties = this.loadProperties(structure, blenderContext);
303
304        // generating meshes
305        //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
306        ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors);
307        verticesAmount = vertexList.size();
308        for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
309            Mesh mesh = new Mesh();
310
311            // creating vertices indices for this mesh
312            List<Integer> indexList = meshEntry.getValue();
313            if(verticesAmount <= Short.MAX_VALUE) {
314            	short[] indices = new short[indexList.size()];
315                for (int i = 0; i < indexList.size(); ++i) {
316                    indices[i] = indexList.get(i).shortValue();
317                }
318                mesh.setBuffer(Type.Index, 1, indices);
319            } else {
320            	int[] indices = new int[indexList.size()];
321                for (int i = 0; i < indexList.size(); ++i) {
322                    indices[i] = indexList.get(i).intValue();
323                }
324                mesh.setBuffer(Type.Index, 1, indices);
325            }
326
327            mesh.setBuffer(verticesBuffer);
328            mesh.setBuffer(verticesBind);
329
330            // setting vertices colors
331            if (verticesColorsBuffer != null) {
332                mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);
333                mesh.getBuffer(Type.Color).setNormalized(true);
334            }
335
336            // setting faces' normals
337            mesh.setBuffer(normalsBuffer);
338            mesh.setBuffer(normalsBind);
339
340            // creating the result
341            Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
342            if (materials != null) {
343                int materialNumber = meshEntry.getKey().intValue();
344                Material material;
345                if (materialNumber >= 0) {
346                    material = materials[materialNumber];
347                    if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) {
348                        if (material.getMaterialDef().getAssetName().contains("Lighting")) {
349                            if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) {
350                                material = material.clone();
351                                material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE,
352                                        materialNumberToTexture.get(Integer.valueOf(materialNumber)));
353                            }
354                        } else {
355                            if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) {
356                                material = material.clone();
357                                material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR,
358                                        materialNumberToTexture.get(Integer.valueOf(materialNumber)));
359                            }
360                        }
361                    }
362                } else {
363                    materialNumber = -1 * (materialNumber + 1);
364                    if (nonTexturedMaterials[materialNumber] == null) {
365                        nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber],
366                                TextureHelper.TEX_IMAGE);
367                    }
368                    material = nonTexturedMaterials[materialNumber];
369                }
370                geometry.setMaterial(material);
371                if (material.isTransparent()) {
372                    geometry.setQueueBucket(Bucket.Transparent);
373                }
374            } else {
375                geometry.setMaterial(blenderContext.getDefaultMaterial());
376            }
377            if (properties != null && properties.getValue() != null) {
378                geometry.setUserData("properties", properties);
379            }
380            geometries.add(geometry);
381        }
382
383        //applying uvCoordinates for all the meshes
384        if (uvCoordsBuffer != null) {
385            for (Geometry geom : geometries) {
386                geom.getMesh().setBuffer(uvCoordsBuffer);
387            }
388        } else {
389            Map<Material, List<Geometry>> materialMap = new HashMap<Material, List<Geometry>>();
390            for (Geometry geom : geometries) {
391                Material material = geom.getMaterial();
392                List<Geometry> geomsWithCommonMaterial = materialMap.get(material);
393                if (geomsWithCommonMaterial == null) {
394                    geomsWithCommonMaterial = new ArrayList<Geometry>();
395                    materialMap.put(material, geomsWithCommonMaterial);
396                }
397                geomsWithCommonMaterial.add(geom);
398
399            }
400            for (Entry<Material, List<Geometry>> entry : materialMap.entrySet()) {
401                MaterialContext materialContext = blenderContext.getMaterialContext(entry.getKey());
402                if (materialContext != null && materialContext.getTexturesCount() > 0) {
403                    VertexBuffer coords = UVCoordinatesGenerator.generateUVCoordinates(materialContext.getUvCoordinatesType(),
404                            materialContext.getProjectionType(), materialContext.getTextureDimension(),
405                            materialContext.getProjection(0), entry.getValue());
406                    //setting the coordinates inside the mesh context
407                    for (Geometry geometry : entry.getValue()) {
408                        meshContext.addUVCoordinates(geometry, coords);
409                    }
410                }
411            }
412        }
413
414        // if there are multiple materials used, extract the shared
415        // vertex data
416        if (geometries.size() > 1){
417            // extract from itself
418            for (Geometry geom : geometries){
419                geom.getMesh().extractVertexData(geom.getMesh());
420            }
421        }
422
423        blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);
424        blenderContext.setMeshContext(structure.getOldMemoryAddress(), meshContext);
425        return geometries;
426    }
427
428    /**
429     * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
430     *
431     * @param normalToAdd
432     *            a normal to be added
433     * @param normalMap
434     *            merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
435     * @param smooth
436     *            the variable that indicates wheather to merge normals (creating the smooth mesh) or not
437     * @param vertices
438     *            a list of vertices read from the blender file
439     */
440    public void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
441        for (Vector3f v : vertices) {
442            Vector3f n = normalMap.get(v);
443            if (!smooth || n == null) {
444                normalMap.put(v, normalToAdd.clone());
445            } else {
446                n.addLocal(normalToAdd).normalizeLocal();
447            }
448        }
449    }
450
451    /**
452     * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created
453     * to tell where the basic vertices are referenced in the result vertex lists. The key of the map is the basic vertex index, and its key
454     * - the reference indices list.
455     *
456     * @param basicVertexIndex
457     *            the index of the vertex from its basic table
458     * @param resultIndex
459     *            the index of the vertex in its result vertex list
460     * @param vertexReferenceMap
461     *            the reference map
462     */
463    protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {
464        List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
465        if (referenceList == null) {
466            referenceList = new ArrayList<Integer>();
467            vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);
468        }
469        referenceList.add(Integer.valueOf(resultIndex));
470    }
471
472    /**
473     * This method returns the vertices colors. Each vertex is stored in byte[4] array.
474     *
475     * @param meshStructure
476     *            the structure containing the mesh data
477     * @param blenderContext
478     *            the blender context
479     * @return a list of vertices colors, each color belongs to a single vertex
480     * @throws BlenderFileException
481     *             this exception is thrown when the blend file structure is somehow invalid or corrupted
482     */
483    public List<byte[]> getVerticesColors(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
484        Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol");
485        List<byte[]> verticesColors = null;
486        List<Structure> mCol = null;
487        if (pMCol.isNotNull()) {
488            verticesColors = new LinkedList<byte[]>();
489            mCol = pMCol.fetchData(blenderContext.getInputStream());
490            for (Structure color : mCol) {
491                byte r = ((Number)color.getFieldValue("r")).byteValue();
492                byte g = ((Number)color.getFieldValue("g")).byteValue();
493                byte b = ((Number)color.getFieldValue("b")).byteValue();
494                byte a = ((Number)color.getFieldValue("a")).byteValue();
495                verticesColors.add(new byte[]{b, g, r, a});
496            }
497        }
498        return verticesColors;
499    }
500
501    /**
502     * This method returns the vertices.
503     *
504     * @param meshStructure
505     *            the structure containing the mesh data
506     * @param blenderContext
507     *            the blender context
508     * @return a list of vertices colors, each color belongs to a single vertex
509     * @throws BlenderFileException
510     *             this exception is thrown when the blend file structure is somehow invalid or corrupted
511     */
512    @SuppressWarnings("unchecked")
513    private Vector3f[] getVertices(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
514        int verticesAmount = ((Number) meshStructure.getFieldValue("totvert")).intValue();
515        Vector3f[] vertices = new Vector3f[verticesAmount];
516        if (verticesAmount == 0) {
517            return vertices;
518        }
519
520        Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");
521        List<Structure> mVerts = pMVert.fetchData(blenderContext.getInputStream());
522        if(this.fixUpAxis) {
523        	for (int i = 0; i < verticesAmount; ++i) {
524                DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
525                vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue());
526            }
527        } else {
528        	for (int i = 0; i < verticesAmount; ++i) {
529                DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
530                vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());
531            }
532        }
533        return vertices;
534    }
535
536    @Override
537    public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
538        return true;
539    }
540}
541