Exemple #1
0
        /// <summary>
        ///     Loads a Fantasy Life ZMDL model.
        ///     Note that ZMDL must start at offset 0x0 (don't try using it for ZMDLs inside containers).
        /// </summary>
        /// <param name="data">Stream of the ZMDL file.</param>
        /// <returns></returns>
        public static RenderBase.OModelGroup load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);

            RenderBase.OModelGroup models = new RenderBase.OModelGroup();
            RenderBase.OModel      model  = new RenderBase.OModel();
            model.name = "model";

            string zmdlMagic = IOUtils.readString(input, 0, 4);

            data.Seek(0x20, SeekOrigin.Begin);
            uint   materialsOffset   = input.ReadUInt32();
            uint   skeletonOffset    = input.ReadUInt32();
            uint   modelOffset       = input.ReadUInt32();
            ushort materialsCount    = input.ReadUInt16();
            ushort bonesCount        = input.ReadUInt16();
            ushort modelObjectsCount = input.ReadUInt16();
            ushort unknowCount       = input.ReadUInt16();

            //Materials
            List <byte> materialObjectBinding = new List <byte>();

            for (int materialIndex = 0; materialIndex < materialsCount; materialIndex++)
            {
                RenderBase.OMaterial material = new RenderBase.OMaterial();

                material.name = IOUtils.readString(input, (uint)(materialsOffset + materialIndex * 0xb4));
                data.Seek(materialsOffset + (materialIndex * 0xb4) + 0x94, SeekOrigin.Begin);
                uint textureReferenceOffset     = input.ReadUInt32();
                uint objectReferenceIndexOffset = input.ReadUInt32();

                data.Seek(objectReferenceIndexOffset, SeekOrigin.Begin);
                materialObjectBinding.Add(input.ReadByte());

                material.name0 = IOUtils.readString(input, textureReferenceOffset);
                data.Seek(textureReferenceOffset + 0x40, SeekOrigin.Begin);
                while ((data.Position & 3) != 0)
                {
                    input.ReadByte();                //Align Word
                }
                data.Seek(0x30, SeekOrigin.Current); //Unknown matrix (possibly UV transform)
                input.ReadUInt32();
                input.ReadByte();
                input.ReadByte();
                byte wrap = input.ReadByte();
                input.ReadByte();

                model.material.Add(material);
            }

            //Skeleton
            for (int boneIndex = 0; boneIndex < bonesCount; boneIndex++)
            {
                RenderBase.OBone bone = new RenderBase.OBone();
                bone.name = IOUtils.readString(input, (uint)(skeletonOffset + boneIndex * 0xcc));

                data.Seek(skeletonOffset + (boneIndex * 0xcc) + 0x40, SeekOrigin.Begin);
                data.Seek(0x64, SeekOrigin.Current); //Unknow matrices, probably transform and other stuff

                bone.translation   = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                bone.rotation      = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                bone.scale         = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                bone.absoluteScale = new RenderBase.OVector3(bone.scale);

                bone.parentId = input.ReadInt16();
                input.ReadUInt16();

                model.skeleton.Add(bone);
            }

            //Meshes
            for (int objIndex = 0; objIndex < modelObjectsCount; objIndex++)
            {
                RenderBase.OMesh obj = new RenderBase.OMesh();
                obj.name = string.Format("mesh_{0}", objIndex);

                data.Seek(modelOffset + objIndex * 0xc4, SeekOrigin.Begin);

                attributeEntry[] attributes = new attributeEntry[9];
                for (int attribute = 0; attribute < 9; attribute++)
                {
                    attributes[attribute].floats = new RenderBase.OVector4(
                        input.ReadSingle(),
                        input.ReadSingle(),
                        input.ReadSingle(),
                        input.ReadSingle());
                    attributes[attribute].offset          = input.ReadByte() * 4;
                    attributes[attribute].attributeLength = input.ReadByte();
                    attributes[attribute].stride          = input.ReadUInt16() * 4;

                    if (attributes[attribute].attributeLength > 0)
                    {
                        switch (attribute)
                        {
                        case aNormal: obj.hasNormal = true; break;

                        case aColor: obj.hasColor = true; break;

                        case aTex0: obj.texUVCount = 1; break;

                        case aNode: obj.hasNode = true; break;

                        case aWeight: obj.hasWeight = true; break;
                        }
                    }
                }

                int  vertexStride       = attributes[8].stride;
                uint facesHeaderOffset  = input.ReadUInt32();
                uint facesHeaderEntries = input.ReadUInt32();
                uint vertexBufferOffset = input.ReadUInt32();
                uint vertexBufferLength = input.ReadUInt32() * 4;

                for (int faceIndex = 0; faceIndex < facesHeaderEntries; faceIndex++)
                {
                    data.Seek(facesHeaderOffset + faceIndex * 0x14, SeekOrigin.Begin);

                    uint boneNodesOffset           = input.ReadUInt32();
                    uint boneNodesEntries          = input.ReadUInt32();
                    uint indexBufferOffset         = input.ReadUInt32();
                    uint indexBufferPrimitiveCount = input.ReadUInt32();
                    input.ReadUInt32();

                    data.Seek(boneNodesOffset, SeekOrigin.Begin);
                    List <byte> nodeList = new List <byte>();
                    for (int n = 0; n < boneNodesEntries; n++)
                    {
                        nodeList.Add(input.ReadByte());
                    }

                    data.Seek(indexBufferOffset, SeekOrigin.Begin);
                    for (int face = 0; face < indexBufferPrimitiveCount; face++)
                    {
                        ushort index = input.ReadUInt16();

                        RenderBase.OVertex vertex = new RenderBase.OVertex();
                        vertex.diffuseColor = 0xffffffff;

                        long position     = data.Position;
                        long vertexOffset = vertexBufferOffset + index * vertexStride;
                        data.Seek(vertexOffset + attributes[aPosition].offset, SeekOrigin.Begin);
                        vertex.position = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                        if (attributes[aNormal].attributeLength > 0)
                        {
                            data.Seek(vertexOffset + attributes[aNormal].offset, SeekOrigin.Begin);
                            vertex.normal = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        }

                        if (attributes[aColor].attributeLength > 0)
                        {
                            data.Seek(vertexOffset + attributes[aColor].offset, SeekOrigin.Begin);
                            uint r = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            uint g = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            uint b = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            uint a = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                        }

                        if (attributes[aTex0].attributeLength > 0)
                        {
                            data.Seek(vertexOffset + attributes[aTex0].offset, SeekOrigin.Begin);
                            vertex.texture0 = new RenderBase.OVector2(input.ReadSingle(), input.ReadSingle());
                        }

                        for (int boneIndex = 0; boneIndex < attributes[aNode].attributeLength; boneIndex++)
                        {
                            data.Seek(vertexOffset + attributes[aNode].offset + (boneIndex * 4), SeekOrigin.Begin);
                            vertex.node.Add(nodeList[(int)input.ReadSingle()]);
                        }

                        for (int boneWeight = 0; boneWeight < attributes[aWeight].attributeLength; boneWeight++)
                        {
                            data.Seek(vertexOffset + attributes[aWeight].offset + (boneWeight * 4), SeekOrigin.Begin);
                            vertex.weight.Add(input.ReadSingle());
                        }

                        MeshUtils.calculateBounds(model, vertex);
                        obj.vertices.Add(vertex);

                        data.Seek(position, SeekOrigin.Begin);
                    }
                }

                int materialId = materialObjectBinding.IndexOf((byte)objIndex);
                if (materialId > -1)
                {
                    obj.materialId = (ushort)materialId;
                }
                model.mesh.Add(obj);
            }

            data.Close();

            models.model.Add(model);
            return(models);
        }
        /// <summary>
        ///     Loads a Fantasy Life ZMDL model.
        ///     Note that ZMDL must start at offset 0x0 (don't try using it for ZMDLs inside containers).
        /// </summary>
        /// <param name="data">Stream of the ZMDL file.</param>
        /// <returns></returns>
        public static RenderBase.OModelGroup load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);

            RenderBase.OModelGroup models = new RenderBase.OModelGroup();
            RenderBase.OModel model = new RenderBase.OModel();
            model.name = "model";

            string zmdlMagic = IOUtils.readString(input, 0, 4);
            data.Seek(0x20, SeekOrigin.Begin);
            uint materialsOffset = input.ReadUInt32();
            uint skeletonOffset = input.ReadUInt32();
            uint modelOffset = input.ReadUInt32();
            ushort materialsCount = input.ReadUInt16();
            ushort bonesCount = input.ReadUInt16();
            ushort modelObjectsCount = input.ReadUInt16();
            ushort unknowCount = input.ReadUInt16();

            //Materials
            List<byte> materialObjectBinding = new List<byte>();
            for (int materialIndex = 0; materialIndex < materialsCount; materialIndex++)
            {
                RenderBase.OMaterial material = new RenderBase.OMaterial();

                material.name = IOUtils.readString(input, (uint)(materialsOffset + materialIndex * 0xb4));
                data.Seek(materialsOffset + (materialIndex * 0xb4) + 0x94, SeekOrigin.Begin);
                uint textureReferenceOffset = input.ReadUInt32();
                uint objectReferenceIndexOffset = input.ReadUInt32();

                data.Seek(objectReferenceIndexOffset, SeekOrigin.Begin);
                materialObjectBinding.Add(input.ReadByte());

                material.name0 = IOUtils.readString(input, textureReferenceOffset);
                data.Seek(textureReferenceOffset + 0x40, SeekOrigin.Begin);
                while ((data.Position & 3) != 0) input.ReadByte(); //Align Word
                data.Seek(0x30, SeekOrigin.Current); //Unknown matrix (possibly UV transform)
                input.ReadUInt32();
                input.ReadByte();
                input.ReadByte();
                byte wrap = input.ReadByte();
                input.ReadByte();

                model.material.Add(material);
            }

            //Skeleton
            for (int boneIndex = 0; boneIndex < bonesCount; boneIndex++)
            {
                RenderBase.OBone bone = new RenderBase.OBone();
                bone.name = IOUtils.readString(input, (uint)(skeletonOffset + boneIndex * 0xcc));

                data.Seek(skeletonOffset + (boneIndex * 0xcc) + 0x40, SeekOrigin.Begin);
                data.Seek(0x64, SeekOrigin.Current); //Unknow matrices, probably transform and other stuff

                bone.translation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                bone.rotation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                bone.scale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                bone.absoluteScale = new RenderBase.OVector3(bone.scale);

                bone.parentId = input.ReadInt16();
                input.ReadUInt16();

                model.skeleton.Add(bone);
            }

            //Meshes
            for (int objIndex = 0; objIndex < modelObjectsCount; objIndex++)
            {
                RenderBase.OMesh obj = new RenderBase.OMesh();
                obj.name = string.Format("mesh_{0}", objIndex);

                data.Seek(modelOffset + objIndex * 0xc4, SeekOrigin.Begin);

                attributeEntry[] attributes = new attributeEntry[9];
                for (int attribute = 0; attribute < 9; attribute++)
                {
                    attributes[attribute].floats = new RenderBase.OVector4(
                        input.ReadSingle(),
                        input.ReadSingle(),
                        input.ReadSingle(),
                        input.ReadSingle());
                    attributes[attribute].offset = input.ReadByte() * 4;
                    attributes[attribute].attributeLength = input.ReadByte();
                    attributes[attribute].stride = input.ReadUInt16() * 4;

                    if (attributes[attribute].attributeLength > 0)
                    {
                        switch (attribute)
                        {
                            case aNormal: obj.hasNormal = true; break;
                            case aColor: obj.hasColor = true; break;
                            case aTex0: obj.texUVCount = 1; break;
                            case aNode: obj.hasNode = true; break;
                            case aWeight: obj.hasWeight = true; break;
                        }
                    }
                }

                int vertexStride = attributes[8].stride;
                uint facesHeaderOffset = input.ReadUInt32();
                uint facesHeaderEntries = input.ReadUInt32();
                uint vertexBufferOffset = input.ReadUInt32();
                uint vertexBufferLength = input.ReadUInt32() * 4;

                for (int faceIndex = 0; faceIndex < facesHeaderEntries; faceIndex++)
                {
                    data.Seek(facesHeaderOffset + faceIndex * 0x14, SeekOrigin.Begin);

                    uint boneNodesOffset = input.ReadUInt32();
                    uint boneNodesEntries = input.ReadUInt32();
                    uint indexBufferOffset = input.ReadUInt32();
                    uint indexBufferPrimitiveCount = input.ReadUInt32();
                    input.ReadUInt32();

                    data.Seek(boneNodesOffset, SeekOrigin.Begin);
                    List<byte> nodeList = new List<byte>();
                    for (int n = 0; n < boneNodesEntries; n++) nodeList.Add(input.ReadByte());

                    data.Seek(indexBufferOffset, SeekOrigin.Begin);
                    for (int face = 0; face < indexBufferPrimitiveCount; face++)
                    {
                        ushort index = input.ReadUInt16();

                        RenderBase.OVertex vertex = new RenderBase.OVertex();
                        vertex.diffuseColor = 0xffffffff;

                        long position = data.Position;
                        long vertexOffset = vertexBufferOffset + index * vertexStride;
                        data.Seek(vertexOffset + attributes[aPosition].offset, SeekOrigin.Begin);
                        vertex.position = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                        if (attributes[aNormal].attributeLength > 0)
                        {
                            data.Seek(vertexOffset + attributes[aNormal].offset, SeekOrigin.Begin);
                            vertex.normal = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        }

                        if (attributes[aColor].attributeLength > 0)
                        {
                            data.Seek(vertexOffset + attributes[aColor].offset, SeekOrigin.Begin);
                            uint r = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            uint g = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            uint b = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            uint a = MeshUtils.saturate(input.ReadSingle() * 0xff);
                            vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                        }

                        if (attributes[aTex0].attributeLength > 0)
                        {
                            data.Seek(vertexOffset + attributes[aTex0].offset, SeekOrigin.Begin);
                            vertex.texture0 = new RenderBase.OVector2(input.ReadSingle(), input.ReadSingle());
                        }

                        for (int boneIndex = 0; boneIndex < attributes[aNode].attributeLength; boneIndex++)
                        {
                            data.Seek(vertexOffset + attributes[aNode].offset + (boneIndex * 4), SeekOrigin.Begin);
                            vertex.node.Add(nodeList[(int)input.ReadSingle()]);
                        }

                        for (int boneWeight = 0; boneWeight < attributes[aWeight].attributeLength; boneWeight++)
                        {
                            data.Seek(vertexOffset + attributes[aWeight].offset + (boneWeight * 4), SeekOrigin.Begin);
                            vertex.weight.Add(input.ReadSingle());
                        }

                        MeshUtils.calculateBounds(model, vertex);
                        obj.vertices.Add(vertex);

                        data.Seek(position, SeekOrigin.Begin);
                    }
                }

                int materialId = materialObjectBinding.IndexOf((byte)objIndex);
                if (materialId > -1) obj.materialId = (ushort)materialId;
                model.mesh.Add(obj);
            }

            data.Close();

            models.model.Add(model);
            return models;
        }