|
@@ -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));
|
|
|
+ }
|
|
|
}
|