Пример #1
0
        private static subMeshInfo getSubMeshInfo(BinaryReader input)
        {
            subMeshInfo output = new subMeshInfo();

            output.cmdBuffers = new List <PICACommandReader>();
            output.nodeLists  = new List <ushort[]>();
            output.vtxLengths = new List <uint>();
            output.idxLengths = new List <uint>();
            output.names      = new List <string>();

            int currCmdIdx = 0;
            int totalCmds  = 0;

            while ((currCmdIdx + 1 < totalCmds) || currCmdIdx == 0)
            {
                uint cmdLength = input.ReadUInt32();
                currCmdIdx = input.ReadInt32();
                totalCmds  = input.ReadInt32();
                input.ReadInt32();

                output.cmdBuffers.Add(new PICACommandReader(input.BaseStream, cmdLength / 4));
            }

            output.count = totalCmds / 3;

            for (int i = 0; i < output.count; i++)
            {
                uint   maybeHash        = input.ReadUInt32();
                uint   subMeshNameLen   = input.ReadUInt32();
                long   subMeshNameStart = input.BaseStream.Position;
                string name             = IOUtils.readStringWithLength(input, subMeshNameLen);

                input.BaseStream.Seek(subMeshNameStart + subMeshNameLen, SeekOrigin.Begin);

                long     nodeListStart = input.BaseStream.Position;
                byte     nodeListLen   = input.ReadByte();
                ushort[] nodeList      = new ushort[nodeListLen];
                for (int n = 0; n < nodeListLen; n++)
                {
                    nodeList[n] = input.ReadByte();
                }

                input.BaseStream.Seek(nodeListStart + 0x20, SeekOrigin.Begin);

                uint vtxCount = input.ReadUInt32();
                uint idxCount = input.ReadUInt32();
                output.vtxLengths.Add(input.ReadUInt32());
                output.idxLengths.Add(input.ReadUInt32());

                output.names.Add(name);
                output.nodeLists.Add(nodeList);
            }

            return(output);
        }
