Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

package com.gebauz.bauzoid.graphics.model;

import java.io.IOException;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Texture;
import com.gebauz.bauzoid.app.Consts;
import com.gebauz.bauzoid.file.File;
import com.gebauz.bauzoid.graphics.Graphics;
import com.gebauz.bauzoid.graphics.model.Mesh;
import com.gebauz.bauzoid.graphics.renderstates.RenderStatesConfiguration;
import com.gebauz.bauzoid.graphics.shader.Effect;
import com.gebauz.bauzoid.graphics.shader.ShaderProgram;
import com.gebauz.bauzoid.graphics.shader.ShaderUtil;
import com.gebauz.bauzoid.graphics.shader.Technique;
import com.gebauz.bauzoid.math.BoundingBox;
import com.gebauz.bauzoid.math.Matrix4;
import com.gebauz.bauzoid.math.Vector2;
import com.gebauz.bauzoid.math.Vector3;
import com.gebauz.bauzoid.math.Vector4;

public class ModelUtil
{
        public static final String LOG_TAG = Consts.LOG_TAG + ":ModelUtil";
        public static boolean verbose = false;
       
        private ModelUtil() {}
       
        public static void log(String tag, String msg)
        {
                if (verbose)
                        Gdx.app.log(tag, msg);
        }
       
        public static Model createModelFromFile(Graphics graphics, FileHandle fileHandle) throws IOException
        {
                File file = new File(fileHandle.read());
                Model model = loadModel(graphics, file);
                file.close();
                return model;
        }

       
        public static Model loadModel(Graphics graphics, File modelFile) throws IOException
        {
                log(LOG_TAG, "Loading file");
               
                byte byteOrdering = modelFile.readByte();
                log(LOG_TAG, "Byte Ordering: " + (byteOrdering == 0 ? "Little Endian" : "Big Endian"));
               
                if (byteOrdering == 0)
                        modelFile.setEndianness(File.Endianness.LITTLE_ENDIAN);
                else
                        modelFile.setEndianness(File.Endianness.BIG_ENDIAN);
               
                String header = modelFile.readString(8);
                log(LOG_TAG, "Header: " + header);
               
                if (header.compareTo("SUXMODEL") != 0)
                        throw new IOException("Header incorrect, must be 'SUXMODEL': " + header);
               
                int version = modelFile.readInt();
                log(LOG_TAG, "Version: " + version);
                if (version != 0)
                        throw new IOException("Version incorrect, must be 0: " + version);
               
                String name = modelFile.readString();
                log(LOG_TAG, "Name: " + name);
               
                int meshCount = modelFile.readInt();
                log(LOG_TAG, "Mesh Count: " + meshCount);
               
                Model model = new Model(name);
               
                Mesh[] meshes = new Mesh[meshCount];
               
                for (int i = 0; i < meshCount; i++)
                {
                        String meshName = modelFile.readString();
                        log(LOG_TAG, "[" + i + "] Mesh Name: " + meshName);

                        meshes[i] = new Mesh(graphics, meshName);

                        BoundingBox box = new BoundingBox();
                       
                        box.min = modelFile.readVector3();
                        log(LOG_TAG, "[" + i + "] BB Min: " + box.min.x + ", " + box.min.y + ", " + box.min.z);
                       
                        box.max = modelFile.readVector3();
                        log(LOG_TAG, "[" + i + "] BB Max: " + box.max.x + ", " + box.max.y + ", " + box.max.z);
                       
                        meshes[i].setBoundingBox(box);                 
               
                        boolean dynamic = modelFile.readBool();
                        log(LOG_TAG, "[" + i + "] Dynamic: " + (dynamic ? "yes" : "no"));

                        boolean deleteSource = modelFile.readBool();
                        log(LOG_TAG, "[" + i + "] DeleteSource: " + (deleteSource ? "yes" : "no"));
                       
                        // geometry
                        Geometry geometry = loadGeometry(graphics, modelFile);
                       
                        meshes[i].setGeometry(geometry);
                       
                        // bones - currently ignored
                        int boneCount = modelFile.readInt();
                        for (int bone = 0; bone < boneCount; bone++)
                        {
                                String boneName = modelFile.readString();
                                Matrix4 boneTransform = modelFile.readMatrix4();
                                Matrix4 boneTransformInverse = modelFile.readMatrix4();
                                Vector3 bonePosition = modelFile.readVector3();
                                // TODO: Quaternion
                                Vector4 boneOrientation = modelFile.readVector4();
                                Vector3 boneScale = modelFile.readVector3();
                        }
                        for (int bone = 0; bone < boneCount; bone++)
                        {
                                int parentBoneIndex = modelFile.readInt();
                        }
                       
                        // groups
                        int groupCount = modelFile.readInt();
                        log(LOG_TAG, "Group Count: " + groupCount);
                       
                        MeshGroup[] groups = new MeshGroup[groupCount];
                       
                        for (int group = 0; group < groupCount; group++)
                        {
                                groups[group] = loadMeshGroup(meshes[i], modelFile);                           
                        }
                       
                        meshes[i].setGroups(groups);
                       
                        break;                         
                }
               
                model.setMeshes(meshes);
               
                log(LOG_TAG, "Loading finished");
               
                return model;
        }
       
