Преглед изворни кода

Feature: added support for line and point type of meshes.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10914 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl пре 11 година
родитељ
комит
dd8b8fbd5f

+ 18 - 6
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java

@@ -1,5 +1,6 @@
 package com.jme3.scene.plugins.blender.meshes;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -14,7 +15,7 @@ import com.jme3.scene.Geometry;
  */
 public class MeshContext {
     /** A map between material index and the geometry. */
-    private Map<Integer, Geometry>                    geometries       = new HashMap<Integer, Geometry>();
+    private Map<Integer, List<Geometry>>              geometries = new HashMap<Integer, List<Geometry>>();
     /** The vertex reference map. */
     private Map<Integer, Map<Integer, List<Integer>>> vertexReferenceMap;
 
@@ -26,7 +27,12 @@ public class MeshContext {
      *            the geometry
      */
     public void putGeometry(Integer materialIndex, Geometry geometry) {
-        geometries.put(materialIndex, geometry);
+        List<Geometry> geomList = geometries.get(materialIndex);
+        if (geomList == null) {
+            geomList = new ArrayList<Geometry>();
+            geometries.put(materialIndex, geomList);
+        }
+        geomList.add(geometry);
     }
 
     /**
@@ -35,7 +41,11 @@ public class MeshContext {
      * @return vertices amount that is used by mesh with the specified material
      */
     public int getVertexCount(int materialIndex) {
-        return geometries.get(materialIndex).getVertexCount();
+        int result = 0;
+        for (Geometry geometry : geometries.get(materialIndex)) {
+            result += geometry.getVertexCount();
+        }
+        return result;
     }
 
     /**
@@ -47,9 +57,11 @@ public class MeshContext {
      *             this exception is thrown when no material is found for the specified geometry
      */
     public int getMaterialIndex(Geometry geometry) {
-        for (Entry<Integer, Geometry> entry : geometries.entrySet()) {
-            if (entry.getValue().equals(geometry)) {
-                return entry.getKey();
+        for (Entry<Integer, List<Geometry>> entry : geometries.entrySet()) {
+            for (Geometry g : entry.getValue()) {
+                if (g.equals(geometry)) {
+                    return entry.getKey();
+                }
             }
         }
         throw new IllegalStateException("Cannot find material index for the given geometry: " + geometry);

+ 28 - 8
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java

@@ -40,9 +40,12 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
 import com.jme3.math.Vector2f;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Usage;
@@ -68,10 +71,12 @@ public class MeshHelper extends AbstractBlenderHelper {
     private static final Logger LOGGER                   = Logger.getLogger(MeshHelper.class.getName());
 
     /** A type of UV data layer in traditional faced mesh (triangles or quads). */
-    public static final int                          UV_DATA_LAYER_TYPE_FMESH = 5;
+    public static final int     UV_DATA_LAYER_TYPE_FMESH = 5;
     /** A type of UV data layer in bmesh type. */
-    public static final int                          UV_DATA_LAYER_TYPE_BMESH = 16;
-    
+    public static final int     UV_DATA_LAYER_TYPE_BMESH = 16;
+    /** A material used for single lines and points. */
+    private Material            blackUnshadedMaterial;
+
     /**
      * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
      * versions.
@@ -143,9 +148,9 @@ public class MeshHelper extends AbstractBlenderHelper {
         LOGGER.fine("Generating meshes.");
         Map<Integer, List<Mesh>> meshes = meshBuilder.buildMeshes();
         geometries = new ArrayList<Geometry>(meshes.size());
-        for(Entry<Integer, List<Mesh>> meshEntry : meshes.entrySet()) {
+        for (Entry<Integer, List<Mesh>> meshEntry : meshes.entrySet()) {
             int materialIndex = meshEntry.getKey();
-            for(Mesh mesh : meshEntry.getValue()) {
+            for (Mesh mesh : meshEntry.getValue()) {
                 LOGGER.fine("Preparing the result part.");
                 Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
                 if (properties != null && properties.getValue() != null) {
@@ -164,7 +169,9 @@ public class MeshHelper extends AbstractBlenderHelper {
         if (materials != null) {
             for (Geometry geometry : geometries) {
                 int materialNumber = meshContext.getMaterialIndex(geometry);
-                if (materials[materialNumber] != null) {
+                if (materialNumber < 0) {
+                    geometry.setMaterial(this.getBlackUnshadedMaterial(blenderContext));
+                } else if (materials[materialNumber] != null) {
                     LinkedHashMap<String, List<Vector2f>> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber);
                     MaterialContext materialContext = materials[materialNumber];
                     materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), uvCoordinates, blenderContext);
@@ -190,7 +197,12 @@ public class MeshHelper extends AbstractBlenderHelper {
             }
 
             for (Geometry geometry : geometries) {
-                geometry.setMaterial(blenderContext.getDefaultMaterial());
+                Mode mode = geometry.getMesh().getMode();
+                if (mode != Mode.Triangles && mode != Mode.TriangleFan && mode != Mode.TriangleStrip) {
+                    geometry.setMaterial(this.getBlackUnshadedMaterial(blenderContext));
+                } else {
+                    geometry.setMaterial(blenderContext.getDefaultMaterial());
+                }
                 if (uvCoordsBuffer != null) {
                     for (VertexBuffer buffer : uvCoordsBuffer) {
                         geometry.getMesh().setBuffer(buffer);
@@ -201,7 +213,7 @@ public class MeshHelper extends AbstractBlenderHelper {
 
         return geometries;
     }
-    
+
     /**
      * Tells if the given mesh structure supports BMesh.
      * 
@@ -214,4 +226,12 @@ public class MeshHelper extends AbstractBlenderHelper {
         Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
         return pMLoop != null && pMPoly != null && pMLoop.isNotNull() && pMPoly.isNotNull();
     }
+
+    private synchronized Material getBlackUnshadedMaterial(BlenderContext blenderContext) {
+        if (blackUnshadedMaterial == null) {
+            blackUnshadedMaterial = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+            blackUnshadedMaterial.setColor("Color", ColorRGBA.Black);
+        }
+        return blackUnshadedMaterial;
+    }
 }

+ 14 - 13
engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/FaceMeshBuilder.java

@@ -32,7 +32,7 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class FaceMeshBuilder {
-    private static final Logger                       LOGGER                   = Logger.getLogger(FaceMeshBuilder.class.getName());
+    private static final Logger                       LOGGER           = Logger.getLogger(FaceMeshBuilder.class.getName());
 
     /** An array of reference vertices. */
     private Vector3f[][]                              verticesAndNormals;
@@ -46,15 +46,15 @@ import com.jme3.util.BufferUtils;
      */
     private Map<Integer, Map<Integer, List<Integer>>> globalVertexReferenceMap;
     /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<Vector3f>>              normalMap                = new HashMap<Integer, List<Vector3f>>();
+    private Map<Integer, List<Vector3f>>              normalMap        = new HashMap<Integer, List<Vector3f>>();
     /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<Vector3f>>              vertexMap                = new HashMap<Integer, List<Vector3f>>();
+    private Map<Integer, List<Vector3f>>              vertexMap        = new HashMap<Integer, List<Vector3f>>();
     /** The following map sorts vertices colors by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<byte[]>>                vertexColorsMap          = new HashMap<Integer, List<byte[]>>();
+    private Map<Integer, List<byte[]>>                vertexColorsMap  = new HashMap<Integer, List<byte[]>>();
     /** The following map sorts indexes by material number (because in jme Mesh can have only one material). */
-    private Map<Integer, List<Integer>>               indexMap                 = new HashMap<Integer, List<Integer>>();
+    private Map<Integer, List<Integer>>               indexMap         = new HashMap<Integer, List<Integer>>();
     /** A collection of user defined UV coordinates (one mesh can have more than one such mappings). */
-    private UserUVCollection                          userUVCollection         = new UserUVCollection();
+    private UserUVCollection                          userUVCollection = new UserUVCollection();
 
     /**
      * Constructor. Stores the given array (not copying it).
@@ -74,7 +74,7 @@ import com.jme3.util.BufferUtils;
     public void readMesh(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
         verticesColors = this.getVerticesColors(structure, blenderContext);
         MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
-        
+
         if (meshHelper.isBMeshCompatible(structure)) {
             this.readBMesh(structure, blenderContext);
         } else {
@@ -91,7 +91,7 @@ import com.jme3.util.BufferUtils;
 
         for (Entry<Integer, List<Integer>> meshEntry : indexMap.entrySet()) {
             int materialIndex = meshEntry.getKey();
-            // key is the material index (or -1 if the material has no texture)
+            // key is the material index
             // value is a list of vertex indices
             Mesh mesh = new Mesh();
 
@@ -148,10 +148,9 @@ import com.jme3.util.BufferUtils;
         LOGGER.fine("Reading BMesh.");
         Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop");
         Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
-        Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
         Map<String, Vector2f[]> uvCoordinatesForFace = new HashMap<String, Vector2f[]>();
 
-        if (pMPoly.isNotNull() && pMLoop.isNotNull() && pMEdge.isNotNull()) {
+        if (pMPoly.isNotNull() && pMLoop.isNotNull()) {
             Map<String, List<Vector2f>> uvs = this.loadUVCoordinates(meshStructure, true, blenderContext);
             List<Structure> polys = pMPoly.fetchData(blenderContext.getInputStream());
             List<Structure> loops = pMLoop.fetchData(blenderContext.getInputStream());
@@ -172,7 +171,7 @@ import com.jme3.util.BufferUtils;
                     int v1 = vertexIndexes[0];
                     int v2 = vertexIndexes[i + 1];
                     int v3 = vertexIndexes[i + 2];
-                    if(vertexColorIndex != null) {
+                    if (vertexColorIndex != null) {
                         vertexColorIndex[0] = loopStart;
                         vertexColorIndex[1] = loopStart + i + 1;
                         vertexColorIndex[2] = loopStart + i + 2;
@@ -237,7 +236,7 @@ import com.jme3.util.BufferUtils;
                 int v2 = ((Number) mFace.getFieldValue("v2")).intValue();
                 int v3 = ((Number) mFace.getFieldValue("v3")).intValue();
                 int v4 = ((Number) mFace.getFieldValue("v4")).intValue();
-                if(vertexColorIndex != null) {
+                if (vertexColorIndex != null) {
                     vertexColorIndex[0] = i * 4;
                     vertexColorIndex[1] = i * 4 + 1;
                     vertexColorIndex[2] = i * 4 + 2;
@@ -256,7 +255,7 @@ import com.jme3.util.BufferUtils;
                             uvCoordinatesForFace.put(entry.getKey(), uvCoordsForASingleFace);
                         }
                     }
-                    if(vertexColorIndex != null) {
+                    if (vertexColorIndex != null) {
                         vertexColorIndex[0] = i * 4;
                         vertexColorIndex[1] = i * 4 + 2;
                         vertexColorIndex[2] = i * 4 + 3;
@@ -282,6 +281,8 @@ import com.jme3.util.BufferUtils;
      *            the material number for this face
      * @param uvsForFace
      *            a 3-element array of vertices UV coordinates mapped to the UV's set name
+     * @param vertexColorIndex
+     *            a table of 3 elements that indicates the verts' colors indexes
      */
     private void appendFace(int v1, int v2, int v3, boolean smooth, int materialNumber, Map<String, Vector2f[]> uvsForFace, int[] vertexColorIndex) {
         if (uvsForFace != null && uvsForFace.size() > 0) {

+ 160 - 2
engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/LineMeshBuilder.java

@@ -1,5 +1,163 @@
 package com.jme3.scene.plugins.blender.meshes.builders;
 
-/*package*/ class LineMeshBuilder {
-  //TODO: this will be implemented soon
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.util.BufferUtils;
+
+/**
+ * A builder that creates a lines mesh. The result is made of lines that do not belong to any face.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class LineMeshBuilder {
+    private static final Logger LOGGER                = Logger.getLogger(LineMeshBuilder.class.getName());
+
+    private static final int    EDGE_NOT_IN_FACE_FLAG = 0x80;
+
+    /** An array of reference vertices. */
+    private Vector3f[][]        verticesAndNormals;
+    /** The vertices of the mesh. */
+    private List<Vector3f>      vertices              = new ArrayList<Vector3f>();
+    /** The normals of the mesh. */
+    private List<Vector3f>      normals              = new ArrayList<Vector3f>();
+    
+    /**
+     * This map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
+     * positions (it simply tells which vertex is referenced where in the result list).
+     */
+    private Map<Integer, List<Integer>> globalVertexReferenceMap;
+    
+    /**
+     * Constructor. Stores the given array (not copying it).
+     * The second argument describes if the model uses generated textures. If yes then no vertex amount optimisation is applied.
+     * The amount of vertices is always faceCount * 3.
+     * @param verticesAndNormals
+     *            the reference vertices and normals array
+     */
+    public LineMeshBuilder(Vector3f[][] verticesAndNormals) {
+        this.verticesAndNormals = verticesAndNormals;
+        globalVertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAndNormals.length);
+    }
+
+    /**
+     * The method reads the mesh. It loads only edges that are marked as not belonging to any face in their flag field.
+     * @param meshStructure
+     *            the mesh structure
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception thrown when reading from the blend file fails
+     */
+    public void readMesh(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.fine("Reading line mesh.");
+        Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
+
+        if (pMEdge.isNotNull()) {
+            List<Structure> edges = pMEdge.fetchData(blenderContext.getInputStream());
+            int vertexIndex = 0;//vertex index in the result mesh
+            for (Structure edge : edges) {
+                int flag = ((Number) edge.getFieldValue("flag")).intValue();
+                if ((flag & EDGE_NOT_IN_FACE_FLAG) != 0) {
+                    int v1 = ((Number) edge.getFieldValue("v1")).intValue();
+                    int v2 = ((Number) edge.getFieldValue("v2")).intValue();
+                    
+                    vertices.add(verticesAndNormals[v1][0]);
+                    normals.add(verticesAndNormals[v1][1]);
+                    this.appendVertexReference(v1, vertexIndex++, globalVertexReferenceMap);
+                    
+                    vertices.add(verticesAndNormals[v2][0]);
+                    normals.add(verticesAndNormals[v2][1]);
+                    this.appendVertexReference(v2, vertexIndex++, globalVertexReferenceMap);
+                }
+            }
+        }
+    }
+
+    /**
+     * Builds the meshes.
+     * @return a map between material index and the mesh
+     */
+    public Map<Integer, Mesh> buildMeshes() {
+        LOGGER.fine("Building line mesh.");
+        Map<Integer, Mesh> result = new HashMap<Integer, Mesh>(1);
+        if (vertices.size() > 0) {
+            Mesh mesh = new Mesh();
+            mesh.setMode(Mode.Lines);
+
+            LOGGER.fine("Creating indices buffer.");
+            if (vertices.size() <= Short.MAX_VALUE) {
+                short[] indices = new short[vertices.size()];
+                for (int i = 0; i < vertices.size(); ++i) {
+                    indices[i] = (short) i;
+                }
+                mesh.setBuffer(Type.Index, 1, indices);
+            } else {
+                int[] indices = new int[vertices.size()];
+                for (int i = 0; i < vertices.size(); ++i) {
+                    indices[i] = i;
+                }
+                mesh.setBuffer(Type.Index, 1, indices);
+            }
+
+            LOGGER.fine("Creating vertices buffer.");
+            VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
+            verticesBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(vertices.toArray(new Vector3f[vertices.size()])));
+            mesh.setBuffer(verticesBuffer);
+
+            LOGGER.fine("Creating normals buffer (in case of lines it is required if skeleton is applied).");
+            VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
+            normalsBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(normals.toArray(new Vector3f[normals.size()])));
+            mesh.setBuffer(normalsBuffer);
+            
+            result.put(-1, mesh);
+        }
+        return result;
+    }
+
+    /**
+     * @return <b>true</b> if the mesh has no vertices and <b>false</b> otherwise
+     */
+    public boolean isEmpty() {
+        return vertices == null;
+    }
+    
+    public Map<Integer, List<Integer>> getGlobalVertexReferenceMap() {
+        return globalVertexReferenceMap;
+    }
+    
+    /**
+     * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created
+     * 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
+     * - the reference indices list.
+     * 
+     * @param basicVertexIndex
+     *            the index of the vertex from its basic table
+     * @param resultIndex
+     *            the index of the vertex in its result vertex list
+     * @param vertexReferenceMap
+     *            the reference map
+     */
+    private void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {
+        List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
+        if (referenceList == null) {
+            referenceList = new ArrayList<Integer>();
+            vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);
+        }
+        referenceList.add(Integer.valueOf(resultIndex));
+    }
 }

+ 53 - 19
engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/MeshBuilder.java

@@ -18,44 +18,78 @@ import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.scene.plugins.blender.materials.MaterialContext;
 
 public class MeshBuilder {
-    private boolean fixUpAxis;
-    //TODO: these will be added soon
-//    private PointMeshBuilder pointMeshBuilder;
-//    private LineMeshBuilder lineMeshBuilder;
-    private FaceMeshBuilder faceMeshBuilder;
+    private boolean          fixUpAxis;
+    private PointMeshBuilder pointMeshBuilder;
+    private LineMeshBuilder  lineMeshBuilder;
+    private FaceMeshBuilder  faceMeshBuilder;
+    /**
+     * This map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
+     * positions (it simply tells which vertex is referenced where in the result list).
+     */
+    private Map<Integer, Map<Integer, List<Integer>>> globalVertexReferenceMap = new HashMap<Integer, Map<Integer,List<Integer>>>();
     
     public MeshBuilder(Structure meshStructure, MaterialContext[] materials, BlenderContext blenderContext) throws BlenderFileException {
         fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
         Vector3f[][] verticesAndNormals = this.getVerticesAndNormals(meshStructure, blenderContext);
-        boolean generatedTexturesPresent = this.areGeneratedTexturesPresent(materials);
-        
-        faceMeshBuilder = new FaceMeshBuilder(verticesAndNormals, generatedTexturesPresent);
+
+        faceMeshBuilder = new FaceMeshBuilder(verticesAndNormals, this.areGeneratedTexturesPresent(materials));
         faceMeshBuilder.readMesh(meshStructure, blenderContext);
+        lineMeshBuilder = new LineMeshBuilder(verticesAndNormals);
+        lineMeshBuilder.readMesh(meshStructure, blenderContext);
+        pointMeshBuilder = new PointMeshBuilder(verticesAndNormals);
+        pointMeshBuilder.readMesh(meshStructure, blenderContext);
     }
-    
+
     public Map<Integer, List<Mesh>> buildMeshes() {
         Map<Integer, List<Mesh>> result = new HashMap<Integer, List<Mesh>>();
-        
+
         Map<Integer, Mesh> meshes = faceMeshBuilder.buildMeshes();
-        for(Entry<Integer, Mesh> entry : meshes.entrySet()) {
+        for (Entry<Integer, Mesh> entry : meshes.entrySet()) {
             List<Mesh> meshList = new ArrayList<Mesh>();
             meshList.add(entry.getValue());
             result.put(entry.getKey(), meshList);
         }
+
+        meshes = lineMeshBuilder.buildMeshes();
+        for (Entry<Integer, Mesh> entry : meshes.entrySet()) {
+            List<Mesh> meshList = result.get(entry.getKey());
+            if (meshList == null) {
+                meshList = new ArrayList<Mesh>();
+                result.put(entry.getKey(), meshList);
+            }
+            meshList.add(entry.getValue());
+        }
+
+        meshes = pointMeshBuilder.buildMeshes();
+        for (Entry<Integer, Mesh> entry : meshes.entrySet()) {
+            List<Mesh> meshList = result.get(entry.getKey());
+            if (meshList == null) {
+                meshList = new ArrayList<Mesh>();
+                result.put(entry.getKey(), meshList);
+            }
+            meshList.add(entry.getValue());
+        }
+
+        globalVertexReferenceMap.putAll(faceMeshBuilder.getVertexReferenceMap());
+        globalVertexReferenceMap.put(-1, lineMeshBuilder.getGlobalVertexReferenceMap());
+        globalVertexReferenceMap.get(-1).putAll(pointMeshBuilder.getGlobalVertexReferenceMap());
         return result;
     }
-    
+
+    /**
+     * @return <b>true</b> if the mesh has no vertices and <b>false</b> otherwise
+     */
     public boolean isEmpty() {
-        return faceMeshBuilder.isEmpty();
+        return faceMeshBuilder.isEmpty() && lineMeshBuilder.isEmpty() && pointMeshBuilder.isEmpty();
     }
-    
+
     /**
      * @return a map that maps vertex index from reference array to its indices in the result list
      */
     public Map<Integer, Map<Integer, List<Integer>>> getVertexReferenceMap() {
-        return faceMeshBuilder.getVertexReferenceMap();
+        return globalVertexReferenceMap;
     }
-    
+
     /**
      * @param materialNumber
      *            the material number that is appied to the mesh
@@ -64,14 +98,14 @@ public class MeshBuilder {
     public LinkedHashMap<String, List<Vector2f>> getUVCoordinates(int materialNumber) {
         return faceMeshBuilder.getUVCoordinates(materialNumber);
     }
-    
+
     /**
      * @return indicates if the mesh has UV coordinates
      */
     public boolean hasUVCoordinates() {
         return faceMeshBuilder.hasUVCoordinates();
     }
-    
+
     /**
      * This method returns the vertices.
      * 
@@ -112,7 +146,7 @@ public class MeshBuilder {
         }
         return result;
     }
-    
+
     /**
      * @return <b>true</b> if the material has at least one generated component and <b>false</b> otherwise
      */

+ 171 - 2
engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/PointMeshBuilder.java

@@ -1,5 +1,174 @@
 package com.jme3.scene.plugins.blender.meshes.builders;
 
-/*package*/ class PointMeshBuilder {
-    //TODO: this will be implemented soon
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.util.BufferUtils;
+
+/**
+ * A builder that creates a points mesh. The result is made of points that do not belong to any edge and face.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class PointMeshBuilder {
+    private static final Logger LOGGER   = Logger.getLogger(PointMeshBuilder.class.getName());
+
+    /** An array of reference vertices. */
+    private Vector3f[][]        verticesAndNormals;
+    /** The vertices of the mesh. */
+    private List<Vector3f>      vertices = new ArrayList<Vector3f>();
+    /** The normals of the mesh. */
+    private List<Vector3f>      normals              = new ArrayList<Vector3f>();
+    
+    /**
+     * This map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
+     * positions (it simply tells which vertex is referenced where in the result list).
+     */
+    private Map<Integer, List<Integer>> globalVertexReferenceMap;
+    
+    /**
+     * Constructor. Stores the given array (not copying it).
+     * The second argument describes if the model uses generated textures. If yes then no vertex amount optimisation is applied.
+     * The amount of vertices is always faceCount * 3.
+     * @param verticesAndNormals
+     *            the reference vertices and normals array
+     */
+    public PointMeshBuilder(Vector3f[][] verticesAndNormals) {
+        this.verticesAndNormals = verticesAndNormals;
+        globalVertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAndNormals.length);
+    }
+
+    /**
+     * The method reads the mesh. Since blender does not store the information in the vertex itself whether it belongs
+     * anywhere or not, we need to check all vertices and use here only those that are not used.
+     * @param meshStructure
+     *            the mesh structure
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception thrown when reading from the blend file fails
+     */
+    public void readMesh(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.fine("Reading points mesh.");
+        Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
+
+        if (pMEdge.isNotNull()) {
+            int count = ((Number) meshStructure.getFieldValue("totvert")).intValue();
+            Set<Vector3f> usedVertices = new HashSet<Vector3f>(count);
+            List<Structure> edges = pMEdge.fetchData(blenderContext.getInputStream());
+
+            for (Structure edge : edges) {
+                int v1 = ((Number) edge.getFieldValue("v1")).intValue();
+                int v2 = ((Number) edge.getFieldValue("v2")).intValue();
+                usedVertices.add(verticesAndNormals[v1][0]);
+                usedVertices.add(verticesAndNormals[v2][0]);
+            }
+
+            if (usedVertices.size() < count) {
+                vertices = new ArrayList<Vector3f>(count - usedVertices.size());
+                int vertexIndex = 0, blenderVertexIndex = 0;
+                for (Vector3f[] vertAndNormal : verticesAndNormals) {
+                    if (!usedVertices.contains(vertAndNormal[0])) {
+                        vertices.add(vertAndNormal[0]);
+                        normals.add(vertAndNormal[1]);
+                        this.appendVertexReference(blenderVertexIndex, vertexIndex++, globalVertexReferenceMap);
+                    }
+                    ++blenderVertexIndex;
+                }
+            }
+        }
+    }
+
+    /**
+     * Builds the meshes.
+     * @return a map between material index and the mesh
+     */
+    public Map<Integer, Mesh> buildMeshes() {
+        LOGGER.fine("Building point mesh.");
+        Map<Integer, Mesh> result = new HashMap<Integer, Mesh>(1);
+
+        if (vertices.size() > 0) {
+            Mesh mesh = new Mesh();
+            mesh.setMode(Mode.Points);
+            mesh.setPointSize(3);
+
+            // the point mesh does not need index buffer, but some modifiers applied by importer need it
+            // the 'alone point' situation should be quite rare so not too many resources are wasted here
+            LOGGER.fine("Creating indices buffer.");
+            if (vertices.size() <= Short.MAX_VALUE) {
+                short[] indices = new short[vertices.size()];
+                for (int i = 0; i < vertices.size(); ++i) {
+                    indices[i] = (short) i;
+                }
+                mesh.setBuffer(Type.Index, 1, indices);
+            } else {
+                int[] indices = new int[vertices.size()];
+                for (int i = 0; i < vertices.size(); ++i) {
+                    indices[i] = i;
+                }
+                mesh.setBuffer(Type.Index, 1, indices);
+            }
+
+            LOGGER.fine("Creating vertices buffer.");
+            VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
+            verticesBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(vertices.toArray(new Vector3f[vertices.size()])));
+            mesh.setBuffer(verticesBuffer);
+            
+            LOGGER.fine("Creating normals buffer (in case of points it is required if skeleton is applied).");
+            VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
+            normalsBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(normals.toArray(new Vector3f[normals.size()])));
+            mesh.setBuffer(normalsBuffer);
+
+            result.put(-1, mesh);
+        }
+        return result;
+    }
+
+    /**
+     * @return <b>true</b> if the mesh has no vertices and <b>false</b> otherwise
+     */
+    public boolean isEmpty() {
+        return vertices == null;
+    }
+    
+    public Map<Integer, List<Integer>> getGlobalVertexReferenceMap() {
+        return globalVertexReferenceMap;
+    }
+    
+    /**
+     * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created
+     * 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
+     * - the reference indices list.
+     * 
+     * @param basicVertexIndex
+     *            the index of the vertex from its basic table
+     * @param resultIndex
+     *            the index of the vertex in its result vertex list
+     * @param vertexReferenceMap
+     *            the reference map
+     */
+    private void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {
+        List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
+        if (referenceList == null) {
+            referenceList = new ArrayList<Integer>();
+            vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);
+        }
+        referenceList.add(Integer.valueOf(resultIndex));
+    }
 }

+ 35 - 13
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java

@@ -119,8 +119,8 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
 
                             for (int i = 0; i < cloneIndexes.limit(); ++i) {
                                 int index = cloneIndexes instanceof ShortBuffer ? ((ShortBuffer) cloneIndexes).get(i) : ((IntBuffer) cloneIndexes).get(i);
-                                if (!modifiedIndexes.contains((int) index)) {
-                                    modifiedIndexes.add((int) index);
+                                if (!modifiedIndexes.contains(index)) {
+                                    modifiedIndexes.add(index);
                                     int valueIndex = index * 3 + mirrorIndex;
 
                                     float value = clonePosition.get(valueIndex);
@@ -155,17 +155,39 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
                             }
                             modifiedIndexes.clear();
 
-                            // flipping index order
-                            for (int i = 0; i < cloneIndexes.limit(); i += 3) {
-                                if (cloneIndexes instanceof ShortBuffer) {
-                                    short index = ((ShortBuffer) cloneIndexes).get(i + 2);
-                                    ((ShortBuffer) cloneIndexes).put(i + 2, ((ShortBuffer) cloneIndexes).get(i + 1));
-                                    ((ShortBuffer) cloneIndexes).put(i + 1, index);
-                                } else {
-                                    int index = ((IntBuffer) cloneIndexes).get(i + 2);
-                                    ((IntBuffer) cloneIndexes).put(i + 2, ((IntBuffer) cloneIndexes).get(i + 1));
-                                    ((IntBuffer) cloneIndexes).put(i + 1, index);
-                                }
+                            LOGGER.finer("Flipping index order.");
+                            switch(mesh.getMode()) {
+                                case Points:
+                                    cloneIndexes.flip();
+                                    break;
+                                case Lines:
+                                    for (int i = 0; i < cloneIndexes.limit(); i += 2) {
+                                        if (cloneIndexes instanceof ShortBuffer) {
+                                            short index = ((ShortBuffer) cloneIndexes).get(i + 1);
+                                            ((ShortBuffer) cloneIndexes).put(i + 1, ((ShortBuffer) cloneIndexes).get(i));
+                                            ((ShortBuffer) cloneIndexes).put(i, index);
+                                        } else {
+                                            int index = ((IntBuffer) cloneIndexes).get(i + 1);
+                                            ((IntBuffer) cloneIndexes).put(i + 1, ((IntBuffer) cloneIndexes).get(i));
+                                            ((IntBuffer) cloneIndexes).put(i, index);
+                                        }
+                                    }
+                                    break;
+                                case Triangles:
+                                    for (int i = 0; i < cloneIndexes.limit(); i += 3) {
+                                        if (cloneIndexes instanceof ShortBuffer) {
+                                            short index = ((ShortBuffer) cloneIndexes).get(i + 2);
+                                            ((ShortBuffer) cloneIndexes).put(i + 2, ((ShortBuffer) cloneIndexes).get(i + 1));
+                                            ((ShortBuffer) cloneIndexes).put(i + 1, index);
+                                        } else {
+                                            int index = ((IntBuffer) cloneIndexes).get(i + 2);
+                                            ((IntBuffer) cloneIndexes).put(i + 2, ((IntBuffer) cloneIndexes).get(i + 1));
+                                            ((IntBuffer) cloneIndexes).put(i + 1, index);
+                                        }
+                                    }
+                                    break;
+                                default:
+                                    throw new IllegalStateException("Invalid mesh mode: " + mesh.getMode());
                             }
 
                             if (mirrorU && clone.getBuffer(Type.TexCoord) != null) {