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 33 package jme3tools.converters.model; 34 35 import com.jme3.bounding.BoundingBox; 36 import com.jme3.math.Transform; 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.Type; 44 import com.jme3.scene.VertexBuffer.Usage; 45 import com.jme3.scene.mesh.IndexBuffer; 46 import com.jme3.util.BufferUtils; 47 import java.nio.*; 48 49 public class FloatToFixed { 50 51 private static final float shortSize = Short.MAX_VALUE - Short.MIN_VALUE; 52 private static final float shortOff = (Short.MAX_VALUE + Short.MIN_VALUE) * 0.5f; 53 54 private static final float byteSize = Byte.MAX_VALUE - Byte.MIN_VALUE; 55 private static final float byteOff = (Byte.MAX_VALUE + Byte.MIN_VALUE) * 0.5f; 56 57 public static void convertToFixed(Geometry geom, Format posFmt, Format nmFmt, Format tcFmt){ 58 geom.updateModelBound(); 59 BoundingBox bbox = (BoundingBox) geom.getModelBound(); 60 Mesh mesh = geom.getMesh(); 61 62 VertexBuffer positions = mesh.getBuffer(Type.Position); 63 VertexBuffer normals = mesh.getBuffer(Type.Normal); 64 VertexBuffer texcoords = mesh.getBuffer(Type.TexCoord); 65 VertexBuffer indices = mesh.getBuffer(Type.Index); 66 67 // positions 68 FloatBuffer fb = (FloatBuffer) positions.getData(); 69 if (posFmt != Format.Float){ 70 Buffer newBuf = VertexBuffer.createBuffer(posFmt, positions.getNumComponents(), 71 mesh.getVertexCount()); 72 Transform t = convertPositions(fb, bbox, newBuf); 73 t.combineWithParent(geom.getLocalTransform()); 74 geom.setLocalTransform(t); 75 76 VertexBuffer newPosVb = new VertexBuffer(Type.Position); 77 newPosVb.setupData(positions.getUsage(), 78 positions.getNumComponents(), 79 posFmt, 80 newBuf); 81 mesh.clearBuffer(Type.Position); 82 mesh.setBuffer(newPosVb); 83 } 84 85 // normals, automatically convert to signed byte 86 fb = (FloatBuffer) normals.getData(); 87 88 ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity()); 89 convertNormals(fb, bb); 90 91 normals = new VertexBuffer(Type.Normal); 92 normals.setupData(Usage.Static, 3, Format.Byte, bb); 93 normals.setNormalized(true); 94 mesh.clearBuffer(Type.Normal); 95 mesh.setBuffer(normals); 96 97 // texcoords 98 fb = (FloatBuffer) texcoords.getData(); 99 if (tcFmt != Format.Float){ 100 Buffer newBuf = VertexBuffer.createBuffer(tcFmt, 101 texcoords.getNumComponents(), 102 mesh.getVertexCount()); 103 convertTexCoords2D(fb, newBuf); 104 105 VertexBuffer newTcVb = new VertexBuffer(Type.TexCoord); 106 newTcVb.setupData(texcoords.getUsage(), 107 texcoords.getNumComponents(), 108 tcFmt, 109 newBuf); 110 mesh.clearBuffer(Type.TexCoord); 111 mesh.setBuffer(newTcVb); 112 } 113 } 114 115 public static void compressIndexBuffer(Mesh mesh){ 116 int vertCount = mesh.getVertexCount(); 117 VertexBuffer vb = mesh.getBuffer(Type.Index); 118 Format targetFmt; 119 if (vb.getFormat() == Format.UnsignedInt && vertCount <= 0xffff){ 120 if (vertCount <= 256) 121 targetFmt = Format.UnsignedByte; 122 else 123 targetFmt = Format.UnsignedShort; 124 }else if (vb.getFormat() == Format.UnsignedShort && vertCount <= 0xff){ 125 targetFmt = Format.UnsignedByte; 126 }else{ 127 return; 128 } 129 130 IndexBuffer src = mesh.getIndexBuffer(); 131 Buffer newBuf = VertexBuffer.createBuffer(targetFmt, vb.getNumComponents(), src.size()); 132 133 VertexBuffer newVb = new VertexBuffer(Type.Index); 134 newVb.setupData(vb.getUsage(), vb.getNumComponents(), targetFmt, newBuf); 135 mesh.clearBuffer(Type.Index); 136 mesh.setBuffer(newVb); 137 138 IndexBuffer dst = mesh.getIndexBuffer(); 139 for (int i = 0; i < src.size(); i++){ 140 dst.put(i, src.get(i)); 141 } 142 } 143 144 private static void convertToFixed(FloatBuffer input, IntBuffer output){ 145 if (output.capacity() < input.capacity()) 146 throw new RuntimeException("Output must be at least as large as input!"); 147 148 input.clear(); 149 output.clear(); 150 for (int i = 0; i < input.capacity(); i++){ 151 output.put( (int) (input.get() * (float)(1<<16)) ); 152 } 153 output.flip(); 154 } 155 156 private static void convertToFloat(IntBuffer input, FloatBuffer output){ 157 if (output.capacity() < input.capacity()) 158 throw new RuntimeException("Output must be at least as large as input!"); 159 160 input.clear(); 161 output.clear(); 162 for (int i = 0; i < input.capacity(); i++){ 163 output.put( ((float)input.get() / (float)(1<<16)) ); 164 } 165 output.flip(); 166 } 167 168 private static void convertToUByte(FloatBuffer input, ByteBuffer output){ 169 if (output.capacity() < input.capacity()) 170 throw new RuntimeException("Output must be at least as large as input!"); 171 172 input.clear(); 173 output.clear(); 174 for (int i = 0; i < input.capacity(); i++){ 175 output.put( (byte) (input.get() * 255f) ); 176 } 177 output.flip(); 178 } 179 180 181 public static VertexBuffer convertToUByte(VertexBuffer vb){ 182 FloatBuffer fb = (FloatBuffer) vb.getData(); 183 ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity()); 184 convertToUByte(fb, bb); 185 186 VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); 187 newVb.setupData(vb.getUsage(), 188 vb.getNumComponents(), 189 Format.UnsignedByte, 190 bb); 191 newVb.setNormalized(true); 192 return newVb; 193 } 194 195 public static VertexBuffer convertToFixed(VertexBuffer vb){ 196 if (vb.getFormat() == Format.Int) 197 return vb; 198 199 FloatBuffer fb = (FloatBuffer) vb.getData(); 200 IntBuffer ib = BufferUtils.createIntBuffer(fb.capacity()); 201 convertToFixed(fb, ib); 202 203 VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); 204 newVb.setupData(vb.getUsage(), 205 vb.getNumComponents(), 206 Format.Int, 207 ib); 208 return newVb; 209 } 210 211 public static VertexBuffer convertToFloat(VertexBuffer vb){ 212 if (vb.getFormat() == Format.Float) 213 return vb; 214 215 IntBuffer ib = (IntBuffer) vb.getData(); 216 FloatBuffer fb = BufferUtils.createFloatBuffer(ib.capacity()); 217 convertToFloat(ib, fb); 218 219 VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); 220 newVb.setupData(vb.getUsage(), 221 vb.getNumComponents(), 222 Format.Float, 223 fb); 224 return newVb; 225 } 226 227 private static void convertNormals(FloatBuffer input, ByteBuffer output){ 228 if (output.capacity() < input.capacity()) 229 throw new RuntimeException("Output must be at least as large as input!"); 230 231 input.clear(); 232 output.clear(); 233 Vector3f temp = new Vector3f(); 234 int vertexCount = input.capacity() / 3; 235 for (int i = 0; i < vertexCount; i++){ 236 BufferUtils.populateFromBuffer(temp, input, i); 237 238 // offset and scale vector into -128 ... 127 239 temp.multLocal(127).addLocal(0.5f, 0.5f, 0.5f); 240 241 // quantize 242 byte v1 = (byte) temp.getX(); 243 byte v2 = (byte) temp.getY(); 244 byte v3 = (byte) temp.getZ(); 245 246 // store 247 output.put(v1).put(v2).put(v3); 248 } 249 } 250 251 private static void convertTexCoords2D(FloatBuffer input, Buffer output){ 252 if (output.capacity() < input.capacity()) 253 throw new RuntimeException("Output must be at least as large as input!"); 254 255 input.clear(); 256 output.clear(); 257 Vector2f temp = new Vector2f(); 258 int vertexCount = input.capacity() / 2; 259 260 ShortBuffer sb = null; 261 IntBuffer ib = null; 262 263 if (output instanceof ShortBuffer) 264 sb = (ShortBuffer) output; 265 else if (output instanceof IntBuffer) 266 ib = (IntBuffer) output; 267 else 268 throw new UnsupportedOperationException(); 269 270 for (int i = 0; i < vertexCount; i++){ 271 BufferUtils.populateFromBuffer(temp, input, i); 272 273 if (sb != null){ 274 sb.put( (short) (temp.getX()*Short.MAX_VALUE) ); 275 sb.put( (short) (temp.getY()*Short.MAX_VALUE) ); 276 }else{ 277 int v1 = (int) (temp.getX() * ((float)(1 << 16))); 278 int v2 = (int) (temp.getY() * ((float)(1 << 16))); 279 ib.put(v1).put(v2); 280 } 281 } 282 } 283 284 private static Transform convertPositions(FloatBuffer input, BoundingBox bbox, Buffer output){ 285 if (output.capacity() < input.capacity()) 286 throw new RuntimeException("Output must be at least as large as input!"); 287 288 Vector3f offset = bbox.getCenter().negate(); 289 Vector3f size = new Vector3f(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent()); 290 size.multLocal(2); 291 292 ShortBuffer sb = null; 293 ByteBuffer bb = null; 294 float dataTypeSize; 295 float dataTypeOffset; 296 if (output instanceof ShortBuffer){ 297 sb = (ShortBuffer) output; 298 dataTypeOffset = shortOff; 299 dataTypeSize = shortSize; 300 }else{ 301 bb = (ByteBuffer) output; 302 dataTypeOffset = byteOff; 303 dataTypeSize = byteSize; 304 } 305 Vector3f scale = new Vector3f(); 306 scale.set(dataTypeSize, dataTypeSize, dataTypeSize).divideLocal(size); 307 308 Vector3f invScale = new Vector3f(); 309 invScale.set(size).divideLocal(dataTypeSize); 310 311 offset.multLocal(scale); 312 offset.addLocal(dataTypeOffset, dataTypeOffset, dataTypeOffset); 313 314 // offset = (-modelOffset * shortSize)/modelSize + shortOff 315 // scale = shortSize / modelSize 316 317 input.clear(); 318 output.clear(); 319 Vector3f temp = new Vector3f(); 320 int vertexCount = input.capacity() / 3; 321 for (int i = 0; i < vertexCount; i++){ 322 BufferUtils.populateFromBuffer(temp, input, i); 323 324 // offset and scale vector into -32768 ... 32767 325 // or into -128 ... 127 if using bytes 326 temp.multLocal(scale); 327 temp.addLocal(offset); 328 329 // quantize and store 330 if (sb != null){ 331 short v1 = (short) temp.getX(); 332 short v2 = (short) temp.getY(); 333 short v3 = (short) temp.getZ(); 334 sb.put(v1).put(v2).put(v3); 335 }else{ 336 byte v1 = (byte) temp.getX(); 337 byte v2 = (byte) temp.getY(); 338 byte v3 = (byte) temp.getZ(); 339 bb.put(v1).put(v2).put(v3); 340 } 341 } 342 343 Transform transform = new Transform(); 344 transform.setTranslation(offset.negate().multLocal(invScale)); 345 transform.setScale(invScale); 346 return transform; 347 } 348 349 } 350