예제 #1
0
    ////////////////////////////
    ////// MCNK Subchunks //////
    ////////////////////////////

    public void ReadMCVT(MemoryStream ADTstream, ADTRootData.MeshChunkData chunkData)
    {
        StreamTools s = new StreamTools();

        for (int v = 1; v <= 145; v++)
        {
            chunkData.VertexHeights.Add(s.ReadFloat(ADTstream));
        }
    }
예제 #2
0
    public void ReadMH2O(MemoryStream ADTstream, int MH2Osize)
    {
        StreamTools s = new StreamTools();
        long        chunkStartPosition = ADTstream.Position;

        // header - SMLiquidChunk
        for (int a = 0; a < 256; a++)
        {
            int offset_instances  = s.ReadLong(ADTstream);      // points to SMLiquidInstance[layer_count]
            int layer_count       = s.ReadLong(ADTstream);      // 0 if the chunk has no liquids. If > 1, the offsets will point to arrays.
            int offset_attributes = s.ReadLong(ADTstream);      // points to mh2o_chunk_attributes, can be ommitted for all-0
            if (offset_instances >= 0)
            {
                // instances @24bytes
                ADTstream.Seek(chunkStartPosition + offset_instances, SeekOrigin.Begin);
                int liquid_type          = s.ReadShort(ADTstream); //DBC - foreign_keyⁱ<uint16_t, &LiquidTypeRec::m_ID> liquid_type;
                int liquid_object_or_lvf = s.ReadShort(ADTstream); //DBC -  foreign_keyⁱ<uint16_t, &LiquidObjectRec::m_ID> liquid_object_or_lvf;
                                                                   // if > 41, an id into DB/LiquidObject. If below, LiquidVertexFormat, used in ADT/v18#instance_vertex_data Note hardcoded LO ids below.
                                                                   // if >= 42, look up via DB/LiquidType and DB/LiquidMaterial, otherwise use liquid_object_or_lvf as LVF
                                                                   // also see below for offset_vertex_data: if that's 0 and lt ≠ 2 → lvf = 2
                float min_height_level = s.ReadFloat(ADTstream);   // used as height if no heightmap given and culling ᵘ
                float max_height_level = s.ReadFloat(ADTstream);   // ≥ WoD ignores value and assumes to both be 0.0 for LVF = 2! ᵘ
                int   x_offset         = ADTstream.ReadByte();     // The X offset of the liquid square (0-7)
                int   y_offset         = ADTstream.ReadByte();     // The Y offset of the liquid square (0-7)
                int   width            = ADTstream.ReadByte();     // The width of the liquid square (1-8)
                int   height           = ADTstream.ReadByte();     // The height of the liquid square (1-8)
                                                                   // The above four members are only used if liquid_object_or_lvf <= 41. Otherwise they are assumed 0, 0, 8, 8. (18179)
                int offset_exists_bitmap = s.ReadLong(ADTstream);  // not all tiles in the instances need to be filled. always 8*8 bits.
                                                                   // offset can be 0 for all-exist. also see (and extend) Talk:ADT/v18#SMLiquidInstance
                int offset_vertex_data = s.ReadLong(ADTstream);    // actual data format defined by LiquidMaterialRec::m_LVF via LiquidTypeRec::m_materialID
                                                                   // if offset = 0 and liquidType ≠ 2, then let LVF = 2, i.e. some ocean shit
            }
            //attributes
            if (offset_attributes >= 0)
            {
                ADTstream.Seek(chunkStartPosition + offset_attributes, SeekOrigin.Begin);
                ulong fishable = s.ReadUint64(ADTstream);               // seems to be usable as visibility information.
                ulong deep     = s.ReadUint64(ADTstream);
            }
        }
        ADTstream.Seek(chunkStartPosition + MH2Osize, SeekOrigin.Begin); // set stream location to right after MH2O
    }
