예제 #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);
        }
예제 #2
0
        public static RenderBase.OModelGroup load(string fileName)
        {
            FileStream   data  = new FileStream(fileName, FileMode.Open);
            BinaryReader input = new BinaryReader(data);

            RenderBase.OModelGroup models;
            RenderBase.OModel      model;

            string extension   = Path.GetExtension(fileName).ToLower();
            string bchFile     = fileName.Replace(extension, ".bch");
            bool   isBCHLoaded = false;

            if (File.Exists(bchFile))
            {
                models = BCH.load(bchFile);
                model  = models.model[0];
                models.model.Clear();
                isBCHLoaded = true;
            }
            else
            {
                models     = new RenderBase.OModelGroup();
                model      = new RenderBase.OModel();
                model.name = "model";
                model.material.Add(new RenderBase.OMaterial());
            }

            ushort format             = input.ReadUInt16();
            bool   isDataWithinHeader = format == 4;

            input.ReadUInt16(); //-1?
            uint contentFlags = input.ReadUInt32();
            bool hasNameTable = (contentFlags & 2) > 0;
            uint mode         = input.ReadUInt32();
            uint meshCount    = input.ReadUInt32();

            List <vtxEntry> vtxDescriptors = new List <vtxEntry>();
            List <idxEntry> idxDescriptors = new List <idxEntry>();

            for (int i = 0; i < meshCount; i++)
            {
                if (mode == 1 && i == 0)
                {
                    vtxDescriptors.Add(getVtxDescriptor(input));
                }

                uint facesCount = input.ReadUInt32();
                for (int j = 0; j < facesCount; j++)
                {
                    idxEntry face = new idxEntry();
                    face.meshIndex = i;
                    uint nodesCount = input.ReadUInt32();
                    for (int k = 0; k < nodesCount; k++)
                    {
                        face.nodeList.Add(input.ReadUInt32());
                    }
                    face.primitiveCount = input.ReadUInt32();
                    if (hasNameTable)
                    {
                        face.nameId = input.ReadUInt32();
                    }
                    if (isDataWithinHeader)
                    {
                        face.buffer = new ushort[face.primitiveCount];
                        for (int k = 0; k < face.primitiveCount; k++)
                        {
                            face.buffer[k] = input.ReadUInt16();
                        }
                        alignWord(input);
                    }

                    idxDescriptors.Add(face);
                }

                if (mode == 0)
                {
                    if (isDataWithinHeader)
                    {
                        vtxEntry desc = getVtxDescriptor(input);
                        desc.buffer = new byte[desc.length];
                        input.Read(desc.buffer, 0, desc.buffer.Length);
                        vtxDescriptors.Add(desc);
                        alignWord(input);
                    }
                    else
                    {
                        vtxDescriptors.Add(getVtxDescriptor(input));
                    }
                }
            }

            List <string> objNameTable = new List <string>();

            if (hasNameTable)
            {
                for (int i = 0; i < meshCount; i++)
                {
                    byte index = input.ReadByte();
                    objNameTable.Add(IOUtils.readString(input, (uint)data.Position, true));
                }
            }

            if (!isDataWithinHeader)
            {
                align(input);
            }
            byte[]   vtxBuffer  = null;
            vtxEntry currVertex = null;
            int      faceIndex  = 0;

            for (int i = 0; i < meshCount; i++)
            {
                if (mode == 0 || i == 0)
                {
                    currVertex = vtxDescriptors[i];
                    if (!isDataWithinHeader)
                    {
                        vtxBuffer = new byte[vtxDescriptors[i].length];
                        input.Read(vtxBuffer, 0, vtxBuffer.Length);
                        align(input);
                    }
                    else
                    {
                        vtxBuffer = currVertex.buffer;
                    }
                }

                RenderBase.OMesh obj;
                if (isBCHLoaded)
                {
                    obj = model.mesh[0];
                    model.mesh.RemoveAt(0);
                }
                else
                {
                    obj      = new RenderBase.OMesh();
                    obj.name = "mesh_" + i.ToString();
                }

                for (int j = 0; j < currVertex.attributes.Count; j++)
                {
                    switch (currVertex.attributes[j].type)
                    {
                    case vtxAttributeType.normal: obj.hasNormal = true; break;

                    case vtxAttributeType.color: obj.hasColor = true; break;

                    case vtxAttributeType.textureCoordinate0: obj.texUVCount = 1; break;

                    case vtxAttributeType.textureCoordinate1: obj.texUVCount = 2; break;

                    case vtxAttributeType.boneIndex: obj.hasNode = true; break;

                    case vtxAttributeType.boneWeight: obj.hasWeight = true; break;
                    }
                }

                for (;;)
                {
                    int indexBufferPos = 0;
                    for (int j = 0; j < idxDescriptors[faceIndex].primitiveCount; j++)
                    {
                        ushort index;
                        if (isDataWithinHeader)
                        {
                            index = idxDescriptors[faceIndex].buffer[indexBufferPos++];
                        }
                        else
                        {
                            index = input.ReadUInt16();
                        }

                        RenderBase.OVertex vertex = new RenderBase.OVertex();
                        vertex.diffuseColor = 0xffffffff;
                        for (int k = 0; k < currVertex.attributes.Count; k++)
                        {
                            vtxAttribute att   = currVertex.attributes[k];
                            int          pos   = (int)(index * currVertex.stride + att.offset);
                            float        scale = att.scale;
                            switch (currVertex.attributes[k].type)
                            {
                            case vtxAttributeType.position: vertex.position = getVector3(vtxBuffer, pos, att.format, scale); break;

                            case vtxAttributeType.normal: vertex.normal = getVector3(vtxBuffer, pos, att.format, scale); break;

                            case vtxAttributeType.color:
                                RenderBase.OVector4 c = getVector4(vtxBuffer, pos, att.format, scale);
                                uint r = MeshUtils.saturate(c.x * 0xff);
                                uint g = MeshUtils.saturate(c.y * 0xff);
                                uint b = MeshUtils.saturate(c.z * 0xff);
                                uint a = MeshUtils.saturate(c.w * 0xff);
                                vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                                break;

                            case vtxAttributeType.textureCoordinate0: vertex.texture0 = getVector2(vtxBuffer, pos, att.format, scale); break;

                            case vtxAttributeType.textureCoordinate1: vertex.texture1 = getVector2(vtxBuffer, pos, att.format, scale); break;

                            case vtxAttributeType.boneIndex:
                                byte n0 = vtxBuffer[pos];
                                byte n1 = vtxBuffer[pos + 1];
                                vertex.node.Add((int)idxDescriptors[faceIndex].nodeList[n0]);
                                vertex.node.Add((int)idxDescriptors[faceIndex].nodeList[n1]);
                                break;

                            case vtxAttributeType.boneWeight:
                                RenderBase.OVector2 w = getVector2(vtxBuffer, pos, att.format, scale);
                                vertex.weight.Add(w.x);
                                vertex.weight.Add(w.y);
                                break;
                            }
                        }

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

                    faceIndex++;
                    if (!isDataWithinHeader)
                    {
                        align(input);
                    }
                    if (faceIndex >= idxDescriptors.Count)
                    {
                        break;
                    }
                    if (idxDescriptors[faceIndex].meshIndex == i)
                    {
                        continue;
                    }
                    break;
                }

                model.mesh.Add(obj);
            }

            models.model.Add(model);

            data.Close();
            return(models);
        }