|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|