예제 #3
0
    // Placement information for WMOs. //
    // Additional to this, the WMOs to render are referenced in each MCRF chunk. (?) //
    public void ReadMODF(MemoryStream ADTobjstream, int MODFsize)
    {
        Flags       f = new Flags();
        StreamTools s = new StreamTools();

        ADTObjData.modelBlockData.WMOInfo = new List <ADTObjData.WMOPlacementInfo>();
        long currentPos = ADTobjstream.Position;

        while (ADTobjstream.Position < currentPos + MODFsize)
        {
            ADTObjData.WMOPlacementInfo data = new ADTObjData.WMOPlacementInfo();

            // references an entry in the MWID chunk, specifying the model to use.
            data.nameID = s.ReadLong(ADTobjstream);

            // this ID should be unique for all ADTs currently loaded. Best, they are unique for the whole map.
            data.uniqueID = s.ReadLong(ADTobjstream);

            // same as in MDDF.
            float Y = ((s.ReadFloat(ADTobjstream) - 17066) * -1) / Settings.worldScale; //-- pos X
            float Z = (s.ReadFloat(ADTobjstream)) / Settings.worldScale;                //-- Height
            float X = ((s.ReadFloat(ADTobjstream) - 17066) * -1) / Settings.worldScale; //-- pos Z
            data.position = new Vector3(X, Z, Y);

            // same as in MDDF.
            float rotX = s.ReadFloat(ADTobjstream);       //-- rot X
            float rotZ = 180 - s.ReadFloat(ADTobjstream); //-- rot Y
            float rotY = s.ReadFloat(ADTobjstream);       //-- rot Z
            data.rotation = Quaternion.Euler(new Vector3(rotX, rotZ, rotY));

            // position plus the transformed wmo bounding box. used for defining if they are rendered as well as collision.
            data.extents = s.ReadBoundingBox(ADTobjstream);

            // values from enum MODFFlags.
            data.flags = f.ReadMODFFlags(ADTobjstream);

            // which WMO doodad set is used.
            data.doodadSet = s.ReadShort(ADTobjstream);

            // which WMO name set is used. Used for renaming goldshire inn to northshire inn while using the same model.
            data.nameSet = s.ReadShort(ADTobjstream);

            // Legion(?)+: has data finally, looks like scaling (same as MDDF). Padding in 0.5.3 alpha.
            int unk = s.ReadShort(ADTobjstream);

            ADTObjData.modelBlockData.WMOInfo.Add(data);
        }
    }
예제 #4
0
    // Placement information for doodads (M2 models). //
    // Additional to this, the models to render are referenced in each MCRF chunk. //
    public void ReadMDDF(MemoryStream ADTobjstream, int MDDFsize)
    {
        Flags       f = new Flags();
        StreamTools s = new StreamTools();

        ADTObjData.modelBlockData.M2Info = new List <ADTObjData.M2PlacementInfo>();
        long currentPos = ADTobjstream.Position;

        while (ADTobjstream.Position < currentPos + MDDFsize)
        {
            ADTObjData.M2PlacementInfo data = new ADTObjData.M2PlacementInfo();

            // References an entry in the MMID chunk, specifying the model to use.
            data.nameID = s.ReadLong(ADTobjstream);

            // This ID should be unique for all ADTs currently loaded.
            // Best, they are unique for the whole map. Blizzard has these unique for the whole game.
            data.uniqueID = s.ReadLong(ADTobjstream);

            // This is relative to a corner of the map. Subtract 17066 from the non vertical values and you should start to see
            // something that makes sense. You'll then likely have to negate one of the non vertical values in whatever coordinate
            // system you're using to finally move it into place.
            float Y = ((s.ReadFloat(ADTobjstream) - 17066) * -1) / Settings.worldScale; //-- pos X
            float Z = (s.ReadFloat(ADTobjstream)) / Settings.worldScale;                //-- Height
            float X = ((s.ReadFloat(ADTobjstream) - 17066) * -1) / Settings.worldScale; //-- pos Z
            data.position = new Vector3(X, Z, Y);

            // degrees. This is not the same coordinate system orientation like the ADT itself! (see history.)
            float rotX = s.ReadFloat(ADTobjstream);       //-- rot X
            float rotZ = 180 - s.ReadFloat(ADTobjstream); //-- rot Y
            float rotY = s.ReadFloat(ADTobjstream);       //-- rot Z
            data.rotation = Quaternion.Euler(new Vector3(rotX, rotZ, rotY));

            // 1024 is the default size equaling 1.0f.
            data.scale = s.ReadShort(ADTobjstream) / 1024.0f;

            // values from struct MDDFFlags.
            data.flags = f.ReadMDDFFlags(ADTobjstream);

            ADTObjData.modelBlockData.M2Info.Add(data);
        }
    }