        public static Geometry loadGeometry(Graphics graphics, File modelFile) throws IOException
        {
                log(LOG_TAG, "=== Geometry Begin ===");
                               
                int version = modelFile.readInt();
                log(LOG_TAG, "Geometry Version: " + version);
                if (version != 2)
                        throw new IOException("Geometry Version incorrect, must be 2: " + version);

                int primitiveType = modelFile.readInt();
                switch (primitiveType)
                {
                case 0:
                        log(LOG_TAG, "PrimitiveType: Triangles");
                        break;
                case 1:
                        log(LOG_TAG, "PrimitiveType: Triangle Strip");
                        break;
                case 2:
                        log(LOG_TAG, "PrimitiveType: Lines");
                        break;
                case 3:
                        log(LOG_TAG, "PrimitiveType: Line Strip");
                        break;
                case 4:
                        log(LOG_TAG, "PrimitiveType: Points");
                        break;
                }
               
                int vertexCount = modelFile.readInt();
                log(LOG_TAG, "Vertex Count: " + vertexCount);
               
                int indexCount = modelFile.readInt();
                log(LOG_TAG, "Index Count: " + indexCount);
               
                int streamCount = modelFile.readInt();
                log(LOG_TAG, "Stream Count: " + streamCount);
               
                VertexStream[] streams = new VertexStream[streamCount];
               
                for (int i = 0; i < streamCount; i++)
                {
                        log(LOG_TAG, "=== BEGIN VertexStream [" + i + "] ===");
                        streams[i] = loadVertexStream(graphics, modelFile);
                        log(LOG_TAG, "=== END VertexStream [" + i + "] ===");
                }
               
                boolean hasIndexStream = modelFile.readBool();
                log(LOG_TAG, "Has Index Stream" + (hasIndexStream ? "Yes" : "No"));
               
                IndexStream indexStream = null;
                if (hasIndexStream)
                {
                        log(LOG_TAG, "=== BEGIN IndexStream ===");
                        indexStream = loadIndexStream(graphics, modelFile);
                        log(LOG_TAG, "=== END IndexStream ===");
                }
               
                Geometry geometry = new Geometry(graphics, Geometry.PrimitiveType.values()[primitiveType], vertexCount, indexCount);
                geometry.setVertexStreams(streams);    
                geometry.setIndexStream(indexStream);
               
                log(LOG_TAG, "=== Geometry End ===");
               
                return geometry;
        }
       
