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
experimental
Kae..pl 11 years ago
parent 69e4392ba9
commit dd8b8fbd5f
  1. 24
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java
  2. 32
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  3. 25
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/FaceMeshBuilder.java
  4. 162
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/LineMeshBuilder.java
  5. 54
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/MeshBuilder.java
  6. 173
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/builders/PointMeshBuilder.java
  7. 48
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java

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

@ -40,9 +40,12 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.asset.BlenderKey.FeaturesToLoad; import com.jme3.asset.BlenderKey.FeaturesToLoad;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Usage; import com.jme3.scene.VertexBuffer.Usage;
@ -68,9 +71,11 @@ public class MeshHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName()); private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName());
/** A type of UV data layer in traditional faced mesh (triangles or quads). */ /** 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. */ /** 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 * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
@ -143,9 +148,9 @@ public class MeshHelper extends AbstractBlenderHelper {
LOGGER.fine("Generating meshes."); LOGGER.fine("Generating meshes.");
Map<Integer, List<Mesh>> meshes = meshBuilder.buildMeshes(); Map<Integer, List<Mesh>> meshes = meshBuilder.buildMeshes();
geometries = new ArrayList<Geometry>(meshes.size()); 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(); int materialIndex = meshEntry.getKey();
for(Mesh mesh : meshEntry.getValue()) { for (Mesh mesh : meshEntry.getValue()) {
LOGGER.fine("Preparing the result part."); LOGGER.fine("Preparing the result part.");
Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh); Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
if (properties != null && properties.getValue() != null) { if (properties != null && properties.getValue() != null) {
@ -164,7 +169,9 @@ public class MeshHelper extends AbstractBlenderHelper {
if (materials != null) { if (materials != null) {
for (Geometry geometry : geometries) { for (Geometry geometry : geometries) {
int materialNumber = meshContext.getMaterialIndex(geometry); 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); LinkedHashMap<String, List<Vector2f>> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber);
MaterialContext materialContext = materials[materialNumber]; MaterialContext materialContext = materials[materialNumber];
materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), uvCoordinates, blenderContext); materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), uvCoordinates, blenderContext);
@ -190,7 +197,12 @@ public class MeshHelper extends AbstractBlenderHelper {
} }
for (Geometry geometry : geometries) { 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) { if (uvCoordsBuffer != null) {
for (VertexBuffer buffer : uvCoordsBuffer) { for (VertexBuffer buffer : uvCoordsBuffer) {
geometry.getMesh().setBuffer(buffer); geometry.getMesh().setBuffer(buffer);
@ -214,4 +226,12 @@ public class MeshHelper extends AbstractBlenderHelper {
Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly"); Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
return pMLoop != null && pMPoly != null && pMLoop.isNotNull() && pMPoly.isNotNull(); 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;
}
} }

@ -32,7 +32,7 @@ import com.jme3.util.BufferUtils;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
/* package */class FaceMeshBuilder { /* 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. */ /** An array of reference vertices. */
private Vector3f[][] verticesAndNormals; private Vector3f[][] verticesAndNormals;
@ -46,15 +46,15 @@ import com.jme3.util.BufferUtils;
*/ */
private Map<Integer, Map<Integer, List<Integer>>> globalVertexReferenceMap; 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). */ /** 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). */ /** 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). */ /** 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). */ /** 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). */ /** 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). * Constructor. Stores the given array (not copying it).
@ -91,7 +91,7 @@ import com.jme3.util.BufferUtils;
for (Entry<Integer, List<Integer>> meshEntry : indexMap.entrySet()) { for (Entry<Integer, List<Integer>> meshEntry : indexMap.entrySet()) {
int materialIndex = meshEntry.getKey(); 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 // value is a list of vertex indices
Mesh mesh = new Mesh(); Mesh mesh = new Mesh();
@ -148,10 +148,9 @@ import com.jme3.util.BufferUtils;
LOGGER.fine("Reading BMesh."); LOGGER.fine("Reading BMesh.");
Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop"); Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop");
Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly"); Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge");
Map<String, Vector2f[]> uvCoordinatesForFace = new HashMap<String, Vector2f[]>(); 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); Map<String, List<Vector2f>> uvs = this.loadUVCoordinates(meshStructure, true, blenderContext);
List<Structure> polys = pMPoly.fetchData(blenderContext.getInputStream()); List<Structure> polys = pMPoly.fetchData(blenderContext.getInputStream());
List<Structure> loops = pMLoop.fetchData(blenderContext.getInputStream()); List<Structure> loops = pMLoop.fetchData(blenderContext.getInputStream());
@ -172,7 +171,7 @@ import com.jme3.util.BufferUtils;
int v1 = vertexIndexes[0]; int v1 = vertexIndexes[0];
int v2 = vertexIndexes[i + 1]; int v2 = vertexIndexes[i + 1];
int v3 = vertexIndexes[i + 2]; int v3 = vertexIndexes[i + 2];
if(vertexColorIndex != null) { if (vertexColorIndex != null) {
vertexColorIndex[0] = loopStart; vertexColorIndex[0] = loopStart;
vertexColorIndex[1] = loopStart + i + 1; vertexColorIndex[1] = loopStart + i + 1;
vertexColorIndex[2] = loopStart + i + 2; vertexColorIndex[2] = loopStart + i + 2;
@ -237,7 +236,7 @@ import com.jme3.util.BufferUtils;
int v2 = ((Number) mFace.getFieldValue("v2")).intValue(); int v2 = ((Number) mFace.getFieldValue("v2")).intValue();
int v3 = ((Number) mFace.getFieldValue("v3")).intValue(); int v3 = ((Number) mFace.getFieldValue("v3")).intValue();
int v4 = ((Number) mFace.getFieldValue("v4")).intValue(); int v4 = ((Number) mFace.getFieldValue("v4")).intValue();
if(vertexColorIndex != null) { if (vertexColorIndex != null) {
vertexColorIndex[0] = i * 4; vertexColorIndex[0] = i * 4;
vertexColorIndex[1] = i * 4 + 1; vertexColorIndex[1] = i * 4 + 1;
vertexColorIndex[2] = i * 4 + 2; vertexColorIndex[2] = i * 4 + 2;
@ -256,7 +255,7 @@ import com.jme3.util.BufferUtils;
uvCoordinatesForFace.put(entry.getKey(), uvCoordsForASingleFace); uvCoordinatesForFace.put(entry.getKey(), uvCoordsForASingleFace);
} }
} }
if(vertexColorIndex != null) { if (vertexColorIndex != null) {
vertexColorIndex[0] = i * 4; vertexColorIndex[0] = i * 4;
vertexColorIndex[1] = i * 4 + 2; vertexColorIndex[1] = i * 4 + 2;
vertexColorIndex[2] = i * 4 + 3; vertexColorIndex[2] = i * 4 + 3;
@ -282,6 +281,8 @@ import com.jme3.util.BufferUtils;
* the material number for this face * the material number for this face
* @param uvsForFace * @param uvsForFace
* a 3-element array of vertices UV coordinates mapped to the UV's set name * 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) { 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) { if (uvsForFace != null && uvsForFace.size() > 0) {

@ -1,5 +1,163 @@
package com.jme3.scene.plugins.blender.meshes.builders; package com.jme3.scene.plugins.blender.meshes.builders;
/*package*/ class LineMeshBuilder { import java.util.ArrayList;
//TODO: this will be implemented soon 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));
}
} }

