diff --git a/engine/src/test/jme3test/tools/TestTextureAtlas.java b/engine/src/test/jme3test/tools/TestTextureAtlas.java new file mode 100644 index 000000000..130b314a7 --- /dev/null +++ b/engine/src/test/jme3test/tools/TestTextureAtlas.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package jme3test.tools; + +import jme3test.model.shape.*; +import com.jme3.app.SimpleApplication; +import com.jme3.asset.plugins.ZipLocator; +import com.jme3.light.AmbientLight; +import com.jme3.light.DirectionalLight; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import jme3tools.optimize.GeometryBatchFactory; + +public class TestTextureAtlas extends SimpleApplication { + + public static void main(String[] args){ + TestTextureAtlas app = new TestTextureAtlas(); + app.start(); + } + + @Override + public void simpleInitApp() { + assetManager.registerLocator("wildhouse.zip", ZipLocator.class.getName()); + Spatial scene = assetManager.loadModel("main.scene"); + + Geometry geom = GeometryBatchFactory.makeAtlasBatch(scene, assetManager, 4096); + + AmbientLight al = new AmbientLight(); + scene.addLight(al); + + DirectionalLight sun = new DirectionalLight(); + sun.setDirection(new Vector3f(0.69077975f, -0.6277887f, -0.35875428f).normalizeLocal()); + sun.setColor(ColorRGBA.White.clone().multLocal(2)); + scene.addLight(sun); + + rootNode.attachChild(geom); + } + +} diff --git a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java index abb89a5f0..72d72a7c4 100644 --- a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java +++ b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java @@ -1,5 +1,7 @@ package jme3tools.optimize; +import com.jme3.asset.AssetManager; +import com.jme3.material.MatParamTexture; import com.jme3.material.Material; import com.jme3.math.Matrix4f; import com.jme3.math.Transform; @@ -10,15 +12,22 @@ import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Usage; import com.jme3.scene.mesh.IndexBuffer; +import com.jme3.texture.Texture; import com.jme3.util.BufferUtils; import com.jme3.util.IntMap.Entry; +import com.jme3.util.TangentBinormalGenerator; import java.nio.Buffer; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import jme3tools.optimize.TextureAtlas.TextureAtlasTile; public class GeometryBatchFactory { + private static final Logger logger = Logger.getLogger(GeometryBatchFactory.class.getName()); + private static void doTransformVerts(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) { Vector3f pos = new Vector3f(); @@ -61,12 +70,24 @@ public class GeometryBatchFactory { /** * Merges all geometries in the collection into - * the output mesh. Does not take into account materials. + * the output mesh. Creates a new material using the TextureAtlas. * * @param geometries * @param outMesh */ public static void mergeGeometries(Collection geometries, Mesh outMesh) { + mergeGeometries(geometries, outMesh, null); + } + + /** + * Merges all geometries in the collection into + * the output mesh. Creates a new material using the TextureAtlas. + * + * @param geometries + * @param outMesh + * @param atlas the TextureAtlas to use + */ + public static void mergeGeometries(Collection geometries, Mesh outMesh, TextureAtlas atlas) { int[] compsForBuf = new int[VertexBuffer.Type.values().length]; Format[] formatForBuf = new Format[compsForBuf.length]; @@ -161,7 +182,7 @@ public class GeometryBatchFactory { if (inBuf == null || outBuf == null) { continue; } - + if (Type.Index.ordinal() == bufType) { int components = compsForBuf[bufType]; @@ -182,6 +203,20 @@ public class GeometryBatchFactory { FloatBuffer inPos = (FloatBuffer) inBuf.getData(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix); + } else if (Type.TexCoord.ordinal() == bufType && atlas != null) { + Texture tex = getMaterialTexture(geom, "DiffuseMap"); + if (tex == null) { + tex = getMaterialTexture(geom, "ColorMap"); + + } + if (tex != null && tex.getKey() != null) { + TextureAtlasTile tile = atlas.getAtlasTile(tex.getKey().getName()); + if (tile != null) { + FloatBuffer inPos = (FloatBuffer) inBuf.getData(); + FloatBuffer outPos = (FloatBuffer) outBuf.getData(); + tile.transformTextureCoords(inPos, globalVertIndex, outPos); + } + } } else { for (int vert = 0; vert < geomVertCount; vert++) { int curGlobalVertIndex = globalVertIndex + vert; @@ -288,6 +323,76 @@ public class GeometryBatchFactory { return retVal; } + public static Geometry makeAtlasBatch(Spatial spat, AssetManager mgr, int atlasSize) { + List geometries = new ArrayList(); + gatherGeoms(spat, geometries); + //TODO: specular etc. maps, needs to use main atlas for locations + TextureAtlas atlas = new TextureAtlas(atlasSize, atlasSize); + for (Geometry geometry : geometries) { + Texture diffuse = getMaterialTexture(geometry, "DiffuseMap"); + Texture normal = getMaterialTexture(geometry, "NormalMap"); + Texture specular = getMaterialTexture(geometry, "SpecularMap"); + if (diffuse == null) { + diffuse = getMaterialTexture(geometry, "ColorMap"); + + } + if (diffuse != null && diffuse.getKey() != null) { + String keyName = diffuse.getKey().getName(); + if (!atlas.addTexture(diffuse, "DiffuseMap")) { + logger.log(Level.WARNING, "Adding diffuse texture {0} to atlas failed, atlas full?", keyName); + } + if (normal != null && normal.getKey() != null) { + atlas.addTexture(diffuse, "NormalMap", keyName); + } + if (specular != null && specular.getKey() != null) { + atlas.addTexture(specular, "SpecularMap", keyName); + } + } + } + Geometry geom = new Geometry(); + Mesh mesh = new Mesh(); + mergeGeometries(geometries, mesh, atlas); + TangentBinormalGenerator.generate(mesh); + mesh.updateCounts(); + mesh.updateBound(); + geom.setMesh(mesh); +// geom.setMesh(new Box(1,1,1)); + +// Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md"); +// Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap"); +// Texture normalMap = atlas.getAtlasTexture("NormalMap"); +// Texture specularMap = atlas.getAtlasTexture("SpecularMap"); +// if (diffuseMap != null) { +// mat.setTexture("DiffuseMap", diffuseMap); +// } +// if (normalMap != null) { +// mat.setTexture("NormalMap", normalMap); +// } +// if (specularMap != null) { +// mat.setTexture("SpecularMap", specularMap); +// } +// mat.setFloat("Shininess", 16.0f); + + Material mat = new Material(mgr, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setTexture("ColorMap", atlas.getAtlasTexture("DiffuseMap")); + + geom.setMaterial(mat); + return geom; + } + + private static Texture getMaterialTexture(Geometry geometry, String mapName) { + Material mat = geometry.getMaterial(); + if (mat == null || mat.getParam(mapName) == null || !(mat.getParam(mapName) instanceof MatParamTexture)) { + return null; + } + MatParamTexture param = (MatParamTexture) mat.getParam(mapName); + Texture texture = param.getTextureValue(); + if (texture == null) { + return null; + } + return texture; + } + private static void gatherGeoms(Spatial scene, List geoms) { if (scene instanceof Node) { Node node = (Node) scene; @@ -335,7 +440,7 @@ public class GeometryBatchFactory { // Since the scene is returned unaltered the transform must be reset scene.setLocalTransform(Transform.IDENTITY); - + return scene; } diff --git a/engine/src/tools/jme3tools/optimize/TextureAtlas.java b/engine/src/tools/jme3tools/optimize/TextureAtlas.java index 3ae249b6b..c2e691f1c 100644 --- a/engine/src/tools/jme3tools/optimize/TextureAtlas.java +++ b/engine/src/tools/jme3tools/optimize/TextureAtlas.java @@ -182,7 +182,7 @@ public class TextureAtlas { Texture2D tex = new Texture2D(new Image(format, atlasWidth, atlasHeight, BufferUtils.createByteBuffer(image))); tex.setMagFilter(Texture.MagFilter.Bilinear); tex.setMinFilter(Texture.MinFilter.BilinearNearestMipMap); - tex.setWrap(Texture.WrapMode.Repeat); + tex.setWrap(Texture.WrapMode.Clamp); return tex; } return null; @@ -267,6 +267,12 @@ public class TextureAtlas { float h = (float) getHeight() / (float) atlasHeight; Vector2f location = new Vector2f(x, y); Vector2f scale = new Vector2f(w, h); +// if (previousLocation.x > 1) { +// previousLocation.x = previousLocation.x - (int) previousLocation.x; +// } +// if (previousLocation.y > 1) { +// previousLocation.y = previousLocation.y - (int) previousLocation.y; +// } return location.addLocal(previousLocation.multLocal(scale)); }