diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java index 7e38ffe09..45742d78b 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java +++ b/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 geometries = new HashMap(); + private Map> geometries = new HashMap>(); /** The vertex reference map. */ private Map>> vertexReferenceMap; @@ -26,7 +27,12 @@ public class MeshContext { * the geometry */ public void putGeometry(Integer materialIndex, Geometry geometry) { - geometries.put(materialIndex, geometry); + List geomList = geometries.get(materialIndex); + if (geomList == null) { + geomList = new ArrayList(); + 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 entry : geometries.entrySet()) { - if (entry.getValue().equals(geometry)) { - return entry.getKey(); + for (Entry> 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); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java index b8678e641..7d32c59b2 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java +++ b/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> meshes = meshBuilder.buildMeshes(); geometries = new ArrayList(meshes.size()); - for(Entry> meshEntry : meshes.entrySet()) { + for (Entry> 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> 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; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/FaceMeshBuilder.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/FaceMeshBuilder.java index 1c1a4c6e5..f3c4916b2 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/FaceMeshBuilder.java +++ b/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>> globalVertexReferenceMap; /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */ - private Map> normalMap = new HashMap>(); + private Map> normalMap = new HashMap>(); /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */ - private Map> vertexMap = new HashMap>(); + private Map> vertexMap = new HashMap>(); /** The following map sorts vertices colors by material number (because in jme Mesh can have only one material). */ - private Map> vertexColorsMap = new HashMap>(); + private Map> vertexColorsMap = new HashMap>(); /** The following map sorts indexes by material number (because in jme Mesh can have only one material). */ - private Map> indexMap = new HashMap>(); + private Map> indexMap = new HashMap>(); /** 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> 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 uvCoordinatesForFace = new HashMap(); - if (pMPoly.isNotNull() && pMLoop.isNotNull() && pMEdge.isNotNull()) { + if (pMPoly.isNotNull() && pMLoop.isNotNull()) { Map> uvs = this.loadUVCoordinates(meshStructure, true, blenderContext); List polys = pMPoly.fetchData(blenderContext.getInputStream()); List 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 uvsForFace, int[] vertexColorIndex) { if (uvsForFace != null && uvsForFace.size() > 0) { diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/LineMeshBuilder.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/LineMeshBuilder.java index 4ecaa09d4..ee3dff683 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/LineMeshBuilder.java +++ b/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 vertices = new ArrayList(); + /** The normals of the mesh. */ + private List normals = new ArrayList(); + + /** + * 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> 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>(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 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 buildMeshes() { + LOGGER.fine("Building line mesh."); + Map result = new HashMap(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 true if the mesh has no vertices and false otherwise + */ + public boolean isEmpty() { + return vertices == null; + } + + public Map> 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> vertexReferenceMap) { + List referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex)); + if (referenceList == null) { + referenceList = new ArrayList(); + vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList); + } + referenceList.add(Integer.valueOf(resultIndex)); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/MeshBuilder.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/MeshBuilder.java index 38aa431d3..9aca51f53 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/MeshBuilder.java +++ b/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>> globalVertexReferenceMap = new HashMap>>(); 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> buildMeshes() { Map> result = new HashMap>(); - + Map meshes = faceMeshBuilder.buildMeshes(); - for(Entry entry : meshes.entrySet()) { + for (Entry entry : meshes.entrySet()) { List meshList = new ArrayList(); meshList.add(entry.getValue()); result.put(entry.getKey(), meshList); } + + meshes = lineMeshBuilder.buildMeshes(); + for (Entry entry : meshes.entrySet()) { + List meshList = result.get(entry.getKey()); + if (meshList == null) { + meshList = new ArrayList(); + result.put(entry.getKey(), meshList); + } + meshList.add(entry.getValue()); + } + + meshes = pointMeshBuilder.buildMeshes(); + for (Entry entry : meshes.entrySet()) { + List meshList = result.get(entry.getKey()); + if (meshList == null) { + meshList = new ArrayList(); + 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 true if the mesh has no vertices and false 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>> 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> 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 true if the material has at least one generated component and false otherwise */ diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/PointMeshBuilder.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/PointMeshBuilder.java index b85a8a71e..3695a0652 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/PointMeshBuilder.java +++ b/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 vertices = new ArrayList(); + /** The normals of the mesh. */ + private List normals = new ArrayList(); + + /** + * 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> 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>(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 usedVertices = new HashSet(count); + List 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(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 buildMeshes() { + LOGGER.fine("Building point mesh."); + Map result = new HashMap(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 true if the mesh has no vertices and false otherwise + */ + public boolean isEmpty() { + return vertices == null; + } + + public Map> 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> vertexReferenceMap) { + List referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex)); + if (referenceList == null) { + referenceList = new ArrayList(); + vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList); + } + referenceList.add(Integer.valueOf(resultIndex)); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java index 08d5b6cd4..92d16ba8a 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java +++ b/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) {