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
3.0
Kae..pl 12 years ago
parent d2d86d649b
commit bf4447bc46
  1. 59
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  2. 1
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
  3. 91
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java
  4. 167
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  5. 181
      engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  6. 9
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  7. 86
      engine/src/blender/com/jme3/scene/plugins/blender/textures/UserUVCollection.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<Number, CombinedTexture> 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<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
public void applyMaterial(Geometry geometry, Long geometriesOMA, LinkedHashMap<String, List<Vector2f>> 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<Number, CombinedTexture> 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<Number, CombinedTexture> 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<Vector2f> 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<Vector2f> 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<Number, List<TextureData>> sortAndFilterTextures(List<TextureData> 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<Number, List<TextureData>> result = new HashMap<Number, List<TextureData>>();
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;
}
}

@ -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);

@ -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<byte[]> 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<Integer, Map<Integer, List<Integer>>> globalVertexReferenceMap;
/** A map between vertex index and its UV coordinates. */
private Map<Integer, Vector2f> uvsMap = new HashMap<Integer, Vector2f>();
/** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
private Map<Integer, List<Vector3f>> normalMap = new HashMap<Integer, List<Vector3f>>();
private Map<Integer, List<Vector3f>> normalMap = new HashMap<Integer, List<Vector3f>>();
/** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
private Map<Integer, List<Vector3f>> vertexMap = new HashMap<Integer, List<Vector3f>>();
private Map<Integer, List<Vector3f>> vertexMap = new HashMap<Integer, List<Vector3f>>();
/** The following map sorts vertices colors by material number (because in jme Mesh can have only one material). */
private Map<Integer, List<byte[]>> vertexColorsMap = new HashMap<Integer, List<byte[]>>();
private Map<Integer, List<byte[]>> vertexColorsMap = new HashMap<Integer, List<byte[]>>();
/** The following map sorts indexes by material number (because in jme Mesh can have only one material). */
private Map<Integer, List<Integer>> indexMap = new HashMap<Integer, List<Integer>>();
/** A map between material number and UV coordinates of mesh that has this material applied. */
private Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>(); // <material_number; list of uv coordinates for mesh's vertices>
private Map<Integer, List<Integer>> indexMap = new HashMap<Integer, List<Integer>>();
/** A collection of user defined UV coordinates (one mesh can have more than one such mappings). */
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<String, Vector2f[]> uvsForFace, boolean quad, int faceIndex) {
if (uvsForFace != null && uvsForFace.size() > 0) {
for (Entry<String, Vector2f[]> 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<Integer, List<Integer>>();
globalVertexReferenceMap.put(materialNumber, vertexReferenceMap);
}
List<Vector2f> uvCoordinatesList = null;
if (uvs != null) {
uvCoordinatesList = uvCoordinates.get(Integer.valueOf(materialNumber));
if (uvCoordinatesList == null) {
uvCoordinatesList = new ArrayList<Vector2f>();
uvCoordinates.put(Integer.valueOf(materialNumber), uvCoordinatesList);
}
}
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<String, Vector2f[]> 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<String, Vector2f[]> 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<String, Vector2f[]> 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<String, Vector2f[]> 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<Vector2f> getUVCoordinates(int materialNumber) {
return uvCoordinates.get(materialNumber);
public LinkedHashMap<String, List<Vector2f>> 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();
}
/**

@ -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<Vector2f> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber);
LinkedHashMap<String, List<Vector2f>> 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<VertexBuffer> uvCoordsBuffer = null;
if (meshBuilder.hasUVCoordinates()) {
List<Vector2f> 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<String, List<Vector2f>> uvs = meshBuilder.getUVCoordinates(0);
if (uvs != null && uvs.size() > 0) {
uvCoordsBuffer = new ArrayList<VertexBuffer>(uvs.size());
int uvIndex = 0;
for (Entry<String, List<Vector2f>> 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<String, List<Vector2f>> loadUVCoordinates(Structure meshStructure, boolean useBMesh, BlenderContext blenderContext) throws BlenderFileException {
Map<String, List<Vector2f>> result = new HashMap<String, List<Vector2f>>();
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<Structure> 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<Structure> uvsStructures = p.fetchData(blenderContext.getInputStream());
List<Vector2f> uvs = new ArrayList<Vector2f>(uvsStructures.size());
for (Structure uvStructure : uvsStructures) {
DynamicArray<Number> loopUVS = (DynamicArray<Number>) 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<Structure> 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<Structure> uvsStructures = p.fetchData(blenderContext.getInputStream());
List<Vector2f> uvs = new ArrayList<Vector2f>(uvsStructures.size());
for (Structure uvStructure : uvsStructures) {
DynamicArray<Number> mFaceUVs = (DynamicArray<Number>) 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<String, Vector2f[]> uvCoordinatesForFace = new HashMap<String, Vector2f[]>();
if (pMPoly.isNotNull() && pMLoop.isNotNull() && pMEdge.isNotNull()) {
Map<String, List<Vector2f>> uvs = this.loadUVCoordinates(meshStructure, true, blenderContext);
int faceIndex = 0;
List<Structure> polys = pMPoly.fetchData(blenderContext.getInputStream());
List<Structure> loops = pMLoop.fetchData(blenderContext.getInputStream());
List<Structure> 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<Number> loopUVS = (DynamicArray<Number>) 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<String, List<Vector2f>> 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<Structure> mFaces = pMFace.isNotNull() ? pMFace.fetchData(blenderContext.getInputStream()) : null;
if (mFaces != null && mFaces.size() > 0) {
Pointer pMTFace = (Pointer) meshStructure.getFieldValue("mtface");
List<Structure> 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<String, List<Vector2f>> uvs = this.loadUVCoordinates(meshStructure, false, blenderContext);
Map<String, Vector2f[]> uvCoordinatesForFace = new HashMap<String, Vector2f[]>();
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<Number> 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<Number>) 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<String, List<Vector2f>> 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<String, List<Vector2f>> 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 {

@ -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<TextureData> textureDatas = new ArrayList<TextureData>();
/** 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<Vector2f> userDefinedUVCoordinates, BlenderContext blenderContext) {
public void flatten(Geometry geometry, Long geometriesOMA, LinkedHashMap<String, List<Vector2f>> 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<Geometry> geometries = (List<Geometry>) 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<Vector2f> 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<Geometry> geometries = (List<Geometry>) 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 <b>true</b> if the types match and <b>false</b> 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<TriangleTextureElement> faceTextures = new ArrayList<TriangleTextureElement>();
List<Vector2f> 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<Vector2f> 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;
}
}

@ -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();
/**

@ -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<Integer, LinkedHashMap<String, List<Vector2f>>> uvCoordinates = new HashMap<Integer, LinkedHashMap<String, List<Vector2f>>>();
/** A map between vertex index and its UV coordinates. */
private Map<String, Map<Integer, Vector2f>> uvsMap = new HashMap<String, Map<Integer, Vector2f>>();
/**
* 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<String, List<Vector2f>> uvsForMaterial = uvCoordinates.get(materialIndex);
if (uvsForMaterial == null) {
uvsForMaterial = new LinkedHashMap<String, List<Vector2f>>();
uvCoordinates.put(materialIndex, uvsForMaterial);
}
// ... then fetch the UVS for the specified UV set name ...
List<Vector2f> uvsForName = uvsForMaterial.get(uvSetName);
if (uvsForName == null) {
uvsForName = new ArrayList<Vector2f>();
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<Integer, Vector2f> uvToVertexIndexMapping = uvsMap.get(uvSetName);
if (uvToVertexIndexMapping == null) {
uvToVertexIndexMapping = new HashMap<Integer, Vector2f>();
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<String, List<Vector2f>> getUVCoordinates(int materialNumber) {
return uvCoordinates.get(materialNumber);
}
/**
* @return indicates if the mesh has UV coordinates
*/
public boolean hasUVCoordinates() {
return uvCoordinates.size() > 0;
}
}
Loading…
Cancel
Save