/// <summary> /// Reads an <see cref="MDXSkin"/> from the data stream. /// </summary> /// <param name="binaryReader">The reader to use.</param> /// <param name="version">The contextually relevant version to target.</param> /// <returns>A fully read skin.</returns> public static MDXSkin ReadMDXSkin(this BinaryReader binaryReader, WarcraftVersion version) { MDXSkin skin = new MDXSkin { VertexIndices = binaryReader.ReadMDXArray <ushort>(), Triangles = binaryReader.ReadMDXArray <ushort>(), VertexProperties = binaryReader.ReadMDXArray <MDXVertexProperty>(), Sections = binaryReader.ReadMDXArray <MDXSkinSection>(version), RenderBatches = binaryReader.ReadMDXArray <MDXRenderBatch>(), BoneCountMax = binaryReader.ReadUInt32() }; return(skin); }
/// <summary> /// Renders the current object in the current OpenGL context. /// </summary> public void Render(Matrix4 viewMatrix, Matrix4 projectionMatrix, ViewportCamera camera) { if (!this.IsInitialized) { return; } Matrix4 modelViewProjection = this.ActorTransform.GetModelMatrix() * viewMatrix * projectionMatrix; GL.UseProgram(this.SimpleShaderID); GL.BindBuffer(BufferTarget.ArrayBuffer, this.VertexBufferID); GL.EnableVertexAttribArray(0); // Position pointer GL.VertexAttribPointer( 0, 3, VertexAttribPointerType.Float, false, 0, 0); // Bone weight pointer GL.VertexAttribPointer( 1, 4, VertexAttribPointerType.Byte, false, 12, 12); // Bone index pointer GL.VertexAttribPointer( 2, 4, VertexAttribPointerType.Byte, false, 16, 16); // Normal pointer GL.VertexAttribPointer( 3, 3, VertexAttribPointerType.Float, false, 20, 20); // UV1 pointer GL.VertexAttribPointer( 4, 2, VertexAttribPointerType.Float, false, 32, 32); // UV2 pointer GL.VertexAttribPointer( 5, 2, VertexAttribPointerType.Float, false, 40, 40); // Simple: Render all skins MDXSkin firstSkin = this.Model.Skins.First(); foreach (MDXSkinSection skinSection in firstSkin.Submeshes) { } }
private void LoadFromStream(Stream dataStream) { using (BinaryReader br = new BinaryReader(dataStream)) { // Read Wrath header or read pre-wrath header WarcraftVersion format = PeekFormat(br); if (format < WarcraftVersion.Wrath) { this.Header = new MDXHeader(br.ReadBytes(324)); } else { ModelObjectFlags flags = PeekFlags(br); if (flags.HasFlag(ModelObjectFlags.HasBlendModeOverrides)) { this.Header = new MDXHeader(br.ReadBytes(308)); } else { this.Header = new MDXHeader(br.ReadBytes(312)); } } // Seek and read model name br.BaseStream.Position = this.Header.NameOffset; this.Name = new string(br.ReadChars((int)this.Header.NameLength)); // Seek to Global Sequences br.BaseStream.Position = this.Header.GlobalSequencesOffset; for (int i = 0; i < this.Header.GlobalSequenceCount; ++i) { this.GlobalSequenceTimestamps.Add(br.ReadUInt32()); } // Seek to Animation Sequences br.BaseStream.Position = this.Header.AnimationSequencesOffset; int sequenceSize = MDXAnimationSequence.GetSize(); for (int i = 0; i < this.Header.AnimationSequenceCount; ++i) { this.AnimationSequences.Add(new MDXAnimationSequence(br.ReadBytes(sequenceSize))); } // Seek to Animation Sequence Lookup Table br.BaseStream.Position = this.Header.AnimationLookupTableOffset; for (int i = 0; i < this.Header.AnimationLookupTableEntryCount; ++i) { this.AnimationSequenceLookupTable.Add(br.ReadInt16()); } if (MDXHeader.GetModelVersion(this.Header.Version) < WarcraftVersion.Wrath) { // Seek to Playable Animations Lookup Table br.BaseStream.Position = this.Header.PlayableAnimationLookupTableOffset; for (int i = 0; i < this.Header.PlayableAnimationLookupTableEntryCount; ++i) { this.PlayableAnimationLookupTable.Add(new MDXPlayableAnimationLookupTableEntry(br.ReadBytes(4))); } } // Seek to bone block br.BaseStream.Position = this.Header.BonesOffset; for (int i = 0; i < this.Header.BoneCount; ++i) { // TODO: properly skip to the next bone record, data is not aligned MDXBone bone = new MDXBone(); bone.AnimationID = br.ReadInt32(); bone.Flags = (MDXBoneFlags)br.ReadUInt32(); bone.ParentBone = br.ReadInt16(); bone.SubmeshID = br.ReadUInt16(); if (MDXHeader.GetModelVersion(this.Header.Version) >= WarcraftVersion.BurningCrusade) { bone.Unknown1 = br.ReadUInt16(); bone.Unknown1 = br.ReadUInt16(); } // TODO: Rework animation track reading // Read bone animation header block //Bone.AnimatedTranslation = new MDXTrack<Vector3f>(br, MDXHeader.GetModelVersion(Header.Version)); //Bone.AnimatedRotation = new MDXTrack<Quaternion>(br, MDXHeader.GetModelVersion(Header.Version)); //Bone.AnimatedScale = new MDXTrack<Vector3f>(br, MDXHeader.GetModelVersion(Header.Version)); bone.PivotPoint = br.ReadVector3(); this.Bones.Add(bone); } /* * // Read bone animation data * foreach (MDXBone Bone in Bones) * { * // Read animation translation block * br.BaseStream.Position = Bone.AnimatedTranslation.Values.ElementsOffset; * for (int j = 0; j < Bone.AnimatedTranslation.Values.Count; ++j) * { * Bone.AnimatedTranslation.Values.Add(br.ReadVector3()); * } * * // Read animation rotation block * br.BaseStream.Position = Bone.AnimatedRotation.ValuesOffset; * for (int j = 0; j < Bone.AnimatedRotation.ValueCount; ++j) * { * if (MDXHeader.GetModelVersion(Header.Version) > MDXFormat.Classic) * { * Bone.AnimatedRotation.Values.Add(br.ReadQuaternion16()); * } * else * { * Bone.AnimatedRotation.Values.Add(br.ReadQuaternion32()); * } * } * * // Read animation scale block * br.BaseStream.Position = Bone.AnimatedScale.ValuesOffset; * for (int j = 0; j < Bone.AnimatedScale.ValueCount; ++j) * { * Bone.AnimatedScale.Values.Add(br.ReadVector3()); * } * } */ // Seek to Skeletal Bone Lookup Table br.BaseStream.Position = this.Header.KeyedBoneLookupTablesOffset; for (int i = 0; i < this.Header.KeyedBoneLookupTableCount; ++i) { this.KeyedBoneLookupTable.Add(br.ReadInt16()); } // Seek to vertex block br.BaseStream.Position = this.Header.VerticesOffset; for (int i = 0; i < this.Header.VertexCount; ++i) { this.Vertices.Add(new MDXVertex(br.ReadBytes(48))); } // Seek to view block if (MDXHeader.GetModelVersion(this.Header.Version) < WarcraftVersion.Wrath) { br.BaseStream.Position = this.Header.LODViewsOffset; // Read the view headers for (int i = 0; i < this.Header.LODViewsCount; ++i) { MDXSkinHeader skinHeader = new MDXSkinHeader(br.ReadBytes(44)); MDXSkin skin = new MDXSkin(); skin.Header = skinHeader; this.Skins.Add(skin); } // Read view data foreach (MDXSkin view in this.Skins) { // Read view vertex indices view.VertexIndices = new List <ushort>(); br.BaseStream.Position = view.Header.VertexIndicesOffset; for (int j = 0; j < view.Header.VertexIndexCount; ++j) { view.VertexIndices.Add(br.ReadUInt16()); } // Read view triangle vertex indices view.Triangles = new List <ushort>(); br.BaseStream.Position = view.Header.TriangleVertexIndicesOffset; for (int j = 0; j < view.Header.TriangleVertexCount; ++j) { view.Triangles.Add(br.ReadUInt16()); } // Read view vertex properties view.VertexProperties = new List <MDXVertexProperty>(); br.BaseStream.Position = view.Header.VertexPropertiesOffset; for (int j = 0; j < view.Header.VertexPropertyCount; ++j) { view.VertexProperties.Add(new MDXVertexProperty(br.ReadByte(), br.ReadByte(), br.ReadByte(), br.ReadByte())); } // Read view submeshes view.Sections = new List <MDXSkinSection>(); br.BaseStream.Position = view.Header.SkinSectionOffset; for (int j = 0; j < view.Header.SkinSectionCount; ++j) { byte[] submeshData; if (MDXHeader.GetModelVersion(this.Header.Version) >= WarcraftVersion.BurningCrusade) { submeshData = br.ReadBytes(48); } else { submeshData = br.ReadBytes(32); } view.Sections.Add(new MDXSkinSection(submeshData)); } view.RenderBatches = new List <MDXRenderBatch>(); br.BaseStream.Position = view.Header.RenderBatchOffset; for (int j = 0; j < view.Header.RenderBatchCount; ++j) { view.RenderBatches.Add(new MDXRenderBatch(br.ReadBytes(24))); } } } else { throw new NotImplementedException(); } /* * // TODO: Rework animation track reading * // Seek to submesh animation block * br.BaseStream.Position = Header.SubmeshColourAnimationsOffset; * for (int i = 0; i < Header.SubmeshColourAnimationCount; ++i) * { * MDXTrack<RGB> ColourTrack = new MDXTrack<RGB>(br, MDXHeader.GetModelVersion(Header.Version)); * MDXTrack<short> OpacityTrack = new MDXTrack<short>(br, MDXHeader.GetModelVersion(Header.Version)); * * MDXSubmeshColourAnimation ColourAnimation = new MDXSubmeshColourAnimation(); * ColourAnimation.ColourTrack = ColourTrack; * ColourAnimation.OpacityTrack = OpacityTrack; * * ColourAnimations.Add(ColourAnimation); * } * // Read submesh animation values * foreach (MDXSubmeshColourAnimation ColourAnimation in ColourAnimations) * { * // Read the colour track * br.BaseStream.Position = ColourAnimation.ColourTrack.ValuesOffset; * for (int j = 0; j < ColourAnimation.ColourTrack.ValueCount; ++j) * { * ColourAnimation.ColourTrack.Values.Add(new RGB(br.ReadVector3())); * } * * // Read the opacity track * br.BaseStream.Position = ColourAnimation.OpacityTrack.ValuesOffset; * for (int j = 0; j < ColourAnimation.OpacityTrack.ValueCount; ++j) * { * ColourAnimation.OpacityTrack.Values.Add(br.ReadInt16()); * } * } */ // TODO: Use this pattern for the tracks as well, where values are outreferenced // from the block // Seek to Texture definition block br.BaseStream.Position = this.Header.TexturesOffset; for (int i = 0; i < this.Header.TextureCount; ++i) { MDXTexture texture = new MDXTexture(br.ReadBytes(16)); this.Textures.Add(texture); } // Read the texture definition strings foreach (MDXTexture texture in this.Textures) { br.BaseStream.Position = texture.FilenameOffset; texture.Filename = new string(br.ReadChars((int)texture.FilenameLength)); } /* * // TODO: Rework animation track reading * // Seek to transparency block * br.BaseStream.Position = Header.TransparencyAnimationsOffset; * for (int i = 0; i < Header.TransparencyAnimationCount; ++i) * { * TransparencyAnimations.Add(new MDXTrack<short>(br, MDXHeader.GetModelVersion(Header.Version))); * } * // Read transparency animation block data * foreach (MDXTrack<short> TransparencyTrack in TransparencyAnimations) * { * // Read the opacity track * br.BaseStream.Position = TransparencyTrack.ValuesOffset; * for (int j = 0; j < TransparencyTrack.ValueCount; ++j) * { * TransparencyTrack.Values.Add(br.ReadInt16()); * } * } * * // TODO: Rework animation track reading * // UV Animations * br.BaseStream.Position = Header.UVTextureAnimationsOffset; * for (int i = 0; i < Header.UVTextureAnimationCount; ++i) * { * br.BaseStream.Position = Header.UVTextureAnimationsOffset + (i * 84); * * MDXUVAnimation UVAnimation = new MDXUVAnimation(); * UVAnimation.TranslationTrack = new MDXTrack<Vector3f>(br, MDXHeader.GetModelVersion(Header.Version)); * UVAnimation.RotationTrack = new MDXTrack<Quaternion>(br, MDXHeader.GetModelVersion(Header.Version)); * UVAnimation.ScaleTrack = new MDXTrack<Vector3f>(br, MDXHeader.GetModelVersion(Header.Version)); * * UVAnimations.Add(UVAnimation); * } * // Read UV animation track data * foreach (MDXUVAnimation UVAnimation in UVAnimations) * { * // Read animation translation block * br.BaseStream.Position = UVAnimation.TranslationTrack.ValuesOffset; * for (int j = 0; j < UVAnimation.TranslationTrack.ValueCount; ++j) * { * UVAnimation.TranslationTrack.Values.Add(br.ReadVector3()); * } * * // Read animation rotation block * br.BaseStream.Position = UVAnimation.RotationTrack.ValuesOffset; * for (int j = 0; j < UVAnimation.RotationTrack.ValueCount; ++j) * { * if (MDXHeader.GetModelVersion(Header.Version) > MDXFormat.Classic) * { * UVAnimation.RotationTrack.Values.Add(br.ReadQuaternion16()); * } * else * { * UVAnimation.RotationTrack.Values.Add(br.ReadQuaternion32()); * } * } * * // Read animation scale block * br.BaseStream.Position = UVAnimation.ScaleTrack.ValuesOffset; * for (int j = 0; j < UVAnimation.ScaleTrack.ValueCount; ++j) * { * UVAnimation.ScaleTrack.Values.Add(br.ReadVector3()); * } * } */ // Replaceable textures // Render flags // Seek to render flag block br.BaseStream.Position = this.Header.RenderFlagsOffset; for (int i = 0; i < this.Header.RenderFlagCount; ++i) { this.RenderFlags.Add(new MDXRenderFlagPair(br.ReadBytes(4))); } // Bone lookup // Texture lookup // Texture unit lookup // Seek to texture unit lookup block br.BaseStream.Position = this.Header.TextureUnitsOffset; for (int i = 0; i < this.Header.TextureUnitCount; ++i) { this.RenderBatchLookupTable.Add(br.ReadInt16()); } // Transparency lookup // Seek to transparency lookup table br.BaseStream.Position = this.Header.TransparencyLookupTablesOffset; for (int i = 0; i < this.Header.TransparencyLookupTableCount; ++i) { this.TransparencyLookupTable.Add(br.ReadInt16()); } // UV animation lookup // Bounding box // Bounding radius // Collision box // Collision radius // Bounding tris // Bounding verts // Bounding normals // Attachments // Attachment lookup // Anim notifies (events) // Lights // Cameras // Camera lookup // Ribbon Emitters // Particle Emitters // Blend maps (if flags say they exist) } }