public static void Load(string dataPath, int uniqueID, Vector3 position, Quaternion rotation, Vector3 scale) { M2Data m2Data = new M2Data(); M2Texture m2Tex = new M2Texture(); m2Data.dataPath = dataPath; m2Data.uniqueID = uniqueID; m2Data.position = position; m2Data.rotation = rotation; m2Data.scale = scale; try { ThreadWorking = true; ParseM2_Root(dataPath, m2Data, m2Tex); ParseM2_Skin(dataPath, m2Data); AllM2Data.Enqueue(m2Data); ThreadWorking = false; } catch (Exception ex) { Debug.Log("Error : Trying to parse M2 - " + dataPath); Debug.LogException(ex); } }
private static void ParseM2_Root(string dataPath, M2Data m2Data, M2Texture m2Tex) { StreamTools s = new StreamTools(); string path = Casc.GetFile(dataPath); byte[] M2MainData = File.ReadAllBytes(path); long streamPosition = 0; using (MemoryStream ms = new MemoryStream(M2MainData)) { while (streamPosition < ms.Length) { ms.Position = streamPosition; int chunkID = s.ReadLong(ms); int chunkSize = s.ReadLong(ms); streamPosition = ms.Position + chunkSize; switch (chunkID) { case (int)ChunkID.M2ChunkID.MD21: ReadMD21(ms, m2Data, m2Tex); break; default: SkipUnknownChunk(ms, chunkID, chunkSize); break; } } }; }
public static void Load(uint FileDataId, int uniqueID, Vector3 position, Quaternion rotation, Vector3 scale, CASCHandler Handler) { M2Data m2Data = new M2Data(); M2Texture m2Tex = new M2Texture(); m2Data.FileDataId = FileDataId; m2Data.uniqueID = uniqueID; m2Data.position = position; m2Data.rotation = rotation; m2Data.scale = scale; try { ThreadWorking = true; ParseM2_Root(FileDataId, m2Data, m2Tex, Handler); foreach (uint skinFile in SkinFiles) { ParseM2_Skin(skinFile, m2Data, Handler); } AllM2Data.Enqueue(m2Data); ThreadWorking = false; } catch (Exception ex) { Debug.Log("Error : Trying to parse M2 - " + FileDataId); Debug.LogException(ex); } }
//Load specific texture private int LoadTexture(M2Texture texture, int i) { int file = -1; int index = Array.FindIndex(character.Options, o => o.Form == character.Form); switch (texture.Type) { case 0: file = Model.TextureIDs[i]; break; case 11: file = character.Choices[index][character.Customization[index]].Textures[0].Texture1; break; case 12: file = character.Choices[index][character.Customization[index]].Textures[0].Texture2; break; case 13: file = character.Choices[index][character.Customization[index]].Textures[0].Texture3; break; } return(file); }
public static void ReadTXID(BinaryReader br, M2Data m2Data, CASCHandler CascHandler, uint Size) { LoadedBLPFileDataIds.Clear(); var numTextures = Size / 4; for (int i = 0; i < numTextures; i++) { uint texture = br.ReadUInt32(); M2Texture m2Texture = new M2Texture(); Texture2Ddata texture2Ddata = new Texture2Ddata(); if (!LoadedBLPFileDataIds.Contains(texture)) { var stream = CascHandler.OpenFile(texture); 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; m2Texture.FileDataId = texture; stream.Close(); stream.Dispose(); LoadedBLPFileDataIds.Add(texture); } m2Data.m2Tex.Add(m2Texture); } }
protected override int LoadTexture(M2Texture texture, int i, out bool skin) { int file = -1; int index; skin = false; switch (texture.Type) { case 0: file = Model.TextureIDs[i]; break; case 1: index = Array.FindIndex(Character.Options, o => o.Name == "Skin Color"); file = Character.Choices[index][Character.Customization[index]].Textures[0].Texture1; skin = true; break; case 2: file = Character.Items[2] != null ? Character.Items[2].LeftTexture : -1; break; case 6: index = Array.FindIndex(Character.Options, o => o.Name == "Hair Color"); file = Character.Choices[index][Character.Customization[index]].Textures[0].Texture1; break; case 19: index = Array.FindIndex(Character.Options, o => o.Name == "Eye Color"); file = Character.Choices[index][Character.Customization[index]].Textures[0].Texture1; break; } return(file); }
//Load specific texture private int LoadTexture(M2Texture texture, int i) { int file = 0; switch (texture.Type) { case 0: file = Model.TextureIDs[i]; break; case 2: file = MainTexture; break; } return(file); }
public M2Texture ToWoW() { var wowTexture = new M2Texture { Type = M2Texture.TextureType.Monster1 }; //TODO type with name if (WrapU) { wowTexture.Flags |= M2Texture.TextureFlags.WrapX; } if (WrapV) { wowTexture.Flags |= M2Texture.TextureFlags.WrapY; } return(wowTexture); }
public static void M2Textures() { SpecialSeek(openedFile.texturesPos); for (int i = 0; i < openedFile.texturesNum; i++) { M2Texture text = new M2Texture(); text.type = reader.ReadUInt32(); text.flags = reader.ReadUInt32(); text.textureLength = reader.ReadUInt32(); text.texturePos = reader.ReadUInt32(); oldPos = reader.BaseStream.Position; SpecialSeek(text.texturePos); text.texturePath = reader.ReadBytes((int)text.textureLength); reader.BaseStream.Seek(oldPos, SeekOrigin.Begin); openedFile.textures.Add(text); } }
public M2Array <M2Texture> GetTextures() { M2Array <M2Texture> textures = new M2Array <M2Texture>(); if (!_model.Has <TEXS>()) { return(textures); } foreach (var t in _model.Get <TEXS>()) { M2Texture texture = new M2Texture() { Flags = (M2Texture.TextureFlags)t.Flags, Name = t.Image, Type = (M2Texture.TextureType)t.ReplaceableId }; textures.Add(texture); } return(textures); }
//Load specific texture private int LoadTexture(M2Texture texture, int i) { int file = -1; int index; switch (texture.Type) { case 0: file = Model.TextureIDs[i]; break; case 2: file = Texture; break; case 8: index = Array.FindIndex(character.Options, o => o.Name == "Paint"); file = character.Choices[index][character.Customization[index]].Textures[0].Texture1; break; } return(file); }
private static void ParseM2_Root(uint fileDataID, M2Data m2Data, M2Texture m2Tex, CASCHandler CascHandler) { long streamPos = 0; using (var stream = CascHandler.OpenFile(fileDataID)) using (BinaryReader reader = new BinaryReader(stream)) { while (streamPos < stream.Length) { stream.Position = streamPos; M2ChunkId chunkID = (M2ChunkId)reader.ReadUInt32(); uint chunkSize = reader.ReadUInt32(); streamPos = stream.Position + chunkSize; switch (chunkID) { case M2ChunkId.MD21: ReadMD21(reader, m2Data, m2Tex); break; case M2ChunkId.SFID: ReadSFID(reader, chunkSize); break; case M2ChunkId.TXID: ReadTXID(reader, m2Data, CascHandler, chunkSize); break; default: SkipUnknownChunk(stream, chunkSize); break; } } }; }
public static void Load(uint FileDataId, int uniqueID, Vector3 position, Quaternion rotation, Vector3 scale, CASCHandler Handler) { M2Data m2Data = new M2Data(); M2Texture m2Tex = new M2Texture(); m2Data.FileDataId = FileDataId; m2Data.uniqueID = uniqueID; m2Data.position = position; m2Data.rotation = rotation; m2Data.scale = scale; ThreadWorking = true; ParseM2_Root(FileDataId, m2Data, m2Tex, Handler); foreach (uint skinFile in SkinFiles) { ParseM2_Skin(skinFile, m2Data, Handler); } AllM2Data.Enqueue(m2Data); ThreadWorking = false; }
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)); } }
//Load texture from casc protected abstract int LoadTexture(M2Texture texture, int i, out bool skin);
/// <inheritdoc/> public void LoadBinaryData(byte[] inData) { using (var ms = new MemoryStream(inData)) using (var br = new BinaryReader(ms)) { br.ReadUInt32(); // Signature Version = br.ReadUInt32(); Name = br.ReadMD20String(br.ReadUInt32(), br.ReadUInt32()); Flags = br.ReadUInt32(); // TODO: Implement Flags // Global Sequences UInt32 count = br.ReadUInt32(); UInt32 offset = br.ReadUInt32(); long headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { UInt32 value = br.ReadUInt32(); GlobalSequences.Add(value); } br.BaseStream.Position = headerpos; // Sequences count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Sequence seq = new M2Sequence(); seq.AnimationID = br.ReadUInt16(); seq.SubAnimationID = br.ReadUInt16(); seq.Length = br.ReadUInt32(); seq.MovingSpeed = br.ReadSingle(); seq.Flags = br.ReadUInt32(); seq.Probability = br.ReadInt16(); seq.Padding = br.ReadUInt16(); seq.MinimumRepetitions = br.ReadUInt32(); seq.MaximumRepetitions = br.ReadUInt32(); seq.BlendTime = br.ReadUInt32(); seq.BoundsMinimumExtend = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); seq.BoundsMaximumExtend = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); seq.BoundRadius = br.ReadSingle(); seq.NextAnimation = br.ReadInt16(); seq.aliasNext = br.ReadUInt16(); Sequences.Add(seq); } br.BaseStream.Position = headerpos; //SequencesLookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { SequencesLookups.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; // Bones count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Bone bone = new M2Bone(); bone.KeyBoneID = br.ReadInt32(); bone.Flags = br.ReadUInt32(); bone.ParentBone = br.ReadInt16(); bone.SubmeshID = br.ReadUInt16(); bone.CompressData[0] = br.ReadUInt16(); bone.CompressData[1] = br.ReadUInt16(); //translation M2Track translation = new M2Track(); translation.readM2Track(br); bone.translation = translation; // rotation M2Track rotation = new M2Track(); rotation.readM2Track(br); bone.rotation = rotation; // Scale M2Track scale = new M2Track(); scale.readM2Track(br); bone.scale = scale; bone.pivot = new C3Vector(br.ReadUInt32(), br.ReadUInt32(), br.ReadUInt32()); Bones.Add(bone); } br.BaseStream.Position = headerpos; // key_bone_lookup count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { KeyBoneLookup.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; // Vetices count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Vertex temp = new M2Vertex(); temp.Pos = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); for (int a = 0; a < 4; a++) { temp.BoneWeights.Add(br.ReadByte()); } for (int a = 0; a < 4; a++) { temp.BoneIndices.Add(br.ReadByte()); } temp.Normal = new C3Vector(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); temp.TexCords.Add(new C2Vector(br.ReadSingle(), br.ReadSingle())); temp.TexCords.Add(new C2Vector(br.ReadSingle(), br.ReadSingle())); } br.BaseStream.Position = headerpos; // Number of Skin profiles NumberSkinProfiles = br.ReadUInt32(); // Colors count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Color temp = new M2Color(); // Color M2Track color = new M2Track(); color.readM2Track(br); temp.Color = color; // Alpha M2Track alpha = new M2Track(); alpha.readM2Track(br); temp.Alpha = alpha; Color.Add(temp); } br.BaseStream.Position = headerpos; // Textures count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Texture temp = new M2Texture(); temp.Type = br.ReadUInt32(); temp.Flags = br.ReadUInt32(); UInt32 tCount = br.ReadUInt32(); UInt32 tOffset = br.ReadUInt32(); long tHeaderpos = br.BaseStream.Position; br.BaseStream.Position = tOffset; temp.Filename = ""; for (int a = 0; a < tCount; a++) { temp.Filename += br.ReadChar(); } br.BaseStream.Position = tHeaderpos; Texture.Add(temp); } br.BaseStream.Position = headerpos; // Texture Weights count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Track temp = new M2Track(); temp.readM2Track(br); TextureWeights.Add(temp); } br.BaseStream.Position = headerpos; // UV Animations count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2TextureTransform temp = new M2TextureTransform(); M2Track translation = new M2Track(); translation.readM2Track(br); temp.Translation = translation; M2Track rotation = new M2Track(); rotation.readM2Track(br); temp.Rotation = rotation; M2Track scaling = new M2Track(); scaling.readM2Track(br); temp.Scaling = scaling; UvAnimations.Add(temp); } br.BaseStream.Position = headerpos; // Texture Replacements count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { TextureReplacements.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; // Materials count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { M2Material temp = new M2Material(); temp.Flags = br.ReadUInt16(); temp.BlendMode = br.ReadUInt16(); Materials.Add(temp); } br.BaseStream.Position = headerpos; // Bone Lookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { BoneLookups.Add(br.ReadUInt16()); } br.BaseStream.Position = headerpos; // Texture Units count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { TextureUnits.Add(br.ReadUInt16()); } br.BaseStream.Position = headerpos; // Texture Weights Lookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { TextureWeightsLookups.Add(br.ReadUInt16()); } br.BaseStream.Position = headerpos; // Animation Lookups count = br.ReadUInt32(); offset = br.ReadUInt32(); headerpos = br.BaseStream.Position; br.BaseStream.Position = offset; for (int i = 0; i < count; i++) { UvAnimationLookups.Add(br.ReadInt16()); } br.BaseStream.Position = headerpos; } }
public static void ReadMD21(BinaryReader br, M2Data m2Data, M2Texture m2Tex) { long md20position = br.BaseStream.Position; int MD20 = br.ReadInt32(); // "MD20". Legion uses a chunked file format starting with MD21. int version = br.ReadInt32(); M2Array name = br.ReadM2Array(); // should be globally unique, used to reload by name in internal clients var flags = br.ReadInt32(); M2Array global_loops = br.ReadM2Array(); // Timestamps used in global looping animations. M2Array sequences = br.ReadM2Array(); // Information about the animations in the model. M2Array sequences_lookups = br.ReadM2Array(); // Mapping of sequence IDs to the entries in the Animation sequences block. M2Array bones = br.ReadM2Array(); // MAX_BONES = 0x100 => Creature\SlimeGiant\GiantSlime.M2 has 312 bones(Wrath) M2Array key_bone_lookup = br.ReadM2Array(); // Lookup table for key skeletal bones. M2Array vertices = br.ReadM2Array(); int num_skin_profiles = br.ReadInt32(); M2Array colors = br.ReadM2Array(); // Color and alpha animations definitions. M2Array textures = br.ReadM2Array(); M2Array texture_weights = br.ReadM2Array(); // Transparency of textures. M2Array texture_transforms = br.ReadM2Array(); M2Array replaceable_texture_lookup = br.ReadM2Array(); M2Array materials = br.ReadM2Array(); // Blending modes / render flags. M2Array bone_lookup_table = br.ReadM2Array(); M2Array texture_lookup_table = br.ReadM2Array(); M2Array tex_unit_lookup_table = br.ReadM2Array(); // ≥ Cata: unused M2Array transparency_lookup_table = br.ReadM2Array(); M2Array texture_transforms_lookup_table = br.ReadM2Array(); m2Data.bounding_box = br.ReadBoundingBoxes(); // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height float bounding_sphere_radius = br.ReadSingle(); // detail doodad draw dist = clamp (bounding_sphere_radius * detailDoodadDensityFade * detailDoodadDist, …) BoundingBox collision_box = br.ReadBoundingBoxes(); float collision_sphere_radius = br.ReadSingle(); M2Array collision_triangles = br.ReadM2Array(); M2Array collision_vertices = br.ReadM2Array(); M2Array collision_normals = br.ReadM2Array(); M2Array attachments = br.ReadM2Array(); // position of equipped weapons or effects M2Array attachment_lookup_table = br.ReadM2Array(); M2Array events = br.ReadM2Array(); // Used for playing sounds when dying and a lot else. M2Array lights = br.ReadM2Array(); // Lights are mainly used in loginscreens but in wands and some doodads too. M2Array cameras = br.ReadM2Array(); // The cameras are present in most models for having a model in the character tab. M2Array camera_lookup_table = br.ReadM2Array(); M2Array ribbon_emitters = br.ReadM2Array(); // Things swirling around. See the CoT-entrance for light-trails. M2Array particle_emitters = br.ReadM2Array(); // Name // br.BaseStream.Position = name.Offset + md20position; for (int n = 0; n < name.Size; n++) { m2Data.name += Convert.ToChar(br.ReadByte()); } // Bones // br.BaseStream.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 = br.ReadInt32(); // Back-reference to the key bone lookup table. -1 if this is no key bone. m2CompBone.flags = br.ReadInt32(); m2CompBone.parent_bone = br.ReadInt16(); m2CompBone.submesh_id = br.ReadUInt16(); m2CompBone.uDistToFurthDesc = br.ReadUInt16(); m2CompBone.uZRatioOfChain = br.ReadUInt16(); translationM2track[cb] = br.ReadM2Track(); rotationM22track[cb] = br.ReadM2Track(); scaleM22track[cb] = br.ReadM2Track(); Vector3 pivotRaw = new Vector3(br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / 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>(); br.BaseStream.Position = translationM2track[ab].Timestamps.Offset + md20position; M2Array m2AnimationOffset = br.ReadM2Array(); br.BaseStream.Position = m2AnimationOffset.Offset; for (int t = 0; t < m2AnimationOffset.Size; t++) { timeStamps.Add(br.ReadInt32()); } positions.timeStamps = timeStamps; // Values // List <Vector3> values = new List <Vector3>(); br.BaseStream.Position = translationM2track[ab].Values.Offset + md20position; M2Array m2AnimationValues = br.ReadM2Array(); br.BaseStream.Position = m2AnimationValues.Offset; for (int t = 0; t < m2AnimationValues.Size; t++) { Vector3 rawPosition = new Vector3(br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / 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>(); br.BaseStream.Position = rotationM22track[ab].Timestamps.Offset + md20position; M2Array m2AnimationOffset = br.ReadM2Array(); br.BaseStream.Position = m2AnimationOffset.Offset; for (int t = 0; t < m2AnimationOffset.Size; t++) { timeStamps.Add(br.ReadInt32()); } rotations.timeStamps = timeStamps; // Values // List <Quaternion> values = new List <Quaternion>(); br.BaseStream.Position = rotationM22track[ab].Values.Offset + md20position; M2Array m2AnimationValues = br.ReadM2Array(); br.BaseStream.Position = m2AnimationValues.Offset; for (int t = 0; t < m2AnimationValues.Size; t++) { Quaternion rawRotation = br.ReadQuaternion(); 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>(); br.BaseStream.Position = scaleM22track[ab].Timestamps.Offset + md20position; M2Array m2AnimationOffset = br.ReadM2Array(); br.BaseStream.Position = m2AnimationOffset.Offset; for (int t = 0; t < m2AnimationOffset.Size; t++) { timeStamps.Add(br.ReadInt32()); } scales.timeStamps = timeStamps; // Values // List <Vector3> values = new List <Vector3>(); br.BaseStream.Position = scaleM22track[ab].Values.Offset + md20position; M2Array m2AnimationValues = br.ReadM2Array(); br.BaseStream.Position = m2AnimationValues.Offset; for (int t = 0; t < m2AnimationValues.Size; t++) { Vector3 rawScale = new Vector3(br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / 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 // br.BaseStream.Position = bone_lookup_table.Offset + md20position; for (int blt = 0; blt < key_bone_lookup.Size; blt++) { m2Data.bone_lookup_table.Add(br.ReadUInt16()); } // Key-Bone Lookup // br.BaseStream.Position = key_bone_lookup.Offset + md20position; for (int kbl = 0; kbl < key_bone_lookup.Size; kbl++) { m2Data.key_bone_lookup.Add(br.ReadUInt16()); } // Vertices // br.BaseStream.Position = vertices.Offset + md20position; m2Data.meshData = new MeshData(); for (int v = 0; v < vertices.Size; v++) { Vector3 rawPosition = new Vector3(br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / Settings.WorldScale, br.ReadSingle() / Settings.WorldScale); m2Data.meshData.pos.Add(new Vector3(-rawPosition.x, rawPosition.z, -rawPosition.y)); m2Data.meshData.bone_weights.Add(new float[] { br.ReadByte() / 255.0f, br.ReadByte() / 255.0f, br.ReadByte() / 255.0f, br.ReadByte() / 255.0f }); m2Data.meshData.bone_indices.Add(new int[] { br.ReadByte(), br.ReadByte(), br.ReadByte(), br.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(br.ReadSingle() * Settings.WorldScale, br.ReadSingle() * Settings.WorldScale, br.ReadSingle() * Settings.WorldScale); m2Data.meshData.normal.Add(new Vector3(-rawnormal.x, rawnormal.z, -rawnormal.y)); m2Data.meshData.tex_coords.Add(new Vector2(br.ReadSingle(), br.ReadSingle())); m2Data.meshData.tex_coords2.Add(new Vector2(br.ReadSingle(), br.ReadSingle())); } // texture_lookup_table // br.BaseStream.Position = texture_lookup_table.Offset + md20position; for (int tl = 0; tl < texture_lookup_table.Size; tl++) { m2Data.textureLookupTable.Add(br.ReadUInt16()); } }