Package info.ata4.unity.cli.extract.mesh

Source Code of info.ata4.unity.cli.extract.mesh.MeshData

/*
** 2014 Juli 10
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
*/
package info.ata4.unity.cli.extract.mesh;

import info.ata4.io.DataInputReader;
import info.ata4.io.buffer.ByteBufferInputStream;
import info.ata4.io.streams.BitInputStream;
import info.ata4.log.LogUtils;
import info.ata4.unity.engine.ChannelInfo;
import info.ata4.unity.engine.CompressedMesh;
import info.ata4.unity.engine.Mesh;
import info.ata4.unity.engine.PackedBitVector;
import info.ata4.unity.engine.StreamInfo;
import info.ata4.unity.engine.SubMesh;
import info.ata4.unity.engine.struct.Color32;
import info.ata4.unity.engine.struct.Vector2f;
import info.ata4.unity.engine.struct.Vector3f;
import info.ata4.unity.engine.struct.Vector4f;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Container for decoded mesh data than is used by MeshWriter objects.
*
* @author Nico Bergemann <barracuda415 at yahoo.de>
*/
class MeshData {
   
    private static final Logger L = LogUtils.getLogger();
   
    private static final int CHANNEL_COUNT = 6;
    private static final int CHANNEL_VERTS = 0;
    private static final int CHANNEL_NORMALS = 1;
    private static final int CHANNEL_COLORS = 2;
    private static final int CHANNEL_UV1 = 3;
    private static final int CHANNEL_UV2 = 4;
    private static final int CHANNEL_TANGENTS = 5;
   
    private final Mesh mesh;
    private final List<Vector3f> vertices = new ArrayList<>();
    private final List<Vector3f> normals = new ArrayList<>();
    private final List<Color32> colors = new ArrayList<>();
    private final List<Vector2f> uv1 = new ArrayList<>();
    private final List<Vector2f> uv2 = new ArrayList<>();
    private final List<Vector4f> tangents = new ArrayList<>();
    private final List<List<Integer>> indices = new ArrayList<>();
    private final List<List<Integer>> triangles = new ArrayList<>();
   
    MeshData(Mesh mesh) throws IOException {
        this.mesh = mesh;
       
        if (mesh.meshCompression == 0) {
            readVertexBuffer();
            readIndexBuffer();
        } else {
            readCompressedMesh();
        }
    }
   
    Mesh getMesh() {
        return mesh;
    }

    List<Vector3f> getVertices() {
        return vertices;
    }

    List<Vector3f> getNormals() {
        return normals;
    }

    List<Color32> getColors() {
        return colors;
    }

    List<Vector2f> getUV1() {
        return uv1;
    }

    List<Vector2f> getUV2() {
        return uv2;
    }

    List<Vector4f> getTangents() {
        return tangents;
    }
   
    List<List<Integer>> getIndices() {
        return indices;
    }

    List<List<Integer>> getTriangles() {
        return triangles;
    }
   
    private int[] readPackedBits(PackedBitVector pbv) throws IOException {
        // don't waste time on empty vectors
        if (pbv.numItems == 0 || pbv.bitSize == 0) {
            return new int[]{};
        }
       
        // the values are packed with a variable bit length
        BitInputStream bis = new BitInputStream(new ByteBufferInputStream(pbv.data));
        bis.setBitLength(pbv.bitSize);
       
        int numItems = pbv.numItems.intValue();
        int[] items = new int[numItems];
        for (int i = 0; i < items.length; i++) {
            items[i] = bis.read();
        }
       
        return items;
    }
   
    private float[] readPackedFloats(PackedBitVector pbv) throws IOException {
        // don't waste time on empty vectors
        if (pbv.numItems == 0 || pbv.bitSize == 0) {
            return new float[]{};
        }
       
        // expand integers to floats using the range and offset floats in the
        // packed bit vector
        int[] items = readPackedBits(pbv);
        float[] floats = new float[items.length];
       
        int maxValue = (1 << pbv.bitSize) - 1;
        float range = pbv.range / maxValue;
        float start = pbv.start;

        for (int i = 0; i < floats.length; i++) {
            floats[i] = items[i] * range + start;
        }
       
        return floats;
    }
   