@ -18,42 +18,76 @@ import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext; import com.jme3.scene.plugins.blender.materials.MaterialContext;
public class MeshBuilder { public class MeshBuilder {
private boolean fixUpAxis; private boolean fixUpAxis;
//TODO: these will be added soon private PointMeshBuilder pointMeshBuilder;
// private PointMeshBuilder pointMeshBuilder; private LineMeshBuilder lineMeshBuilder;
// private LineMeshBuilder lineMeshBuilder; private FaceMeshBuilder faceMeshBuilder;
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 { public MeshBuilder(Structure meshStructure, MaterialContext[] materials, BlenderContext blenderContext) throws BlenderFileException {
fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis(); fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
Vector3f[][] verticesAndNormals = this.getVerticesAndNormals(meshStructure, blenderContext); 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); 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() { public Map<Integer, List<Mesh>> buildMeshes() {
Map<Integer, List<Mesh>> result = new HashMap<Integer, List<Mesh>>(); Map<Integer, List<Mesh>> result = new HashMap<Integer, List<Mesh>>();
Map<Integer, Mesh> meshes = faceMeshBuilder.buildMeshes(); 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>(); List<Mesh> meshList = new ArrayList<Mesh>();
meshList.add(entry.getValue()); meshList.add(entry.getValue());
result.put(entry.getKey(), meshList); 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 result;
} }
/**
* @return <b>true</b> if the mesh has no vertices and <b>false</b> otherwise
*/
public boolean isEmpty() { 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 * @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() { public Map<Integer, Map<Integer, List<Integer>>> getVertexReferenceMap() {
return faceMeshBuilder.getVertexReferenceMap(); return globalVertexReferenceMap;
} }
/** /**

@ -1,5 +1,174 @@
package com.jme3.scene.plugins.blender.meshes.builders; package com.jme3.scene.plugins.blender.meshes.builders;
/*package*/ class PointMeshBuilder { import java.util.ArrayList;
//TODO: this will be implemented soon 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));
}
} }

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

Loading…
Cancel
Save