Home | History | Annotate | Download | only in textures
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.scene.plugins.blender.textures;
     33 
     34 import com.jme3.bounding.BoundingBox;
     35 import com.jme3.bounding.BoundingSphere;
     36 import com.jme3.bounding.BoundingVolume;
     37 import com.jme3.math.Vector2f;
     38 import com.jme3.math.Vector3f;
     39 import com.jme3.scene.Geometry;
     40 import com.jme3.scene.Mesh;
     41 import com.jme3.scene.VertexBuffer;
     42 import com.jme3.scene.VertexBuffer.Format;
     43 import com.jme3.scene.VertexBuffer.Usage;
     44 import com.jme3.util.BufferUtils;
     45 import java.nio.FloatBuffer;
     46 import java.util.List;
     47 import java.util.logging.Logger;
     48 
     49 /**
     50  * This class is used for UV coordinates generation.
     51  * @author Marcin Roguski (Kaelthas)
     52  */
     53 public class UVCoordinatesGenerator {
     54 	private static final Logger	LOGGER						= Logger.getLogger(UVCoordinatesGenerator.class.getName());
     55 
     56 	// texture UV coordinates types
     57 	public static final int		TEXCO_ORCO					= 1;
     58 	public static final int		TEXCO_REFL					= 2;
     59 	public static final int		TEXCO_NORM					= 4;
     60 	public static final int		TEXCO_GLOB					= 8;
     61 	public static final int		TEXCO_UV					= 16;
     62 	public static final int		TEXCO_OBJECT				= 32;
     63 	public static final int		TEXCO_LAVECTOR				= 64;
     64 	public static final int		TEXCO_VIEW					= 128;
     65 	public static final int		TEXCO_STICKY				= 256;
     66 	public static final int		TEXCO_OSA					= 512;
     67 	public static final int		TEXCO_WINDOW				= 1024;
     68 	public static final int		NEED_UV						= 2048;
     69 	public static final int		TEXCO_TANGENT				= 4096;
     70 	// still stored in vertex->accum, 1 D
     71 	public static final int		TEXCO_PARTICLE_OR_STRAND	= 8192;													// strand is used
     72 	public static final int		TEXCO_STRESS				= 16384;
     73 	public static final int		TEXCO_SPEED					= 32768;
     74 
     75 	// 2D texture mapping (projection)
     76 	public static final int		PROJECTION_FLAT				= 0;
     77 	public static final int		PROJECTION_CUBE				= 1;
     78 	public static final int		PROJECTION_TUBE				= 2;
     79 	public static final int		PROJECTION_SPHERE			= 3;
     80 
     81 	/**
     82 	 * This method generates UV coordinates for the given mesh.
     83 	 * IMPORTANT! This method assumes that all geometries represent one node.
     84 	 * Each containing mesh with separate material.
     85 	 * So all meshes have the same reference to vertex table which stores all their vertices.
     86 	 * @param texco
     87 	 *        texture coordinates type
     88 	 * @param projection
     89 	 *        the projection type for 2D textures
     90 	 * @param textureDimension
     91 	 *        the dimension of the texture (only 2D and 3D)
     92 	 * @param coordinatesSwappingIndexes
     93 	 *        an array that tells how UV-coordinates need to be swapped
     94 	 * @param geometries
     95 	 *        a list of geometries the UV coordinates will be applied to
     96 	 * @return created UV-coordinates buffer
     97 	 */
     98 	public static VertexBuffer generateUVCoordinates(int texco, int projection, int textureDimension, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
     99 		if (textureDimension != 2 && textureDimension != 3) {
    100 			throw new IllegalStateException("Unsupported texture dimension: " + textureDimension);
    101 		}
    102 
    103 		VertexBuffer result = new VertexBuffer(VertexBuffer.Type.TexCoord);
    104 		Mesh mesh = geometries.get(0).getMesh();
    105 		BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
    106 		float[] inputData = null;// positions, normals, reflection vectors, etc.
    107 
    108 		switch (texco) {
    109 			case TEXCO_ORCO:
    110 				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
    111 				break;
    112 			case TEXCO_UV:
    113 				FloatBuffer uvCoordinatesBuffer = BufferUtils.createFloatBuffer(mesh.getVertexCount() * textureDimension);
    114 				Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
    115 				for (int i = 0; i < mesh.getVertexCount(); ++i) {
    116 					Vector2f uv = data[i % 3];
    117 					uvCoordinatesBuffer.put(uv.x);
    118 					uvCoordinatesBuffer.put(uv.y);
    119 					if(textureDimension == 3) {
    120 						uvCoordinatesBuffer.put(0);
    121 					}
    122 				}
    123 				result.setupData(Usage.Static, textureDimension, Format.Float, uvCoordinatesBuffer);
    124 				break;
    125 			case TEXCO_NORM:
    126 				inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
    127 				break;
    128 			case TEXCO_REFL:
    129 			case TEXCO_GLOB:
    130 			case TEXCO_TANGENT:
    131 			case TEXCO_STRESS:
    132 			case TEXCO_LAVECTOR:
    133 			case TEXCO_OBJECT:
    134 			case TEXCO_OSA:
    135 			case TEXCO_PARTICLE_OR_STRAND:
    136 			case TEXCO_SPEED:
    137 			case TEXCO_STICKY:
    138 			case TEXCO_VIEW:
    139 			case TEXCO_WINDOW:
    140 				LOGGER.warning("Texture coordinates type not currently supported: " + texco);
    141 				break;
    142 			default:
    143 				throw new IllegalStateException("Unknown texture coordinates value: " + texco);
    144 		}
    145 
    146 		if (inputData != null) {// make calculations
    147 			if (textureDimension == 2) {
    148 				switch (projection) {
    149 					case PROJECTION_FLAT:
    150 						inputData = UVProjectionGenerator.flatProjection(mesh, bb);
    151 						break;
    152 					case PROJECTION_CUBE:
    153 						inputData = UVProjectionGenerator.cubeProjection(mesh, bb);
    154 						break;
    155 					case PROJECTION_TUBE:
    156 						BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
    157 						inputData = UVProjectionGenerator.tubeProjection(mesh, bt);
    158 						break;
    159 					case PROJECTION_SPHERE:
    160 						BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
    161 						inputData = UVProjectionGenerator.sphereProjection(mesh, bs);
    162 						break;
    163 					default:
    164 						throw new IllegalStateException("Unknown projection type: " + projection);
    165 				}
    166 			} else {
    167 				Vector3f min = bb.getMin(null);
    168 				float[] uvCoordsResults = new float[4];//used for coordinates swapping
    169 				float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 };
    170 
    171 				// now transform the coordinates so that they are in the range of <0; 1>
    172 				for (int i = 0; i < inputData.length; i += 3) {
    173 					uvCoordsResults[1] = (inputData[i] - min.x) / ext[0];
    174 					uvCoordsResults[2] = (inputData[i + 1] - min.y) / ext[1];
    175 					uvCoordsResults[3] = (inputData[i + 2] - min.z) / ext[2];
    176 
    177 
    178 					inputData[i] = uvCoordsResults[coordinatesSwappingIndexes[0]];
    179 					inputData[i + 1] = uvCoordsResults[coordinatesSwappingIndexes[1]];
    180 					inputData[i + 2] = uvCoordsResults[coordinatesSwappingIndexes[2]];
    181 				}
    182 			}
    183 			result.setupData(Usage.Static, textureDimension, Format.Float, BufferUtils.createFloatBuffer(inputData));
    184 		}
    185 
    186 		// each mesh will have the same coordinates
    187 		for (Geometry geometry : geometries) {
    188 			mesh = geometry.getMesh();
    189 			mesh.clearBuffer(VertexBuffer.Type.TexCoord);// in case there are coordinates already set
    190 			mesh.setBuffer(result);
    191 		}
    192 
    193 		return result;
    194 	}
    195 
    196 	/**
    197 	 * This method returns the bounding box of the given geometries.
    198 	 * @param geometries
    199 	 *        the list of geometries
    200 	 * @return bounding box of the given geometries
    201 	 */
    202 	/* package */static BoundingBox getBoundingBox(List<Geometry> geometries) {
    203 		BoundingBox result = null;
    204 		for (Geometry geometry : geometries) {
    205 			BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry.getMesh());
    206 			if (result == null) {
    207 				result = bb;
    208 			} else {
    209 				result.merge(bb);
    210 			}
    211 		}
    212 		return result;
    213 	}
    214 
    215 	/**
    216 	 * This method returns the bounding box of the given mesh.
    217 	 * @param mesh
    218 	 *        the mesh
    219 	 * @return bounding box of the given mesh
    220 	 */
    221 	/* package */static BoundingBox getBoundingBox(Mesh mesh) {
    222 		mesh.updateBound();
    223 		BoundingVolume bv = mesh.getBound();
    224 		if (bv instanceof BoundingBox) {
    225 			return (BoundingBox) bv;
    226 		} else if (bv instanceof BoundingSphere) {
    227 			BoundingSphere bs = (BoundingSphere) bv;
    228 			float r = bs.getRadius();
    229 			return new BoundingBox(bs.getCenter(), r, r, r);
    230 		} else {
    231 			throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
    232 		}
    233 	}
    234 
    235 	/**
    236 	 * This method returns the bounding sphere of the given geometries.
    237 	 * @param geometries
    238 	 *        the list of geometries
    239 	 * @return bounding sphere of the given geometries
    240 	 */
    241 	/* package */static BoundingSphere getBoundingSphere(List<Geometry> geometries) {
    242 		BoundingSphere result = null;
    243 		for (Geometry geometry : geometries) {
    244 			BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometry.getMesh());
    245 			if (result == null) {
    246 				result = bs;
    247 			} else {
    248 				result.merge(bs);
    249 			}
    250 		}
    251 		return result;
    252 	}
    253 
    254 	/**
    255 	 * This method returns the bounding sphere of the given mesh.
    256 	 * @param mesh
    257 	 *        the mesh
    258 	 * @return bounding sphere of the given mesh
    259 	 */
    260 	/* package */static BoundingSphere getBoundingSphere(Mesh mesh) {
    261 		mesh.updateBound();
    262 		BoundingVolume bv = mesh.getBound();
    263 		if (bv instanceof BoundingBox) {
    264 			BoundingBox bb = (BoundingBox) bv;
    265 			float r = Math.max(bb.getXExtent(), bb.getYExtent());
    266 			r = Math.max(r, bb.getZExtent());
    267 			return new BoundingSphere(r, bb.getCenter());
    268 		} else if (bv instanceof BoundingSphere) {
    269 			return (BoundingSphere) bv;
    270 		} else {
    271 			throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
    272 		}
    273 	}
    274 
    275 	/**
    276 	 * This method returns the bounding tube of the given mesh.
    277 	 * @param mesh
    278 	 *        the mesh
    279 	 * @return bounding tube of the given mesh
    280 	 */
    281 	/* package */static BoundingTube getBoundingTube(Mesh mesh) {
    282 		Vector3f center = new Vector3f();
    283 		float maxx = -Float.MAX_VALUE, minx = Float.MAX_VALUE;
    284 		float maxy = -Float.MAX_VALUE, miny = Float.MAX_VALUE;
    285 		float maxz = -Float.MAX_VALUE, minz = Float.MAX_VALUE;
    286 
    287 		FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
    288 		int limit = positions.limit();
    289 		for (int i = 0; i < limit; i += 3) {
    290 			float x = positions.get(i);
    291 			float y = positions.get(i + 1);
    292 			float z = positions.get(i + 2);
    293 			center.addLocal(x, y, z);
    294 			maxx = x > maxx ? x : maxx;
    295 			minx = x < minx ? x : minx;
    296 			maxy = y > maxy ? y : maxy;
    297 			miny = y < miny ? y : miny;
    298 			maxz = z > maxz ? z : maxz;
    299 			minz = z < minz ? z : minz;
    300 		}
    301 		center.divideLocal(limit / 3);
    302 
    303 		float radius = Math.max(maxx - minx, maxy - miny) * 0.5f;
    304 		return new BoundingTube(radius, maxz - minz, center);
    305 	}
    306 
    307 	/**
    308 	 * This method returns the bounding tube of the given geometries.
    309 	 * @param geometries
    310 	 *        the list of geometries
    311 	 * @return bounding tube of the given geometries
    312 	 */
    313 	/* package */static BoundingTube getBoundingTube(List<Geometry> geometries) {
    314 		BoundingTube result = null;
    315 		for (Geometry geometry : geometries) {
    316 			BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometry.getMesh());
    317 			if (result == null) {
    318 				result = bt;
    319 			} else {
    320 				result.merge(bt);
    321 			}
    322 		}
    323 		return result;
    324 	}
    325 
    326 	/**
    327 	 * A very simple bounding tube. Id holds only the basic data bout the bounding tube
    328 	 * and does not provide full functionality of a BoundingVolume.
    329 	 * Should be replaced with a bounding tube that extends the BoundingVolume if it is ever created.
    330 	 * @author Marcin Roguski (Kaelthas)
    331 	 */
    332 	/* package */static class BoundingTube {
    333 		private float		radius;
    334 		private float		height;
    335 		private Vector3f	center;
    336 
    337 		/**
    338 		 * Constructor creates the tube with the given params.
    339 		 * @param radius
    340 		 *        the radius of the tube
    341 		 * @param height
    342 		 *        the height of the tube
    343 		 * @param center
    344 		 *        the center of the tube
    345 		 */
    346 		public BoundingTube(float radius, float height, Vector3f center) {
    347 			this.radius = radius;
    348 			this.height = height;
    349 			this.center = center;
    350 		}
    351 
    352 		/**
    353 		 * This method merges two bounding tubes.
    354 		 * @param boundingTube
    355 		 *        bounding tube to be merged woth the current one
    356 		 * @return new instance of bounding tube representing the tubes' merge
    357 		 */
    358 		public BoundingTube merge(BoundingTube boundingTube) {
    359 			// get tubes (tube1.radius >= tube2.radius)
    360 			BoundingTube tube1, tube2;
    361 			if (this.radius >= boundingTube.radius) {
    362 				tube1 = this;
    363 				tube2 = boundingTube;
    364 			} else {
    365 				tube1 = boundingTube;
    366 				tube2 = this;
    367 			}
    368 			float r1 = tube1.radius;
    369 			float r2 = tube2.radius;
    370 
    371 			float minZ = Math.min(tube1.center.z - tube1.height * 0.5f, tube2.center.z - tube2.height * 0.5f);
    372 			float maxZ = Math.max(tube1.center.z + tube1.height * 0.5f, tube2.center.z + tube2.height * 0.5f);
    373 			float height = maxZ - minZ;
    374 			Vector3f distance = tube2.center.subtract(tube1.center);
    375 			Vector3f center = tube1.center.add(distance.mult(0.5f));
    376 			distance.z = 0;// projecting this vector on XY plane
    377 			float d = distance.length();
    378 			// d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the inside
    379 			// d > r1 - r2: tube2 is outside or touches tube1 or crosses tube1
    380 			float radius = d <= r1 - r2 ? tube1.radius : (d + r1 + r2) * 0.5f;
    381 			return new BoundingTube(radius, height, center);
    382 		}
    383 
    384 		/**
    385 		 * This method returns the radius of the tube.
    386 		 * @return the radius of the tube
    387 		 */
    388 		public float getRadius() {
    389 			return radius;
    390 		}
    391 
    392 		/**
    393 		 * This method returns the height of the tube.
    394 		 * @return the height of the tube
    395 		 */
    396 		public float getHeight() {
    397 			return height;
    398 		}
    399 
    400 		/**
    401 		 * This method returns the center of the tube.
    402 		 * @return the center of the tube
    403 		 */
    404 		public Vector3f getCenter() {
    405 			return center;
    406 		}
    407 	}
    408 }
    409