        public static VertexStream loadVertexStream(Graphics graphics, File modelFile) throws IOException
        {              
                VertexStream.StreamType streamType = (modelFile.readInt() == 1) ? VertexStream.StreamType.INDIVIDUAL : VertexStream.StreamType.INTERLEAVED;
                log(LOG_TAG, "Stream Type: " + streamType.toString());
               
                String streamName = modelFile.readString();
                log(LOG_TAG, "Stream Name: " + streamName);
               
                int streamVertexCount = modelFile.readInt();
                log(LOG_TAG, "Stream Vertex Count: " + streamVertexCount);
               
                int coordsPerElement = modelFile.readInt();
                log(LOG_TAG, "Coords per Element: " + coordsPerElement);
               
                int bytesPerElement = modelFile.readInt();
                log(LOG_TAG, "Bytes per Element: " + bytesPerElement);
               
                // Stream Begin
                byte[] streamBuffer = loadStream(modelFile);
                // Stream End          
               
                VertexAttribute[] attribs = null;
               
                if (streamType == VertexStream.StreamType.INDIVIDUAL)
                {
                        attribs = new VertexAttribute[1];
                        attribs[0] = loadAttribute(modelFile);
                }
                else if (streamType == VertexStream.StreamType.INTERLEAVED)
                {
                        int vertexAttribCount = modelFile.readInt();
                        log(LOG_TAG, "Vertex Attrib Count: " + vertexAttribCount);
                       
                        attribs = new VertexAttribute[vertexAttribCount];
                       
                        for (int attrib = 0; attrib < vertexAttribCount; attrib++)
                        {
                                log(LOG_TAG, "=== BEGIN VertexAttribute [" + attrib + "] ===");
                                // Vertex Attribute
                                attribs[attrib] = loadAttribute(modelFile);
                                log(LOG_TAG, "=== END VertexAttribute [" + attrib + "] ===");
                        }
                }
                else
                {
                        throw new IOException();
                }
               
                VertexStream stream = new VertexStream(graphics, streamType, streamName, attribs, false);
                stream.setData(streamBuffer, coordsPerElement, bytesPerElement);
               
                return stream;
        }
       
        public static VertexAttribute loadAttribute(File modelFile) throws IOException
        {
                int suxType = modelFile.readInt();                                     
                log(LOG_TAG, "Attrib Data Type: " + suxType);
               
                if (!VertexAttribute.isValidType(suxType))
                        throw new IOException("Invalid vertex attribute type: " + suxType);
               
                int dataFormat = modelFile.readInt();
                log(LOG_TAG, "Attrib Data Format: " + dataFormat);
               
                if (dataFormat != 0)
                        throw new IOException("Unsupported Data Format (must be 0 / Float): " + dataFormat);
               
                int coordsPerElement = modelFile.readInt();
                log(LOG_TAG, "Attrib Coords Per Element: " + coordsPerElement);
               
                int byteOffset = modelFile.readInt();
                log(LOG_TAG, "Attrib Byte Offset: " + byteOffset);
               
                VertexAttribute attrib = new VertexAttribute(VertexAttribute.toInternalType(suxType), coordsPerElement, byteOffset);
               
                return attrib;
        }
       
        public static IndexStream loadIndexStream(Graphics graphics, File modelFile) throws IOException
        {
                int indexCount = modelFile.readInt();
                log(LOG_TAG, "Index Count: " + indexCount);
               
                int dataFormat = modelFile.readInt();
                log(LOG_TAG, "Data Format: " + dataFormat);
                if (dataFormat != 1)
                        throw new IOException("Data Format unsupported, must be 1 (Int16): " + dataFormat);
               
                // Stream Begin
                byte[] streamBuffer = loadStream(modelFile);
                // Stream End
               
                IndexStream indexStream = new IndexStream(graphics);
                indexStream.setData(streamBuffer);
               
                return indexStream;
        }

