Ejemplo n.º 1
0
        /// <summary>
        /// Converts internal Mesh fields into Halo 2 compatible resource format
        /// </summary>
        /// <param name="resource_out">returns with array of Resource-meta structs represnting the blocks in the resource</param>
        /// <returns>array of bytes which holds the serialized Halo 2 resource</returns>
        public byte[] Serialize(out DResource[] resource_out, out DCompressionRanges compression_ranges, DCompressionRanges input_compression = null)
        {
            /* Intent: Write out the this Model instance data into a format that
             * the halo 2 version of blam! engine can use.
             * The resources that we will be focusing on are ShaderGroups, Indices,
             * Vertex position, texcoord, tangent space vectors, and a simple bonemap.*/

            Log.Info(@"Entering Model.Serialize()");

            if (input_compression == null) { input_compression = GenerateCompressionData(); }     // Check that we have compression data available before continuing
            compression_ranges = input_compression;

            DResource[] resource = new DResource[7];                    // Create resource defintion array
            MemoryStream buffer = new MemoryStream();
            BinaryWriter bin = new BinaryWriter(buffer);                // BinaryWriter

            Log.Info(string.Format(@"Writing header_tag @{0}", bin.BaseStream.Position));
            bin.WriteFourCC("blkh");                // Write the header_tag value
            bin.Write(0);                           // [uint] resource_data_size  (reserve)

            // * Begin resource_header // size: 112

            Log.Info(string.Format(@"Writing shader_groups_count = {0} @{1}", Primitives.Length, bin.BaseStream.Position));
            bin.Write(Primitives.Length);           // * 0x00: shader_groups_count;
            bin.Write(new byte[28]);                // * 0x08: some unused thing... count.
            Log.Info(string.Format(@"Writing indices_count = {0} @{1}", Indices.Length, bin.BaseStream.Position));
            bin.Write(Indices.Length);              // * 0x20: indices_count;
            bin.Write(new byte[20]);
            bin.Write(3);                           // * 0x38: vertex_resource_count; //special
            bin.Write(new byte[40]);
            bin.Write(1);                           // * 0x64: bone_map_count;
            bin.Write(new byte[8]);

            Log.Info(string.Format(@"Resource data_start_offset = {0}", bin.BaseStream.Position));
            var resource_data_start_offset = bin.BaseStream.Position;   // This is the offset to which all DResource block_offsets are written from

            bin.WriteFourCC("rsrc");             // shader_group resource begin
            resource[0] = new DResource(0, 72, Primitives.Length * 72, (int)(bin.BaseStream.Position - resource_data_start_offset));
            foreach (var group in Primitives)
                bin.Write(group);                // shader_group data

            bin.WriteFourCC("rsrc");             // indices resource begin
            resource[1] = new DResource(32, sizeof(ushort), sizeof(ushort) * this.Indices.Length, (int)(bin.BaseStream.Position - resource_data_start_offset));
            foreach (ushort index in this.Indices)  // write each index ushort
                bin.Write(index);                // pad to word boundary
            bin.WritePadding(4);

            bin.WriteFourCC("rsrc");             // vertex_data_header?
            resource[2] = new DResource(56, 32, 32 * 3, (int)(bin.BaseStream.Position - resource_data_start_offset));
            bin.Write(VERTEX_RESOURCE_HEADER_DATA); // laziness TODO: write out proper headers here to allow for other types

            bin.WriteFourCC("rsrc");
            resource[3] = new DResource(56, 0, this.Coordinates.Length * sizeof(ushort) * 3, (int)(bin.BaseStream.Position - resource_data_start_offset), true);
            {
                foreach (var vertex in this.Coordinates)
                {
                    bin.Write(Deflate(input_compression.X, vertex.X));
                    bin.Write(Deflate(input_compression.Y, vertex.Y));
                    bin.Write(Deflate(input_compression.Z, vertex.Z));
                }
                bin.WritePadding(4);
            }
            bin.WriteFourCC("rsrc");
            resource[4] = new DResource(56, 1, this.TextureCoordinates.Length * sizeof(ushort) * 2, (int)(bin.BaseStream.Position - resource_data_start_offset), true);
            {
                foreach (var vertex in this.TextureCoordinates)
                {
                    bin.Write(Deflate(input_compression.U, vertex.X));
                    bin.Write(Deflate(input_compression.V, vertex.Y));  // flip this still?
                }
            }
            bin.WriteFourCC("rsrc");
            resource[5] = new DResource(56, 2, this.Normals.Length * sizeof(uint) * 3, (int)(bin.BaseStream.Position - resource_data_start_offset), true);
            for(int i = 0; i < this.Normals.Length;++i)
            {
                bin.Write((uint)(Vector3t)Normals[i]);    //cast to vector3t is destructive...
                bin.Write((uint)(Vector3t)Tangents[i]);
                bin.Write((uint)(Vector3t)Bitangents[i]);
            }
            bin.WriteFourCC("rsrc");
            resource[6] = new DResource(100, 1, 1, (int)(bin.BaseStream.Position - resource_data_start_offset));
            bin.Write(0);                // default bone-map (no bones)
            bin.WriteFourCC("blkf");
            int resource_size = (int)bin.BaseStream.Position;
            bin.Seek(4, SeekOrigin.Begin);
            bin.Write(resource_size - 124);

            // debug dump
            #if DEBUG
            try
            {
                using (var file = File.OpenWrite(@"D:\halo_2\model_raw.bin"))
                {
                    file.Write(buffer.ToArray(), 0, (int)buffer.Length);
                }
            }
            catch { }
            #endif
            // end debug dump

            // 2. create a sections meta file for this, a bounding box, heck a whole mesh, why not.
            resource_out = resource;
            return buffer.ToArray();
        }
