Home | History | Annotate | Download | only in model
      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