        public static byte[] loadStream(File modelFile) throws IOException
        {
                // Store endianness because the stream might change it
                File.Endianness endianness = modelFile.getEndianness();
               
                byte byteOrdering = modelFile.readByte();
                log(LOG_TAG, "Stream Byte Ordering: " + (byteOrdering == 0 ? "Little Endian" : "Big Endian"));
               
                if (byteOrdering == 0)
                        modelFile.setEndianness(File.Endianness.LITTLE_ENDIAN);
                else
                        modelFile.setEndianness(File.Endianness.BIG_ENDIAN);
               
                byte charFormat = modelFile.readByte();
                log(LOG_TAG, "Stream CharFormat: " + (charFormat == 0 ? "ASCII" : "UTF16"));
               
                int byteLength = modelFile.readInt();
                log(LOG_TAG, "Stream Byte Length: " + byteLength);
               
                byte[] buffer = new byte[byteLength];
                modelFile.readFully(buffer, 0, byteLength);
               
                // Restore endianness
                modelFile.setEndianness(endianness);
               
                return buffer;
        }
       
        public static MeshGroup loadMeshGroup(Mesh mesh, File modelFile) throws IOException
        {
                log(LOG_TAG, "=== Mesh.Group Begin ===");
               
                String name = modelFile.readString();
                log(LOG_TAG, "Name: " + name);
               
                String effectFile = modelFile.readString();
                log(LOG_TAG, "-- Effect file: " + effectFile);
               
                MeshGroup group = new MeshGroup(mesh, name);
               
                // TODO: do actual parsing of external technique
                Effect effect = new Effect(mesh.getGraphics(), effectFile);
               
                ShaderProgram shader = ShaderUtil.createShaderFromFile(mesh.getGraphics(), Gdx.files.internal("data/shaders/default.vs"), Gdx.files.internal("data/shaders/default.fs"));
                Technique[] effectTechs = new Technique[1];
                effectTechs[0] = new Technique(effect, "diffuse", shader);
               
                effect.setTechniques(effectTechs);
               
                int techniqueCount = modelFile.readInt();
                log(LOG_TAG, "-- Technique Count: " + techniqueCount);
               
                MeshGroup.TechniqueRef[] techRefs = new MeshGroup.TechniqueRef[techniqueCount];
               
                for (int i = 0; i < techniqueCount; i++)
                {
                        String staticTechniqueName = modelFile.readString();
                        log(LOG_TAG, "-- Static Technique: " + staticTechniqueName);
                        String skinnedTechniqueName = modelFile.readString();
                        log(LOG_TAG, "-- Skinned Technique: " + skinnedTechniqueName);
                       
                        // TODO: read technique from parsed effect file
                        techRefs[i] = group.new TechniqueRef(effectTechs[0], effectTechs[0]);
                }
               
                group.setTechniqueRefs(techRefs);
               
                String bonesParameterName = modelFile.readString();
               
                int firstIndex = modelFile.readInt();
                log(LOG_TAG, "First Index: " + firstIndex);
                int lastIndex = modelFile.readInt();
                log(LOG_TAG, "Last Index: " + lastIndex);
               
                int firstVertex = modelFile.readInt();
                log(LOG_TAG, "First Vertex: " + firstVertex);
                int lastVertex = modelFile.readInt();
                log(LOG_TAG, "Last Vertex: " + lastVertex);
               
                group.setIndices(firstIndex, lastIndex, firstVertex, lastVertex);
               
                // TODO: make render state class
                RenderStatesConfiguration rs = loadRenderState(mesh.getGraphics(), modelFile);
               
                group.setRenderStates(rs);
               
                int variablesCount = modelFile.readInt();
                log(LOG_TAG, "== Variable Count: " + variablesCount);
               
                Effect.Variable[] vars = new Effect.Variable[variablesCount];
               
                for (int i = 0; i < variablesCount; i++)
                {
                        String variableName = modelFile.readString();
                       
                        log(LOG_TAG, "-- [" + i + "] Variable Name: " + variableName);
                       
                        int variableType = modelFile.readInt();
                       
                        switch (variableType)
                        {
                        case Effect.Variable.TYPE_TEXTURE2D: // Texture2D
                        case Effect.Variable.TYPE_TEXTURE3D: // Texture3D
                        case Effect.Variable.TYPE_TEXTURECUBE: // TextureCube
                                {
                                        String texFile = modelFile.readString();
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Texture (" + texFile + ")");
                                       
                                        Texture texture = new Texture(Gdx.files.internal("data/" + texFile));
                                        vars[i] = effect.new VariableTexture(variableName, texture);
                                       
                                        break;
                                }
                        case Effect.Variable.TYPE_BOOL: // Bool
                                {
                                        boolean b = modelFile.readBool();
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: bool (" + (b ? "true" : "false") + ")");
                                       
                                        vars[i] = effect.new VariableBool(variableName, b);
                                       
                                        break;
                                }
                        case Effect.Variable.TYPE_INT: // Int
                                {
                                        int n = modelFile.readInt();
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: int (" + n + ")");
                                       
                                        vars[i] = effect.new VariableInt(variableName, n);
                                       
                                        break;
                                }
                        case Effect.Variable.TYPE_FLOAT: // Float
                                {
                                        float f = modelFile.readFloat();
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: float (" + f + ")");
                                       
                                        vars[i] = effect.new VariableFloat(variableName, f);
                                       
                                        break;
                                }
                        case Effect.Variable.TYPE_VECTOR2: // Vector2
                                {
                                        Vector2 v2 = modelFile.readVector2();
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Vector2 (" + v2.x + ", " + v2.y + ")");
                                       
                                        vars[i] = effect.new VariableVector2(variableName, v2);
                                       
                                        break;
                                }
                        case Effect.Variable.TYPE_VECTOR3: // Vector3
                                {
                                        Vector3 v3 = modelFile.readVector3();
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Vector3 (" + v3.x + ", " + v3.y + ", " + v3.z + ")");
                                       
                                        vars[i] = effect.new VariableVector3(variableName, v3);
                                       
                                        break;
                                }
                        case Effect.Variable.TYPE_VECTOR4: // Vector4
                                {
                                        Vector4 v4 = modelFile.readVector4();
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Vector3 (" + v4.x + ", " + v4.y + ", " + v4.z + ", " + v4.w + ")");
                                       
                                        vars[i] = effect.new VariableVector4(variableName, v4);
                                       
                                        break;
                                }
                        }
                }
               
                effect.setVariables(vars);
                effect.connectVariables();
                group.setEffect(effect);
               
                // Bone Reindexing for tighter usage of uniforms
                int boneIndexCount = modelFile.readInt();
                for (int i = 0; i < boneIndexCount; i++)
                {
                        int originalIndex = modelFile.readInt();
                        int newIndex = modelFile.readInt();
                }
               
                log(LOG_TAG, "=== Mesh.Group End===");
                return group;
        }
       