Ejemplo n.º 2
0
 internal DCompressionRanges GenerateCompressionData()
 {
     DCompressionRanges compression = new DCompressionRanges();
     compression.X = new Range(Coordinates[0].X, Coordinates[0].X);
     compression.Y = new Range(Coordinates[0].Y, Coordinates[0].Y);
     compression.Z = new Range(Coordinates[0].Z, Coordinates[0].Z);
     compression.U = new Range(TextureCoordinates[0].X, TextureCoordinates[0].X);
     compression.V = new Range(TextureCoordinates[0].Y, TextureCoordinates[0].Y);
     for (int i = 0; i < Coordinates.Length; ++i)
     {
         compression.X = Range.Include(compression.X, Coordinates[i].X);
         compression.Y = Range.Include(compression.Y, Coordinates[i].Y);
         compression.Z = Range.Include(compression.Z, Coordinates[i].Z);
         compression.U = Range.Include(compression.U, TextureCoordinates[i].X);
         compression.V = Range.Include(compression.V, TextureCoordinates[i].Y);
     }
     compression.Expand(0.0001f);
     return compression;
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Deserializes a Halo 2 formatted raw-resource block and initializes the Mesh object from it
        /// </summary>
        /// <param name="raw_data"></param>
        /// <param name="raw_resources"></param>
        /// <param name="compression_ranges"></param>
        /// <returns></returns>
        protected bool Load(byte[] raw_data, IEnumerable<DResource> raw_resources, DCompressionRanges compression_ranges, int header_size)
        {
            int[] vertex_resource_sizes = new int[0];
            VertexResource[] vertex_resource_types = new VertexResource[0];

            int stream_length = BitConverter.ToInt32(raw_data, 4);
            int first_address = 4 + 4 + header_size;                                                /* 0:header_four_cc : "blkh"
                                                                                                     * 4:total_resource_data_length
                                                                                                     * 8:header_fields */

            MemoryStream resource_stream = new MemoryStream(raw_data, first_address, stream_length, false);  /* Create a stream containing the resource_data
                                                                                                     * from the raw_data byte array
                                                                                                     * */
            BinaryReader binary_reader = new BinaryReader(resource_stream);

            /*  Intent: switch each raw resource and load the mesh-related data from the resource_stream
             * */
            foreach (var resource in raw_resources)
            {
                if (resource.first_ != 0) continue;                                         // skip the vertex resources and other wierd resources

                int count = BitConverter.ToInt32(raw_data, 8 + resource.header_address);    // get the header_value (which is 'count' of blocks for this resource)...
                resource_stream.Position = resource.resource_offset;                        // move stream to offset of resource data

                switch (resource.header_address)
                {
                    #region Shader Groups
                    // case: Shader Groups
                    case 0:
                        // initialize the shader_groups array;
                        Primitives = new MeshPrimitive[count];
                        // read each block
                        for (int i = 0; i < count; i++)
                        {
                            Primitives[i] = binary_reader.ReadDefinition<MeshPrimitive>();
                        }
                        break;
                    #endregion
                    #region Indices
                    case 32:
                        Indices = new ushort[count];
                        for (int i = 0; i < count; i++)
                        {
                            if (resource.data_size__or__first_index != sizeof(short)) throw new Exception(":D");
                            Indices[i] = binary_reader.ReadUInt16();
                        }
                        break;
                    #endregion
                    #region Vertex Resources pass-1
                    case 56:
                        /* Process the first resource at offset 56 which should be the resource describing all
                         * other vertex-data resources after it. Load from this resource the size and type enum
                         * of the vertex_data resources.
                         * */
                        switch (resource.first_)
                        {
                            case 0:
                                vertex_resource_sizes = new int[count];
                                vertex_resource_types = new VertexResource[count];
                                for (int i = 0; i < count; i++)
                                {
                                    byte[] buffer = binary_reader.ReadBytes(resource.data_size__or__first_index);
                                    vertex_resource_types[i] = (VertexResource)((buffer[0] << 8) | buffer[1]);
                                    vertex_resource_sizes[i] = buffer[1];
                                }
                                break;
                        }
                        break;
                    #endregion
                    case 100:
                        Nodes = binary_reader.ReadBytes(count);
                        break;
                }
            }
            var vertex_resources = raw_resources.Where(x => x.first_ == 2).ToArray();

            /* Intent: process vertex resources by type and load additional bone information if present
             * */
            int vertex_count = vertex_resources[0].resource_length / vertex_resource_sizes[0];

            #region Vertex Data Field Initialization
            /* Intent: intialize all fields to empty arrays to prevent the unintialized value compiler error,
             * switch through all the vertex_resources we are going to process and create an array to hold all
             * the members we will be reading. */
            this.Coordinates = new Vector3[0];
            this.TextureCoordinates = new Vector2[0];
            this.Normals = new Vector3[0];
            this.Tangents = new Vector3[0];
            this.Bitangents = new Vector3[0];
            this.VertexWeights = new VertexWeight[0];

            foreach (var vertex_resource in vertex_resources)                   /* Switch through all the loaded
                                                                                 * vertex_resources */
            {
                switch (vertex_resource_types[vertex_resource.data_size__or__first_index])
                {
                    case VertexResource.coordinate_with_skinned_node:
                    case VertexResource.coordinate_with_rigid_node:             /* if the resource contains skeleton nodes:
                                                                                 * initialize the VertexWeights array*/
                        this.VertexWeights = new VertexWeight[vertex_count];
                        goto case VertexResource.coordinate_compressed;         /*  also load a vertex coordinate array */

                    case VertexResource.coordinate_float:
                    case VertexResource.coordinate_compressed:                  /* if the resource contains vertex coordinates:
                                                                                 * initialize the VertexCoordinates array */
                        this.Coordinates = new Vector3[vertex_count];
                        break;

                    case VertexResource.texture_coordinate_compressed:
                    case VertexResource.texture_coordinate_float_pc:
                    case VertexResource.texture_coordinate_float:                /* if the resource contains texture coordinates:
                                                                                  * initialize the TextureCoordinates array */
                        this.TextureCoordinates = new Vector2[vertex_count];
                        break;
                    case VertexResource.tangent_space_unit_vectors_compressed:   /* if the resource contains tangent-space data:
                                                                                  * initialize the TBN vector arrays */
                    case VertexResource.tangent_space_unit_vectors_float:
                        this.Normals = new Vector3[vertex_count];
                        this.Tangents = new Vector3[vertex_count];
                        this.Bitangents = new Vector3[vertex_count];
                        break;
                }
            }
            #endregion

            #region Vertex Data Loading
            foreach (var vertex_resource in vertex_resources)
            {
                binary_reader.BaseStream.Position = vertex_resource.resource_offset;
                var buffer = binary_reader.ReadBytes(vertex_resource.resource_length);
                var stride = vertex_resource_sizes[vertex_resource.data_size__or__first_index];
                for (int i = 0; i < vertex_count; ++i)
                {
                    switch (vertex_resource_types[vertex_resource.data_size__or__first_index])
                    {
                        case VertexResource.coordinate_float:
                            this.Coordinates[i] = new Vector3(
                                BitConverter.ToSingle(buffer, i * stride),
                                BitConverter.ToSingle(buffer, (i * stride) + 4),
                                BitConverter.ToSingle(buffer, (i * stride) + 8));
                            break;
                        case VertexResource.coordinate_compressed:
                            this.Coordinates[i] = new Vector3(
                                BitConverter.ToInt16(buffer, i * stride),
                                BitConverter.ToInt16(buffer, (i * stride) + 2),
                                BitConverter.ToInt16(buffer, (i * stride) + 4));
                            this.Coordinates[i].X = Inflate(this.Coordinates[i].X, compression_ranges.X);
                            this.Coordinates[i].Y = Inflate(this.Coordinates[i].Y, compression_ranges.Y);
                            this.Coordinates[i].Z = Inflate(this.Coordinates[i].Z, compression_ranges.Z);
                            break;
                        case VertexResource.coordinate_with_rigid_node:
                            VertexWeights[i] = new VertexWeight(buffer[(i * stride) + 6]);
                            goto case VertexResource.coordinate_compressed;
                        case VertexResource.coordinate_with_skinned_node:
                            var bone0 = buffer[(i * stride) + 6];
                            var bone1 = buffer[(i * stride) + 7];
                            var weight0 = (float)buffer[(i * stride) + 9] / (float)byte.MaxValue;
                            var weight1 = (float)buffer[(i * stride) + 10] / (float)byte.MaxValue;
                            VertexWeights[i] = new VertexWeight()
                            {
                                Bone0 = bone0,
                                Bone1 = bone1,
                                Bone0_weight = weight0,
                                Bone1_weight = weight1
                            };
                            goto case VertexResource.coordinate_compressed;
                        case VertexResource.texture_coordinate_float:
                        case VertexResource.texture_coordinate_float_pc:
                            this.TextureCoordinates[i] = new Vector2(
                            BitConverter.ToSingle(buffer, i * stride),
                            BitConverter.ToSingle(buffer, (i * stride) + 4));
                            break;
                        case VertexResource.texture_coordinate_compressed:
                            this.TextureCoordinates[i] = new Vector2(
                            BitConverter.ToInt16(buffer, i * stride),
                            BitConverter.ToInt16(buffer, (i * stride) + 2));
                            this.TextureCoordinates[i].X = Inflate(this.TextureCoordinates[i].X, compression_ranges.U);
                            this.TextureCoordinates[i].Y = Inflate(this.TextureCoordinates[i].Y, compression_ranges.V);
                            break;
                        case VertexResource.tangent_space_unit_vectors_compressed:
                            this.Normals[i] = (Vector3)new Vector3t(BitConverter.ToUInt32(buffer, i * stride));
                            this.Tangents[i] = (Vector3)new Vector3t(BitConverter.ToUInt32(buffer, (i * stride) + 4));
                            this.Bitangents[i] = (Vector3)new Vector3t(BitConverter.ToUInt32(buffer, i * (stride) + 8));
                            break;
                        case VertexResource.tangent_space_unit_vectors_float:
                            this.Normals[i] = new Vector3(
                                BitConverter.ToSingle(buffer, 0 + i * stride),
                                BitConverter.ToSingle(buffer, 4 + i * stride),
                                BitConverter.ToSingle(buffer, 8 + i * stride));
                            this.Tangents[i] = new Vector3(
                                BitConverter.ToSingle(buffer, 12 + i * stride),
                                BitConverter.ToSingle(buffer, 16 + i * stride),
                                BitConverter.ToSingle(buffer, 20 + i * stride));
                            this.Bitangents[i] = new Vector3(
                                BitConverter.ToSingle(buffer, 24 + i * stride),
                                BitConverter.ToSingle(buffer, 28 + i * stride),
                                BitConverter.ToSingle(buffer, 32 + i * stride));
                            break;
                    }
                }
            }
            #endregion

            return true;
        }