From bf4447bc46accdeaa0104121e1de498f3dedfb0b Mon Sep 17 00:00:00 2001 From: "Kae..pl" Date: Thu, 25 Jul 2013 10:19:34 +0000 Subject: [PATCH] Feature: loading separate UV sets defined by user and loading LightMap. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10737 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../blender/materials/MaterialContext.java | 59 +++--- .../blender/materials/MaterialHelper.java | 1 + .../plugins/blender/meshes/MeshBuilder.java | 91 +++++---- .../plugins/blender/meshes/MeshHelper.java | 167 +++++++++++----- .../blender/textures/CombinedTexture.java | 181 +++++------------- .../blender/textures/TextureHelper.java | 9 +- .../blender/textures/UserUVCollection.java | 86 +++++++++ 7 files changed, 342 insertions(+), 252 deletions(-) create mode 100644 engine/src/blender/com/jme3/scene/plugins/blender/textures/UserUVCollection.java 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 c9a3f16d9..8deb198f3 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 @@ -2,9 +2,11 @@ package com.jme3.scene.plugins.blender.materials; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.material.Material; @@ -44,6 +46,7 @@ public final class MaterialContext { public static final int MTEX_SPEC = 0x04; public static final int MTEX_EMIT = 0x40; public static final int MTEX_ALPHA = 0x80; + public static final int MTEX_AMB = 0x800; /* package */final String name; /* package */final Map loadedTextures; @@ -109,7 +112,11 @@ public final class MaterialContext { textureData.mtex = p.fetchData(blenderContext.getInputStream()).get(0); textureData.uvCoordinatesType = ((Number) textureData.mtex.getFieldValue("texco")).intValue(); textureData.projectionType = ((Number) textureData.mtex.getFieldValue("mapping")).intValue(); - + textureData.uvCoordinatesName = textureData.mtex.getFieldValue("uvName").toString(); + if(textureData.uvCoordinatesName != null && textureData.uvCoordinatesName.trim().length() == 0) { + textureData.uvCoordinatesName = null; + } + Pointer pTex = (Pointer) textureData.mtex.getFieldValue("tex"); if (pTex.isNotNull()) { Structure tex = pTex.fetchData(blenderContext.getInputStream()).get(0); @@ -136,7 +143,8 @@ public final class MaterialContext { float[] color = new float[] { ((Number) textureData.mtex.getFieldValue("r")).floatValue(), ((Number) textureData.mtex.getFieldValue("g")).floatValue(), ((Number) textureData.mtex.getFieldValue("b")).floatValue() }; float colfac = ((Number) textureData.mtex.getFieldValue("colfac")).floatValue(); TextureBlender textureBlender = TextureBlenderFactory.createTextureBlender(texture.getImage().getFormat(), texflag, negateTexture, blendType, diffuseColorArray, color, colfac); - combinedTexture.add(texture, textureBlender, textureData.uvCoordinatesType, textureData.projectionType, textureData.textureStructure, blenderContext); + combinedTexture.add(texture, textureBlender, textureData.uvCoordinatesType, textureData.projectionType, + textureData.textureStructure, textureData.uvCoordinatesName, blenderContext); } } if (combinedTexture.getTexturesCount() > 0) { @@ -176,7 +184,7 @@ public final class MaterialContext { * @param blenderContext * the blender context */ - public void applyMaterial(Geometry geometry, Long geometriesOMA, List userDefinedUVCoordinates, BlenderContext blenderContext) { + public void applyMaterial(Geometry geometry, Long geometriesOMA, LinkedHashMap> userDefinedUVCoordinates, BlenderContext blenderContext) { Material material = null; if (shadeless) { material = new Material(blenderContext.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); @@ -206,30 +214,24 @@ public final class MaterialContext { // applying textures if (loadedTextures != null && loadedTextures.size() > 0) { - Entry basicUVSOwner = null; + int textureIndex = 0; + if(loadedTextures.size() > 8) { + LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different textures. JME supports only {0} UV mappings.", TextureHelper.TEXCOORD_TYPES.length); + } for (Entry entry : loadedTextures.entrySet()) { - CombinedTexture combinedTexture = entry.getValue(); - combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext); + if(textureIndex < TextureHelper.TEXCOORD_TYPES.length) { + CombinedTexture combinedTexture = entry.getValue(); + combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext); - if (basicUVSOwner == null) { - basicUVSOwner = entry; - } else { - combinedTexture.castToUVS(basicUVSOwner.getValue(), blenderContext); this.setTexture(material, entry.getKey().intValue(), combinedTexture.getResultTexture()); + List uvs = entry.getValue().getResultUVS(); + VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]); + uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); + geometry.getMesh().setBuffer(uvCoordsBuffer); + } else { + LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length); } } - - if (basicUVSOwner != null) { - this.setTexture(material, basicUVSOwner.getKey().intValue(), basicUVSOwner.getValue().getResultTexture()); - List basicUVS = basicUVSOwner.getValue().getResultUVS(); - VertexBuffer uvCoordsBuffer = new VertexBuffer(VertexBuffer.Type.TexCoord); - uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(basicUVS.toArray(new Vector2f[basicUVS.size()]))); - geometry.getMesh().setBuffer(uvCoordsBuffer); - } - } else if (userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) { - VertexBuffer uvCoordsBuffer = new VertexBuffer(VertexBuffer.Type.TexCoord); - uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(userDefinedUVCoordinates.toArray(new Vector2f[userDefinedUVCoordinates.size()]))); - geometry.getMesh().setBuffer(uvCoordsBuffer); } // applying additional data @@ -237,11 +239,7 @@ public final class MaterialContext { if (vertexColor) { material.setBoolean(shadeless ? "VertexColor" : "UseVertexColor", true); } - if (this.faceCullMode != null) { - material.getAdditionalRenderState().setFaceCullMode(faceCullMode); - } else { - material.getAdditionalRenderState().setFaceCullMode(blenderContext.getBlenderKey().getFaceCullMode()); - } + material.getAdditionalRenderState().setFaceCullMode(faceCullMode != null ? faceCullMode : blenderContext.getBlenderKey().getFaceCullMode()); if (transparent) { material.setTransparent(true); material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); @@ -282,6 +280,9 @@ public final class MaterialContext { LOGGER.warning("JME does not support alpha map on unshaded material. Material name is " + name); } break; + case MTEX_AMB: + material.setTexture(MaterialHelper.TEXTURE_TYPE_LIGHTMAP, texture); + break; default: LOGGER.severe("Unknown mapping type: " + mapTo); } @@ -311,7 +312,7 @@ public final class MaterialContext { * @return a map with sorted and filtered textures */ private Map> sortAndFilterTextures(List textures) { - int[] mappings = new int[] { MTEX_COL, MTEX_NOR, MTEX_EMIT, MTEX_SPEC, MTEX_ALPHA }; + int[] mappings = new int[] { MTEX_COL, MTEX_NOR, MTEX_EMIT, MTEX_SPEC, MTEX_ALPHA, MTEX_AMB }; Map> result = new HashMap>(); for (TextureData data : textures) { Number mapto = (Number) data.mtex.getFieldValue("mapto"); @@ -413,5 +414,7 @@ public final class MaterialContext { Structure textureStructure; int uvCoordinatesType; int projectionType; + /** The name of the user's UV coordinates that are used for this texture. */ + String uvCoordinatesName; } } 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 f02916684..d07d0948c 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 @@ -65,6 +65,7 @@ public class MaterialHelper extends AbstractBlenderHelper { public static final String TEXTURE_TYPE_SPECULAR = "SpecularMap"; public static final String TEXTURE_TYPE_GLOW = "GlowMap"; public static final String TEXTURE_TYPE_ALPHA = "AlphaMap"; + public static final String TEXTURE_TYPE_LIGHTMAP = "LightMap"; public static final Integer ALPHA_MASK_NONE = Integer.valueOf(0); public static final Integer ALPHA_MASK_CIRCLE = Integer.valueOf(1); 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 index 6280deaae..b01679040 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java @@ -3,13 +3,16 @@ package com.jme3.scene.plugins.blender.meshes; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.logging.Logger; import com.jme3.math.FastMath; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; +import com.jme3.scene.plugins.blender.textures.UserUVCollection; import com.jme3.util.BufferUtils; /** @@ -17,8 +20,8 @@ import com.jme3.util.BufferUtils; * * @author Marcin Roguski (Kaelthas) */ -/*package*/class MeshBuilder { - private static final Logger LOGGER = Logger.getLogger(MeshBuilder.class.getName()); +/* package */class MeshBuilder { + private static final Logger LOGGER = Logger.getLogger(MeshBuilder.class.getName()); /** An array of reference vertices. */ private Vector3f[][] verticesAndNormals; @@ -26,25 +29,21 @@ import com.jme3.util.BufferUtils; private List verticesColors; /** 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 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>(); + 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>(); + private Map> vertexMap = new HashMap>(); /** The following map sorts vertices colors by material number (because in jme Mesh can have only one material). */ - private Map> vertexColorsMap = new HashMap>(); + private Map> vertexColorsMap = 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>(); // + private Map> indexMap = new HashMap>(); + /** A collection of user defined UV coordinates (one mesh can have more than one such mappings). */ + private UserUVCollection userUVCollection = new UserUVCollection(); /** * Constructor. Stores the given array (not copying it). @@ -100,16 +99,20 @@ import com.jme3.util.BufferUtils; * 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 + * @param uvsForFace + * a 3-element array of vertices UV coordinates mapped to the UV's set name * @param quad * indicates if the appended face is a part of a quad face (used for creating vertex colors buffer) * @param faceIndex * the face index (used for creating vertex colors buffer) */ - public void appendFace(int v1, int v2, int v3, boolean smooth, int materialNumber, Vector2f[] uvs, boolean quad, int faceIndex) { - if (uvs != null && uvs.length != 3) { - throw new IllegalArgumentException("UV coordinates must be a 3-element array!"); + public void appendFace(int v1, int v2, int v3, boolean smooth, int materialNumber, Map uvsForFace, boolean quad, int faceIndex) { + if (uvsForFace != null && uvsForFace.size() > 0) { + for (Entry entry : uvsForFace.entrySet()) { + if (entry.getValue().length != 3) { + throw new IllegalArgumentException("UV coordinates must be a 3-element array!" + (entry.getKey() != null ? " (UV set name: " + entry.getKey() + ')' : "")); + } + } } // getting the required lists @@ -139,14 +142,6 @@ import com.jme3.util.BufferUtils; 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); - } - } faceIndex *= 4; if (quad) { @@ -159,38 +154,55 @@ import com.jme3.util.BufferUtils; if (smooth && !usesGeneratedTextures) { for (int i = 0; i < 3; ++i) { if (!vertexReferenceMap.containsKey(index[i])) { + //if this index is not yet used then create another face this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap); + if (uvsForFace != null) { + for (Entry entry : uvsForFace.entrySet()) { + userUVCollection.addUV(materialNumber, entry.getKey(), entry.getValue()[i], vertexList.size()); + } + } + vertexList.add(verticesAndNormals[index[i]][0]); if (verticesColors != null) { vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i])); } normalList.add(verticesAndNormals[index[i]][1]); - if (uvCoordinatesList != null) { - uvsMap.put(vertexList.size(), uvs[i]); - uvCoordinatesList.add(uvs[i]); - } + index[i] = vertexList.size() - 1; - } else if (uvCoordinatesList != null) { + } else if (uvsForFace != null) { + //if the index is used then check if the vertexe's UV coordinates match, if yes then the vertex doesn't have separate UV's + //in different faces so we can use it here as well, if UV's are different in separate faces the we need to add this vert + //because in jme one vertex can have only on UV coordinate boolean vertexAlreadyUsed = false; for (Integer vertexIndex : vertexReferenceMap.get(index[i])) { - if (uvs[i].equals(uvsMap.get(vertexIndex))) { + int vertexUseCounter = 0; + for (Entry entry : uvsForFace.entrySet()) { + if (entry.getValue()[i].equals(userUVCollection.getUVForVertex(entry.getKey(), vertexIndex))) { + ++vertexUseCounter; + } + } + if (vertexUseCounter == uvsForFace.size()) { vertexAlreadyUsed = true; index[i] = vertexIndex; break; } } + if (!vertexAlreadyUsed) { + // treat this face as a new one because its vertices have separate UV's this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap); - uvsMap.put(vertexList.size(), uvs[i]); + for (Entry entry : uvsForFace.entrySet()) { + userUVCollection.addUV(materialNumber, entry.getKey(), entry.getValue()[i], vertexList.size()); + } vertexList.add(verticesAndNormals[index[i]][0]); if (verticesColors != null) { vertexColorsList.add(verticesColors.get(faceIndex + vertexColorIndex[i])); } normalList.add(verticesAndNormals[index[i]][1]); - uvCoordinatesList.add(uvs[i]); index[i] = vertexList.size() - 1; } } else { + //use this index again index[i] = vertexList.indexOf(verticesAndNormals[index[i]][0]); } indexList.add(index[i]); @@ -200,9 +212,10 @@ import com.jme3.util.BufferUtils; 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]); + if (uvsForFace != null) { + for (Entry entry : uvsForFace.entrySet()) { + userUVCollection.addUV(materialNumber, entry.getKey(), entry.getValue()[i], vertexList.size()); + } } vertexList.add(verticesAndNormals[index[i]][0]); if (verticesColors != null) { @@ -288,15 +301,15 @@ import com.jme3.util.BufferUtils; * 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); + public LinkedHashMap> getUVCoordinates(int materialNumber) { + return userUVCollection.getUVCoordinates(materialNumber); } /** * @return indicates if the mesh has UV coordinates */ public boolean hasUVCoordinates() { - return uvCoordinates.size() > 0; + return userUVCollection.hasUVCoordinates(); } /** 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 30ec799d8..a029b2a1a 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 @@ -32,7 +32,10 @@ package com.jme3.scene.plugins.blender.meshes; import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.logging.Logger; @@ -55,6 +58,7 @@ 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.plugins.blender.textures.TextureHelper; import com.jme3.util.BufferUtils; /** @@ -63,7 +67,12 @@ import com.jme3.util.BufferUtils; * @author Marcin Roguski (Kaelthas) */ public class MeshHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName()); + private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName()); + + /** A type of UV data layer in traditional faced mesh (triangles or quads). */ + private static final int UV_DATA_LAYER_TYPE_FMESH = 5; + /** A type of UV data layer in bmesh type. */ + private static final int UV_DATA_LAYER_TYPE_BMESH = 16; /** * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender @@ -213,7 +222,7 @@ public class MeshHelper extends AbstractBlenderHelper { for (Geometry geometry : geometries) { int materialNumber = meshContext.getMaterialIndex(geometry); if (materials[materialNumber] != null) { - List uvCoordinates = meshBuilder.getUVCoordinates(materialNumber); + LinkedHashMap> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber); MaterialContext materialContext = materials[materialNumber]; materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), uvCoordinates, blenderContext); } else { @@ -223,17 +232,26 @@ public class MeshHelper extends AbstractBlenderHelper { } } else { // add UV coordinates if they are defined even if the material is not applied to the model - VertexBuffer uvCoordsBuffer = null; + List uvCoordsBuffer = null; if (meshBuilder.hasUVCoordinates()) { - List uvs = meshBuilder.getUVCoordinates(0); - uvCoordsBuffer = new VertexBuffer(Type.TexCoord); - uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); + Map> uvs = meshBuilder.getUVCoordinates(0); + if (uvs != null && uvs.size() > 0) { + uvCoordsBuffer = new ArrayList(uvs.size()); + int uvIndex = 0; + for (Entry> entry : uvs.entrySet()) { + VertexBuffer buffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[uvIndex++]); + buffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(entry.getValue().toArray(new Vector2f[uvs.size()]))); + uvCoordsBuffer.add(buffer); + } + } } for (Geometry geometry : geometries) { geometry.setMaterial(blenderContext.getDefaultMaterial()); if (uvCoordsBuffer != null) { - geometry.getMesh().setBuffer(uvCoordsBuffer); + for (VertexBuffer buffer : uvCoordsBuffer) { + geometry.getMesh().setBuffer(buffer); + } } } } @@ -254,6 +272,67 @@ public class MeshHelper extends AbstractBlenderHelper { return pMLoop != null && pMPoly != null && pMLoop.isNotNull() && pMPoly.isNotNull(); } + /** + * The method loads the UV coordinates. The result is a map where the key is the user's UV set name and the values are UV coordinates. + * But depending on the mesh type (triangle/quads or bmesh) the lists in the map have different meaning. + * For bmesh they are enlisted just like they are stored in the blend file (in loops). + * For traditional faces every 4 UV's should be assigned for a single face. + * @param meshStructure + * the mesh structure + * @param useBMesh + * tells if we should load the coordinates from loops of from faces + * @param blenderContext + * the blender context + * @return a map that sorts UV coordinates between different UV sets + * @throws BlenderFileException + * an exception is thrown when problems with blend file occur + */ + @SuppressWarnings("unchecked") + private Map> loadUVCoordinates(Structure meshStructure, boolean useBMesh, BlenderContext blenderContext) throws BlenderFileException { + Map> result = new HashMap>(); + if (useBMesh) { + // in this case the UV's are assigned to vertices (an array is the same length as the vertex array) + Structure loopData = (Structure) meshStructure.getFieldValue("ldata"); + Pointer pLoopDataLayers = (Pointer) loopData.getFieldValue("layers"); + List loopDataLayers = pLoopDataLayers.fetchData(blenderContext.getInputStream()); + for (Structure structure : loopDataLayers) { + Pointer p = (Pointer) structure.getFieldValue("data"); + if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == UV_DATA_LAYER_TYPE_BMESH) { + String uvSetName = structure.getFieldValue("name").toString(); + List uvsStructures = p.fetchData(blenderContext.getInputStream()); + List uvs = new ArrayList(uvsStructures.size()); + for (Structure uvStructure : uvsStructures) { + DynamicArray loopUVS = (DynamicArray) uvStructure.getFieldValue("uv"); + uvs.add(new Vector2f(loopUVS.get(0).floatValue(), loopUVS.get(1).floatValue())); + } + result.put(uvSetName, uvs); + } + } + } else { + // in this case UV's are assigned to faces (the array has the same legnth as the faces count) + Structure facesData = (Structure) meshStructure.getFieldValue("fdata"); + Pointer pFacesDataLayers = (Pointer) facesData.getFieldValue("layers"); + List facesDataLayers = pFacesDataLayers.fetchData(blenderContext.getInputStream()); + for (Structure structure : facesDataLayers) { + Pointer p = (Pointer) structure.getFieldValue("data"); + if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == UV_DATA_LAYER_TYPE_FMESH) { + String uvSetName = structure.getFieldValue("name").toString(); + List uvsStructures = p.fetchData(blenderContext.getInputStream()); + List uvs = new ArrayList(uvsStructures.size()); + for (Structure uvStructure : uvsStructures) { + DynamicArray mFaceUVs = (DynamicArray) uvStructure.getFieldValue("uv"); + uvs.add(new Vector2f(mFaceUVs.get(0).floatValue(), mFaceUVs.get(1).floatValue())); + uvs.add(new Vector2f(mFaceUVs.get(2).floatValue(), mFaceUVs.get(3).floatValue())); + uvs.add(new Vector2f(mFaceUVs.get(4).floatValue(), mFaceUVs.get(5).floatValue())); + uvs.add(new Vector2f(mFaceUVs.get(6).floatValue(), mFaceUVs.get(7).floatValue())); + } + result.put(uvSetName, uvs); + } + } + } + return result; + } + /** * This method reads the mesh from the new BMesh system. * @@ -267,33 +346,26 @@ public class MeshHelper extends AbstractBlenderHelper { * an exception is thrown when there are problems with the * blender file */ - @SuppressWarnings("unchecked") private void readBMesh(MeshBuilder meshBuilder, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException { Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop"); Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly"); Pointer pMEdge = (Pointer) meshStructure.getFieldValue("medge"); - Pointer pMLoopUV = (Pointer) meshStructure.getFieldValue("mloopuv"); - Vector2f[] uvCoordinatesForFace = new Vector2f[3]; + Map uvCoordinatesForFace = new HashMap(); if (pMPoly.isNotNull() && pMLoop.isNotNull() && pMEdge.isNotNull()) { + Map> uvs = this.loadUVCoordinates(meshStructure, true, blenderContext); int faceIndex = 0; List polys = pMPoly.fetchData(blenderContext.getInputStream()); List loops = pMLoop.fetchData(blenderContext.getInputStream()); - List loopuvs = pMLoopUV.isNotNull() ? pMLoopUV.fetchData(blenderContext.getInputStream()) : null; for (Structure poly : polys) { int materialNumber = ((Number) poly.getFieldValue("mat_nr")).intValue(); int loopStart = ((Number) poly.getFieldValue("loopstart")).intValue(); int totLoop = ((Number) poly.getFieldValue("totloop")).intValue(); boolean smooth = (((Number) poly.getFieldValue("flag")).byteValue() & 0x01) != 0x00; int[] vertexIndexes = new int[totLoop]; - Vector2f[] uvs = loopuvs != null ? new Vector2f[totLoop] : null; for (int i = loopStart; i < loopStart + totLoop; ++i) { vertexIndexes[i - loopStart] = ((Number) loops.get(i).getFieldValue("v")).intValue(); - if (uvs != null) { - DynamicArray loopUVS = (DynamicArray) loopuvs.get(i).getFieldValue("uv"); - uvs[i - loopStart] = new Vector2f(loopUVS.get(0).floatValue(), loopUVS.get(1).floatValue()); - } } int i = 0; @@ -302,15 +374,19 @@ public class MeshHelper extends AbstractBlenderHelper { int v2 = vertexIndexes[i + 1]; int v3 = vertexIndexes[i + 2]; - if (uvs != null) {// uvs always must be added wheater we - // have texture or not - uvCoordinatesForFace[0] = uvs[0]; - uvCoordinatesForFace[1] = uvs[i + 1]; - uvCoordinatesForFace[2] = uvs[i + 2]; + if (uvs != null) { + // uvs always must be added wheater we have texture or not + for (Entry> entry : uvs.entrySet()) { + Vector2f[] uvCoordsForASingleFace = new Vector2f[3]; + uvCoordsForASingleFace[0] = entry.getValue().get(loopStart); + uvCoordsForASingleFace[1] = entry.getValue().get(loopStart + i + 1); + uvCoordsForASingleFace[2] = entry.getValue().get(loopStart + i + 2); + uvCoordinatesForFace.put(entry.getKey(), uvCoordsForASingleFace); + } } meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, false, faceIndex); - + uvCoordinatesForFace.clear(); ++i; } ++faceIndex; @@ -332,38 +408,26 @@ public class MeshHelper extends AbstractBlenderHelper { * an exception is thrown when there are problems with the * blender file */ - @SuppressWarnings("unchecked") private void readTraditionalFaces(MeshBuilder meshBuilder, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException { Pointer pMFace = (Pointer) meshStructure.getFieldValue("mface"); List mFaces = pMFace.isNotNull() ? pMFace.fetchData(blenderContext.getInputStream()) : null; if (mFaces != null && mFaces.size() > 0) { - Pointer pMTFace = (Pointer) meshStructure.getFieldValue("mtface"); - List mtFaces = null; - - if (pMTFace.isNotNull()) { - mtFaces = pMTFace.fetchData(blenderContext.getInputStream()); - int facesAmount = ((Number) meshStructure.getFieldValue("totface")).intValue(); - if (mtFaces.size() != facesAmount) { - throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!"); - } - } - - // indicates if the material with the specified number should have a - // texture attached - Vector2f[] uvCoordinatesForFace = new Vector2f[3]; + // indicates if the material with the specified number should have a texture attached + Map> uvs = this.loadUVCoordinates(meshStructure, false, blenderContext); + Map uvCoordinatesForFace = new HashMap(); for (int i = 0; i < mFaces.size(); ++i) { Structure mFace = mFaces.get(i); int materialNumber = ((Number) mFace.getFieldValue("mat_nr")).intValue(); boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00; - DynamicArray uvs = null; - - if (mtFaces != null) { - Structure mtFace = mtFaces.get(i); + if (uvs != null) { // uvs always must be added wheater we have texture or not - uvs = (DynamicArray) mtFace.getFieldValue("uv"); - 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()); + for (Entry> entry : uvs.entrySet()) { + Vector2f[] uvCoordsForASingleFace = new Vector2f[3]; + uvCoordsForASingleFace[0] = entry.getValue().get(i * 4); + uvCoordsForASingleFace[1] = entry.getValue().get(i * 4 + 1); + uvCoordsForASingleFace[2] = entry.getValue().get(i * 4 + 2); + uvCoordinatesForFace.put(entry.getKey(), uvCoordsForASingleFace); + } } int v1 = ((Number) mFace.getFieldValue("v1")).intValue(); @@ -372,13 +436,20 @@ public class MeshHelper extends AbstractBlenderHelper { int v4 = ((Number) mFace.getFieldValue("v4")).intValue(); meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, false, i); + uvCoordinatesForFace.clear(); if (v4 > 0) { if (uvs != null) { - 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()); + // uvs always must be added wheater we have texture or not + for (Entry> entry : uvs.entrySet()) { + Vector2f[] uvCoordsForASingleFace = new Vector2f[3]; + uvCoordsForASingleFace[0] = entry.getValue().get(i * 4); + uvCoordsForASingleFace[1] = entry.getValue().get(i * 4 + 2); + uvCoordsForASingleFace[2] = entry.getValue().get(i * 4 + 3); + uvCoordinatesForFace.put(entry.getKey(), uvCoordsForASingleFace); + } } meshBuilder.appendFace(v1, v3, v4, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace, true, i); + uvCoordinatesForFace.clear(); } } } else { 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 7694285c0..fac8ca0e7 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 @@ -2,10 +2,9 @@ package com.jme3.scene.plugins.blender.textures; import java.awt.Graphics2D; import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.ArrayList; -import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.logging.Logger; @@ -46,8 +45,6 @@ public class CombinedTexture { private final int mappingType; /** The data for each of the textures. */ private List textureDatas = new ArrayList(); - /** The variable indicates if the texture was already triangulated or not. */ - private boolean wasTriangulated; /** The result texture. */ private Texture resultTexture; /** The UV values for the result texture. */ @@ -77,10 +74,12 @@ public class CombinedTexture { * the type of UV coordinates projection (for flat textures) * @param textureStructure * the texture sructure + * @param uvCoordinatesName + * the name of the used user's UV coordinates for this texture * @param blenderContext * the blender context */ - public void add(Texture texture, TextureBlender textureBlender, int uvCoordinatesType, int projectionType, Structure textureStructure, BlenderContext blenderContext) { + public void add(Texture texture, TextureBlender textureBlender, int uvCoordinatesType, int projectionType, Structure textureStructure, String uvCoordinatesName, BlenderContext blenderContext) { if (!(texture instanceof GeneratedTexture) && !(texture instanceof Texture2D)) { throw new IllegalArgumentException("Unsupported texture type: " + (texture == null ? "null" : texture.getClass())); } @@ -92,6 +91,7 @@ public class CombinedTexture { textureData.uvCoordinatesType = UVCoordinatesType.valueOf(uvCoordinatesType); textureData.projectionType = UVProjectionType.valueOf(projectionType); textureData.textureStructure = textureStructure; + textureData.uvCoordinatesName = uvCoordinatesName; if (textureDatas.size() > 0 && this.isWithoutAlpha(textureData, blenderContext)) { textureDatas.clear();// clear previous textures, they will be covered anyway @@ -119,11 +119,12 @@ public class CombinedTexture { * the blender context */ @SuppressWarnings("unchecked") - public void flatten(Geometry geometry, Long geometriesOMA, List userDefinedUVCoordinates, BlenderContext blenderContext) { + public void flatten(Geometry geometry, Long geometriesOMA, LinkedHashMap> userDefinedUVCoordinates, BlenderContext blenderContext) { TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); Mesh mesh = geometry.getMesh(); Texture previousTexture = null; UVCoordinatesType masterUVCoordinatesType = null; + String masterUserUVSetName = null; for (TextureData textureData : textureDatas) { // decompress compressed textures (all will be merged into one texture anyway) if (textureDatas.size() > 1 && textureData.texture.getImage().getFormat().isCompressed()) { @@ -138,7 +139,12 @@ public class CombinedTexture { resultTexture = textureData.texture; if (textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) { - resultUVS = userDefinedUVCoordinates; + if(textureData.uvCoordinatesName == null) { + resultUVS = userDefinedUVCoordinates.values().iterator().next();//get the first UV available + } else { + resultUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName); + } + masterUserUVSetName = textureData.uvCoordinatesName; } else { List geometries = (List) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE); resultUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries); @@ -161,7 +167,9 @@ public class CombinedTexture { triangulatedTexture.blend(textureData.textureBlender, (TriangulatedTexture) resultTexture, blenderContext); resultTexture = previousTexture = triangulatedTexture; } else if (textureData.texture instanceof Texture2D) { - if (masterUVCoordinatesType == textureData.uvCoordinatesType && resultTexture instanceof Texture2D) { + if (this.isUVTypesMatch(masterUVCoordinatesType, masterUserUVSetName, + textureData.uvCoordinatesType, textureData.uvCoordinatesName) && + resultTexture instanceof Texture2D) { this.scale((Texture2D) textureData.texture, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight()); this.merge((Texture2D) resultTexture, (Texture2D) textureData.texture); previousTexture = resultTexture; @@ -173,7 +181,11 @@ public class CombinedTexture { // first triangulate the current texture List textureUVS = null; if (textureData.uvCoordinatesType == UVCoordinatesType.TEXCO_UV && userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) { - textureUVS = userDefinedUVCoordinates; + if(textureData.uvCoordinatesName == null) { + textureUVS = userDefinedUVCoordinates.values().iterator().next();//get the first UV available + } else { + textureUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName); + } } else { List geometries = (List) blenderContext.getLoadedFeature(geometriesOMA, LoadedFeatureDataType.LOADED_FEATURE); textureUVS = UVCoordinatesGenerator.generateUVCoordinatesFor2DTexture(mesh, textureData.uvCoordinatesType, textureData.projectionType, geometries); @@ -196,7 +208,6 @@ public class CombinedTexture { } resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS(); resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture(); - wasTriangulated = true; } // setting additional data @@ -207,6 +218,31 @@ public class CombinedTexture { resultTexture.setMinFilter(MinFilter.NearestNoMipMaps); } + /** + * The method checks if the texture UV coordinates match. + * It the types are equal and different then UVCoordinatesType.TEXCO_UV then we consider them a match. + * If they are both UVCoordinatesType.TEXCO_UV then they match only when their UV sets names are equal. + * In other cases they are considered NOT a match. + * @param type1 the UV coord type + * @param uvSetName1 the user's UV coords set name (considered only for UVCoordinatesType.TEXCO_UV) + * @param type2 the UV coord type + * @param uvSetName2 the user's UV coords set name (considered only for UVCoordinatesType.TEXCO_UV) + * @return true if the types match and false otherwise + */ + private boolean isUVTypesMatch(UVCoordinatesType type1, String uvSetName1, + UVCoordinatesType type2, String uvSetName2) { + if(type1 == type2) { + if(type1 == UVCoordinatesType.TEXCO_UV) { + if(uvSetName1 != null && uvSetName2 != null && uvSetName1.equals(uvSetName2)) { + return true; + } + } else { + return true; + } + } + return false; + } + /** * This method blends the texture. * @@ -228,129 +264,6 @@ public class CombinedTexture { } } - /** - * This method casts the current image to the basic UV's owner UV's - * coordinates. - * - * @param basicUVSOwner - * the owner of the UV's we cast to - * @param blenderContext - * the blender context - */ - public void castToUVS(CombinedTexture basicUVSOwner, BlenderContext blenderContext) { - if (resultUVS.size() != basicUVSOwner.resultUVS.size()) { - throw new IllegalStateException("The amount of UV coordinates must be equal in order to cast one UV's onto another!"); - } - if (!resultUVS.equals(basicUVSOwner.resultUVS)) { - if (!basicUVSOwner.wasTriangulated) { - throw new IllegalStateException("The given texture must be triangulated!"); - } - if (!this.wasTriangulated) { - resultTexture = new TriangulatedTexture((Texture2D) resultTexture, resultUVS, blenderContext); - resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS(); - resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture(); - } - // casting algorithm - TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - ImageLoader imageLoader = new ImageLoader(); - List faceTextures = new ArrayList(); - List basicUVS = basicUVSOwner.getResultUVS(); - int[] imageRectangle = new int[4];// minX, minY, maxX, maxY - int[] sourceSize = new int[2], targetSize = new int[2];// width, - // height - Vector2f[] destinationUVS = new Vector2f[3]; - Vector2f[] sourceUVS = new Vector2f[3]; - List partImageUVS = Arrays.asList(new Vector2f(), new Vector2f(), new Vector2f()); - int faceIndex = 0; - - for (int i = 0; i < basicUVS.size(); i += 3) { - // destination size nad UVS - destinationUVS[0] = basicUVS.get(i); - destinationUVS[1] = basicUVS.get(i + 1); - destinationUVS[2] = basicUVS.get(i + 2); - this.computeImageRectangle(destinationUVS, imageRectangle, basicUVSOwner.resultTexture.getImage().getWidth(), basicUVSOwner.resultTexture.getImage().getHeight(), blenderContext); - targetSize[0] = imageRectangle[2] - imageRectangle[0]; - targetSize[1] = imageRectangle[3] - imageRectangle[1]; - for (int j = 0; j < 3; ++j) { - partImageUVS.get(j).set((basicUVSOwner.resultTexture.getImage().getWidth() * destinationUVS[j].x - imageRectangle[0]) / targetSize[0], (basicUVSOwner.resultTexture.getImage().getHeight() * destinationUVS[j].y - imageRectangle[1]) / targetSize[1]); - } - - // source size and UVS (translate UVS to (0,0) and stretch it to - // the borders of the image) - sourceUVS[0] = resultUVS.get(i); - sourceUVS[1] = resultUVS.get(i + 1); - sourceUVS[2] = resultUVS.get(i + 2); - this.computeImageRectangle(sourceUVS, imageRectangle, resultTexture.getImage().getWidth(), resultTexture.getImage().getHeight(), blenderContext); - sourceSize[0] = imageRectangle[2] - imageRectangle[0]; - sourceSize[1] = imageRectangle[3] - imageRectangle[1]; - float xTranslateFactor = imageRectangle[0] / (float) resultTexture.getImage().getWidth(); - float xStreachFactor = resultTexture.getImage().getWidth() / (float) sourceSize[0]; - float yTranslateFactor = imageRectangle[1] / (float) resultTexture.getImage().getHeight(); - float yStreachFactor = resultTexture.getImage().getHeight() / (float) sourceSize[1]; - for (int j = 0; j < 3; ++j) { - sourceUVS[j].x = (sourceUVS[j].x - xTranslateFactor) * xStreachFactor; - sourceUVS[j].y = (sourceUVS[j].y - yTranslateFactor) * yStreachFactor; - } - - AffineTransform affineTransform = textureHelper.createAffineTransform(sourceUVS, partImageUVS.toArray(new Vector2f[3]), sourceSize, targetSize); - - Image image = textureHelper.getSubimage(resultTexture.getImage(), imageRectangle[0], imageRectangle[1], imageRectangle[2], imageRectangle[3]); - - // compute the result texture - BufferedImage sourceImage = ImageToAwt.convert(image, false, true, 0); - - BufferedImage targetImage = new BufferedImage(targetSize[0], targetSize[1], sourceImage.getType()); - Graphics2D g = targetImage.createGraphics(); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g.drawImage(sourceImage, affineTransform, null); - g.dispose(); - - Image output = imageLoader.load(targetImage, false); - faceTextures.add(new TriangleTextureElement(faceIndex++, output, partImageUVS, false, blenderContext)); - } - TriangulatedTexture triangulatedTexture = new TriangulatedTexture(faceTextures, blenderContext); - triangulatedTexture.setKeepIdenticalTextures(false); - resultTexture = triangulatedTexture.getResultTexture(); - resultUVS = basicUVS; - } - } - - /** - * This method computes the rectangle of an image constrained by the - * triangle UV coordinates. - * - * @param triangleVertices - * the triangle UV coordinates - * @param result - * the array where the result is stored - * @param totalImageWidth - * the total image width - * @param totalImageHeight - * the total image height - * @param blenderContext - * the blender context - */ - private void computeImageRectangle(Vector2f[] triangleVertices, int[] result, int totalImageWidth, int totalImageHeight, BlenderContext blenderContext) { - TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); - - float minX = Math.min(triangleVertices[0].x, triangleVertices[1].x); - minX = Math.min(minX, triangleVertices[2].x); - - float maxX = Math.max(triangleVertices[0].x, triangleVertices[1].x); - maxX = Math.max(maxX, triangleVertices[2].x); - - float minY = Math.min(triangleVertices[0].y, triangleVertices[1].y); - minY = Math.min(minY, triangleVertices[2].y); - - float maxY = Math.max(triangleVertices[0].y, triangleVertices[1].y); - maxY = Math.max(maxY, triangleVertices[2].y); - - result[0] = textureHelper.getPixelPosition(minX, totalImageWidth); - result[1] = textureHelper.getPixelPosition(minY, totalImageHeight); - result[2] = textureHelper.getPixelPosition(maxX, totalImageWidth); - result[3] = textureHelper.getPixelPosition(maxY, totalImageHeight); - } - /** * @return the result texture */ @@ -546,5 +459,7 @@ public class CombinedTexture { public UVProjectionType projectionType; /** The texture sructure. */ public Structure textureStructure; + /** The name of the user's UV coordinates that are used for this texture. */ + public String uvCoordinatesName; } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java index 815c50498..c120cbb88 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java @@ -53,6 +53,7 @@ import com.jme3.asset.GeneratedTextureKey; import com.jme3.asset.TextureKey; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; +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.BlenderContext.LoadedFeatureDataType; @@ -94,11 +95,11 @@ public class TextureHelper extends AbstractBlenderHelper { public static final int TEX_MUSGRAVE = 11; public static final int TEX_VORONOI = 12; public static final int TEX_DISTNOISE = 13; - public static final int TEX_POINTDENSITY = 14; // v. - // 25+ - public static final int TEX_VOXELDATA = 15; // v. - // 25+ + public static final int TEX_POINTDENSITY = 14; // v. 25+ + public static final int TEX_VOXELDATA = 15; // v. 25+ + public static final Type[] TEXCOORD_TYPES = new Type[] { Type.TexCoord, Type.TexCoord2, Type.TexCoord3, Type.TexCoord4, Type.TexCoord5, Type.TexCoord6, Type.TexCoord7, Type.TexCoord8 }; + private TextureGeneratorFactory textureGeneratorFactory = new TextureGeneratorFactory(); /** diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UserUVCollection.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UserUVCollection.java new file mode 100644 index 000000000..083e5f50d --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UserUVCollection.java @@ -0,0 +1,86 @@ +package com.jme3.scene.plugins.blender.textures; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.jme3.math.Vector2f; + +/** + * A collection of UV coordinates. The coords are stored in groups defined by the material index and their UV set name. + * + * @author Kaelthas (Marcin Roguski) + */ +public class UserUVCollection { + /** A map between material number and UV coordinates of mesh that has this material applied. */ + private Map>> uvCoordinates = new HashMap>>(); + /** A map between vertex index and its UV coordinates. */ + private Map> uvsMap = new HashMap>(); + + /** + * Adds a single UV coordinates for a specified vertex index. + * @param materialIndex + * the material index + * @param uvSetName + * the UV set name + * @param uv + * the added UV coordinates + * @param jmeVertexIndex + * the index of the vertex in result jme mesh + */ + public void addUV(int materialIndex, String uvSetName, Vector2f uv, int jmeVertexIndex) { + // first get all UV sets for the specified material ... + LinkedHashMap> uvsForMaterial = uvCoordinates.get(materialIndex); + if (uvsForMaterial == null) { + uvsForMaterial = new LinkedHashMap>(); + uvCoordinates.put(materialIndex, uvsForMaterial); + } + + // ... then fetch the UVS for the specified UV set name ... + List uvsForName = uvsForMaterial.get(uvSetName); + if (uvsForName == null) { + uvsForName = new ArrayList(); + uvsForMaterial.put(uvSetName, uvsForName); + } + + // ... add the UV coordinates to the proper list ... + uvsForName.add(uv); + + // ... and add the mapping of the UV coordinates to a vertex index for the specified UV set + Map uvToVertexIndexMapping = uvsMap.get(uvSetName); + if (uvToVertexIndexMapping == null) { + uvToVertexIndexMapping = new HashMap(); + uvsMap.put(uvSetName, uvToVertexIndexMapping); + } + uvToVertexIndexMapping.put(jmeVertexIndex, uv); + } + + /** + * @param uvSetName + * the name of the UV set + * @param vertexIndex + * the vertex index corresponds to the index in jme mesh and not the original one in blender + * @return + */ + public Vector2f getUVForVertex(String uvSetName, int vertexIndex) { + return uvsMap.get(uvSetName).get(vertexIndex); + } + + /** + * @param materialNumber + * the material number that is appied to the mesh + * @return UV coordinates of vertices that belong to the required mesh part + */ + public LinkedHashMap> getUVCoordinates(int materialNumber) { + return uvCoordinates.get(materialNumber); + } + + /** + * @return indicates if the mesh has UV coordinates + */ + public boolean hasUVCoordinates() { + return uvCoordinates.size() > 0; + } +}