159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/*
259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  Copyright (c) 2009-2012 jMonkeyEngine
359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  All rights reserved.
459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  Redistribution and use in source and binary forms, with or without
659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  modification, are permitted provided that the following conditions are
759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  met:
859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  * Redistributions of source code must retain the above copyright
1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *    notice, this list of conditions and the following disclaimer.
1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  * Redistributions in binary form must reproduce the above copyright
1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *    notice, this list of conditions and the following disclaimer in the
1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *    documentation and/or other materials provided with the distribution.
1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  * Neither the name of 'jMonkeyEngine' nor the names of its contributors
1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *    may be used to endorse or promote products derived from this software
1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *    without specific prior written permission.
1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage jme3tools.optimize;
3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.asset.AssetKey;
3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.asset.AssetManager;
3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.material.MatParamTexture;
3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.material.Material;
3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Vector2f;
3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Geometry;
4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Mesh;
4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Spatial;
4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer;
4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer.Type;
4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.texture.Image;
4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.texture.Image.Format;
4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.texture.Texture;
4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.texture.Texture2D;
4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.util.BufferUtils;
4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.lang.reflect.InvocationTargetException;
5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.ByteBuffer;
5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.FloatBuffer;
5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.ArrayList;
5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.HashMap;
5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.List;
5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.Map;
5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.TreeMap;
5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Level;
5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger;
5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/**
6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <b><code>TextureAtlas</code></b> allows combining multiple textures to one texture atlas.
6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p>After the TextureAtlas has been created with a certain size, textures can be added for
6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * freely chosen "map names". The textures are automatically placed on the atlas map and the
6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * image data is stored in a byte array for each map name. Later each map can be retrieved as
6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * a Texture to be used further in materials.</p>
6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p>The first map name used is the "master map" that defines new locations on the atlas. Secondary
6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * textures (other map names) have to reference a texture of the master map to position the texture
7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * on the secondary map. This is necessary as the maps share texture coordinates and thus need to be
7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * placed at the same location on both maps.</p>
7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p>The helper methods that work with <code>Geometry</code> objects handle the <em>DiffuseMap</em> or <em>ColorMap</em> as the master map and
7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * additionally handle <em>NormalMap</em> and <em>SpecularMap</em> as secondary maps.</p>
7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p>The textures are referenced by their <b>asset key name</b> and for each texture the location
7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * inside the atlas is stored. A texture with an existing key name is never added more than once
7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * to the atlas. You can access the information for each texture or geometry texture via helper methods.</p>
7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p>The TextureAtlas also allows you to change the texture coordinates of a mesh or geometry
8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * to point at the new locations of its texture inside the atlas (if the texture exists inside the atlas).</p>
8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p>Note that models that use texture coordinates outside the 0-1 range (repeating/wrapping textures)
8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * will not work correctly as their new coordinates leak into other parts of the atlas and thus display
8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * other textures instead of repeating the texture.</p>
8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p>Also note that textures are not scaled and the atlas needs to be large enough to hold all textures.
8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * All methods that allow adding textures return false if the texture could not be added due to the
8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * atlas being full. Furthermore secondary textures (normal, spcular maps etc.) have to be the same size
9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * as the main (e.g. DiffuseMap) texture.</p>
9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <p><b>Usage examples</b></p>
9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Create one geometry out of several geometries that are loaded from a j3o file:
9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <pre>
9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Node scene = assetManager.loadModel("Scenes/MyScene.j3o");
9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Geometry geom = TextureAtlas.makeAtlasBatch(scene);
9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * rootNode.attachChild(geom);
9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * </pre>
9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Create a texture atlas and change the texture coordinates of one geometry:
10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * <pre>
10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Node scene = assetManager.loadModel("Scenes/MyScene.j3o");
10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * //either auto-create from node:
10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * TextureAtlas atlas = TextureAtlas.createAtlas(scene);
10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * //or create manually by adding textures or geometries with textures
10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * TextureAtlas atlas = new TextureAtlas(1024,1024);
10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * atlas.addTexture(myTexture, "DiffuseMap");
10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * atlas.addGeometry(myGeometry);
10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * //create material and set texture
10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md");
11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * mat.setTexture("DiffuseMap", atlas.getAtlasTexture("DiffuseMap"));
11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * //change one geometry to use atlas, apply texture coordinates and replace material.
11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Geometry geom = scene.getChild("MyGeometry");
11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * atlas.applyCoords(geom);
11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * geom.setMaterial(mat);
11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * </pre>
11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author normenhansen, Lukasz Bruun - lukasz.dk
11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class TextureAtlas {
12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private static final Logger logger = Logger.getLogger(TextureAtlas.class.getName());
12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Map<String, byte[]> images;
12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int atlasWidth, atlasHeight;
12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Format format = Format.ABGR8;
12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Node root;
12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Map<String, TextureAtlasTile> locationMap;
12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Map<String, String> mapNameMap;
12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private String rootMapName;
12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TextureAtlas(int width, int height) {
13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.atlasWidth = width;
13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.atlasHeight = height;
13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        root = new Node(0, 0, width, height);
13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        locationMap = new TreeMap<String, TextureAtlasTile>();
13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        mapNameMap = new HashMap<String, String>();
13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Add a geometries DiffuseMap (or ColorMap), NormalMap and SpecularMap to the atlas.
14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param geometry
14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return false if the atlas is full.
14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public boolean addGeometry(Geometry geometry) {
14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture diffuse = getMaterialTexture(geometry, "DiffuseMap");
14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture normal = getMaterialTexture(geometry, "NormalMap");
14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture specular = getMaterialTexture(geometry, "SpecularMap");
14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (diffuse == null) {
14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            diffuse = getMaterialTexture(geometry, "ColorMap");
14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (diffuse != null && diffuse.getKey() != null) {
15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            String keyName = diffuse.getKey().toString();
15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!addTexture(diffuse, "DiffuseMap")) {
15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return false;
15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else {
15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (normal != null && normal.getKey() != null) {
15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    addTexture(diffuse, "NormalMap", keyName);
15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (specular != null && specular.getKey() != null) {
16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    addTexture(specular, "SpecularMap", keyName);
16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return true;
16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return true;
16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Add a texture for a specific map name
17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param texture A texture to add to the atlas.
17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mapName A freely chosen map name that can be later retrieved as a Texture. The first map name supplied will be the master map.
17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return false if the atlas is full.
17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public boolean addTexture(Texture texture, String mapName) {
17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (texture == null) {
17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Texture cannot be null!");
17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        String name = textureName(texture);
17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (texture.getImage() != null && name != null) {
18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return addImage(texture.getImage(), name, mapName, null);
18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Texture has no asset key name!");
18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Add a texture for a specific map name at the location of another existing texture on the master map.
18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param texture A texture to add to the atlas.
18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mapName A freely chosen map name that can be later retrieved as a Texture.
19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param masterTexture The master texture for determining the location, it has to exist in tha master map.
19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void addTexture(Texture texture, String mapName, Texture masterTexture) {
19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        String sourceTextureName = textureName(masterTexture);
19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (sourceTextureName == null) {
19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Supplied master map texture has no asset key name!");
19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            addTexture(texture, mapName, sourceTextureName);
19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Add a texture for a specific map name at the location of another existing texture (on the master map).
20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param texture A texture to add to the atlas.
20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mapName A freely chosen map name that can be later retrieved as a Texture.
20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param sourceTextureName Name of the master map used for the location.
20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void addTexture(Texture texture, String mapName, String sourceTextureName) {
20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (texture == null) {
20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Texture cannot be null!");
21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        String name = textureName(texture);
21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (texture.getImage() != null && name != null) {
21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            addImage(texture.getImage(), name, mapName, sourceTextureName);
21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Texture has no asset key name!");
21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private String textureName(Texture texture) {
22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (texture == null) {
22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        AssetKey key = texture.getKey();
22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (key != null) {
22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return key.toString();
22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private boolean addImage(Image image, String name, String mapName, String sourceTextureName) {
23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (rootMapName == null) {
23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            rootMapName = mapName;
23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (sourceTextureName == null && !rootMapName.equals(mapName)) {
23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Atlas already has a master map called " + rootMapName + "."
23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    + " Textures for new maps have to use a texture from the master map for their location.");
23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TextureAtlasTile location = locationMap.get(name);
24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (location != null) {
24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //have location for texture
24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!mapName.equals(mapNameMap.get(name))) {
24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                logger.log(Level.WARNING, "Same texture " + name + " is used in different maps! (" + mapName + " and " + mapNameMap.get(name) + "). Location will be based on location in " + mapNameMap.get(name) + "!");
24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                drawImage(image, location.getX(), location.getY(), mapName);
24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return true;
24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else {
24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return true;
24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (sourceTextureName == null) {
25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //need to make new tile
25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Node node = root.insert(image);
25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (node == null) {
25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return false;
25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            location = node.location;
25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //got old tile to align to
25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            location = locationMap.get(sourceTextureName);
25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (location == null) {
26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                throw new IllegalStateException("Cannot find master map texture for " + name + ".");
26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (location.width != image.getWidth() || location.height != image.getHeight()) {
26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                throw new IllegalStateException(mapName + " " + name + " does not fit " + rootMapName + " tile size. Make sure all textures (diffuse, normal, specular) for one model are the same size.");
26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        mapNameMap.put(name, mapName);
26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        locationMap.put(name, location);
26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        drawImage(image, location.getX(), location.getY(), mapName);
26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return true;
26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private void drawImage(Image source, int x, int y, String mapName) {
27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (images == null) {
27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            images = new HashMap<String, byte[]>();
27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        byte[] image = images.get(mapName);
27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (image == null) {
27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            image = new byte[atlasWidth * atlasHeight * 4];
27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            images.put(mapName, image);
27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //TODO: all buffers?
28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ByteBuffer sourceData = source.getData(0);
28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int height = source.getHeight();
28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int width = source.getWidth();
28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Image newImage = null;
28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int yPos = 0; yPos < height; yPos++) {
28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int xPos = 0; xPos < width; xPos++) {
28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int i = ((xPos + x) + (yPos + y) * atlasWidth) * 4;
28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (source.getFormat() == Format.ABGR8) {
28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int j = (xPos + yPos * width) * 4;
29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i] = sourceData.get(j); //a
29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 1] = sourceData.get(j + 1); //b
29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 2] = sourceData.get(j + 2); //g
29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 3] = sourceData.get(j + 3); //r
29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (source.getFormat() == Format.BGR8) {
29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int j = (xPos + yPos * width) * 3;
29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i] = 1; //a
29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 1] = sourceData.get(j); //b
29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 2] = sourceData.get(j + 1); //g
29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 3] = sourceData.get(j + 2); //r
30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (source.getFormat() == Format.RGB8) {
30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int j = (xPos + yPos * width) * 3;
30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i] = 1; //a
30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 1] = sourceData.get(j + 2); //b
30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 2] = sourceData.get(j + 1); //g
30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 3] = sourceData.get(j); //r
30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (source.getFormat() == Format.RGBA8) {
30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int j = (xPos + yPos * width) * 4;
30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i] = sourceData.get(j + 3); //a
30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 1] = sourceData.get(j + 2); //b
31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 2] = sourceData.get(j + 1); //g
31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 3] = sourceData.get(j); //r
31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (source.getFormat() == Format.Luminance8) {
31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int j = (xPos + yPos * width) * 1;
31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i] = 1; //a
31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 1] = sourceData.get(j); //b
31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 2] = sourceData.get(j); //g
31759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 3] = sourceData.get(j); //r
31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (source.getFormat() == Format.Luminance8Alpha8) {
31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int j = (xPos + yPos * width) * 2;
32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i] = sourceData.get(j + 1); //a
32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 1] = sourceData.get(j); //b
32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 2] = sourceData.get(j); //g
32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    image[i + 3] = sourceData.get(j); //r
32459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else {
32559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    //ImageToAwt conversion
32659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (newImage == null) {
32759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        newImage = convertImageToAwt(source);
32859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (newImage != null) {
32959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            source = newImage;
33059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            sourceData = source.getData(0);
33159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            int j = (xPos + yPos * width) * 4;
33259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            image[i] = sourceData.get(j); //a
33359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            image[i + 1] = sourceData.get(j + 1); //b
33459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            image[i + 2] = sourceData.get(j + 2); //g
33559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            image[i + 3] = sourceData.get(j + 3); //r
33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }else{
33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            throw new UnsupportedOperationException("Cannot draw or convert textures with format " + source.getFormat());
33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else {
34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        throw new UnsupportedOperationException("Cannot draw textures with format " + source.getFormat());
34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Image convertImageToAwt(Image source) {
34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //use awt dependent classes without actual dependency via reflection
34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        try {
35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Class clazz = Class.forName("jme3tools.converters.ImageToAwt");
35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (clazz == null) {
35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return null;
35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Image newImage = new Image(format, source.getWidth(), source.getHeight(), BufferUtils.createByteBuffer(source.getWidth() * source.getHeight() * 4));
35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            clazz.getMethod("convert", Image.class, Image.class).invoke(clazz.newInstance(), source, newImage);
35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return newImage;
35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (InstantiationException ex) {
35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (IllegalAccessException ex) {
35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (IllegalArgumentException ex) {
36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (InvocationTargetException ex) {
36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (NoSuchMethodException ex) {
36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (SecurityException ex) {
36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } catch (ClassNotFoundException ex) {
36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Get the <code>TextureAtlasTile</code> for the given Texture
37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param texture The texture to retrieve the <code>TextureAtlasTile</code> for.
37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return
37259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TextureAtlasTile getAtlasTile(Texture texture) {
37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        String sourceTextureName = textureName(texture);
37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (sourceTextureName != null) {
37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getAtlasTile(sourceTextureName);
37759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
37859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
37959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
38259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Get the <code>TextureAtlasTile</code> for the given Texture
38359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param assetName The texture to retrieve the <code>TextureAtlasTile</code> for.
38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return
38559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
38659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private TextureAtlasTile getAtlasTile(String assetName) {
38759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return locationMap.get(assetName);
38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
38959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
39159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Creates a new atlas texture for the given map name.
39259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mapName
39359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return
39459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
39559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Texture getAtlasTexture(String mapName) {
39659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (images == null) {
39759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
39859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
39959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        byte[] image = images.get(mapName);
40059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (image != null) {
40159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Texture2D tex = new Texture2D(new Image(format, atlasWidth, atlasHeight, BufferUtils.createByteBuffer(image)));
40259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            tex.setMagFilter(Texture.MagFilter.Bilinear);
40359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            tex.setMinFilter(Texture.MinFilter.BilinearNearestMipMap);
40459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            tex.setWrap(Texture.WrapMode.Clamp);
40559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return tex;
40659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
40759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
40859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
40959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
41059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
41159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Applies the texture coordinates to the given geometry
41259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * if its DiffuseMap or ColorMap exists in the atlas.
41359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param geom The geometry to change the texture coordinate buffer on.
41459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return true if texture has been found and coords have been changed, false otherwise.
41559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
41659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public boolean applyCoords(Geometry geom) {
41759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return applyCoords(geom, 0, geom.getMesh());
41859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
41959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
42159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Applies the texture coordinates to the given output mesh
42259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * if the DiffuseMap or ColorMap of the input geometry exist in the atlas.
42359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param geom The geometry to change the texture coordinate buffer on.
42459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offset Target buffer offset.
42559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param outMesh The mesh to set the coords in (can be same as input).
42659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return true if texture has been found and coords have been changed, false otherwise.
42759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
42859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public boolean applyCoords(Geometry geom, int offset, Mesh outMesh) {
42959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Mesh inMesh = geom.getMesh();
43059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        geom.computeWorldMatrix();
43159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        VertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord);
43359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        VertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord);
43459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (inBuf == null || outBuf == null) {
43659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Geometry mesh has no texture coordinate buffer.");
43759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
43859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture tex = getMaterialTexture(geom, "DiffuseMap");
44059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (tex == null) {
44159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            tex = getMaterialTexture(geom, "ColorMap");
44259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
44359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
44459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (tex != null) {
44559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TextureAtlasTile tile = getAtlasTile(tex);
44659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (tile != null) {
44759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                FloatBuffer inPos = (FloatBuffer) inBuf.getData();
44859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                FloatBuffer outPos = (FloatBuffer) outBuf.getData();
44959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                tile.transformTextureCoords(inPos, offset, outPos);
45059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return true;
45159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else {
45259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return false;
45359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
45459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
45559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Geometry has no proper texture.");
45659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
45759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
45859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
45959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
46059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Create a texture atlas for the given root node, containing DiffuseMap, NormalMap and SpecularMap.
46159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param root The rootNode to create the atlas for.
46259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param atlasSize The size of the atlas (width and height).
46359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return Null if the atlas cannot be created because not all textures fit.
46459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
46559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public static TextureAtlas createAtlas(Spatial root, int atlasSize) {
46659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Geometry> geometries = new ArrayList<Geometry>();
46759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        GeometryBatchFactory.gatherGeoms(root, geometries);
46859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TextureAtlas atlas = new TextureAtlas(atlasSize, atlasSize);
46959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (Geometry geometry : geometries) {
47059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!atlas.addGeometry(geometry)) {
47159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                logger.log(Level.WARNING, "Texture atlas size too small, cannot add all textures");
47259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return null;
47359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
47459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
47559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return atlas;
47659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
47759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
47959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Creates one geometry out of the given root spatial and merges all single
48059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * textures into one texture of the given size.
48159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param spat The root spatial of the scene to batch
48259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param mgr An assetmanager that can be used to create the material.
48359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param atlasSize A size for the atlas texture, it has to be large enough to hold all single textures.
48459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return A new geometry that uses the generated texture atlas and merges all meshes of the root spatial, null if the atlas cannot be created because not all textures fit.
48559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
48659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public static Geometry makeAtlasBatch(Spatial spat, AssetManager mgr, int atlasSize) {
48759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Geometry> geometries = new ArrayList<Geometry>();
48859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        GeometryBatchFactory.gatherGeoms(spat, geometries);
48959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TextureAtlas atlas = createAtlas(spat, atlasSize);
49059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (atlas == null) {
49159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
49259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
49359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Geometry geom = new Geometry();
49459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Mesh mesh = new Mesh();
49559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        GeometryBatchFactory.mergeGeometries(geometries, mesh);
49659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        applyAtlasCoords(geometries, mesh, atlas);
49759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        mesh.updateCounts();
49859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        mesh.updateBound();
49959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        geom.setMesh(mesh);
50059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
50159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md");
50259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        mat.getAdditionalRenderState().setAlphaTest(true);
50359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap");
50459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture normalMap = atlas.getAtlasTexture("NormalMap");
50559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture specularMap = atlas.getAtlasTexture("SpecularMap");
50659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (diffuseMap != null) {
50759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            mat.setTexture("DiffuseMap", diffuseMap);
50859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
50959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (normalMap != null) {
51059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            mat.setTexture("NormalMap", normalMap);
51159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
51259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (specularMap != null) {
51359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            mat.setTexture("SpecularMap", specularMap);
51459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
51559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        mat.setFloat("Shininess", 16.0f);
51659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
51759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        geom.setMaterial(mat);
51859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return geom;
51959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
52059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private static void applyAtlasCoords(List<Geometry> geometries, Mesh outMesh, TextureAtlas atlas) {
52259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int globalVertIndex = 0;
52359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (Geometry geom : geometries) {
52559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Mesh inMesh = geom.getMesh();
52659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            geom.computeWorldMatrix();
52759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int geomVertCount = inMesh.getVertexCount();
52959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            VertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord);
53159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            VertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord);
53259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (inBuf == null || outBuf == null) {
53459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                continue;
53559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
53659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            atlas.applyCoords(geom, globalVertIndex, outMesh);
53859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            globalVertIndex += geomVertCount;
54059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
54159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
54259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
54359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private static Texture getMaterialTexture(Geometry geometry, String mapName) {
54459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Material mat = geometry.getMaterial();
54559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (mat == null || mat.getParam(mapName) == null || !(mat.getParam(mapName) instanceof MatParamTexture)) {
54659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
54759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
54859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        MatParamTexture param = (MatParamTexture) mat.getParam(mapName);
54959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Texture texture = param.getTextureValue();
55059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (texture == null) {
55159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
55259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
55359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return texture;
55459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
55559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
55659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
55759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
55859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private class Node {
55959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
56059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public TextureAtlasTile location;
56159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public Node child[];
56259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public boolean occupied;
56359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
56459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public Node(int x, int y, int width, int height) {
56559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            location = new TextureAtlasTile(x, y, width, height);
56659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            child = new Node[2];
56759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            child[0] = null;
56859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            child[1] = null;
56959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            occupied = false;
57059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
57159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
57259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public boolean isLeaf() {
57359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return child[0] == null && child[1] == null;
57459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
57559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
57659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // Algorithm from http://www.blackpawn.com/texts/lightmaps/
57759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public Node insert(Image image) {
57859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!isLeaf()) {
57959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Node newNode = child[0].insert(image);
58059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
58159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (newNode != null) {
58259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return newNode;
58359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
58459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
58559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return child[1].insert(image);
58659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else {
58759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (occupied) {
58859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return null; // occupied
58959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
59059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
59159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (image.getWidth() > location.getWidth() || image.getHeight() > location.getHeight()) {
59259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return null; // does not fit
59359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
59459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
59559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (image.getWidth() == location.getWidth() && image.getHeight() == location.getHeight()) {
59659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    occupied = true; // perfect fit
59759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return this;
59859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
59959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
60059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int dw = location.getWidth() - image.getWidth();
60159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int dh = location.getHeight() - image.getHeight();
60259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
60359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (dw > dh) {
60459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    child[0] = new Node(location.getX(), location.getY(), image.getWidth(), location.getHeight());
60559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    child[1] = new Node(location.getX() + image.getWidth(), location.getY(), location.getWidth() - image.getWidth(), location.getHeight());
60659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else {
60759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    child[0] = new Node(location.getX(), location.getY(), location.getWidth(), image.getHeight());
60859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    child[1] = new Node(location.getX(), location.getY() + image.getHeight(), location.getWidth(), location.getHeight() - image.getHeight());
60959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
61059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
61159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return child[0].insert(image);
61259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
61359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
61459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
61559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
61659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public class TextureAtlasTile {
61759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
61859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        private int x;
61959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        private int y;
62059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        private int width;
62159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        private int height;
62259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
62359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public TextureAtlasTile(int x, int y, int width, int height) {
62459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.x = x;
62559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.y = y;
62659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.width = width;
62759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.height = height;
62859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
62959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
63059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        /**
63159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         * Get the transformed texture coordinate for a given input location.
63259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         * @param previousLocation The old texture coordinate.
63359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         * @return The new texture coordinate inside the atlas.
63459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         */
63559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public Vector2f getLocation(Vector2f previousLocation) {
63659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            float x = (float) getX() / (float) atlasWidth;
63759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            float y = (float) getY() / (float) atlasHeight;
63859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            float w = (float) getWidth() / (float) atlasWidth;
63959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            float h = (float) getHeight() / (float) atlasHeight;
64059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector2f location = new Vector2f(x, y);
64159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            float prevX = previousLocation.x;
64259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            float prevY = previousLocation.y;
64359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            location.addLocal(prevX * w, prevY * h);
64459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return location;
64559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
64659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
64759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        /**
64859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         * Transforms a whole texture coordinates buffer.
64959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         * @param inBuf The input texture buffer.
65059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         * @param offset The offset in the output buffer
65159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         * @param outBuf The output buffer.
65259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta         */
65359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public void transformTextureCoords(FloatBuffer inBuf, int offset, FloatBuffer outBuf) {
65459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector2f tex = new Vector2f();
65559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
65659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // offset is given in element units
65759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // convert to be in component units
65859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            offset *= 2;
65959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
66059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = 0; i < inBuf.capacity() / 2; i++) {
66159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                tex.x = inBuf.get(i * 2 + 0);
66259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                tex.y = inBuf.get(i * 2 + 1);
66359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Vector2f location = getLocation(tex);
66459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //TODO: add proper texture wrapping for atlases..
66559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                outBuf.put(offset + i * 2 + 0, location.x);
66659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                outBuf.put(offset + i * 2 + 1, location.y);
66759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
66859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
66959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
67059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public int getX() {
67159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return x;
67259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
67359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
67459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public int getY() {
67559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return y;
67659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
67759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
67859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public int getWidth() {
67959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return width;
68059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
68159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
68259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public int getHeight() {
68359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return height;
68459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
68559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
68659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta}
687