diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java index 1a077335e..2dd1997d8 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java @@ -1,31 +1,40 @@ package com.jme3.scene.plugins.blender.curves; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +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.*; +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.*; +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.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; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Logger; /** * A class that is used in mesh calculations. @@ -396,7 +405,6 @@ public class CurvesHelper extends AbstractBlenderHelper { protected List applyBevelAndTaper(Curve curve, List 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 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 diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java b/engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java index 59f20e901..c3ad655a2 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java @@ -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(); + } + structures.add(null); } } } else { diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java index f27ebb610..9bc7fd988 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java @@ -195,43 +195,45 @@ public final class MaterialContext { } //applying textures - for(Entry entry : loadedTextures.entrySet()) { - CombinedTexture combinedTexture = entry.getValue(); - combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext); - VertexBuffer.Type uvCoordinatesType = null; - - switch(entry.getKey().intValue()) { - case MTEX_COL: - uvCoordinatesType = VertexBuffer.Type.TexCoord; - material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, - combinedTexture.getResultTexture()); - break; - case MTEX_NOR: - uvCoordinatesType = VertexBuffer.Type.TexCoord2; - material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, combinedTexture.getResultTexture()); - break; - case MTEX_SPEC: - uvCoordinatesType = VertexBuffer.Type.TexCoord3; - material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, combinedTexture.getResultTexture()); - break; - case MTEX_EMIT: - uvCoordinatesType = VertexBuffer.Type.TexCoord4; - material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, combinedTexture.getResultTexture()); - break; - case MTEX_ALPHA: - uvCoordinatesType = VertexBuffer.Type.TexCoord5; - material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, combinedTexture.getResultTexture()); - break; - default: - LOGGER.severe("Unknown mapping type: " + entry.getKey().intValue()); - } - - //applying texture coordinates - if(uvCoordinatesType != null) { - VertexBuffer uvCoordsBuffer = new VertexBuffer(uvCoordinatesType); - uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, - BufferUtils.createFloatBuffer(combinedTexture.getResultUVS().toArray(new Vector2f[combinedTexture.getResultUVS().size()]))); - geometry.getMesh().setBuffer(uvCoordsBuffer); + if(!noTextures) { + for(Entry entry : loadedTextures.entrySet()) { + CombinedTexture combinedTexture = entry.getValue(); + combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext); + VertexBuffer.Type uvCoordinatesType = null; + + switch(entry.getKey().intValue()) { + case MTEX_COL: + uvCoordinatesType = VertexBuffer.Type.TexCoord; + material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, + combinedTexture.getResultTexture()); + break; + case MTEX_NOR: + uvCoordinatesType = VertexBuffer.Type.TexCoord2; + material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, combinedTexture.getResultTexture()); + break; + case MTEX_SPEC: + uvCoordinatesType = VertexBuffer.Type.TexCoord3; + material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, combinedTexture.getResultTexture()); + break; + case MTEX_EMIT: + uvCoordinatesType = VertexBuffer.Type.TexCoord4; + material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, combinedTexture.getResultTexture()); + break; + case MTEX_ALPHA: + uvCoordinatesType = VertexBuffer.Type.TexCoord5; + material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, combinedTexture.getResultTexture()); + break; + default: + LOGGER.severe("Unknown mapping type: " + entry.getKey().intValue()); + } + + //applying texture coordinates + if(uvCoordinatesType != null) { + VertexBuffer uvCoordsBuffer = new VertexBuffer(uvCoordinatesType); + uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, + BufferUtils.createFloatBuffer(combinedTexture.getResultUVS().toArray(new Vector2f[combinedTexture.getResultUVS().size()]))); + geometry.getMesh().setBuffer(uvCoordsBuffer); + } } } @@ -254,6 +256,20 @@ public final class MaterialContext { geometry.setMaterial(material); } + /** + * @return true if the material has at least one generated texture and false otherwise + */ + public boolean hasGeneratedTextures() { + if(loadedTextures != null) { + for(Entry 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). diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java index 218bbf4e0..8a57ba0b0 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java @@ -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); } } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java new file mode 100644 index 000000000..d5afc8b19 --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java @@ -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>> globalVertexReferenceMap; + /** A map between vertex and its normal vector. */ + private Map globalNormalMap = new HashMap(); + + /** A map between vertex index and its UV coordinates. */ + private Map uvsMap = new HashMap(); + /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */ + private Map> normalMap = new HashMap>(); + /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */ + private Map> vertexMap = new HashMap>(); + /** The following map sorts indexes by material number (because in jme Mesh can have only one material). */ + private Map> indexMap = new HashMap>(); + /** A map between material number and UV coordinates of mesh that has this material applied. */ + private Map> uvCoordinates = new HashMap>();// + + /** + * 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>>(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 indexList = indexMap.get(materialNumber); + if (indexList == null) { + indexList = new ArrayList(); + indexMap.put(materialNumber, indexList); + } + List vertexList = vertexMap.get(materialNumber); + if (vertexList == null) { + vertexList = new ArrayList(); + vertexMap.put(materialNumber, vertexList); + } + List normalList = normalMap.get(materialNumber); + if (normalList == null) { + normalList = new ArrayList(); + normalMap.put(materialNumber, normalList); + } + Map> vertexReferenceMap = globalVertexReferenceMap.get(materialNumber); + if(vertexReferenceMap == null) { + vertexReferenceMap = new HashMap>(); + globalVertexReferenceMap.put(materialNumber, vertexReferenceMap); + } + + List uvCoordinatesList = null; + if(uvs != null) { + uvCoordinatesList = uvCoordinates.get(Integer.valueOf(materialNumber)); + if(uvCoordinatesList == null) { + uvCoordinatesList = new ArrayList(); + 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>> 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> 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 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 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> vertexReferenceMap) { + List referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex)); + if (referenceList == null) { + referenceList = new ArrayList(); + vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList); + } + referenceList.add(Integer.valueOf(resultIndex)); + } +} diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java index 5a58fb7aa..e273d4bc6 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java @@ -1,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 mesh; - /** Vertex list that is referenced by all the geometries. */ - private List vertexList; + /** A map between material index and the geometry. */ + private Map geometries = new HashMap(); /** The vertex reference map. */ - private Map> vertexReferenceMap; + private Map>> vertexReferenceMap; /** The UV-coordinates for each of the geometries. */ private Map uvCoordinates = new HashMap(); /** Bind buffer for vertices is stored here and applied when required. */ @@ -27,50 +26,44 @@ public class MeshContext { private VertexBuffer bindNormalBuffer; /** - * This method returns the referenced mesh. - * - * @return the referenced mesh - */ - public List getMesh() { - return mesh; - } - - /** - * This method sets the referenced mesh. - * - * @param mesh - * the referenced mesh + * Adds a geometry for the specified material index. + * @param materialIndex the material index + * @param geometry the geometry */ - public void setMesh(List mesh) { - this.mesh = mesh; + public void putGeometry(Integer materialIndex, Geometry geometry) { + geometries.put(materialIndex, geometry); } - + /** - * This method returns the vertex list. - * - * @return the vertex list + * @param materialIndex the material index + * @return vertices amount that is used by mesh with the specified material */ - public List getVertexList() { - return vertexList; + public int getVertexCount(int materialIndex) { + return geometries.get(materialIndex).getVertexCount(); } - + /** - * This method sets the vertex list. - * - * @param vertexList - * 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 void setVertexList(List vertexList) { - this.vertexList = vertexList; + public int getMaterialIndex(Geometry geometry) { + for(Entry entry : geometries.entrySet()) { + if(entry.getValue().equals(geometry)) { + return entry.getKey(); + } + } + throw new IllegalStateException("Cannot find material index for the given geometry: " + geometry); } - + /** * This method returns the vertex reference map. * * @return the vertex reference map */ - public Map> getVertexReferenceMap() { - return vertexReferenceMap; + public Map> getVertexReferenceMap(int materialIndex) { + return vertexReferenceMap.get(materialIndex); } /** @@ -79,7 +72,7 @@ public class MeshContext { * @param vertexReferenceMap * the vertex reference map */ - public void setVertexReferenceMap(Map> vertexReferenceMap) { + public void setVertexReferenceMap(Map>> vertexReferenceMap) { this.vertexReferenceMap = vertexReferenceMap; } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java index 798d62efc..bb550e316 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java @@ -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 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> meshesMap = new HashMap>(); Pointer pMFace = (Pointer) structure.getFieldValue("mface"); List mFaces = null; if (pMFace.isNotNull()) { @@ -131,7 +135,6 @@ public class MeshHelper extends AbstractBlenderHelper { } Pointer pMTFace = (Pointer) structure.getFieldValue("mtface"); - Map> uvCoordinates = new HashMap>();// List 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 normalMap = new HashMap(verticesAmount); - - List normalList = new ArrayList(); - List vertexList = new ArrayList(); // indicates if the material with the specified number should have a texture attached Map materialNumberToTexture = new HashMap(); - // 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> vertexReferenceMap = new HashMap>(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 uvCoordinatesList = uvCoordinates.get(Integer.valueOf(matNr)); - if(uvCoordinatesList == null) { - uvCoordinatesList = new ArrayList(); - 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) 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 indexList = meshesMap.get(materialNumber); - if (indexList == null) { - indexList = new ArrayList(); - 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(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(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 meshToMAterialMap = new HashMap(meshesMap.size()); - for (Entry> meshEntry : meshesMap.entrySet()) { + for (Entry> 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 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 uvCoordinates = meshBuilder.getUVCoordinates(materialNumber); boolean noTextures = false; if(materialNumber < 0) { materialNumber = -1 * (materialNumber + 1); noTextures = true; } - MaterialContext materialContext = materials[materialNumber]; - materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), noTextures, uvCoordinates.get(Integer.valueOf(materialNumber)), blenderContext); + if(materials[materialNumber] != null) { + MaterialContext materialContext = materials[materialNumber]; + 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 uvs = uvCoordinates.get(0); + if(meshBuilder.hasUVCoordinates()) { + List 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,62 +325,23 @@ 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 - */ - public void addNormal(Vector3f normalToAdd, Map 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 - */ - protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map> vertexReferenceMap) { - List referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex)); - if (referenceList == null) { - referenceList = new ArrayList(); - vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList); - } - referenceList.add(Integer.valueOf(resultIndex)); + * @return true if the material has at least one generated component and false otherwise + */ + private boolean areGeneratedTexturesPresent(MaterialContext[] materials) { + if(materials != null) { + for(MaterialContext material : materials) { + if(material != null && material.hasGeneratedTextures()) { + return true; + } + } + } + return false; } - + /** * This method returns the vertices colors. Each vertex is stored in byte[4] array. * diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java index e482cbc04..1680f6437 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java @@ -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 animations = new ArrayList(); List 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,22 +171,32 @@ import java.util.logging.Logger; // setting weights for bones List geomList = (List) 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(); - if(meshContext.getBindNormalBuffer() != null) { - mesh.setBuffer(meshContext.getBindNormalBuffer()); - } - if(meshContext.getBindPoseBuffer() != null) { - mesh.setBuffer(meshContext.getBindPoseBuffer()); - } - //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); + 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()); + } + if(meshContext.getBindPoseBuffer() != null) { + mesh.setBuffer(meshContext.getBindPoseBuffer()); + } + //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); + } + } 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 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,49 +285,52 @@ import java.util.logging.Logger; */ private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map> vertexReferenceMap, Map 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 dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per - // vertex in blender) + List 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 vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here - if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :) - int weightIndex = 0; - List dw = pDW.fetchData(blenderContext.getInputStream()); - for (Structure deformWeight : dw) { - Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); - - // Remove this code if 4 weights limitation is removed - if (weightIndex == 4) { - LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex }); - break; - } - - // null here means that we came accross group that has no bone attached to - if (boneIndex != null) { - float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); - if (weight == 0.0f) { - weight = 1; - boneIndex = Integer.valueOf(0); + 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 dw = pDW.fetchData(blenderContext.getInputStream()); + for (Structure deformWeight : dw) { + Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); + + // Remove this code if 4 weights limitation is removed + if (weightIndex == 4) { + LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex }); + break; } - // we apply the weight to all referenced vertices - for (Integer index : vertexIndices) { - weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); - indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); + + // null here means that we came accross group that has no bone attached to + if (boneIndex != null) { + float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); + if (weight == 0.0f) { + weight = 1; + boneIndex = Integer.valueOf(0); + } + // we apply the weight to all referenced vertices + for (Integer index : vertexIndices) { + weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); + indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); + } } + ++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); } - ++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; @@ -334,10 +338,8 @@ import java.util.logging.Logger; } 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 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 diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java index 4d08e78f9..7378a2eab 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java @@ -140,8 +140,7 @@ public class ModifierHelper extends AbstractBlenderHelper { if (pAction.isNotNull()) { Structure action = pAction.fetchData(blenderContext.getInputStream()).get(0); List 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); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java index 08c201bce..8107a47fd 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java @@ -215,6 +215,20 @@ public class CombinedTexture { return textureDatas.size(); } + /** + * @return true if the texture has at least one generated texture component and false 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. diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java index 719c5f0a9..f1d970461 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java @@ -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); } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java index 80cee1a22..52805f36f 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java @@ -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);