예제 #5
0
    public static void ParseSkin(MemoryStream ms, M2Data m2Data)
    {
        StreamTools s = new StreamTools();

        string  magic        = s.ReadFourCC(ms);    // 'SKIN'
        M2Array indices      = s.ReadM2Array(ms);
        M2Array triangles    = s.ReadM2Array(ms);
        M2Array bones        = s.ReadM2Array(ms);
        M2Array submeshes    = s.ReadM2Array(ms);
        M2Array batches      = s.ReadM2Array(ms); // nTexture_units
        int     boneCountMax = s.ReadLong(ms);    // WoW takes this and divides it by the number of bones in each submesh, then stores the biggest one.
                                                  // Maximum number of bones per drawcall for each view. Related to (old) GPU numbers of registers.
                                                  // Values seen : 256, 64, 53, 21
        M2Array shadow_batches = s.ReadM2Array(ms);

        /// Read Batches ///
        ms.Seek(batches.offset, SeekOrigin.Begin);
        for (var batch = 0; batch < batches.size; batch++)
        {
            M2BatchIndices m2BatchIndices = new M2BatchIndices();

            m2BatchIndices.M2Batch_flags            = s.ReadShort(ms);      // probably two uint8_t? -- Usually 16 for static textures, and 0 for animated textures. &0x1: materials invert something; &0x2: transform &0x4: projected texture; &0x10: something batch compatible; &0x20: projected texture?; &0x40: transparency something
            m2BatchIndices.M2Batch_shader_id        = s.ReadShort(ms);      // See below.
            m2BatchIndices.M2Batch_submesh_index    = s.ReadShort(ms);      // A duplicate entry of a submesh from the list above.
            m2BatchIndices.M2Batch_submesh_index2   = s.ReadShort(ms);      // See below.
            m2BatchIndices.M2Batch_color_index      = s.ReadShort(ms);      // A Color out of the Colors-Block or -1 if none.
            m2BatchIndices.M2Batch_render_flags     = s.ReadShort(ms);      // The renderflags used on this texture-unit.
            m2BatchIndices.M2Batch_layer            = s.ReadShort(ms);      //
            m2BatchIndices.M2Batch_op_count         = s.ReadShort(ms);      // 1 to 4. See below. Also seems to be the number of textures to load, starting at the texture lookup in the next field (0x10).
            m2BatchIndices.M2Batch_texture          = s.ReadShort(ms);      // Index into Texture lookup table
            m2BatchIndices.M2Batch_tex_unit_number2 = s.ReadShort(ms);      // Index into the texture unit lookup table.
            m2BatchIndices.M2Batch_transparency     = s.ReadShort(ms);      // Index into transparency lookup table.
            m2BatchIndices.M2Batch_texture_anim     = s.ReadShort(ms);      // Index into uvanimation lookup table.

            m2Data.m2BatchIndices.Add(m2BatchIndices);
        }

        // Read SubMesh Data //

        int[] Indices   = new int[indices.size];
        int[] Triangles = new int[triangles.size];

        int[] skinSectionId         = new int[submeshes.size];                  // Mesh part ID, see below.
        int[] submesh_StartVertex   = new int[submeshes.size];                  // Starting vertex number.
        int[] submesh_NbrVerts      = new int[submeshes.size];                  // Number of vertices.
        int[] submesh_StartTriangle = new int[submeshes.size];                  // Starting triangle index (that's 3* the number of triangles drawn so far).
        int[] submesh_NbrTris       = new int[submeshes.size];                  // Number of triangle indices.

        int[] submesh_boneCount      = new int[submeshes.size];                 // Number of elements in the bone lookup table. Max seems to be 256 in Wrath. Shall be ≠ 0.
        int[] submesh_boneComboIndex = new int[submeshes.size];                 // Starting index in the bone lookup table.
        int[] submesh_boneInfluences = new int[submeshes.size];                 // <= 4
                                                                                // from <=BC documentation: Highest number of bones needed at one time in this Submesh --Tinyn (wowdev.org)
                                                                                // In 2.x this is the amount of of bones up the parent-chain affecting the submesh --NaK
                                                                                // Highest number of bones referenced by a vertex of this submesh. 3.3.5a and suspectedly all other client revisions. -- Skarn
        int[]     submesh_centerBoneIndex    = new int[submeshes.size];
        Vector3[] submesh_centerPosition     = new Vector3[submeshes.size];     // Average position of all the vertices in the sub mesh.
        Vector3[] submesh_sortCenterPosition = new Vector3[submeshes.size];     // The center of the box when an axis aligned box is built around the vertices in the submesh.
        float[]   submesh_sortRadius         = new float[submeshes.size];       // Distance of the vertex farthest from CenterBoundingBox.

        /// Indices ///
        ms.Seek(indices.offset, SeekOrigin.Begin);
        for (var ind = 0; ind < indices.size; ind++)
        {
            Indices[ind] = s.ReadShort(ms);
        }

        /// triangles ///
        ms.Seek(triangles.offset, SeekOrigin.Begin);
        for (var tri = 0; tri < triangles.size; tri++)
        {
            Triangles[tri] = s.ReadShort(ms);
        }

        /// submeshes ///
        ms.Seek(submeshes.offset, SeekOrigin.Begin);
        for (var sub = 0; sub < submeshes.size; sub++)
        {
            skinSectionId[sub] = s.ReadUint16(ms);
            int Level = s.ReadUint16(ms);                            // (level << 16) is added (|ed) to startTriangle and alike to avoid having to increase those fields to uint32s.
            submesh_StartVertex[sub]   = s.ReadUint16(ms) + (Level << 16);
            submesh_NbrVerts[sub]      = s.ReadUint16(ms);
            submesh_StartTriangle[sub] = s.ReadUint16(ms) + (Level << 16);
            submesh_NbrTris[sub]       = s.ReadUint16(ms);

            submesh_boneCount[sub]       = s.ReadUint16(ms);
            submesh_boneComboIndex[sub]  = s.ReadUint16(ms);
            submesh_boneInfluences[sub]  = s.ReadUint16(ms);
            submesh_centerBoneIndex[sub] = s.ReadUint16(ms);
            Vector3 raw_centerPosition = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
            submesh_centerPosition[sub] = new Vector3(-raw_centerPosition.x, raw_centerPosition.z, -raw_centerPosition.y);
            Vector3 raw_sortCenterPosition = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
            submesh_sortCenterPosition[sub] = new Vector3(-raw_sortCenterPosition.x, raw_sortCenterPosition.z, -raw_sortCenterPosition.y);
            submesh_sortRadius[sub]         = s.ReadFloat(ms);
        }

        /// Assemble Submeshes ///
        m2Data.submeshData = new List <SubmeshData>();
        for (int sm = 0; sm < submeshes.size; sm++)
        {
            Vector3[] vertList  = new Vector3[submesh_NbrVerts[sm]];
            Vector3[] normsList = new Vector3[submesh_NbrVerts[sm]];
            Vector2[] uvsList   = new Vector2[submesh_NbrVerts[sm]];
            Vector2[] uvs2List  = new Vector2[submesh_NbrVerts[sm]];

            BoneWeights[] boneWeights = new BoneWeights[submesh_NbrVerts[sm]];

            for (int vn = 0; vn < submesh_NbrVerts[sm]; vn++)
            {
                vertList[vn]  = m2Data.meshData.pos[vn + submesh_StartVertex[sm]];
                normsList[vn] = m2Data.meshData.normal[vn + submesh_StartVertex[sm]];
                uvsList[vn]   = m2Data.meshData.tex_coords[vn + submesh_StartVertex[sm]];
                uvs2List[vn]  = m2Data.meshData.tex_coords2[vn + submesh_StartVertex[sm]];

                BoneWeights boneWeightVert = new BoneWeights();
                int[]       boneIndex      = new int[4];
                float[]     boneWeight     = new float[4];

                for (int bn = 0; bn < 4; bn++)
                {
                    boneIndex[bn]  = m2Data.meshData.bone_indices[vn + submesh_boneComboIndex[sm]][bn];
                    boneWeight[bn] = m2Data.meshData.bone_weights[vn + submesh_boneComboIndex[sm]][bn];
                }
                boneWeightVert.boneIndex  = boneIndex;
                boneWeightVert.boneWeight = boneWeight;
                boneWeights[vn]           = boneWeightVert;
            }

            int[] triList = new int[submesh_NbrTris[sm]];
            for (var t = 0; t < submesh_NbrTris[sm]; t++)
            {
                //triList[t] = Triangles[t + submesh_StartTriangle[sm]] - submesh_StartVertex[sm];  // using Separate Meshes, reset first triangle to index 0;
                triList[t] = Triangles[t + submesh_StartTriangle[sm]];                              // using Unity Submeshes, don't reset first triangle to index 0;
            }


            SubmeshData submeshData = new SubmeshData();

            submeshData.ID        = skinSectionId[sm];
            submeshData.vertList  = vertList;
            submeshData.normsList = normsList;
            submeshData.uvsList   = uvsList;
            submeshData.uvs2List  = uvs2List;
            Array.Reverse(triList);
            submeshData.triList                = triList;
            submeshData.submesh_StartVertex    = submesh_StartVertex[sm];
            submeshData.boneWeights            = boneWeights;
            submeshData.submesh_boneCount      = submesh_boneCount[sm];
            submeshData.submesh_boneInfluences = submesh_boneInfluences[sm];
            m2Data.submeshData.Add(submeshData);
        }

        /// Assemble Bone Data ///
        ///
    }
