//////////////////////////// ////// 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)); } }
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 }
// 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); } }
// 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); } }
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 /// /// }
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); }
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)); } }
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); } }