    private float[] readPackedNormals(PackedBitVector pbv, PackedBitVector pbvSigns) throws IOException {
        int[] items = readPackedBits(pbv); // 2 elements per vertex
        int[] signs = readPackedBits(pbvSigns); // one element per vertex
       
        // don't waste time on empty vectors
        if (items.length == 0 || signs.length == 0) {
            return new float[]{};
        }
       
        // convert signage 0 to -1
        for (int i = 0; i < signs.length; i++) {
            if (signs[i] == 0) {
                signs[i] = -1;
            }
        }
       
        float[] floats = new float[signs.length * 3]; // 3 elements per vertex
       
        int maxValue = (1 << pbv.bitSize) - 1;
        float range = pbv.range / maxValue;
        float start = pbv.start;

        for (int i = 0; i < floats.length / 3; i++) {
            float x = items[i * 2] * range + start;
            float y = items[i * 2 + 1] * range + start;
           
            // reconstruct z using x^2 + y^2 + z^2 = 1, since a normal vector
            // can be represented as a dot on the surface of a sphere
            float z = (float) ((1 - Math.pow(x, 2) - Math.pow(y, 2)) * signs[i]);
           
            floats[i * 3] = x;
            floats[i * 3 + 1] = y;
            floats[i * 3 + 2] = z;
        }
       
        return floats;
    }

    private void readVertexBuffer() throws IOException {
        // get vertex buffer
        ByteBuffer vertexBuffer = mesh.vertexData.dataSize;
        vertexBuffer.order(ByteOrder.LITTLE_ENDIAN);
       
        L.log(Level.FINE, "Vertex buffer size: {0}", vertexBuffer.capacity());

        DataInputReader in = DataInputReader.newReader(vertexBuffer);

        List<StreamInfo> streams = mesh.vertexData.streams;
        List<ChannelInfo> channels = mesh.vertexData.channels;
       
        for (StreamInfo stream : streams) {
            // skip empty channels
            if (stream.channelMask.longValue() == 0) {
                continue;
            }

            vertexBuffer.position(stream.offset.intValue());

            // read vertex data from each vertex and channel
            for (int i = 0; i < mesh.vertexData.vertexCount; i++) {
                for (int j = 0; j < CHANNEL_COUNT; j++) {
                    // skip unselected channels
                    if ((stream.channelMask.longValue() & 1 << j) == 0) {
                        continue;
                    }
                   
                    boolean half = false;
                    ChannelInfo channel = null;

                    // channels may not be available in older versions
                    if (!channels.isEmpty()) {
                        channel = channels.get(j);
                        half = channel.format == 1;
                    }

                    switch (j) {
                        case CHANNEL_VERTS:
                            Vector3f v = new Vector3f();
                            v.setHalf(half);
                            v.read(in);
                            vertices.add(v);
                            break;

                        case CHANNEL_NORMALS:
                            Vector3f vn = new Vector3f();
                            vn.setHalf(half);
                            vn.read(in);
                            normals.add(vn);
                            if (half && channel != null && channel.dimension == 4) {
                                in.skipBytes(2); // padding?
                            }
                            break;

                        case CHANNEL_COLORS:
                            Color32 c = new Color32();
                            c.read(in);
                            colors.add(c);
                            break;

                        case CHANNEL_UV1:
                        case CHANNEL_UV2:
                            Vector2f vt = new Vector2f();
                            vt.setHalf(half);
                            vt.read(in);
                            if (j == CHANNEL_UV1) {
                                uv1.add(vt);
                            } else {
                                uv2.add(vt);
                            }
                            break;

                        case CHANNEL_TANGENTS:
                            Vector4f t = new Vector4f();
                            t.setHalf(half);
                            t.read(in);
                            tangents.add(t);
                            break;
                    }
                }
               
                in.align(stream.stride.intValue());
            }
        }
    }

