Issue 466: Blender loader duplicates vertices
Done. Hope it does not break anything ... too much :) git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9509 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
4e987ae63e
commit
d63683d240
@ -1,23 +1,5 @@
|
||||
package com.jme3.scene.plugins.blender.curves;
|
||||
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.*;
|
||||
import com.jme3.math.Spline.SplineType;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.*;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
|
||||
import com.jme3.scene.plugins.blender.meshes.MeshHelper;
|
||||
import com.jme3.scene.plugins.blender.objects.Properties;
|
||||
import com.jme3.scene.shape.Curve;
|
||||
import com.jme3.scene.shape.Surface;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
@ -27,6 +9,33 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Spline;
|
||||
import com.jme3.math.Spline.SplineType;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.math.Vector4f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
|
||||
import com.jme3.scene.plugins.blender.objects.Properties;
|
||||
import com.jme3.scene.shape.Curve;
|
||||
import com.jme3.scene.shape.Surface;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
/**
|
||||
* A class that is used in mesh calculations.
|
||||
* @author Marcin Roguski
|
||||
@ -396,7 +405,6 @@ public class CurvesHelper extends AbstractBlenderHelper {
|
||||
protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject,
|
||||
boolean smooth, BlenderContext blenderContext) {
|
||||
float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position));
|
||||
MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
|
||||
float curveLength = curve.getLength();
|
||||
//TODO: use the smooth var
|
||||
|
||||
@ -512,7 +520,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
|
||||
int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]);
|
||||
for (int i = 0; i < allIndices.length - 3; i += 3) {
|
||||
Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
|
||||
meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
|
||||
this.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
|
||||
}
|
||||
if (normalBuffers[geomIndex] == null) {
|
||||
normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3);
|
||||
@ -541,6 +549,29 @@ public class CurvesHelper extends AbstractBlenderHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
|
||||
*
|
||||
* @param normalToAdd
|
||||
* a normal to be added
|
||||
* @param normalMap
|
||||
* merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
|
||||
* @param smooth
|
||||
* the variable that indicates wheather to merge normals (creating the smooth mesh) or not
|
||||
* @param vertices
|
||||
* a list of vertices read from the blender file
|
||||
*/
|
||||
private void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
|
||||
for (Vector3f v : vertices) {
|
||||
Vector3f n = normalMap.get(v);
|
||||
if (!smooth || n == null) {
|
||||
normalMap.put(v, normalToAdd.clone());
|
||||
} else {
|
||||
n.addLocal(normalToAdd).normalizeLocal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loads the taper object.
|
||||
* @param taperStructure
|
||||
|
@ -103,6 +103,13 @@ public class Pointer {
|
||||
} else {
|
||||
structures.addAll(p.fetchData(inputStream));
|
||||
}
|
||||
} else {
|
||||
//it is necessary to put null's if the pointer is null, ie. in materials array that is attached to the mesh, the index
|
||||
//of the material is important, that is why we need null's to indicate that some materials' slots are empty
|
||||
if(structures == null) {
|
||||
structures = new ArrayList<Structure>();
|
||||
}
|
||||
structures.add(null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -195,6 +195,7 @@ public final class MaterialContext {
|
||||
}
|
||||
|
||||
//applying textures
|
||||
if(!noTextures) {
|
||||
for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
|
||||
CombinedTexture combinedTexture = entry.getValue();
|
||||
combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
|
||||
@ -234,6 +235,7 @@ public final class MaterialContext {
|
||||
geometry.getMesh().setBuffer(uvCoordsBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//applying additional data
|
||||
material.setName(name);
|
||||
@ -254,6 +256,20 @@ public final class MaterialContext {
|
||||
geometry.setMaterial(material);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <b>true</b> if the material has at least one generated texture and <b>false</b> otherwise
|
||||
*/
|
||||
public boolean hasGeneratedTextures() {
|
||||
if(loadedTextures != null) {
|
||||
for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
|
||||
if(entry.getValue().hasGeneratedTextures()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sorts the textures by their mapping type.
|
||||
* In each group only textures of one type are put (either two- or three-dimensional).
|
||||
|
@ -337,7 +337,7 @@ public class MaterialHelper extends AbstractBlenderHelper {
|
||||
materials = new MaterialContext[materialStructures.size()];
|
||||
int i = 0;
|
||||
for (Structure s : materialStructures) {
|
||||
materials[i++] = materialHelper.toMaterialContext(s, blenderContext);
|
||||
materials[i++] = s == null ? null : materialHelper.toMaterialContext(s, blenderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,244 @@
|
||||
package com.jme3.scene.plugins.blender.meshes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/*package*/ class MeshBuilder {
|
||||
/** An array of reference vertices. */
|
||||
private Vector3f[] vertices;
|
||||
/** A variable that indicates if the model uses generated textures. */
|
||||
private boolean usesGeneratedTextures;
|
||||
|
||||
/** 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;
|
||||
/** A map between vertex and its normal vector. */
|
||||
private Map<Vector3f, Vector3f> globalNormalMap = new HashMap<Vector3f, Vector3f>();
|
||||
|
||||
/** A map between vertex index and its UV coordinates. */
|
||||
private Map<Integer, Vector2f> uvsMap = new HashMap<Integer, Vector2f>();
|
||||
/** 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>>();
|
||||
/** 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>>();
|
||||
/** 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>>();
|
||||
/** A map between material number and UV coordinates of mesh that has this material applied. */
|
||||
private Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>();//<material_number; list of uv coordinates for mesh's vertices>
|
||||
|
||||
/**
|
||||
* 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 vertices the reference vertices array
|
||||
* @param usesGeneratedTextures a variable that indicates if the model uses generated textures or not
|
||||
*/
|
||||
public MeshBuilder(Vector3f[] vertices, boolean usesGeneratedTextures) {
|
||||
if(vertices == null || vertices.length == 0) {
|
||||
throw new IllegalArgumentException("No vertices loaded to build mesh.");
|
||||
}
|
||||
this.vertices = vertices;
|
||||
this.usesGeneratedTextures = usesGeneratedTextures;
|
||||
globalVertexReferenceMap = new HashMap<Integer, Map<Integer, List<Integer>>>(vertices.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a face to the mesh.
|
||||
* @param v1 index of the 1'st vertex from the reference vertex table
|
||||
* @param v2 index of the 2'nd vertex from the reference vertex table
|
||||
* @param v3 index of the 3'rd vertex from the reference vertex table
|
||||
* @param smooth indicates if this face should have smooth shading or flat shading
|
||||
* @param materialNumber the material number for this face
|
||||
* @param uvs a 3-element array of vertices UV coordinates
|
||||
*/
|
||||
public void appendFace(int v1, int v2, int v3, boolean smooth, int materialNumber, Vector2f[] uvs) {
|
||||
if(uvs != null && uvs.length != 3) {
|
||||
throw new IllegalArgumentException("UV coordinates must be a 3-element array!");
|
||||
}
|
||||
List<Integer> indexList = indexMap.get(materialNumber);
|
||||
if (indexList == null) {
|
||||
indexList = new ArrayList<Integer>();
|
||||
indexMap.put(materialNumber, indexList);
|
||||
}
|
||||
List<Vector3f> vertexList = vertexMap.get(materialNumber);
|
||||
if (vertexList == null) {
|
||||
vertexList = new ArrayList<Vector3f>();
|
||||
vertexMap.put(materialNumber, vertexList);
|
||||
}
|
||||
List<Vector3f> normalList = normalMap.get(materialNumber);
|
||||
if (normalList == null) {
|
||||
normalList = new ArrayList<Vector3f>();
|
||||
normalMap.put(materialNumber, normalList);
|
||||
}
|
||||
Map<Integer, List<Integer>> vertexReferenceMap = globalVertexReferenceMap.get(materialNumber);
|
||||
if(vertexReferenceMap == null) {
|
||||
vertexReferenceMap = new HashMap<Integer, List<Integer>>();
|
||||
globalVertexReferenceMap.put(materialNumber, vertexReferenceMap);
|
||||
}
|
||||
|
||||
List<Vector2f> uvCoordinatesList = null;
|
||||
if(uvs != null) {
|
||||
uvCoordinatesList = uvCoordinates.get(Integer.valueOf(materialNumber));
|
||||
if(uvCoordinatesList == null) {
|
||||
uvCoordinatesList = new ArrayList<Vector2f>();
|
||||
uvCoordinates.put(Integer.valueOf(materialNumber), uvCoordinatesList);
|
||||
}
|
||||
}
|
||||
|
||||
Integer[] index = new Integer[] {v1, v2, v3};
|
||||
Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);
|
||||
this.addNormal(n, globalNormalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);
|
||||
if(smooth && !usesGeneratedTextures) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if(!vertexReferenceMap.containsKey(index[i])) {
|
||||
this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
|
||||
vertexList.add(vertices[index[i]]);
|
||||
normalList.add(globalNormalMap.get(vertices[index[i]]));
|
||||
if(uvCoordinatesList != null) {
|
||||
uvsMap.put(vertexList.size(), uvs[i]);
|
||||
uvCoordinatesList.add(uvs[i]);
|
||||
}
|
||||
index[i] = vertexList.size() - 1;
|
||||
} else if(uvCoordinatesList != null) {
|
||||
boolean vertexAlreadyUsed = false;
|
||||
for(Integer vertexIndex : vertexReferenceMap.get(index[i])) {
|
||||
if(uvs[i].equals(uvsMap.get(vertexIndex))) {
|
||||
vertexAlreadyUsed = true;
|
||||
index[i] = vertexIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!vertexAlreadyUsed) {
|
||||
this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
|
||||
uvsMap.put(vertexList.size(), uvs[i]);
|
||||
vertexList.add(vertices[index[i]]);
|
||||
normalList.add(globalNormalMap.get(vertices[index[i]]));
|
||||
uvCoordinatesList.add(uvs[i]);
|
||||
index[i] = vertexList.size() - 1;
|
||||
}
|
||||
} else {
|
||||
index[i] = vertexList.indexOf(vertices[index[i]]);
|
||||
}
|
||||
indexList.add(index[i]);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
indexList.add(vertexList.size());
|
||||
this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
|
||||
if(uvCoordinatesList != null) {
|
||||
uvCoordinatesList.add(uvs[i]);
|
||||
uvsMap.put(vertexList.size(), uvs[i]);
|
||||
}
|
||||
vertexList.add(vertices[index[i]]);
|
||||
normalList.add(globalNormalMap.get(vertices[index[i]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return result vertices array
|
||||
*/
|
||||
public Vector3f[] getVertices(int materialNumber) {
|
||||
return vertexMap.get(materialNumber).toArray(new Vector3f[vertexMap.get(materialNumber).size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of result vertices
|
||||
*/
|
||||
public int getVerticesAmount(int materialNumber) {
|
||||
return vertexMap.get(materialNumber).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return normals result array
|
||||
*/
|
||||
public Vector3f[] getNormals(int materialNumber) {
|
||||
return normalMap.get(materialNumber).toArray(new Vector3f[normalMap.get(materialNumber).size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a map between material number and the mesh part vertices indices
|
||||
*/
|
||||
public Map<Integer, List<Integer>> getMeshesMap() {
|
||||
return indexMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of meshes the source mesh was split into (depends on the applied materials count)
|
||||
*/
|
||||
public int getMeshesPartAmount() {
|
||||
return indexMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param materialNumber the material number that is appied to the mesh
|
||||
* @return UV coordinates of vertices that belong to the required mesh part
|
||||
*/
|
||||
public List<Vector2f> getUVCoordinates(int materialNumber) {
|
||||
return uvCoordinates.get(materialNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return indicates if the mesh has UV coordinates
|
||||
*/
|
||||
public boolean hasUVCoordinates() {
|
||||
return uvCoordinates.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
|
||||
*
|
||||
* @param normalToAdd
|
||||
* a normal to be added
|
||||
* @param normalMap
|
||||
* merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
|
||||
* @param smooth
|
||||
* the variable that indicates wheather to merge normals (creating the smooth mesh) or not
|
||||
* @param vertices
|
||||
* a list of vertices read from the blender file
|
||||
*/
|
||||
private void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
|
||||
for (Vector3f v : vertices) {
|
||||
Vector3f n = normalMap.get(v);
|
||||
if (!smooth || n == null) {
|
||||
normalMap.put(v, normalToAdd.clone());
|
||||
} else {
|
||||
n.addLocal(normalToAdd).normalizeLocal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
package com.jme3.scene.plugins.blender.meshes;
|
||||
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
|
||||
/**
|
||||
* Class that holds information about the mesh.
|
||||
@ -13,12 +14,10 @@ import java.util.Map;
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class MeshContext {
|
||||
/** The mesh stored here as a list of geometries. */
|
||||
private List<Geometry> mesh;
|
||||
/** Vertex list that is referenced by all the geometries. */
|
||||
private List<Vector3f> vertexList;
|
||||
/** A map between material index and the geometry. */
|
||||
private Map<Integer, Geometry> geometries = new HashMap<Integer, Geometry>();
|
||||
/** The vertex reference map. */
|
||||
private Map<Integer, List<Integer>> vertexReferenceMap;
|
||||
private Map<Integer, Map<Integer, List<Integer>>> vertexReferenceMap;
|
||||
/** The UV-coordinates for each of the geometries. */
|
||||
private Map<Geometry, VertexBuffer> uvCoordinates = new HashMap<Geometry, VertexBuffer>();
|
||||
/** Bind buffer for vertices is stored here and applied when required. */
|
||||
@ -27,41 +26,35 @@ public class MeshContext {
|
||||
private VertexBuffer bindNormalBuffer;
|
||||
|
||||
/**
|
||||
* This method returns the referenced mesh.
|
||||
*
|
||||
* @return the referenced mesh
|
||||
* Adds a geometry for the specified material index.
|
||||
* @param materialIndex the material index
|
||||
* @param geometry the geometry
|
||||
*/
|
||||
public List<Geometry> getMesh() {
|
||||
return mesh;
|
||||
public void putGeometry(Integer materialIndex, Geometry geometry) {
|
||||
geometries.put(materialIndex, geometry);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the referenced mesh.
|
||||
*
|
||||
* @param mesh
|
||||
* the referenced mesh
|
||||
* @param materialIndex the material index
|
||||
* @return vertices amount that is used by mesh with the specified material
|
||||
*/
|
||||
public void setMesh(List<Geometry> mesh) {
|
||||
this.mesh = mesh;
|
||||
public int getVertexCount(int materialIndex) {
|
||||
return geometries.get(materialIndex).getVertexCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the vertex list.
|
||||
*
|
||||
* @return the vertex list
|
||||
* Returns material index for the geometry.
|
||||
* @param geometry the geometry
|
||||
* @return material index
|
||||
* @throws IllegalStateException this exception is thrown when no material is found for the specified geometry
|
||||
*/
|
||||
public List<Vector3f> getVertexList() {
|
||||
return vertexList;
|
||||
public int getMaterialIndex(Geometry geometry) {
|
||||
for(Entry<Integer, Geometry> entry : geometries.entrySet()) {
|
||||
if(entry.getValue().equals(geometry)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the vertex list.
|
||||
*
|
||||
* @param vertexList
|
||||
* the vertex list
|
||||
*/
|
||||
public void setVertexList(List<Vector3f> vertexList) {
|
||||
this.vertexList = vertexList;
|
||||
}
|
||||
throw new IllegalStateException("Cannot find material index for the given geometry: " + geometry);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,8 +62,8 @@ public class MeshContext {
|
||||
*
|
||||
* @return the vertex reference map
|
||||
*/
|
||||
public Map<Integer, List<Integer>> getVertexReferenceMap() {
|
||||
return vertexReferenceMap;
|
||||
public Map<Integer, List<Integer>> getVertexReferenceMap(int materialIndex) {
|
||||
return vertexReferenceMap.get(materialIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +72,7 @@ public class MeshContext {
|
||||
* @param vertexReferenceMap
|
||||
* the vertex reference map
|
||||
*/
|
||||
public void setVertexReferenceMap(Map<Integer, List<Integer>> vertexReferenceMap) {
|
||||
public void setVertexReferenceMap(Map<Integer, Map<Integer, List<Integer>>> vertexReferenceMap) {
|
||||
this.vertexReferenceMap = vertexReferenceMap;
|
||||
}
|
||||
|
||||
|
@ -31,8 +31,15 @@
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.meshes;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Geometry;
|
||||
@ -54,13 +61,6 @@ import com.jme3.scene.plugins.blender.objects.Properties;
|
||||
import com.jme3.scene.plugins.blender.textures.TextureHelper;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* A class that is used in mesh calculations.
|
||||
@ -109,16 +109,20 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
String name = structure.getName();
|
||||
MeshContext meshContext = new MeshContext();
|
||||
|
||||
// reading materials
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
MaterialContext[] materials = null;
|
||||
if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
|
||||
materials = materialHelper.getMaterials(structure, blenderContext);
|
||||
}
|
||||
|
||||
// reading vertices
|
||||
Vector3f[] vertices = this.getVertices(structure, blenderContext);
|
||||
int verticesAmount = vertices.length;
|
||||
MeshBuilder meshBuilder = new MeshBuilder(vertices, this.areGeneratedTexturesPresent(materials));
|
||||
|
||||
// vertices Colors
|
||||
List<byte[]> verticesColors = this.getVerticesColors(structure, blenderContext);
|
||||
|
||||
// reading faces
|
||||
// the following map sorts faces by material number (because in jme Mesh can have only one material)
|
||||
Map<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();
|
||||
Pointer pMFace = (Pointer) structure.getFieldValue("mface");
|
||||
List<Structure> mFaces = null;
|
||||
if (pMFace.isNotNull()) {
|
||||
@ -131,7 +135,6 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
|
||||
Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
|
||||
Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>();//<material_number; list of uv coordinates for mesh's vertices>
|
||||
List<Structure> mtFaces = null;
|
||||
|
||||
if (pMTFace.isNotNull()) {
|
||||
@ -142,17 +145,10 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// normalMap merges normals of faces that will be rendered smooth
|
||||
Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);
|
||||
|
||||
List<Vector3f> normalList = new ArrayList<Vector3f>();
|
||||
List<Vector3f> vertexList = new ArrayList<Vector3f>();
|
||||
// indicates if the material with the specified number should have a texture attached
|
||||
Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();
|
||||
// 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)
|
||||
Map<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAmount);
|
||||
int vertexColorIndex = 0;
|
||||
Vector2f[] uvCoordinatesForFace = new Vector2f[3];
|
||||
for (int i = 0; i < mFaces.size(); ++i) {
|
||||
Structure mFace = mFaces.get(i);
|
||||
int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
|
||||
@ -161,28 +157,17 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
boolean materialWithoutTextures = false;
|
||||
Pointer pImage = null;
|
||||
|
||||
List<Vector2f> uvCoordinatesList = uvCoordinates.get(Integer.valueOf(matNr));
|
||||
if(uvCoordinatesList == null) {
|
||||
uvCoordinatesList = new ArrayList<Vector2f>();
|
||||
uvCoordinates.put(Integer.valueOf(matNr), uvCoordinatesList);
|
||||
}
|
||||
|
||||
if (mtFaces != null) {
|
||||
Structure mtFace = mtFaces.get(i);
|
||||
pImage = (Pointer) mtFace.getFieldValue("tpage");
|
||||
materialWithoutTextures = pImage.isNull();
|
||||
// uvs always must be added wheater we have texture or not
|
||||
uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
||||
uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
|
||||
uvCoordinatesForFace[1] = new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue());
|
||||
uvCoordinatesForFace[2] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
|
||||
}
|
||||
Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);
|
||||
List<Integer> indexList = meshesMap.get(materialNumber);
|
||||
if (indexList == null) {
|
||||
indexList = new ArrayList<Integer>();
|
||||
meshesMap.put(materialNumber, indexList);
|
||||
}
|
||||
|
||||
// attaching image to texture (face can have UV's and image whlie its material may have no texture attached)
|
||||
if (pImage != null && pImage.isNotNull() && !materialNumberToTexture.containsKey(materialNumber)) {
|
||||
@ -198,46 +183,14 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
int v3 = ((Number) mFace.getFieldValue("v3")).intValue();
|
||||
int v4 = ((Number) mFace.getFieldValue("v4")).intValue();
|
||||
|
||||
Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);
|
||||
this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);
|
||||
normalList.add(normalMap.get(vertices[v1]));
|
||||
normalList.add(normalMap.get(vertices[v2]));
|
||||
normalList.add(normalMap.get(vertices[v3]));
|
||||
|
||||
this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
|
||||
indexList.add(vertexList.size());
|
||||
vertexList.add(vertices[v1]);
|
||||
|
||||
this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap);
|
||||
indexList.add(vertexList.size());
|
||||
vertexList.add(vertices[v2]);
|
||||
|
||||
this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
|
||||
indexList.add(vertexList.size());
|
||||
vertexList.add(vertices[v3]);
|
||||
|
||||
meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace);
|
||||
if (v4 > 0) {
|
||||
if (uvs != null) {
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
||||
uvCoordinatesList.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
|
||||
uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
|
||||
uvCoordinatesForFace[1] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
|
||||
uvCoordinatesForFace[2] = new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue());
|
||||
}
|
||||
this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
|
||||
indexList.add(vertexList.size());
|
||||
vertexList.add(vertices[v1]);
|
||||
|
||||
this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
|
||||
indexList.add(vertexList.size());
|
||||
vertexList.add(vertices[v3]);
|
||||
|
||||
this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap);
|
||||
indexList.add(vertexList.size());
|
||||
vertexList.add(vertices[v4]);
|
||||
|
||||
this.addNormal(n, normalMap, smooth, vertices[v4]);
|
||||
normalList.add(normalMap.get(vertices[v1]));
|
||||
normalList.add(normalMap.get(vertices[v3]));
|
||||
normalList.add(normalMap.get(vertices[v4]));
|
||||
meshBuilder.appendFace(v1, v3, v4, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace);
|
||||
|
||||
if (verticesColors != null) {
|
||||
verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex));
|
||||
@ -251,10 +204,7 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
meshContext.setVertexList(vertexList);
|
||||
meshContext.setVertexReferenceMap(vertexReferenceMap);
|
||||
|
||||
Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
|
||||
meshContext.setVertexReferenceMap(meshBuilder.getVertexReferenceMap());
|
||||
|
||||
// reading vertices groups (from the parent)
|
||||
Structure parent = blenderContext.peekParent();
|
||||
@ -266,48 +216,23 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
verticesGroups[defIndex++] = def.getFieldValue("name").toString();
|
||||
}
|
||||
|
||||
// reading materials
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
MaterialContext[] materials = null;
|
||||
if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
|
||||
materials = materialHelper.getMaterials(structure, blenderContext);
|
||||
}
|
||||
|
||||
// creating the result meshes
|
||||
geometries = new ArrayList<Geometry>(meshesMap.size());
|
||||
|
||||
VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
|
||||
verticesBuffer.setupData(Usage.Static, 3, Format.Float,
|
||||
BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()])));
|
||||
|
||||
// initial vertex position (used with animation)
|
||||
VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);
|
||||
verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));
|
||||
|
||||
VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
|
||||
normalsBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(normals));
|
||||
|
||||
// initial normals position (used with animation)
|
||||
VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
|
||||
normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
|
||||
geometries = new ArrayList<Geometry>(meshBuilder.getMeshesPartAmount());
|
||||
|
||||
//reading custom properties
|
||||
Properties properties = this.loadProperties(structure, blenderContext);
|
||||
|
||||
// generating meshes
|
||||
//FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
|
||||
ByteBuffer verticesColorsBuffer = this.createByteBuffer(verticesColors);
|
||||
verticesAmount = vertexList.size();
|
||||
Map<Mesh, Integer> meshToMAterialMap = new HashMap<Mesh, Integer>(meshesMap.size());
|
||||
for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
|
||||
for (Entry<Integer, List<Integer>> meshEntry : meshBuilder.getMeshesMap().entrySet()) {
|
||||
int materialIndex = meshEntry.getKey();
|
||||
//key is the material index (or -1 if the material has no texture)
|
||||
//value is a list of vertex indices
|
||||
Mesh mesh = new Mesh();
|
||||
meshToMAterialMap.put(mesh, meshEntry.getKey());
|
||||
|
||||
// creating vertices indices for this mesh
|
||||
List<Integer> indexList = meshEntry.getValue();
|
||||
if(verticesAmount <= Short.MAX_VALUE) {
|
||||
if(meshBuilder.getVerticesAmount(materialIndex) <= Short.MAX_VALUE) {
|
||||
short[] indices = new short[indexList.size()];
|
||||
for (int i = 0; i < indexList.size(); ++i) {
|
||||
indices[i] = indexList.get(i).shortValue();
|
||||
@ -321,6 +246,20 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
mesh.setBuffer(Type.Index, 1, indices);
|
||||
}
|
||||
|
||||
VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
|
||||
verticesBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(meshBuilder.getVertices(materialIndex)));
|
||||
|
||||
// initial vertex position (used with animation)
|
||||
VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);
|
||||
verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));
|
||||
|
||||
VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
|
||||
normalsBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(meshBuilder.getNormals(materialIndex)));
|
||||
|
||||
// initial normals position (used with animation)
|
||||
VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
|
||||
normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
|
||||
|
||||
mesh.setBuffer(verticesBuffer);
|
||||
meshContext.setBindPoseBuffer(verticesBind);//this is stored in the context and applied when needed (when animation is applied to the mesh)
|
||||
|
||||
@ -340,6 +279,7 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
geometry.setUserData("properties", properties);
|
||||
}
|
||||
geometries.add(geometry);
|
||||
meshContext.putGeometry(materialIndex, geometry);
|
||||
}
|
||||
|
||||
//store the data in blender context before applying the material
|
||||
@ -349,20 +289,30 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
//apply materials only when all geometries are in place
|
||||
if(materials != null) {
|
||||
for(Geometry geometry : geometries) {
|
||||
int materialNumber = meshToMAterialMap.get(geometry.getMesh()).intValue();
|
||||
int materialNumber = meshContext.getMaterialIndex(geometry);
|
||||
List<Vector2f> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber);
|
||||
boolean noTextures = false;
|
||||
if(materialNumber < 0) {
|
||||
materialNumber = -1 * (materialNumber + 1);
|
||||
noTextures = true;
|
||||
}
|
||||
if(materials[materialNumber] != null) {
|
||||
MaterialContext materialContext = materials[materialNumber];
|
||||
materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), noTextures, uvCoordinates.get(Integer.valueOf(materialNumber)), blenderContext);
|
||||
materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), noTextures, uvCoordinates, blenderContext);
|
||||
} else {
|
||||
geometry.setMaterial(blenderContext.getDefaultMaterial());
|
||||
if(uvCoordinates != null) {
|
||||
VertexBuffer uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
|
||||
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
|
||||
geometry.getMesh().setBuffer(uvCoordsBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//add UV coordinates if they are defined even if the material is not applied to the model
|
||||
VertexBuffer uvCoordsBuffer = null;
|
||||
if(uvCoordinates.size() > 0) {
|
||||
List<Vector2f> uvs = uvCoordinates.get(0);
|
||||
if(meshBuilder.hasUVCoordinates()) {
|
||||
List<Vector2f> uvs = meshBuilder.getUVCoordinates(-1);
|
||||
uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
|
||||
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
|
||||
}
|
||||
@ -375,60 +325,21 @@ public class MeshHelper extends AbstractBlenderHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// if there are multiple materials used, extract the shared
|
||||
// vertex data
|
||||
if (geometries.size() > 1){
|
||||
// extract from itself
|
||||
for (Geometry geom : geometries){
|
||||
geom.getMesh().extractVertexData(geom.getMesh());
|
||||
}
|
||||
}
|
||||
|
||||
return geometries;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
|
||||
*
|
||||
* @param normalToAdd
|
||||
* a normal to be added
|
||||
* @param normalMap
|
||||
* merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
|
||||
* @param smooth
|
||||
* the variable that indicates wheather to merge normals (creating the smooth mesh) or not
|
||||
* @param vertices
|
||||
* a list of vertices read from the blender file
|
||||
* @return <b>true</b> if the material has at least one generated component and <b>false</b> otherwise
|
||||
*/
|
||||
public void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
|
||||
for (Vector3f v : vertices) {
|
||||
Vector3f n = normalMap.get(v);
|
||||
if (!smooth || n == null) {
|
||||
normalMap.put(v, normalToAdd.clone());
|
||||
} else {
|
||||
n.addLocal(normalToAdd).normalizeLocal();
|
||||
private boolean areGeneratedTexturesPresent(MaterialContext[] materials) {
|
||||
if(materials != null) {
|
||||
for(MaterialContext material : materials) {
|
||||
if(material != null && material.hasGeneratedTextures()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected 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));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,19 +53,14 @@ import java.util.logging.Logger;
|
||||
// If you decide to remove this limitation, remove this code.
|
||||
// Rémy
|
||||
|
||||
private Skeleton skeleton;
|
||||
private Structure objectStructure;
|
||||
private Structure meshStructure;
|
||||
|
||||
/** Loaded animation data. */
|
||||
private AnimData animData;
|
||||
/** Old memory address of the mesh that will have the skeleton applied. */
|
||||
private Long meshOMA;
|
||||
/**
|
||||
* The maxiumum amount of bone groups applied to a single vertex (max =
|
||||
* MAXIMUM_WEIGHTS_PER_VERTEX).
|
||||
*/
|
||||
private int boneGroups;
|
||||
/** The weights of vertices. */
|
||||
private VertexBuffer verticesWeights;
|
||||
/** The indexes of bones applied to vertices. */
|
||||
private VertexBuffer verticesWeightsIndices;
|
||||
|
||||
/**
|
||||
* This constructor reads animation data from the object structore. The
|
||||
@ -83,9 +78,7 @@ import java.util.logging.Logger;
|
||||
*/
|
||||
public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
|
||||
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert
|
||||
// =
|
||||
// DeformVERTices
|
||||
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
||||
|
||||
// if pDvert==null then there are not vertex groups and no need to load
|
||||
// skeleton (untill bone envelopes are supported)
|
||||
@ -120,17 +113,17 @@ import java.util.logging.Logger;
|
||||
}
|
||||
bonesList.add(0, new Bone(""));
|
||||
Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
|
||||
Skeleton skeleton = new Skeleton(bones);
|
||||
skeleton = new Skeleton(bones);
|
||||
this.objectStructure = objectStructure;
|
||||
this.meshStructure = meshStructure;
|
||||
|
||||
// read mesh indexes
|
||||
this.meshOMA = meshStructure.getOldMemoryAddress();
|
||||
this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, blenderContext);
|
||||
|
||||
// read animations
|
||||
ArrayList<Animation> animations = new ArrayList<Animation>();
|
||||
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
|
||||
if (actionHeaders != null) {// it may happen that the model has
|
||||
// armature with no actions
|
||||
if (actionHeaders != null) {// it may happen that the model has armature with no actions
|
||||
for (FileBlockHeader header : actionHeaders) {
|
||||
Structure actionStructure = header.getStructure(blenderContext);
|
||||
String actionName = actionStructure.getName();
|
||||
@ -178,8 +171,18 @@ import java.util.logging.Logger;
|
||||
// setting weights for bones
|
||||
List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||
MeshContext meshContext = blenderContext.getMeshContext(meshOMA);
|
||||
int[] bonesGroups = new int[] { 0 };
|
||||
for (Geometry geom : geomList) {
|
||||
int materialIndex = meshContext.getMaterialIndex(geom);
|
||||
Mesh mesh = geom.getMesh();
|
||||
|
||||
try {
|
||||
VertexBuffer[] buffers = this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, materialIndex, bonesGroups, blenderContext);
|
||||
if (buffers != null) {
|
||||
mesh.setMaxNumWeights(bonesGroups[0]);
|
||||
mesh.setBuffer(buffers[0]);
|
||||
mesh.setBuffer(buffers[1]);
|
||||
|
||||
if(meshContext.getBindNormalBuffer() != null) {
|
||||
mesh.setBuffer(meshContext.getBindNormalBuffer());
|
||||
}
|
||||
@ -189,11 +192,11 @@ import java.util.logging.Logger;
|
||||
//change the usage type of vertex and normal buffers from Static to Stream
|
||||
mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
|
||||
mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
|
||||
|
||||
if (this.verticesWeights != null) {
|
||||
mesh.setMaxNumWeights(this.boneGroups);
|
||||
mesh.setBuffer(this.verticesWeights);
|
||||
mesh.setBuffer(this.verticesWeightsIndices);
|
||||
}
|
||||
} catch (BlenderFileException e) {
|
||||
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||
this.invalid = true;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,18 +242,16 @@ import java.util.logging.Logger;
|
||||
* this exception is thrown when the blend file structure is
|
||||
* somehow invalid or corrupted
|
||||
*/
|
||||
private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
|
||||
private VertexBuffer[] readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, int materialIndex,
|
||||
int[] bonesGroups, BlenderContext blenderContext) throws BlenderFileException {
|
||||
ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
|
||||
Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
|
||||
Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton, blenderContext);
|
||||
|
||||
int[] bonesGroups = new int[] { 0 };
|
||||
MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress());
|
||||
|
||||
VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexList().size(), bonesGroups, meshContext.getVertexReferenceMap(), groupToBoneIndexMap, blenderContext);
|
||||
this.verticesWeights = boneWeightsAndIndex[0];
|
||||
this.verticesWeightsIndices = boneWeightsAndIndex[1];
|
||||
this.boneGroups = bonesGroups[0];
|
||||
return this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexCount(materialIndex),
|
||||
bonesGroups, meshContext.getVertexReferenceMap(materialIndex), groupToBoneIndexMap, blenderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,18 +285,19 @@ import java.util.logging.Logger;
|
||||
*/
|
||||
private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext)
|
||||
throws BlenderFileException {
|
||||
bonesGroups[0] = 0;
|
||||
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
||||
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
||||
ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
||||
if (pDvert.isNotNull()) {// assigning weights and bone indices
|
||||
List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per
|
||||
// vertex in blender)
|
||||
List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per vertex in blender)
|
||||
int vertexIndex = 0;
|
||||
|
||||
for (Structure dvert : dverts) {
|
||||
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex
|
||||
// (max. 4 in JME)
|
||||
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
||||
List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
|
||||
if(vertexIndices != null) {
|
||||
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex (max. 4 in JME)
|
||||
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
||||
if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :)
|
||||
int weightIndex = 0;
|
||||
List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
|
||||
@ -323,21 +325,21 @@ import java.util.logging.Logger;
|
||||
}
|
||||
++weightIndex;
|
||||
}
|
||||
bonesGroups[0] = Math.max(bonesGroups[0], weightIndex);
|
||||
} else {
|
||||
for (Integer index : vertexIndices) {
|
||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
|
||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
++vertexIndex;
|
||||
}
|
||||
} else {
|
||||
// always bind all vertices to 0-indexed bone
|
||||
// this bone makes the model look normally if vertices have no bone
|
||||
// assigned
|
||||
// and it is used in object animation, so if we come accross object
|
||||
// animation
|
||||
// we can use the 0-indexed bone for this
|
||||
// assigned and it is used in object animation, so if we come accross object
|
||||
// animation we can use the 0-indexed bone for this
|
||||
for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
|
||||
// we apply the weight to all referenced vertices
|
||||
for (Integer index : vertexIndexList) {
|
||||
@ -347,7 +349,9 @@ import java.util.logging.Logger;
|
||||
}
|
||||
}
|
||||
|
||||
bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
|
||||
bonesGroups[0] = Math.max(bonesGroups[0], 1);
|
||||
|
||||
this.endBoneAssigns(vertexListSize, weightsFloatData);
|
||||
VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
|
||||
verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);
|
||||
|
||||
@ -365,22 +369,10 @@ import java.util.logging.Logger;
|
||||
* @param weightsFloatData
|
||||
* weights for vertices
|
||||
*/
|
||||
private int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
|
||||
int maxWeightsPerVert = 0;
|
||||
private void endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
|
||||
weightsFloatData.rewind();
|
||||
for (int v = 0; v < vertCount; ++v) {
|
||||
float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();
|
||||
|
||||
if (w3 != 0) {
|
||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
|
||||
} else if (w2 != 0) {
|
||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
|
||||
} else if (w1 != 0) {
|
||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
|
||||
} else if (w0 != 0) {
|
||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
|
||||
}
|
||||
|
||||
float sum = w0 + w1 + w2 + w3;
|
||||
if (sum != 1f && sum != 0.0f) {
|
||||
weightsFloatData.position(weightsFloatData.position() - 4);
|
||||
@ -393,7 +385,6 @@ import java.util.logging.Logger;
|
||||
}
|
||||
}
|
||||
weightsFloatData.rewind();
|
||||
return maxWeightsPerVert;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -140,8 +140,7 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
||||
if (pAction.isNotNull()) {
|
||||
Structure action = pAction.fetchData(blenderContext.getInputStream()).get(0);
|
||||
List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(blenderContext);
|
||||
if (actionChannels.size() == 1) {// object's animtion action has
|
||||
// only one channel
|
||||
if (actionChannels.size() == 1) {// object's animtion action has only one channel
|
||||
Pointer pChannelIpo = (Pointer) actionChannels.get(0).getFieldValue("ipo");
|
||||
Structure ipoStructure = pChannelIpo.fetchData(blenderContext.getInputStream()).get(0);
|
||||
Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
|
||||
|
@ -215,6 +215,20 @@ public class CombinedTexture {
|
||||
return textureDatas.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <b>true</b> if the texture has at least one generated texture component and <b>false</b> otherwise
|
||||
*/
|
||||
public boolean hasGeneratedTextures() {
|
||||
if(textureDatas != null) {
|
||||
for(TextureData textureData : textureDatas) {
|
||||
if(textureData.texture instanceof GeneratedTexture) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merges two given textures. The result is stored in the
|
||||
* 'target' texture.
|
||||
|
@ -107,8 +107,10 @@ import java.util.TreeSet;
|
||||
return o1.faceIndex - o2.faceIndex;
|
||||
}
|
||||
});
|
||||
int[] indices = new int[3];
|
||||
for (int i = 0; i < mesh.getTriangleCount(); ++i) {
|
||||
triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, blenderContext));
|
||||
mesh.getTriangle(i, indices);
|
||||
triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, indices, blenderContext));
|
||||
}
|
||||
return new TriangulatedTexture(triangleTextureElements, blenderContext);
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ import jme3tools.converters.ImageToAwt;
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
*/
|
||||
public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, BlenderContext blenderContext) {
|
||||
public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, int[] uvIndices, BlenderContext blenderContext) {
|
||||
this.faceIndex = faceIndex;
|
||||
|
||||
// compute the face vertices from the UV coordinates
|
||||
@ -512,11 +512,10 @@ import jme3tools.converters.ImageToAwt;
|
||||
float height = boundingBox.getYExtent() * 2;
|
||||
float depth = boundingBox.getZExtent() * 2;
|
||||
|
||||
int uvIndex = faceIndex * 3;
|
||||
Vector3f min = boundingBox.getMin(null);
|
||||
Vector3f v1 = min.add(uv[uvIndex].x * width, uv[uvIndex].y * height, uv[uvIndex].z * depth);
|
||||
Vector3f v2 = min.add(uv[uvIndex + 1].x * width, uv[uvIndex + 1].y * height, uv[uvIndex + 1].z * depth);
|
||||
Vector3f v3 = min.add(uv[uvIndex + 2].x * width, uv[uvIndex + 2].y * height, uv[uvIndex + 2].z * depth);
|
||||
Vector3f v1 = min.add(uv[uvIndices[0]].x * width, uv[uvIndices[0]].y * height, uv[uvIndices[0]].z * depth);
|
||||
Vector3f v2 = min.add(uv[uvIndices[1]].x * width, uv[uvIndices[1]].y * height, uv[uvIndices[1]].z * depth);
|
||||
Vector3f v3 = min.add(uv[uvIndices[2]].x * width, uv[uvIndices[2]].y * height, uv[uvIndices[2]].z * depth);
|
||||
|
||||
// get the rectangle envelope for the triangle
|
||||
RectangleEnvelope envelope = this.getTriangleEnvelope(v1, v2, v3);
|
||||
|
Loading…
x
Reference in New Issue
Block a user