diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/DataRepository.java b/engine/src/blender/com/jme3/scene/plugins/blender/DataRepository.java index 68dc1875f..8a63e3499 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/DataRepository.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/DataRepository.java @@ -48,6 +48,7 @@ import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.DnaBlockData; import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.Structure; +import com.jme3.scene.plugins.blender.materials.MaterialContext; import com.jme3.scene.plugins.blender.modifiers.Modifier; /** @@ -88,6 +89,8 @@ public class DataRepository { protected Map> modifiers = new HashMap>(); /** A list of constraints for the specified object. */ protected Map> constraints = new HashMap>(); + /** A map of material contexts. */ + protected Map materialContexts = new HashMap(); /** A map og helpers that perform loading. */ private Map helpers = new HashMap(); @@ -394,6 +397,29 @@ public class DataRepository { public List getConstraints(Long objectOMA) { return constraints.get(objectOMA); } + + /** + * This method sets the material context for the given material. + * If the context is already set it will be replaced. + * @param material + * the material + * @param materialContext + * the material's context + */ + public void setMaterialContext(Material material, MaterialContext materialContext) { + this.materialContexts.put(material, materialContext); + } + + /** + * This method returns the material context for the given material. + * If no context exists then null is returned. + * @param material + * the material + * @return material's context + */ + public MaterialContext getMaterialContext(Material material) { + return materialContexts.get(material); + } /** * This metod returns the default material. 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 c8f41b5a9..9cc776f25 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 @@ -13,23 +13,25 @@ import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.textures.TextureHelper; import com.jme3.texture.Texture.Type; -/*package*/final class MaterialContext { +public final class MaterialContext { private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName()); - public final String name; - public final List mTexs; - public final List textures; - public final int texturesCount; - public final Type textureType; - public final int textureCoordinatesType; + /*package*/ final String name; + /*package*/ final List mTexs; + /*package*/ final List textures; + /*package*/ final int texturesCount; + /*package*/ final Type textureType; - public final boolean shadeless; - public final boolean vertexColor; - public final boolean transparent; - public final boolean vtangent; + /*package*/ final boolean shadeless; + /*package*/ final boolean vertexColor; + /*package*/ final boolean transparent; + /*package*/ final boolean vtangent; + + /*package*/ int uvCoordinatesType = -1; + /*package*/ int projectionType; @SuppressWarnings("unchecked") - public MaterialContext(Structure structure, DataRepository dataRepository) throws BlenderFileException { + /*package*/ MaterialContext(Structure structure, DataRepository dataRepository) throws BlenderFileException { name = structure.getName(); int mode = ((Number) structure.getFieldValue("mode")).intValue(); @@ -43,16 +45,16 @@ import com.jme3.texture.Texture.Type; DynamicArray mtexsArray = (DynamicArray) structure.getFieldValue("mtex"); int separatedTextures = ((Number) structure.getFieldValue("septex")).intValue(); Type firstTextureType = null; - int texco = -1; for (int i = 0; i < mtexsArray.getTotalSize(); ++i) { Pointer p = mtexsArray.get(i); if (p.isNotNull() && (separatedTextures & 1 << i) == 0) { Structure mtex = p.fetchData(dataRepository.getInputStream()).get(0); //the first texture determines the texture coordinates type - if(texco == -1) { - texco = ((Number) mtex.getFieldValue("texco")).intValue(); - } else if(texco != ((Number) mtex.getFieldValue("texco")).intValue()) { + if(uvCoordinatesType == -1) { + uvCoordinatesType = ((Number) mtex.getFieldValue("texco")).intValue(); + projectionType = ((Number) mtex.getFieldValue("mapping")).intValue(); + } else if(uvCoordinatesType != ((Number) mtex.getFieldValue("texco")).intValue()) { LOGGER.log(Level.WARNING, "The texture with index: {0} has different UV coordinates type than the first texture! This texture will NOT be loaded!", i+1); continue; } @@ -79,10 +81,39 @@ import com.jme3.texture.Texture.Type; } this.texturesCount = mTexs.size(); - this.textureCoordinatesType = texco; this.textureType = firstTextureType; } + /** + * This method returns the current material's texture UV coordinates type. + * @return uv coordinates type + */ + public int getUvCoordinatesType() { + return uvCoordinatesType; + } + + /** + * This method returns the proper projection type for the material's texture. + * This applies only to 2D textures. + * @return texture's projection type + */ + public int getProjectionType() { + return projectionType; + } + + /** + * This method returns current material's texture dimension. + * @return the material's texture dimension + */ + public int getTextureDimension() { + return this.textureType == Type.TwoDimensional ? 2 : 3; + } + + /** + * This method determines the type of the texture. + * @param texType texture type (from blender) + * @return texture type (used by jme) + */ private Type getType(int texType) { switch (texType) { case TextureHelper.TEX_IMAGE:// (it is first because probably this will be most commonly used) 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 905f016e6..c3cd83fab 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 @@ -178,6 +178,16 @@ public class MaterialHelper extends AbstractBlenderHelper { this.faceCullMode = faceCullMode; } + /** + * This method converts the material structure to jme Material. + * @param structure + * structure with material data + * @param dataRepository + * the data repository + * @return jme material + * @throws BlenderFileException + * an exception is throw when problems with blend file occur + */ public Material toMaterial(Structure structure, DataRepository dataRepository) throws BlenderFileException { LOGGER.log(Level.INFO, "Loading material."); if (structure == null) { @@ -216,7 +226,7 @@ public class MaterialHelper extends AbstractBlenderHelper { // NOTE: Enable mipmaps FOR ALL TEXTURES EVER texture.setMinFilter(MinFilter.Trilinear); -//TODO: textures merging + if ((mapto & 0x01) != 0) {// Col // Map to COLOR channel or DIFFUSE // Set diffuse to white so it doesn't get multiplied by texture @@ -228,7 +238,7 @@ public class MaterialHelper extends AbstractBlenderHelper { float colfac = ((Number) mtex.getFieldValue("colfac")).floatValue(); texture = textureHelper.blendTexture(new float[] {1, 1, 1}, texture, color, colfac, blendType, negateTexture, dataRepository); texture.setWrap(WrapMode.Repeat); - + //TODO: textures merging if (materialContext.shadeless) { texturesMap.put(firstTextureType==Type.ThreeDimensional ? TEXTURE_TYPE_3D : TEXTURE_TYPE_COLOR, texture); } else { @@ -316,6 +326,7 @@ public class MaterialHelper extends AbstractBlenderHelper { result.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); } + dataRepository.setMaterialContext(result, materialContext); dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, result); return result; } 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 8064b7ccd..40b261327 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 @@ -63,6 +63,7 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.file.DynamicArray; import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Structure; +import com.jme3.scene.plugins.blender.materials.MaterialContext; import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.scene.plugins.blender.objects.Properties; import com.jme3.scene.plugins.blender.textures.TextureHelper; @@ -391,9 +392,24 @@ public class MeshHelper extends AbstractBlenderHelper { for(Geometry geom : geometries) { geom.getMesh().setBuffer(uvCoordsBuffer); } - } else {//TODO: get the proper texture coordinates type - UVCoordinatesGenerator.generateUVCoordinates(UVCoordinatesGenerator.TEXCO_ORCO, - com.jme3.texture.Texture.Type.ThreeDimensional, geometries); + } else { + Map> materialMap = new HashMap>(); + for(Geometry geom : geometries) { + Material material = geom.getMaterial(); + List geomsWithCommonMaterial = materialMap.get(material); + if(geomsWithCommonMaterial==null) { + geomsWithCommonMaterial = new ArrayList(); + materialMap.put(material, geomsWithCommonMaterial); + } + geomsWithCommonMaterial.add(geom); + + } + for(Entry> entry : materialMap.entrySet()) { + MaterialContext materialContext = dataRepository.getMaterialContext(entry.getKey()); + UVCoordinatesGenerator.generateUVCoordinates(materialContext.getUvCoordinatesType(), + materialContext.getProjectionType(), + materialContext.getTextureDimension(), entry.getValue()); + } } dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java index e4fcf684e..1447871f0 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java @@ -38,6 +38,7 @@ import java.util.logging.Logger; import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingSphere; import com.jme3.bounding.BoundingVolume; +import com.jme3.math.Triangle; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; @@ -45,7 +46,6 @@ import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.texture.Texture.Type; import com.jme3.util.BufferUtils; /** @@ -55,6 +55,7 @@ import com.jme3.util.BufferUtils; public class UVCoordinatesGenerator { private static final Logger LOGGER = Logger.getLogger(UVCoordinatesGenerator.class.getName()); + // texture UV coordinates types public static final int TEXCO_ORCO = 1; public static final int TEXCO_REFL = 2; public static final int TEXCO_NORM = 4; @@ -70,49 +71,61 @@ public class UVCoordinatesGenerator { public static final int TEXCO_TANGENT = 4096; // still stored in vertex->accum, 1 D public static final int TEXCO_PARTICLE_OR_STRAND = 8192; // strand is used - // for normal - // materials, - // particle for halo - // materials public static final int TEXCO_STRESS = 16384; public static final int TEXCO_SPEED = 32768; + // 2D texture mapping (projection) + public static final int PROJECTION_FLAT = 0; + public static final int PROJECTION_CUBE = 1; + public static final int PROJECTION_TUBE = 2; + public static final int PROJECTION_SPHERE = 3; + /** - * This method generates UV coordinates for the given geometries. + * This method generates UV coordinates for the given mesh. + * IMPORTANT! This method assumes that all geometries represent one node. + * Each containing mesh with separate material. + * So all meshes have the same reference to vertex table which stores all their vertices. * @param texco * texture coordinates type - * @param textureType - * the type of the texture (only 2D and 3D) + * @param projection + * the projection type for 2D textures + * @param textureDimension + * the dimension of the texture (only 2D and 3D) * @param geometries - * a list of geometries that will have coordinates applied + * a list of geometries the UV coordinates will be applied to */ - public static void generateUVCoordinates(int texco, Type textureType, List geometries) { - for (Geometry geometry : geometries) { - UVCoordinatesGenerator.generateUVCoordinates(texco, textureType, geometry.getMesh()); + public static void generateUVCoordinates(int texco, int projection, int textureDimension, List geometries) { + if (textureDimension != 2 && textureDimension != 3) { + throw new IllegalStateException("Unsupported texture dimension: " + textureDimension); } - } - /** - * This method generates UV coordinates for the given mesh. - * @param texco - * texture coordinates type - * @param textureType - * the type of the texture (only 2D and 3D) - * @param mesh - * a mesh that will have coordinates applied - */ - public static void generateUVCoordinates(int texco, Type textureType, Mesh mesh) { - VertexBuffer result = null; + VertexBuffer result = new VertexBuffer(VertexBuffer.Type.TexCoord); + Mesh mesh = geometries.get(0).getMesh(); + BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries); + switch (texco) { case TEXCO_ORCO: - if (textureType == Type.TwoDimensional) { - - } else if (textureType == Type.ThreeDimensional) { - BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(mesh); - - result = new VertexBuffer(com.jme3.scene.VertexBuffer.Type.TexCoord); - FloatBuffer positions = mesh.getFloatBuffer(com.jme3.scene.VertexBuffer.Type.Position); - float[] uvCoordinates = BufferUtils.getFloatArray(positions); + float[] uvCoordinates = null; + if (textureDimension == 2) { + switch (projection) { + case PROJECTION_FLAT: + uvCoordinates = UVCoordinatesGenerator.flatProjection(mesh, bb); + break; + case PROJECTION_CUBE: + uvCoordinates = UVCoordinatesGenerator.cubeProjection(mesh, bb); + break; + case PROJECTION_TUBE: + uvCoordinates = UVCoordinatesGenerator.tubeProjection(mesh, bb); + break; + case PROJECTION_SPHERE: + uvCoordinates = UVCoordinatesGenerator.sphereProjection(mesh, bb); + break; + default: + throw new IllegalStateException("Unknown projection type: " + projection); + } + } else { + FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position); + uvCoordinates = BufferUtils.getFloatArray(positions); Vector3f min = bb.getMin(null); float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 }; @@ -122,10 +135,22 @@ public class UVCoordinatesGenerator { uvCoordinates[i + 1] = (uvCoordinates[i + 1] - min.y) / ext[1]; uvCoordinates[i + 2] = (uvCoordinates[i + 2] - min.z) / ext[2]; } - - result.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(uvCoordinates)); + result.setupData(Usage.Static, textureDimension, Format.Float, BufferUtils.createFloatBuffer(uvCoordinates)); + } + result.setupData(Usage.Static, textureDimension, Format.Float, BufferUtils.createFloatBuffer(uvCoordinates)); + break; + case TEXCO_UV: + if (textureDimension == 2) { + FloatBuffer uvCoordinatesBuffer = BufferUtils.createFloatBuffer(mesh.getVertexCount() << 1); + Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) }; + for (int i = 0; i < mesh.getVertexCount(); ++i) { + Vector2f uv = data[i % 3]; + uvCoordinatesBuffer.put(uv.x); + uvCoordinatesBuffer.put(uv.y); + } + result.setupData(Usage.Static, textureDimension, Format.Float, uvCoordinatesBuffer); } else { - throw new IllegalStateException("Unsupported texture type: " + textureType); + } break; case TEXCO_GLOB: @@ -133,8 +158,6 @@ public class UVCoordinatesGenerator { break; case TEXCO_TANGENT: - break; - case TEXCO_UV: break; case TEXCO_STRESS: @@ -157,38 +180,110 @@ public class UVCoordinatesGenerator { throw new IllegalStateException("Unknown texture coordinates value: " + texco); } - mesh.clearBuffer(VertexBuffer.Type.TexCoord);// in case there are coordinates already set - mesh.setBuffer(result); + // each mesh will have the same coordinates + for (Geometry geometry : geometries) { + mesh = geometry.getMesh(); + mesh.clearBuffer(VertexBuffer.Type.TexCoord);// in case there are coordinates already set + mesh.setBuffer(result); + } } /** * Flat projection for 2D textures. * @param mesh * mesh that is to be projected + * @param bb + * the bounding box for projecting * @return UV coordinates after the projection */ - public Vector2f[] flatProjection(Mesh mesh) { - return null;// TODO: implement + private static float[] flatProjection(Mesh mesh, BoundingBox bb) { + if (bb == null) { + bb = UVCoordinatesGenerator.getBoundingBox(mesh); + } + Vector3f min = bb.getMin(null); + float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f }; + FloatBuffer positions = mesh.getFloatBuffer(com.jme3.scene.VertexBuffer.Type.Position); + float[] uvCoordinates = new float[positions.limit() / 3 * 2]; + for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) { + uvCoordinates[j] = (positions.get(i) - min.x) / ext[0]; + uvCoordinates[j + 1] = (positions.get(i + 1) - min.y) / ext[1]; + // skip the Z-coordinate + } + return uvCoordinates; } /** * Cube projection for 2D textures. * @param mesh * mesh that is to be projected + * @param bb + * the bounding box for projecting * @return UV coordinates after the projection */ - public Vector2f[] cubeProjection(Mesh mesh) { - return null;// TODO: implement + private static float[] cubeProjection(Mesh mesh, BoundingBox bb) { + Triangle triangle = new Triangle(); + Vector3f x = new Vector3f(1, 0, 0); + Vector3f y = new Vector3f(0, 1, 0); + Vector3f z = new Vector3f(0, 0, 1); + Vector3f min = bb.getMin(null); + float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f }; + + float[] uvCoordinates = new float[mesh.getTriangleCount() * 6];// 6 == 3 * 2 + float borderAngle = (float)Math.sqrt(2.0f)/2.0f; + for (int i = 0, pointIndex = 0; i < mesh.getTriangleCount(); ++i) { + mesh.getTriangle(i, triangle); + Vector3f n = triangle.getNormal(); + float dotNX = Math.abs(n.dot(x)); + float dorNY = Math.abs(n.dot(y)); + float dotNZ = Math.abs(n.dot(z)); + if (dotNX > borderAngle) { + if (dotNZ < borderAngle) {// discard X-coordinate + uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1]; + uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2]; + uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1]; + uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2]; + uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1]; + uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2]; + } else {// discard Z-coordinate + uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1]; + uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1]; + uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1]; + } + } else { + if (dorNY > borderAngle) {// discard Y-coordinate + uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2]; + uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2]; + uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2]; + } else {// discard Z-coordinate + uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1]; + uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1]; + uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0]; + uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1]; + } + } + triangle.setNormal(null);//clear the previous normal vector + } + return uvCoordinates; } /** * Tube projection for 2D textures. * @param mesh * mesh that is to be projected + * @param bb + * the bounding box for projecting * @return UV coordinates after the projection */ - public Vector2f[] tubeProjection(Mesh mesh) { + private static float[] tubeProjection(Mesh mesh, BoundingBox bb) { return null;// TODO: implement } @@ -196,9 +291,11 @@ public class UVCoordinatesGenerator { * Sphere projection for 2D textures. * @param mesh * mesh that is to be projected + * @param bb + * the bounding box for projecting * @return UV coordinates after the projection */ - public Vector2f[] sphereProjection(Mesh mesh) { + private static float[] sphereProjection(Mesh mesh, BoundingBox bb) { return null;// TODO: implement // Vector2f[] uvTable = new Vector2f[vertexList.size()]; // Ray ray = new Ray();