예제 #6
0
    public void ReadMCNK(MemoryStream ADTstream, int MCNKchunkNumber, int MCNKsize)
    {
        StreamTools s = new StreamTools();
        Flags       f = new Flags();

        ADTRootData.MeshChunkData chunkData = new ADTRootData.MeshChunkData();
        long MCNKchnkPos = ADTstream.Position;

        // <Header> - 128 bytes
        chunkData.flags = f.ReadMCNKflags(ADTstream);

        chunkData.IndexX  = s.ReadLong(ADTstream);
        chunkData.IndexY  = s.ReadLong(ADTstream);
        chunkData.nLayers = s.ReadLong(ADTstream);  // maximum 4
        int nDoodadRefs = s.ReadLong(ADTstream);

        chunkData.holes_high_res = s.ReadUint64(ADTstream);  // only used with flags.high_res_holes
        int ofsLayer    = s.ReadLong(ADTstream);
        int ofsRefs     = s.ReadLong(ADTstream);
        int ofsAlpha    = s.ReadLong(ADTstream);
        int sizeAlpha   = s.ReadLong(ADTstream);
        int ofsShadow   = s.ReadLong(ADTstream); // only with flags.has_mcsh
        int sizeShadow  = s.ReadLong(ADTstream);
        int areaid      = s.ReadLong(ADTstream); // in alpha: both zone id and sub zone id, as uint16s.
        int nMapObjRefs = s.ReadLong(ADTstream);

        chunkData.holes_low_res = s.ReadShort(ADTstream);
        int unknown_but_used = s.ReadShort(ADTstream);       // in alpha: padding

        byte[] ReallyLowQualityTextureingMap = new byte[16]; // uint2_t[8][8] "predTex", It is used to determine which detail doodads to show. Values are an array of two bit
        for (int b = 0; b < 16; b++)
        {
            ReallyLowQualityTextureingMap[b] = (byte)ADTstream.ReadByte();
        }
        // unsigned integers, naming the layer.
        ulong noEffectDoodad = s.ReadUint64(ADTstream);                 // WoD: may be an explicit MCDD chunk
        int   ofsSndEmitters = s.ReadLong(ADTstream);
        int   nSndEmitters   = s.ReadLong(ADTstream);                   // will be set to 0 in the client if ofsSndEmitters doesn't point to MCSE!
        int   ofsLiquid      = s.ReadLong(ADTstream);
        int   sizeLiquid     = s.ReadLong(ADTstream);                   // 8 when not used; only read if >8.

        // in alpha, remainder is padding but unused.
        chunkData.MeshPosition = new Vector3(s.ReadFloat(ADTstream), s.ReadFloat(ADTstream), s.ReadFloat(ADTstream));
        int ofsMCCV = s.ReadLong(ADTstream);                             // only with flags.has_mccv, had uint32_t textureId; in ObscuR's structure.
        int ofsMCLV = s.ReadLong(ADTstream);                             // introduced in Cataclysm
        int unused  = s.ReadLong(ADTstream);                             // currently unused

        // </header>

        if (!chunkData.flags.has_mccv)
        {
            FillMCCV(chunkData); // fill vertex shading with 127...
        }
        long streamPosition = ADTstream.Position;

        while (streamPosition < MCNKchnkPos + MCNKsize)
        {
            ADTstream.Position = streamPosition;
            int chunkID   = s.ReadLong(ADTstream);
            int chunkSize = s.ReadLong(ADTstream);
            streamPosition = ADTstream.Position + chunkSize;
            switch (chunkID)
            {
            case (int)ChunkID.ADT.MCVT:
                ReadMCVT(ADTstream, chunkData);     // vertex heights
                break;

            case (int)ChunkID.ADT.MCLV:
                ReadMCLV(ADTstream, chunkData);     // chunk lighting
                break;

            case (int)ChunkID.ADT.MCCV:
                ReadMCCV(ADTstream, chunkData);     // vertex shading
                break;

            case (int)ChunkID.ADT.MCNR:
                ReadMCNR(ADTstream, chunkData);     // normals
                break;

            case (int)ChunkID.ADT.MCSE:
                ReadMCSE(ADTstream, chunkData, chunkSize);     // sound emitters
                break;

            case (int)ChunkID.ADT.MCBB:
                ReadMCBB(ADTstream, chunkData, chunkSize);
                break;

            case (int)ChunkID.ADT.MCDD:
                ReadMCDD(ADTstream, chunkData, chunkSize);
                break;

            default:
                SkipUnknownChunk(ADTstream, chunkID, chunkSize);
                break;
            }
        }
        ADTRootData.meshBlockData.meshChunksData.Add(chunkData);
    }
