blob: 0d0647aab0d3c2868f14178b286f470100fe7a1c [file] [log] [blame]
/*
* 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;
}
}