| /* |
| * Copyright (c) 2009-2010 jMonkeyEngine |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package jme3tools.converters.model; |
| |
| import com.jme3.bounding.BoundingBox; |
| import com.jme3.math.Transform; |
| import com.jme3.math.Vector2f; |
| import com.jme3.math.Vector3f; |
| import com.jme3.scene.Geometry; |
| import com.jme3.scene.Mesh; |
| import com.jme3.scene.VertexBuffer; |
| import com.jme3.scene.VertexBuffer.Format; |
| import com.jme3.scene.VertexBuffer.Type; |
| import com.jme3.scene.VertexBuffer.Usage; |
| import com.jme3.scene.mesh.IndexBuffer; |
| import com.jme3.util.BufferUtils; |
| import java.nio.*; |
| |
| public class FloatToFixed { |
| |
| private static final float shortSize = Short.MAX_VALUE - Short.MIN_VALUE; |
| private static final float shortOff = (Short.MAX_VALUE + Short.MIN_VALUE) * 0.5f; |
| |
| private static final float byteSize = Byte.MAX_VALUE - Byte.MIN_VALUE; |
| private static final float byteOff = (Byte.MAX_VALUE + Byte.MIN_VALUE) * 0.5f; |
| |
| public static void convertToFixed(Geometry geom, Format posFmt, Format nmFmt, Format tcFmt){ |
| geom.updateModelBound(); |
| BoundingBox bbox = (BoundingBox) geom.getModelBound(); |
| Mesh mesh = geom.getMesh(); |
| |
| VertexBuffer positions = mesh.getBuffer(Type.Position); |
| VertexBuffer normals = mesh.getBuffer(Type.Normal); |
| VertexBuffer texcoords = mesh.getBuffer(Type.TexCoord); |
| VertexBuffer indices = mesh.getBuffer(Type.Index); |
| |
| // positions |
| FloatBuffer fb = (FloatBuffer) positions.getData(); |
| if (posFmt != Format.Float){ |
| Buffer newBuf = VertexBuffer.createBuffer(posFmt, positions.getNumComponents(), |
| mesh.getVertexCount()); |
| Transform t = convertPositions(fb, bbox, newBuf); |
| t.combineWithParent(geom.getLocalTransform()); |
| geom.setLocalTransform(t); |
| |
| VertexBuffer newPosVb = new VertexBuffer(Type.Position); |
| newPosVb.setupData(positions.getUsage(), |
| positions.getNumComponents(), |
| posFmt, |
| newBuf); |
| mesh.clearBuffer(Type.Position); |
| mesh.setBuffer(newPosVb); |
| } |
| |
| // normals, automatically convert to signed byte |
| fb = (FloatBuffer) normals.getData(); |
| |
| ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity()); |
| convertNormals(fb, bb); |
| |
| normals = new VertexBuffer(Type.Normal); |
| normals.setupData(Usage.Static, 3, Format.Byte, bb); |
| normals.setNormalized(true); |
| mesh.clearBuffer(Type.Normal); |
| mesh.setBuffer(normals); |
| |
| // texcoords |
| fb = (FloatBuffer) texcoords.getData(); |
| if (tcFmt != Format.Float){ |
| Buffer newBuf = VertexBuffer.createBuffer(tcFmt, |
| texcoords.getNumComponents(), |
| mesh.getVertexCount()); |
| convertTexCoords2D(fb, newBuf); |
| |
| VertexBuffer newTcVb = new VertexBuffer(Type.TexCoord); |
| newTcVb.setupData(texcoords.getUsage(), |
| texcoords.getNumComponents(), |
| tcFmt, |
| newBuf); |
| mesh.clearBuffer(Type.TexCoord); |
| mesh.setBuffer(newTcVb); |
| } |
| } |
| |
| public static void compressIndexBuffer(Mesh mesh){ |
| int vertCount = mesh.getVertexCount(); |
| VertexBuffer vb = mesh.getBuffer(Type.Index); |
| Format targetFmt; |
| if (vb.getFormat() == Format.UnsignedInt && vertCount <= 0xffff){ |
| if (vertCount <= 256) |
| targetFmt = Format.UnsignedByte; |
| else |
| targetFmt = Format.UnsignedShort; |
| }else if (vb.getFormat() == Format.UnsignedShort && vertCount <= 0xff){ |
| targetFmt = Format.UnsignedByte; |
| }else{ |
| return; |
| } |
| |
| IndexBuffer src = mesh.getIndexBuffer(); |
| Buffer newBuf = VertexBuffer.createBuffer(targetFmt, vb.getNumComponents(), src.size()); |
| |
| VertexBuffer newVb = new VertexBuffer(Type.Index); |
| newVb.setupData(vb.getUsage(), vb.getNumComponents(), targetFmt, newBuf); |
| mesh.clearBuffer(Type.Index); |
| mesh.setBuffer(newVb); |
| |
| IndexBuffer dst = mesh.getIndexBuffer(); |
| for (int i = 0; i < src.size(); i++){ |
| dst.put(i, src.get(i)); |
| } |
| } |
| |
| private static void convertToFixed(FloatBuffer input, IntBuffer output){ |
| if (output.capacity() < input.capacity()) |
| throw new RuntimeException("Output must be at least as large as input!"); |
| |
| input.clear(); |
| output.clear(); |
| for (int i = 0; i < input.capacity(); i++){ |
| output.put( (int) (input.get() * (float)(1<<16)) ); |
| } |
| output.flip(); |
| } |
| |
| private static void convertToFloat(IntBuffer input, FloatBuffer output){ |
| if (output.capacity() < input.capacity()) |
| throw new RuntimeException("Output must be at least as large as input!"); |
| |
| input.clear(); |
| output.clear(); |
| for (int i = 0; i < input.capacity(); i++){ |
| output.put( ((float)input.get() / (float)(1<<16)) ); |
| } |
| output.flip(); |
| } |
| |
| private static void convertToUByte(FloatBuffer input, ByteBuffer output){ |
| if (output.capacity() < input.capacity()) |
| throw new RuntimeException("Output must be at least as large as input!"); |
| |
| input.clear(); |
| output.clear(); |
| for (int i = 0; i < input.capacity(); i++){ |
| output.put( (byte) (input.get() * 255f) ); |
| } |
| output.flip(); |
| } |
| |
| |
| public static VertexBuffer convertToUByte(VertexBuffer vb){ |
| FloatBuffer fb = (FloatBuffer) vb.getData(); |
| ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity()); |
| convertToUByte(fb, bb); |
| |
| VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); |
| newVb.setupData(vb.getUsage(), |
| vb.getNumComponents(), |
| Format.UnsignedByte, |
| bb); |
| newVb.setNormalized(true); |
| return newVb; |
| } |
| |
| public static VertexBuffer convertToFixed(VertexBuffer vb){ |
| if (vb.getFormat() == Format.Int) |
| return vb; |
| |
| FloatBuffer fb = (FloatBuffer) vb.getData(); |
| IntBuffer ib = BufferUtils.createIntBuffer(fb.capacity()); |
| convertToFixed(fb, ib); |
| |
| VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); |
| newVb.setupData(vb.getUsage(), |
| vb.getNumComponents(), |
| Format.Int, |
| ib); |
| return newVb; |
| } |
| |
| public static VertexBuffer convertToFloat(VertexBuffer vb){ |
| if (vb.getFormat() == Format.Float) |
| return vb; |
| |
| IntBuffer ib = (IntBuffer) vb.getData(); |
| FloatBuffer fb = BufferUtils.createFloatBuffer(ib.capacity()); |
| convertToFloat(ib, fb); |
| |
| VertexBuffer newVb = new VertexBuffer(vb.getBufferType()); |
| newVb.setupData(vb.getUsage(), |
| vb.getNumComponents(), |
| Format.Float, |
| fb); |
| return newVb; |
| } |
| |
| private static void convertNormals(FloatBuffer input, ByteBuffer output){ |
| if (output.capacity() < input.capacity()) |
| throw new RuntimeException("Output must be at least as large as input!"); |
| |
| input.clear(); |
| output.clear(); |
| Vector3f temp = new Vector3f(); |
| int vertexCount = input.capacity() / 3; |
| for (int i = 0; i < vertexCount; i++){ |
| BufferUtils.populateFromBuffer(temp, input, i); |
| |
| // offset and scale vector into -128 ... 127 |
| temp.multLocal(127).addLocal(0.5f, 0.5f, 0.5f); |
| |
| // quantize |
| byte v1 = (byte) temp.getX(); |
| byte v2 = (byte) temp.getY(); |
| byte v3 = (byte) temp.getZ(); |
| |
| // store |
| output.put(v1).put(v2).put(v3); |
| } |
| } |
| |
| private static void convertTexCoords2D(FloatBuffer input, Buffer output){ |
| if (output.capacity() < input.capacity()) |
| throw new RuntimeException("Output must be at least as large as input!"); |
| |
| input.clear(); |
| output.clear(); |
| Vector2f temp = new Vector2f(); |
| int vertexCount = input.capacity() / 2; |
| |
| ShortBuffer sb = null; |
| IntBuffer ib = null; |
| |
| if (output instanceof ShortBuffer) |
| sb = (ShortBuffer) output; |
| else if (output instanceof IntBuffer) |
| ib = (IntBuffer) output; |
| else |
| throw new UnsupportedOperationException(); |
| |
| for (int i = 0; i < vertexCount; i++){ |
| BufferUtils.populateFromBuffer(temp, input, i); |
| |
| if (sb != null){ |
| sb.put( (short) (temp.getX()*Short.MAX_VALUE) ); |
| sb.put( (short) (temp.getY()*Short.MAX_VALUE) ); |
| }else{ |
| int v1 = (int) (temp.getX() * ((float)(1 << 16))); |
| int v2 = (int) (temp.getY() * ((float)(1 << 16))); |
| ib.put(v1).put(v2); |
| } |
| } |
| } |
| |
| private static Transform convertPositions(FloatBuffer input, BoundingBox bbox, Buffer output){ |
| if (output.capacity() < input.capacity()) |
| throw new RuntimeException("Output must be at least as large as input!"); |
| |
| Vector3f offset = bbox.getCenter().negate(); |
| Vector3f size = new Vector3f(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent()); |
| size.multLocal(2); |
| |
| ShortBuffer sb = null; |
| ByteBuffer bb = null; |
| float dataTypeSize; |
| float dataTypeOffset; |
| if (output instanceof ShortBuffer){ |
| sb = (ShortBuffer) output; |
| dataTypeOffset = shortOff; |
| dataTypeSize = shortSize; |
| }else{ |
| bb = (ByteBuffer) output; |
| dataTypeOffset = byteOff; |
| dataTypeSize = byteSize; |
| } |
| Vector3f scale = new Vector3f(); |
| scale.set(dataTypeSize, dataTypeSize, dataTypeSize).divideLocal(size); |
| |
| Vector3f invScale = new Vector3f(); |
| invScale.set(size).divideLocal(dataTypeSize); |
| |
| offset.multLocal(scale); |
| offset.addLocal(dataTypeOffset, dataTypeOffset, dataTypeOffset); |
| |
| // offset = (-modelOffset * shortSize)/modelSize + shortOff |
| // scale = shortSize / modelSize |
| |
| input.clear(); |
| output.clear(); |
| Vector3f temp = new Vector3f(); |
| int vertexCount = input.capacity() / 3; |
| for (int i = 0; i < vertexCount; i++){ |
| BufferUtils.populateFromBuffer(temp, input, i); |
| |
| // offset and scale vector into -32768 ... 32767 |
| // or into -128 ... 127 if using bytes |
| temp.multLocal(scale); |
| temp.addLocal(offset); |
| |
| // quantize and store |
| if (sb != null){ |
| short v1 = (short) temp.getX(); |
| short v2 = (short) temp.getY(); |
| short v3 = (short) temp.getZ(); |
| sb.put(v1).put(v2).put(v3); |
| }else{ |
| byte v1 = (byte) temp.getX(); |
| byte v2 = (byte) temp.getY(); |
| byte v3 = (byte) temp.getZ(); |
| bb.put(v1).put(v2).put(v3); |
| } |
| } |
| |
| Transform transform = new Transform(); |
| transform.setTranslation(offset.negate().multLocal(invScale)); |
| transform.setScale(invScale); |
| return transform; |
| } |
| |
| } |