        @SuppressWarnings("unused")
        public static RenderStatesConfiguration loadRenderState(Graphics graphics, File modelFile) throws IOException
        {
                RenderStatesConfiguration rs = new RenderStatesConfiguration(graphics);
               
                // Alpha test not supported
                boolean alphaTestEnabled = modelFile.readBool();
                float alphaTestThreshold = modelFile.readFloat();
                int alphaTestComparisonFunc = modelFile.readInt();
               
                boolean blendingEnabled = modelFile.readBool();
                int blendingMode = modelFile.readInt();
               
                rs.setBlendingEnabled(blendingEnabled);
                rs.setBlendingMode(blendingMode);
               
                boolean cullingEnabled = modelFile.readBool();
                int cullingWinding = modelFile.readInt();
               
                rs.setCullingEnabled(cullingEnabled);
                rs.setCullingWinding(cullingWinding);
               
                boolean depthTestEnabled = modelFile.readBool();
                boolean depthBufferWriteEnabled = modelFile.readBool();
                int depthTestComparisonFunc = modelFile.readInt();
               
                rs.setDepthTestEnabled(depthTestEnabled);
                rs.setDepthBufferWriteEnabled(depthBufferWriteEnabled);
                rs.setDepthTestComparisonFunc(depthTestComparisonFunc);
               
                // Polygon Mode not supported
                int polygonMode = modelFile.readInt();
               
                return rs;
        }
}