• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	 */
generateUVCoordinates(int texco, int projection, int textureDimension, int[] coordinatesSwappingIndexes, List<Geometry> geometries)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 	 */
getBoundingBox(List<Geometry> geometries)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 	 */
getBoundingBox(Mesh mesh)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 	 */
getBoundingSphere(List<Geometry> geometries)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 	 */
getBoundingSphere(Mesh mesh)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 	 */
getBoundingTube(Mesh mesh)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 		 */
getRadius()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 		 */
getHeight()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 		 */
getCenter()404 		public Vector3f getCenter() {
405 			return center;
406 		}
407 	}
408 }
409