package com.jme3.scene.plugins.blender.meshes.builders;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
public class MeshBuilder {
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);
faceMeshBuilder = new FaceMeshBuilder(verticesAndNormals, this.areGeneratedTexturesPresent(materials));
faceMeshBuilder.readMesh(meshStructure, blenderContext);
lineMeshBuilder = new LineMeshBuilder(verticesAndNormals);
lineMeshBuilder.readMesh(meshStructure);
pointMeshBuilder = new PointMeshBuilder(verticesAndNormals);
pointMeshBuilder.readMesh(meshStructure);
}
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()) {
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() && 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 globalVertexReferenceMap;
}
/**
* @param materialNumber
* the material number that is appied to the mesh
* @return UV coordinates of vertices that belong to the required mesh part
*/
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.
*
* @param meshStructure
* the structure containing the mesh data
* @return a list of two - element arrays, the first element is the vertex and the second - its normal
* @throws BlenderFileException
* this exception is thrown when the blend file structure is somehow invalid or corrupted
*/
@SuppressWarnings("unchecked")
private Vector3f[][] getVerticesAndNormals(Structure meshStructure) throws BlenderFileException {
int count = ((Number) meshStructure.getFieldValue("totvert")).intValue();
Vector3f[][] result = new Vector3f[count][2];
if (count == 0) {
return result;
}
Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");
List<Structure> mVerts = pMVert.fetchData();
if (fixUpAxis) {
for (int i = 0; i < count; ++i) {
DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
result[i][0] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue());
DynamicArray<Number> normals = (DynamicArray<Number>) mVerts.get(i).getFieldValue("no");
result[i][1] = new Vector3f(normals.get(0).shortValue() / 32767.0f, normals.get(2).shortValue() / 32767.0f, -normals.get(1).shortValue() / 32767.0f);
}
} else {
for (int i = 0; i < count; ++i) {
DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
result[i][0] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());
DynamicArray<Number> normals = (DynamicArray<Number>) mVerts.get(i).getFieldValue("no");
result[i][1] = new Vector3f(normals.get(0).shortValue() / 32767.0f, normals.get(1).shortValue() / 32767.0f, normals.get(2).shortValue() / 32767.0f);
}
}
return result;
}
/**
* @return <b>true</b> if the material has at least one generated component and <b>false</b> otherwise
*/
private boolean areGeneratedTexturesPresent(MaterialContext[] materials) {
if (materials != null) {
for (MaterialContext material : materials) {
if (material != null && material.hasGeneratedTextures()) {
return true;
}
}
}
return false;
}
}