    private void readIndexBuffer() throws IOException {
        mesh.indexBuffer.order(ByteOrder.LITTLE_ENDIAN);
        DataInputReader in = DataInputReader.newReader(mesh.indexBuffer);

        for (SubMesh subMesh : mesh.subMeshes) {
            List<Integer> subMeshIndices = new ArrayList<>();
            List<Integer> subMeshTriangles = new ArrayList<>();
           
            in.position(subMesh.firstByte.longValue());
            for (long j = 0; j < subMesh.indexCount.longValue(); j++) {
                subMeshIndices.add(in.readUnsignedShort());
            }
           
            // read triangle strips if topology/isTriStrip is not zero
            if (subMesh.topology.longValue() == 0) {
                // use indices as is
                subMeshTriangles.addAll(subMeshIndices);
            } else {
                LinkedList<Integer> tris = new LinkedList<>();
                for (int k = 0; k < subMeshIndices.size(); k++) {
                    tris.addLast(subMeshIndices.get(k));

                    // fill deque with three indices
                    if (tris.size() < 3) {
                        continue;
                    }

                    int t1 = tris.get(0);
                    int t2 = tris.get(1);
                    int t3 = tris.get(2);
                   
                    // shift deque
                    tris.removeFirst();

                    // skip degenerated triangles
                    if (t1 == t2 || t1 == t3 || t3 == t2) {
                        continue;
                    }

                    // switch winding direction every other triangle
                    if (k % 2 == 0) {
                        subMeshTriangles.add(t1);
                        subMeshTriangles.add(t2);
                        subMeshTriangles.add(t3);
                    } else {
                        subMeshTriangles.add(t3);
                        subMeshTriangles.add(t2);
                        subMeshTriangles.add(t1);
                    }
                }
            }
           
            indices.add(subMeshIndices);
            triangles.add(subMeshTriangles);
        }
    }

    private void readCompressedMesh() throws IOException {
        CompressedMesh cmesh = mesh.compressedMesh;

        float[] vertexFloats = readPackedFloats(cmesh.vertices);
        for (int i = 0; i < vertexFloats.length / 3; i++) {
            Vector3f v = new Vector3f();
            v.x = vertexFloats[i * 3];
            v.y = vertexFloats[i * 3 + 1];
            v.z = vertexFloats[i * 3 + 2];
            vertices.add(v);
        }

        float[] normalFloats = readPackedNormals(cmesh.normals, cmesh.normalSigns);
        for (int i = 0; i < normalFloats.length / 3; i++) {
            Vector3f vn = new Vector3f();
            vn.x = normalFloats[i * 3];
            vn.y = normalFloats[i * 3 + 1];
            vn.z = normalFloats[i * 3 + 2];
            normals.add(vn);
        }

        float[] uvFloats = readPackedFloats(cmesh.UV);
        for (int i = 0; i < uvFloats.length / 2; i++) {
            Vector2f vt = new Vector2f();
            vt.x = uvFloats[i * 2];
            vt.y = uvFloats[i * 2 + 1];
            if (i < vertexFloats.length / 3) {
                uv1.add(vt);
            } else {
                uv2.add(vt);
            }
        }

        int[] colorInts = readPackedBits(cmesh.colors);
        for (int i = 0; i < colorInts.length; i++) {
            Color32 c = new Color32();
            c.fromInt(colorInts[i]);
            colors.add(c);
        }
       
        // TODO: works for triangulated meshes only!
        int[] triangleInts = readPackedBits(cmesh.triangles);
        for (SubMesh subMesh : mesh.subMeshes) {
            List<Integer> subMeshIndices = new ArrayList<>();
            List<Integer> subMeshTriangles = new ArrayList<>();
           
            final int vertOfs = subMesh.firstVertex.intValue();
            final int vertCount = subMesh.vertexCount.intValue();
            for (int j = vertOfs; j < vertCount; j++) {
                subMeshTriangles.add(triangleInts[j]);
                subMeshIndices.add(triangleInts[j]);
            }
           
            indices.add(subMeshIndices);
            triangles.add(subMeshTriangles);
        }
    }
}
TOP

Related Classes of info.ata4.unity.cli.extract.mesh.MeshData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.