Пример #2
0
        public static RenderBase.OModel loadModel(Stream data, bool keepOpen = false)
        {
            RenderBase.OModel mdl   = new RenderBase.OModel();
            BinaryReader      input = new BinaryReader(data);

            mdl.name = "model";

            long mdlStart = data.Position;

            data.Seek(0x10, SeekOrigin.Current);
            ulong mdlMagic  = input.ReadUInt64(); //gfmodel string
            uint  mdlLength = input.ReadUInt32();

            input.ReadUInt32(); //-1

            string[] effectNames   = getStrTable(input);
            string[] textureNames  = getStrTable(input);
            string[] materialNames = getStrTable(input);
            string[] meshNames     = getStrTable(input);

            input.BaseStream.Seek(0x20, SeekOrigin.Current); //2 float4 (Maybe 2 Quaternions?)

            mdl.transform     = new RenderBase.OMatrix();
            mdl.transform.M11 = input.ReadSingle();
            mdl.transform.M12 = input.ReadSingle();
            mdl.transform.M13 = input.ReadSingle();
            mdl.transform.M14 = input.ReadSingle();

            mdl.transform.M21 = input.ReadSingle();
            mdl.transform.M22 = input.ReadSingle();
            mdl.transform.M23 = input.ReadSingle();
            mdl.transform.M24 = input.ReadSingle();

            mdl.transform.M31 = input.ReadSingle();
            mdl.transform.M32 = input.ReadSingle();
            mdl.transform.M33 = input.ReadSingle();
            mdl.transform.M34 = input.ReadSingle();

            mdl.transform.M41 = input.ReadSingle();
            mdl.transform.M42 = input.ReadSingle();
            mdl.transform.M43 = input.ReadSingle();
            mdl.transform.M44 = input.ReadSingle();

            uint unkDataLen      = input.ReadUInt32();
            uint unkDataRelStart = input.ReadUInt32();

            input.ReadUInt32();
            input.ReadUInt32();

            input.BaseStream.Seek(unkDataRelStart + unkDataLen, SeekOrigin.Current); //???

            uint bonesCount = input.ReadUInt32();

            input.BaseStream.Seek(0xc, SeekOrigin.Current);

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

            for (int b = 0; b < bonesCount; b++)
            {
                string boneName   = IOUtils.readStringWithLength(input, input.ReadByte());
                string parentName = IOUtils.readStringWithLength(input, input.ReadByte());
                byte   flags      = input.ReadByte();

                RenderBase.OBone bone = new RenderBase.OBone();

                bone.name     = boneName;
                bone.parentId = (short)boneNames.IndexOf(parentName);

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

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

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

                bone.absoluteScale = new RenderBase.OVector3(bone.scale);

                mdl.skeleton.Add(bone);
                boneNames.Add(boneName);
            }

            //Materials
            List <string> matMeshBinding = new List <string>();

            input.BaseStream.Seek(mdlStart + mdlLength + 0x20, SeekOrigin.Begin);

            for (int m = 0; m < materialNames.Length; m++)
            {
                RenderBase.OMaterial mat = new RenderBase.OMaterial();

                mat.name = materialNames[m];

                ulong matMagic  = input.ReadUInt64(); //material string
                uint  matLength = input.ReadUInt32();
                input.ReadUInt32();                   //-1

                long matStart = data.Position;

                string[] unkNames = new string[4];

                for (int n = 0; n < 4; n++)
                {
                    uint maybeHash = input.ReadUInt32();
                    byte nameLen   = input.ReadByte();

                    unkNames[n] = IOUtils.readStringWithLength(input, nameLen);
                }

                matMeshBinding.Add(unkNames[0]);

                data.Seek(0xac, SeekOrigin.Current);

                long textureCoordsStart = data.Position;

                for (int unit = 0; unit < 3; unit++)
                {
                    data.Seek(textureCoordsStart + unit * 0x42, SeekOrigin.Begin);

                    uint   maybeHash = input.ReadUInt32();
                    string texName   = IOUtils.readStringWithLength(input, input.ReadByte());

                    if (texName == string.Empty)
                    {
                        break;
                    }

                    switch (unit)
                    {
                    case 0: mat.name0 = texName; break;

                    case 1: mat.name1 = texName; break;

                    case 2: mat.name2 = texName; break;
                    }

                    ushort unitIdx = input.ReadUInt16();

                    mat.textureCoordinator[unit].scaleU     = input.ReadSingle();
                    mat.textureCoordinator[unit].scaleV     = input.ReadSingle();
                    mat.textureCoordinator[unit].rotate     = input.ReadSingle();
                    mat.textureCoordinator[unit].translateU = input.ReadSingle();
                    mat.textureCoordinator[unit].translateV = input.ReadSingle();

                    uint texMapperU = input.ReadUInt32();
                    uint texMapperV = input.ReadUInt32();

                    mat.textureMapper[unit].wrapU = (RenderBase.OTextureWrap)(texMapperU & 7);
                    mat.textureMapper[unit].wrapV = (RenderBase.OTextureWrap)(texMapperV & 7);
                }

                mdl.material.Add(mat);

                input.BaseStream.Seek(matStart + matLength, SeekOrigin.Begin);
            }

            //Meshes
            for (int m = 0; m < meshNames.Length; m++)
            {
                ulong meshMagic  = input.ReadUInt64(); //mesh string
                uint  meshLength = input.ReadUInt32();
                input.ReadUInt32();                    //-1

                long meshStart = data.Position;
                //Mesh name and other stuff goes here

                input.BaseStream.Seek(0x80, SeekOrigin.Current);

                subMeshInfo info = getSubMeshInfo(input);

                for (int sm = 0; sm < info.count; sm++)
                {
                    RenderBase.OMesh obj = new RenderBase.OMesh();

                    obj.isVisible  = true;
                    obj.name       = info.names[sm];
                    obj.materialId = (ushort)matMeshBinding.IndexOf(obj.name);

                    ushort[] nodeList = info.nodeLists[sm];

                    //NOTE: All Addresses on commands are set to 0x99999999 and are probably relocated by game engine
                    PICACommandReader vtxCmdReader = info.cmdBuffers[sm * 3 + 0];
                    PICACommandReader idxCmdReader = info.cmdBuffers[sm * 3 + 2];

                    uint vshAttributesBufferStride = vtxCmdReader.getVSHAttributesBufferStride(0);
                    uint vshTotalAttributes        = vtxCmdReader.getVSHTotalAttributes(0);
                    PICACommand.vshAttribute[] vshMainAttributesBufferPermutation = vtxCmdReader.getVSHAttributesBufferPermutation();
                    uint[] vshAttributesBufferPermutation = vtxCmdReader.getVSHAttributesBufferPermutation(0);
                    PICACommand.attributeFormat[] vshAttributesBufferFormat = vtxCmdReader.getVSHAttributesBufferFormat();

                    for (int attribute = 0; attribute < vshTotalAttributes; attribute++)
                    {
                        switch (vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]])
                        {
                        case PICACommand.vshAttribute.normal: obj.hasNormal = true; break;

                        case PICACommand.vshAttribute.tangent: obj.hasTangent = true; break;

                        case PICACommand.vshAttribute.color: obj.hasColor = true; break;

                        case PICACommand.vshAttribute.textureCoordinate0: obj.texUVCount = Math.Max(obj.texUVCount, 1); break;

                        case PICACommand.vshAttribute.textureCoordinate1: obj.texUVCount = Math.Max(obj.texUVCount, 2); break;

                        case PICACommand.vshAttribute.textureCoordinate2: obj.texUVCount = Math.Max(obj.texUVCount, 3); break;
                        }
                    }

                    PICACommand.indexBufferFormat idxBufferFormat = idxCmdReader.getIndexBufferFormat();
                    uint idxBufferTotalVertices = idxCmdReader.getIndexBufferTotalVertices();

                    obj.hasNode   = true;
                    obj.hasWeight = true;

                    long vtxBufferStart = data.Position;

                    input.BaseStream.Seek(info.vtxLengths[sm], SeekOrigin.Current);

                    long idxBufferStart = data.Position;

                    for (int faceIndex = 0; faceIndex < idxBufferTotalVertices; faceIndex++)
                    {
                        ushort index = 0;

                        switch (idxBufferFormat)
                        {
                        case PICACommand.indexBufferFormat.unsignedShort: index = input.ReadUInt16(); break;

                        case PICACommand.indexBufferFormat.unsignedByte: index = input.ReadByte(); break;
                        }

                        long dataPosition = data.Position;
                        long vertexOffset = vtxBufferStart + (index * vshAttributesBufferStride);
                        data.Seek(vertexOffset, SeekOrigin.Begin);

                        RenderBase.OVertex vertex = new RenderBase.OVertex();
                        vertex.diffuseColor = 0xffffffff;
                        // Fix weight problems
                        vertex.weight.Add(1);
                        vertex.weight.Add(0);
                        vertex.weight.Add(0);
                        vertex.weight.Add(0);

                        for (int attribute = 0; attribute < vshTotalAttributes; attribute++)
                        {
                            //gdkchan self note: The Attribute type flags are used for something else on Bone Weight (and bone index?)
                            PICACommand.vshAttribute    att    = vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]];
                            PICACommand.attributeFormat format = vshAttributesBufferFormat[vshAttributesBufferPermutation[attribute]];
                            if (att == PICACommand.vshAttribute.boneWeight)
                            {
                                format.type = PICACommand.attributeFormatType.unsignedByte;
                            }
                            RenderBase.OVector4 vector = getVector(input, format);

                            switch (att)
                            {
                            case PICACommand.vshAttribute.position:
                                vertex.position = new RenderBase.OVector3(vector.x, vector.y, vector.z);
                                break;

                            case PICACommand.vshAttribute.normal:
                                vertex.normal = new RenderBase.OVector3(vector.x, vector.y, vector.z);
                                break;

                            case PICACommand.vshAttribute.tangent:
                                vertex.tangent = new RenderBase.OVector3(vector.x, vector.y, vector.z);
                                break;

                            case PICACommand.vshAttribute.color:
                                uint r = MeshUtils.saturate(vector.x);
                                uint g = MeshUtils.saturate(vector.y);
                                uint b = MeshUtils.saturate(vector.z);
                                uint a = MeshUtils.saturate(vector.w);
                                vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                                break;

                            case PICACommand.vshAttribute.textureCoordinate0:
                                vertex.texture0 = new RenderBase.OVector2(vector.x, vector.y);
                                break;

                            case PICACommand.vshAttribute.textureCoordinate1:
                                vertex.texture1 = new RenderBase.OVector2(vector.x, vector.y);
                                break;

                            case PICACommand.vshAttribute.textureCoordinate2:
                                vertex.texture2 = new RenderBase.OVector2(vector.x, vector.y);
                                break;

                            case PICACommand.vshAttribute.boneIndex:
                                addNode(vertex.node, nodeList, (int)vector.x);
                                if (format.attributeLength > 0)
                                {
                                    addNode(vertex.node, nodeList, (int)vector.y);
                                }
                                if (format.attributeLength > 1)
                                {
                                    addNode(vertex.node, nodeList, (int)vector.z);
                                }
                                if (format.attributeLength > 2)
                                {
                                    addNode(vertex.node, nodeList, (int)vector.w);
                                }
                                break;

                            case PICACommand.vshAttribute.boneWeight:
                                vertex.weight[0] = (vector.x / 255f);
                                if (format.attributeLength > 0)
                                {
                                    vertex.weight[1] = (vector.y / 255f);
                                }
                                if (format.attributeLength > 1)
                                {
                                    vertex.weight[2] = (vector.z / 255f);
                                }
                                if (format.attributeLength > 2)
                                {
                                    vertex.weight[3] = (vector.w / 255f);
                                }
                                break;
                            }
                        }

                        //If the node list have 4 or less bones, then there is no need to store the indices per vertex
                        //Instead, the entire list is used, since it supports up to 4 bones.
                        if (vertex.node.Count == 0 && nodeList.Length <= 4)
                        {
                            for (int n = 0; n < nodeList.Length; n++)
                            {
                                vertex.node.Add(nodeList[n]);
                            }
                            if (vertex.weight.Count == 0)
                            {
                                vertex.weight.Add(1);
                            }
                        }

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

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

                    input.BaseStream.Seek(idxBufferStart + info.idxLengths[sm], SeekOrigin.Begin);

                    mdl.mesh.Add(obj);
                }

                input.BaseStream.Seek(meshStart + meshLength, SeekOrigin.Begin);
            }

            if (!keepOpen)
            {
                data.Close();
            }

            return(mdl);
        }