public M2SkinProfile GetSkin() { var geos = _model.Get <GEOS>().ToList(); var materials = _model.Get <MTLS>()?.ToArray(); var geoa = _model.Get <GEOA>()?.ToList(); M2SkinProfile profile = new M2SkinProfile() { Bones = 256, Indices = Enumerable.Range(0, geos.Sum(x => x.Vertices.Count)).Select(x => (ushort)x).ToM2Array(), Triangles = new M2Array <ushort>() }; ushort offset = 0; foreach (var geo in geos) { profile.Triangles.AddRange(geo.FaceVertices.SelectMany(y => y.ToArray(offset))); offset += (ushort)geo.NrOfVertices; M2SkinSection section = new M2SkinSection() { StartBones = (ushort)(profile.Submeshes?.Sum(x => x.NBones) ?? 0), StartTriangle = (ushort)(profile.Submeshes?.Sum(x => x.NTriangles) ?? 0), StartVertex = (ushort)(profile.Submeshes?.Sum(x => x.NVertices) ?? 0), NBones = (ushort)geo.GroupedVertices.Count, NTriangles = (ushort)geo.NrOfFaceVertices, NVertices = (ushort)geo.NrOfVertices, CenterBoundingBox = geo.GetCenter(), CenterMass = geo.GetCenter(), }; section.Radius = geo.Vertices.Select(x => x.ToC3Vector).Max(x => x.Distance(section.CenterBoundingBox)); profile.Submeshes.Add(section); } //M2 Batches if (materials == null) { return(profile); } for (int m = 0; m < materials.Length; m++) { for (int l = 0; l < materials[m].Layers.Count; l++) { var layer = materials[m].Layers[l]; M2Batch batch = new M2Batch() { Flags = 16, //Usually 16 for static textures Flags2 = (byte)layer.PriorityPlane, ShaderId = 0, SubmeshIndex = (ushort)geos.FindIndex(x => x.MaterialId == m), SubmeshIndex2 = (ushort)geos.FindIndex(x => x.MaterialId == m), ColorIndex = -1, OpCount = 1, RenderFlags = (ushort)profile.TextureUnits.Count, Texture = (ushort)layer.TextureId, TexUnitNumber2 = (ushort)layer.TextureId, Layer = (ushort)l, TextureAnim = (ushort)(layer.TextureAnimationId + 1), //MDX is -1 based }; if (batch.TextureAnim > 0) { batch.Flags = 0; //0 for animated textures } if (geoa != null) { batch.ColorIndex = (short)geoa.FindIndex(x => x.GeosetId - 1 == batch.SubmeshIndex); } Model.TransLookup.Add((short)Model.TransLookup.Count); profile.TextureUnits.Add(batch); } } return(profile); }
public static void ReadSkin(uint fileDataId, M2Model model) { var stream = CASC.OpenFile(fileDataId); if (stream == null) { return; } using (var reader = new BinaryReader(stream)) { var magic = reader.ReadUInt32().Flip(); if (magic != (uint)Chunks.SKIN) { throw new Exception($"{fileDataId} is not a SKIN! {magic:X}"); } var indices = reader.ReadM2Array(); var triangles = reader.ReadM2Array(); var bones = reader.ReadM2Array(); var submeshes = reader.ReadM2Array(); var batches = reader.ReadM2Array(); var boneCountMax = reader.ReadUInt32(); var shadowBatches = reader.ReadM2Array(); // Read Triangles reader.BaseStream.Position = triangles.Offset; var triangleList = new int[triangles.Size]; for (var i = 0; i < triangles.Size; ++i) { triangleList[i] = reader.ReadUInt16(); } // Read Batches reader.BaseStream.Position = batches.Offset; for (var i = 0; i < batches.Size; ++i) { var skinBatch = new M2Batch { Flag1 = reader.ReadByte(), Flag2 = reader.ReadByte(), ShaderId = reader.ReadUInt16(), SkinSectionId = reader.ReadUInt16(), GeosetIndex = reader.ReadUInt16(), ColorIndex = reader.ReadUInt16(), MaterialIndex = reader.ReadUInt16(), MaterialLayer = reader.ReadUInt16(), TextureCount = reader.ReadUInt16(), TextureComboIndex = reader.ReadUInt16(), TextureCoordComboIndex = reader.ReadUInt16(), TextureTransformComboIndex = reader.ReadUInt16(), TextureWeightComboIndex = reader.ReadUInt16() }; model.BatchIndices.Add(skinBatch); } // Read Submeshes reader.BaseStream.Position = submeshes.Offset; for (var i = 0; i < submeshes.Size; ++i) { var submesh = new M2Submesh(); submesh.SkinSectionId = reader.ReadUInt16(); var submeshLevel = reader.ReadUInt16(); submesh.VertexStart = reader.ReadUInt16() + (submeshLevel << 16); submesh.VertexCount = reader.ReadUInt16(); submesh.TriangleStart = reader.ReadUInt16() + (submeshLevel << 16); submesh.TriangleCount = reader.ReadUInt16(); submesh.BoneCount = reader.ReadUInt16(); submesh.BoneComboIndex = reader.ReadUInt16(); submesh.BoneInfluence = reader.ReadUInt16(); submesh.CenterBoneIndex = reader.ReadUInt16(); var centerPosition = new Vector3(reader.ReadSingle() / WorldConstants.WorldScale, reader.ReadSingle() / WorldConstants.WorldScale, reader.ReadSingle() / WorldConstants.WorldScale); submesh.CenterPosition = new Vector3(-centerPosition.x, centerPosition.z, -centerPosition.y); var sortCenterPosition = new Vector3(reader.ReadSingle() / WorldConstants.WorldScale, reader.ReadSingle() / WorldConstants.WorldScale, reader.ReadSingle() / WorldConstants.WorldScale); submesh.SortCenterPosition = new Vector3(-sortCenterPosition.x, sortCenterPosition.z, -sortCenterPosition.y); submesh.SortRadius = reader.ReadSingle(); submesh.Vertices = new Vector3[submesh.VertexCount]; submesh.Normals = new Vector3[submesh.VertexCount]; submesh.UVs = new Vector2[submesh.VertexCount]; submesh.UV2s = new Vector2[submesh.VertexCount]; Parallel.For(0, submesh.VertexCount, j => { submesh.Vertices[j] = model.MeshData.Vertices[j + submesh.VertexStart]; submesh.Normals[j] = model.MeshData.Normals[j + submesh.VertexStart]; submesh.UVs[j] = model.MeshData.TexCoords[j + submesh.VertexStart]; submesh.UV2s[j] = model.MeshData.TexCoords2[j + submesh.VertexStart]; }); submesh.Triangles = new int[submesh.TriangleCount]; Parallel.For(0, submesh.TriangleCount, j => { submesh.Triangles[j] = triangleList[j + submesh.TriangleStart]; }); model.Submeshes.Add(submesh); } } }