예제 #7
0
    public static void ReadMD21(MemoryStream ms, M2Data m2Data, M2Texture m2Tex)
    {
        long md20position = ms.Position;

        StreamTools s                               = new StreamTools();
        int         MD20                            = s.ReadLong(ms);    // "MD20". Legion uses a chunked file format starting with MD21.
        int         version                         = s.ReadLong(ms);
        M2Array     name                            = s.ReadM2Array(ms); // should be globally unique, used to reload by name in internal clients
        var         flags                           = s.ReadLong(ms);
        M2Array     global_loops                    = s.ReadM2Array(ms); // Timestamps used in global looping animations.
        M2Array     sequences                       = s.ReadM2Array(ms); // Information about the animations in the model.
        M2Array     sequences_lookups               = s.ReadM2Array(ms); // Mapping of sequence IDs to the entries in the Animation sequences block.
        M2Array     bones                           = s.ReadM2Array(ms); // MAX_BONES = 0x100 => Creature\SlimeGiant\GiantSlime.M2 has 312 bones(Wrath)
        M2Array     key_bone_lookup                 = s.ReadM2Array(ms); // Lookup table for key skeletal bones.
        M2Array     vertices                        = s.ReadM2Array(ms);
        int         num_skin_profiles               = s.ReadLong(ms);
        M2Array     colors                          = s.ReadM2Array(ms); // Color and alpha animations definitions.
        M2Array     textures                        = s.ReadM2Array(ms);
        M2Array     texture_weights                 = s.ReadM2Array(ms); // Transparency of textures.
        M2Array     texture_transforms              = s.ReadM2Array(ms);
        M2Array     replaceable_texture_lookup      = s.ReadM2Array(ms);
        M2Array     materials                       = s.ReadM2Array(ms); // Blending modes / render flags.
        M2Array     bone_lookup_table               = s.ReadM2Array(ms);
        M2Array     texture_lookup_table            = s.ReadM2Array(ms);
        M2Array     tex_unit_lookup_table           = s.ReadM2Array(ms); // ≥ Cata: unused
        M2Array     transparency_lookup_table       = s.ReadM2Array(ms);
        M2Array     texture_transforms_lookup_table = s.ReadM2Array(ms);

        m2Data.bounding_box = s.ReadBoundingBox(ms);                    // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height
        float       bounding_sphere_radius  = s.ReadFloat(ms);          // detail doodad draw dist = clamp (bounding_sphere_radius * detailDoodadDensityFade * detailDoodadDist, …)
        BoundingBox collision_box           = s.ReadBoundingBox(ms);
        float       collision_sphere_radius = s.ReadFloat(ms);

        M2Array collision_triangles     = s.ReadM2Array(ms);
        M2Array collision_vertices      = s.ReadM2Array(ms);
        M2Array collision_normals       = s.ReadM2Array(ms);
        M2Array attachments             = s.ReadM2Array(ms);            // position of equipped weapons or effects
        M2Array attachment_lookup_table = s.ReadM2Array(ms);
        M2Array events              = s.ReadM2Array(ms);                // Used for playing sounds when dying and a lot else.
        M2Array lights              = s.ReadM2Array(ms);                // Lights are mainly used in loginscreens but in wands and some doodads too.
        M2Array cameras             = s.ReadM2Array(ms);                // The cameras are present in most models for having a model in the character tab.
        M2Array camera_lookup_table = s.ReadM2Array(ms);
        M2Array ribbon_emitters     = s.ReadM2Array(ms);                // Things swirling around. See the CoT-entrance for light-trails.
        M2Array particle_emitters   = s.ReadM2Array(ms);

        // Name //
        ms.Position = name.offset + md20position;
        for (int n = 0; n < name.size; n++)
        {
            m2Data.name += Convert.ToChar(ms.ReadByte());
        }


        // Bones //
        ms.Position = bones.offset + md20position;
        M2TrackBase[] translationM2track = new M2TrackBase[bones.size];
        M2TrackBase[] rotationM22track   = new M2TrackBase[bones.size];
        M2TrackBase[] scaleM22track      = new M2TrackBase[bones.size];
        for (int cb = 0; cb < bones.size; cb++)
        {
            M2CompBone m2CompBone = new M2CompBone();

            m2CompBone.key_bone_id      = s.ReadLong(ms);               // Back-reference to the key bone lookup table. -1 if this is no key bone.
            m2CompBone.flags            = s.ReadUint32(ms);
            m2CompBone.parent_bone      = s.ReadShort(ms);
            m2CompBone.submesh_id       = s.ReadUint16(ms);
            m2CompBone.uDistToFurthDesc = s.ReadUint16(ms);
            m2CompBone.uZRatioOfChain   = s.ReadUint16(ms);

            translationM2track[cb] = s.ReadM2Track(ms);
            rotationM22track[cb]   = s.ReadM2Track(ms);
            scaleM22track[cb]      = s.ReadM2Track(ms);

            Vector3 pivotRaw = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
            m2CompBone.pivot = new Vector3(-pivotRaw.x, pivotRaw.z, -pivotRaw.y);

            m2Data.m2CompBone.Add(m2CompBone);
        }


        // Animations //
        int numberOfAnimations = 0;

        for (int ab = 0; ab < bones.size; ab++)
        {
            List <Animation_Vector3>    bone_position_animations = new List <Animation_Vector3>();
            List <Animation_Quaternion> bone_rotation_animations = new List <Animation_Quaternion>();
            List <Animation_Vector3>    bone_scale_animations    = new List <Animation_Vector3>();

            // Position //
            int numberOfPositionAnimations = translationM2track[ab].Timestamps.size;
            if (numberOfAnimations < numberOfPositionAnimations)
            {
                numberOfAnimations = numberOfPositionAnimations;
            }
            for (int at = 0; at < numberOfPositionAnimations; at++)
            {
                Animation         bone_animation = new Animation();
                Animation_Vector3 positions      = new Animation_Vector3();

                // Timestamps //
                List <int> timeStamps = new List <int>();
                ms.Position = translationM2track[ab].Timestamps.offset + md20position;
                M2Array m2AnimationOffset = s.ReadM2Array(ms);
                ms.Position = m2AnimationOffset.offset;
                for (int t = 0; t < m2AnimationOffset.size; t++)
                {
                    timeStamps.Add(s.ReadLong(ms));
                }
                positions.timeStamps = timeStamps;

                // Values //
                List <Vector3> values = new List <Vector3>();
                ms.Position = translationM2track[ab].Values.offset + md20position;
                M2Array m2AnimationValues = s.ReadM2Array(ms);
                ms.Position = m2AnimationValues.offset;
                for (int t = 0; t < m2AnimationValues.size; t++)
                {
                    Vector3 rawPosition = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
                    values.Add(new Vector3(-rawPosition.x, rawPosition.z, -rawPosition.y));
                }
                positions.values = values;
                bone_position_animations.Add(positions);
            }


            // Rotation //
            int numberOfRotationAnimations = rotationM22track[ab].Timestamps.size;
            if (numberOfAnimations < numberOfRotationAnimations)
            {
                numberOfAnimations = numberOfRotationAnimations;
            }
            for (int ar = 0; ar < numberOfRotationAnimations; ar++)
            {
                Animation_Quaternion rotations = new Animation_Quaternion();

                // Timestamps //
                List <int> timeStamps = new List <int>();
                ms.Position = rotationM22track[ab].Timestamps.offset + md20position;
                M2Array m2AnimationOffset = s.ReadM2Array(ms);
                ms.Position = m2AnimationOffset.offset;
                for (int t = 0; t < m2AnimationOffset.size; t++)
                {
                    timeStamps.Add(s.ReadLong(ms));
                }
                rotations.timeStamps = timeStamps;

                // Values //
                List <Quaternion> values = new List <Quaternion>();
                ms.Position = rotationM22track[ab].Values.offset + md20position;
                M2Array m2AnimationValues = s.ReadM2Array(ms);
                ms.Position = m2AnimationValues.offset;
                for (int t = 0; t < m2AnimationValues.size; t++)
                {
                    Quaternion rawRotation = s.ReadQuaternion16(ms);
                    values.Add(new Quaternion(rawRotation.x, rawRotation.y, rawRotation.z, rawRotation.w));
                }
                rotations.values = values;
                bone_rotation_animations.Add(rotations);
            }

            // Scale //
            int numberOfScaleAnimations = scaleM22track[ab].Timestamps.size;
            if (numberOfAnimations < numberOfScaleAnimations)
            {
                numberOfAnimations = numberOfScaleAnimations;
            }
            for (int aS = 0; aS < numberOfScaleAnimations; aS++)
            {
                Animation_Vector3 scales = new Animation_Vector3();

                // Timestamps //
                List <int> timeStamps = new List <int>();
                ms.Position = scaleM22track[ab].Timestamps.offset + md20position;
                M2Array m2AnimationOffset = s.ReadM2Array(ms);
                ms.Position = m2AnimationOffset.offset;
                for (int t = 0; t < m2AnimationOffset.size; t++)
                {
                    timeStamps.Add(s.ReadLong(ms));
                }
                scales.timeStamps = timeStamps;

                // Values //
                List <Vector3> values = new List <Vector3>();
                ms.Position = scaleM22track[ab].Values.offset + md20position;
                M2Array m2AnimationValues = s.ReadM2Array(ms);
                ms.Position = m2AnimationValues.offset;
                for (int t = 0; t < m2AnimationValues.size; t++)
                {
                    Vector3 rawScale = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
                    values.Add(new Vector3(-rawScale.x, rawScale.z, -rawScale.y));
                }
                scales.values = values;
                bone_scale_animations.Add(scales);
            }
            //Debug.Log(numberOfPositionAnimations + " " + numberOfRotationAnimations + " " + numberOfScaleAnimations);
            m2Data.position_animations.Add(bone_position_animations);
            m2Data.rotation_animations.Add(bone_rotation_animations);
            m2Data.scale_animations.Add(bone_scale_animations);
        }
        m2Data.numberOfAnimations = numberOfAnimations;

        // Bone Lookup Table //
        ms.Position = bone_lookup_table.offset + md20position;
        for (int blt = 0; blt < key_bone_lookup.size; blt++)
        {
            m2Data.bone_lookup_table.Add(s.ReadUint16(ms));
        }

        // Key-Bone Lookup //
        ms.Position = key_bone_lookup.offset + md20position;
        for (int kbl = 0; kbl < key_bone_lookup.size; kbl++)
        {
            m2Data.key_bone_lookup.Add(s.ReadShort(ms));
        }

        // Vertices //
        ms.Position     = vertices.offset + md20position;
        m2Data.meshData = new MeshData();
        for (int v = 0; v < vertices.size; v++)
        {
            Vector3 rawPosition = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
            m2Data.meshData.pos.Add(new Vector3(-rawPosition.x, rawPosition.z, -rawPosition.y));
            m2Data.meshData.bone_weights.Add(new float[] { ms.ReadByte() / 255.0f, ms.ReadByte() / 255.0f, ms.ReadByte() / 255.0f, ms.ReadByte() / 255.0f });
            m2Data.meshData.bone_indices.Add(new int[] { ms.ReadByte(), ms.ReadByte(), ms.ReadByte(), ms.ReadByte() });
            //Debug.Log(m2Data.meshData.bone_indices[v][0] + " " + m2Data.meshData.bone_indices[v][1] + " " + m2Data.meshData.bone_indices[v][2] + " " + m2Data.meshData.bone_indices[v][3]);
            Vector3 rawnormal = new Vector3(s.ReadFloat(ms) * Settings.worldScale, s.ReadFloat(ms) * Settings.worldScale, s.ReadFloat(ms) * Settings.worldScale);
            m2Data.meshData.normal.Add(new Vector3(-rawnormal.x, rawnormal.z, -rawnormal.y));
            m2Data.meshData.tex_coords.Add(new Vector2(s.ReadFloat(ms), s.ReadFloat(ms)));
            m2Data.meshData.tex_coords2.Add(new Vector2(s.ReadFloat(ms), s.ReadFloat(ms)));
        }

        // Textures //
        ms.Position = textures.offset + md20position;
        for (int t = 0; t < textures.size; t++)
        {
            M2Texture m2Texture = new M2Texture();

            m2Texture.type  = s.ReadLong(ms);
            m2Texture.flags = s.ReadLong(ms);

            M2Array filename = s.ReadM2Array(ms);

            // seek to filename and read //
            long savePosition = ms.Position;
            ms.Position = filename.offset + md20position;
            string fileNameString = "";
            for (int n = 0; n < filename.size; n++)
            {
                fileNameString += Convert.ToChar(ms.ReadByte());
            }
            ms.Position = savePosition;

            string fileNameStringFix = fileNameString.TrimEnd(fileNameString[fileNameString.Length - 1]);
            m2Texture.filename = fileNameStringFix;

            Texture2Ddata texture2Ddata = new Texture2Ddata();

            if (fileNameStringFix.Length > 1)
            {
                if (!LoadedBLPs.Contains(fileNameStringFix))
                {
                    string  extractedPath = Casc.GetFile(fileNameStringFix);
                    Stream  stream        = File.Open(extractedPath, FileMode.Open);
                    BLP     blp           = new BLP();
                    byte[]  data          = blp.GetUncompressed(stream, true);
                    BLPinfo info          = blp.Info();
                    texture2Ddata.hasMipmaps    = info.hasMipmaps;
                    texture2Ddata.width         = info.width;
                    texture2Ddata.height        = info.height;
                    texture2Ddata.textureFormat = info.textureFormat;
                    texture2Ddata.TextureData   = data;
                    m2Texture.texture2Ddata     = texture2Ddata;
                    stream.Close();
                    stream.Dispose();
                    stream = null;
                    LoadedBLPs.Add(fileNameString);
                }
            }
            m2Data.m2Tex.Add(m2Texture);
        }

        // texture_lookup_table //
        ms.Position = texture_lookup_table.offset + md20position;
        for (int tl = 0; tl < texture_lookup_table.size; tl++)
        {
            m2Data.textureLookupTable.Add(s.ReadUint16(ms));
        }
    }
예제 #8
0
    public void ReadMTXP(MemoryStream ADTtexstream, int MTXPsize)  // 16 bytes per MTEX texture
    {
        StreamTools s = new StreamTools();
        Flags       f = new Flags();

        ADTTexData.textureBlockData.MTXP = true;
        for (int i = 0; i < MTXPsize / 16; i++)
        {
            ADTTexData.textureBlockData.textureFlags.Add(ADTTexData.textureBlockData.terrainTexturePaths[i], f.ReadTerrainTextureFlag(ADTtexstream));
            // default 0.0 -- the _h texture values are scaled to [0, value) to determine actual "height".
            // this determines if textures overlap or not (e.g. roots on top of roads).
            ADTTexData.textureBlockData.heightScales.Add(ADTTexData.textureBlockData.terrainTexturePaths[i], s.ReadFloat(ADTtexstream));
            // default 1.0 -- note that _h based chunks are still influenced by MCAL (blendTex below)
            ADTTexData.textureBlockData.heightOffsets.Add(ADTTexData.textureBlockData.terrainTexturePaths[i], s.ReadFloat(ADTtexstream));
            // no default, no non-zero values in 20490
            int padding = s.ReadLong(ADTtexstream);
        }
    }