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