public static void WriteMeshData(Stream stream, Pmo pmo) { bool hasSwappedToSecondModel = false; // Write Mesh Data. for (int j = 0; j < pmo.Meshes.Count; j++) { if (!hasSwappedToSecondModel && pmo.Meshes[j].MeshNumber == 1 && pmo.header.MeshOffset0 != 0) { hasSwappedToSecondModel = true; for (uint b = 0; b < 0xC; b++) { stream.Write((byte)0x00); } for (uint b = 0; stream.Position % 0x10 != 0; b++) { stream.Write((byte)0x00); } } MeshChunks chunk = pmo.Meshes[j]; Mapping.WriteObject <Pmo.MeshSection>(stream, chunk.SectionInfo); if (chunk.SectionInfo_opt1 != null) { Mapping.WriteObject <Pmo.MeshSectionOptional1>(stream, chunk.SectionInfo_opt1); } if (chunk.SectionInfo_opt2 != null) { Mapping.WriteObject <Pmo.MeshSectionOptional2>(stream, chunk.SectionInfo_opt2); } if (chunk.TriangleStripValues.Length > 0) { for (int z = 0; z < chunk.TriangleStripValues.Length; z++) { stream.Write((ushort)chunk.TriangleStripValues[z]); } } for (int k = 0; k < pmo.Meshes[j].SectionInfo.VertexCount; k++) { long vertexStartPos = stream.Position; int vertexIncreaseAmount = 0; VertexFlags flags = Pmo.GetFlags(chunk.SectionInfo); // Write Joints. if (flags.WeightFormat != CoordinateFormat.NO_VERTEX) { for (int w = 0; w < flags.SkinningWeightsCount + 1; w++) { int currentIndex = w + (k * (flags.SkinningWeightsCount + 1)); switch (flags.WeightFormat) { case CoordinateFormat.NORMALIZED_8_BITS: stream.Write((byte)(chunk.jointWeights[k].weights[currentIndex] * 127.0f)); break; case CoordinateFormat.NORMALIZED_16_BITS: stream.Write((byte)(chunk.jointWeights[k].weights[currentIndex] * 32767.0f)); break; case CoordinateFormat.FLOAT_32_BITS: StreamExtensions.Write(stream, chunk.jointWeights[k].weights[currentIndex]); break; } } } // Write Texture Coords. switch (flags.TextureCoordinateFormat) { case CoordinateFormat.NORMALIZED_8_BITS: stream.Write((byte)(chunk.textureCoordinates[k].X * 128.0f)); stream.Write((byte)(chunk.textureCoordinates[k].Y * 128.0f)); break; case CoordinateFormat.NORMALIZED_16_BITS: vertexIncreaseAmount = ((0x2 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x1)) & 0x1); for (int a = 0; a < vertexIncreaseAmount; a++) { stream.Write((byte)0xAB); } stream.Write((ushort)(chunk.textureCoordinates[k].X * 32768.0f)); stream.Write((ushort)(chunk.textureCoordinates[k].Y * 32768.0f)); break; case CoordinateFormat.FLOAT_32_BITS: vertexIncreaseAmount = ((0x4 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x3)) & 0x3); for (int a = 0; a < vertexIncreaseAmount; a++) { stream.Write((byte)0xAB); } StreamExtensions.Write(stream, chunk.textureCoordinates[k].X); StreamExtensions.Write(stream, chunk.textureCoordinates[k].Y); break; } // Write colors. switch (flags.ColorFormat) { case Pmo.ColorFormat.NO_COLOR: break; case Pmo.ColorFormat.BGR_5650_16BITS: break; case Pmo.ColorFormat.ABGR_5551_16BITS: break; case Pmo.ColorFormat.ABGR_4444_16BITS: break; case Pmo.ColorFormat.ABGR_8888_32BITS: vertexIncreaseAmount = ((0x4 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x3)) & 0x3); for (int a = 0; a < vertexIncreaseAmount; a++) { stream.Write((byte)0xAB); } stream.Write((byte)(chunk.colors[k].X)); stream.Write((byte)(chunk.colors[k].Y)); stream.Write((byte)(chunk.colors[k].Z)); stream.Write((byte)(chunk.colors[k].W)); break; } // Write vertices. switch (flags.PositionFormat) { case CoordinateFormat.NORMALIZED_8_BITS: StreamExtensions.Write(stream, (sbyte)(chunk.vertices[k].X * 128.0f)); StreamExtensions.Write(stream, (sbyte)(chunk.vertices[k].Y * 128.0f)); StreamExtensions.Write(stream, (sbyte)(chunk.vertices[k].Z * 128.0f)); break; case CoordinateFormat.NORMALIZED_16_BITS: vertexIncreaseAmount = ((0x2 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x1)) & 0x1); for (int a = 0; a < vertexIncreaseAmount; a++) { stream.Write((byte)0xAB); } StreamExtensions.Write(stream, (short)(chunk.vertices[k].X * 32768.0f)); StreamExtensions.Write(stream, (short)(chunk.vertices[k].Y * 32768.0f)); StreamExtensions.Write(stream, (short)(chunk.vertices[k].Z * 32768.0f)); break; case CoordinateFormat.FLOAT_32_BITS: vertexIncreaseAmount = ((0x4 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x3)) & 0x3); for (int a = 0; a < vertexIncreaseAmount; a++) { stream.Write((byte)0xAB); } StreamExtensions.Write(stream, chunk.vertices[k].X); StreamExtensions.Write(stream, chunk.vertices[k].Y); StreamExtensions.Write(stream, chunk.vertices[k].Z); break; } int padding = ((int)vertexStartPos + chunk.SectionInfo.VertexSize) - (int)stream.Position; for (int p = 0; p < padding; p++) { stream.Write((byte)0xAB); } } // Remainder. uint remainder = (uint)stream.Position % 4; for (int p = 0; p < remainder; p++) { stream.Write((byte)0x00); } if (j == (pmo.Meshes.Count - 1)) { for (uint b = 0; b < 0xC; b++) { try { stream.Write((byte)0x00); } catch (Exception ex) { ex.ToString(); } } int pd = (int)stream.Position % 0x10; for (int n = 0; pd != 0; n++) { try { stream.Write((byte)0x00); } catch (Exception ex) { ex.ToString(); } pd = (int)stream.Position % 0x10; } } } }
public static void ReadMeshData(Stream stream, Pmo pmo, int MeshNumber = 0) { // Go to mesh position. if (MeshNumber == 0) { stream.Seek(pmo.PMO_StartPosition + pmo.header.MeshOffset0, SeekOrigin.Begin); } else { stream.Seek(pmo.PMO_StartPosition + pmo.header.MeshOffset1, SeekOrigin.Begin); } UInt16 VertCnt = 0xFFFF; while (VertCnt > 0) { MeshChunks meshChunk = new MeshChunks(); meshChunk.MeshNumber = MeshNumber; meshChunk.SectionInfo = Mapping.ReadObject <MeshSection>(stream); // Exit if Vertex Count is zero. if (meshChunk.SectionInfo.VertexCount <= 0) { break; } meshChunk.TextureID = meshChunk.SectionInfo.TextureID; VertexFlags flags = GetFlags(meshChunk.SectionInfo); bool isColorFlagRisen = flags.UniformDiffuseFlag; if (pmo.header.SkeletonOffset != 0) { meshChunk.SectionInfo_opt1 = Mapping.ReadObject <MeshSectionOptional1>(stream); } if (isColorFlagRisen) { meshChunk.SectionInfo_opt2 = Mapping.ReadObject <MeshSectionOptional2>(stream); } if (meshChunk.SectionInfo.TriangleStripCount > 0) { meshChunk.TriangleStripValues = new UInt16[meshChunk.SectionInfo.TriangleStripCount]; for (int i = 0; i < meshChunk.SectionInfo.TriangleStripCount; i++) { meshChunk.TriangleStripValues[i] = stream.ReadUInt16(); } } // Get Formats. CoordinateFormat TexCoordFormat = flags.TextureCoordinateFormat; CoordinateFormat VertexPositionFormat = flags.PositionFormat; CoordinateFormat WeightFormat = flags.WeightFormat; ColorFormat ColorFormat = flags.ColorFormat; UInt32 SkinningWeightsCount = flags.SkinningWeightsCount; BinaryReader r = new BinaryReader(stream); long positionAfterHeader = stream.Position; if (meshChunk.SectionInfo.TriangleStripCount > 0) { int vertInd = 0; for (int p = 0; p < meshChunk.SectionInfo.TriangleStripCount; p++) { for (int s = 0; s < (meshChunk.TriangleStripValues[p] - 2); s++) { if (s % 2 == 0) { meshChunk.Indices.Add(vertInd + s + 0); meshChunk.Indices.Add(vertInd + s + 1); meshChunk.Indices.Add(vertInd + s + 2); } else { meshChunk.Indices.Add(vertInd + s + 0); meshChunk.Indices.Add(vertInd + s + 2); meshChunk.Indices.Add(vertInd + s + 1); } } vertInd += meshChunk.TriangleStripValues[p]; } } else { if (flags.Primitive == PrimitiveType.PRIMITIVE_TRIANGLE_STRIP) { for (int s = 0; s < (meshChunk.SectionInfo.VertexCount - 2); s++) { if (s % 2 == 0) { meshChunk.Indices.Add(s + 0); meshChunk.Indices.Add(s + 1); meshChunk.Indices.Add(s + 2); } else { meshChunk.Indices.Add(s + 1); meshChunk.Indices.Add(s + 0); meshChunk.Indices.Add(s + 2); } } } } for (int v = 0; v < meshChunk.SectionInfo.VertexCount; v++) { long vertexStartPos = stream.Position; int vertexIncreaseAmount = 0; // Vertex Weights. if (pmo.header.SkeletonOffset != 0 && WeightFormat != CoordinateFormat.NO_VERTEX) { WeightData WeightList = new WeightData(); WeightList.weights = new List <float>(); WeightList.coordFormart = WeightFormat; for (int i = 0; i < (SkinningWeightsCount + 1); i++) { switch (WeightFormat) { case CoordinateFormat.NORMALIZED_8_BITS: WeightList.weights.Add(stream.ReadByte() / 128.0f); break; case CoordinateFormat.NORMALIZED_16_BITS: WeightList.weights.Add(stream.ReadUInt16() / 32768.0f); break; case CoordinateFormat.FLOAT_32_BITS: WeightList.weights.Add(stream.ReadFloat()); break; case CoordinateFormat.NO_VERTEX: break; } } meshChunk.jointWeights.Add(WeightList); } Vector2 currentTexCoord = new Vector2(0, 0); switch (TexCoordFormat) { case CoordinateFormat.NORMALIZED_8_BITS: currentTexCoord.X = stream.ReadByte() / 128.0f; currentTexCoord.Y = stream.ReadByte() / 128.0f; meshChunk.textureCoordinates.Add(currentTexCoord); break; case CoordinateFormat.NORMALIZED_16_BITS: vertexIncreaseAmount = ((0x2 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x1)) & 0x1); stream.Seek(vertexIncreaseAmount, SeekOrigin.Current); currentTexCoord.X = stream.ReadUInt16() / 32768.0f; currentTexCoord.Y = stream.ReadUInt16() / 32768.0f; meshChunk.textureCoordinates.Add(currentTexCoord); break; case CoordinateFormat.FLOAT_32_BITS: vertexIncreaseAmount = ((0x4 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x3)) & 0x3); stream.Seek(vertexIncreaseAmount, SeekOrigin.Current); currentTexCoord.X = stream.ReadFloat(); currentTexCoord.Y = stream.ReadFloat(); meshChunk.textureCoordinates.Add(currentTexCoord); break; case CoordinateFormat.NO_VERTEX: meshChunk.textureCoordinates.Add(currentTexCoord); break; } Vector4 col; if (isColorFlagRisen) { uint c = meshChunk.SectionInfo_opt2.DiffuseColor; col.X = c % 0x100; col.Y = (c >> 8) % 0x100; col.Z = (c >> 16) % 0x100; col.W = (c >> 24) % 0x100; meshChunk.colors.Add(col); } else { switch (ColorFormat) { case Pmo.ColorFormat.NO_COLOR: meshChunk.colors.Add(new Vector4(0xFF, 0xFF, 0xFF, 0xFF)); break; case Pmo.ColorFormat.BGR_5650_16BITS: stream.ReadUInt16(); break; case Pmo.ColorFormat.ABGR_5551_16BITS: stream.ReadUInt16(); break; case Pmo.ColorFormat.ABGR_4444_16BITS: stream.ReadUInt16(); break; case Pmo.ColorFormat.ABGR_8888_32BITS: vertexIncreaseAmount = ((0x4 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x3)) & 0x3); stream.Seek(vertexIncreaseAmount, SeekOrigin.Current); col.X = stream.ReadByte(); col.Y = stream.ReadByte(); col.Z = stream.ReadByte(); col.W = stream.ReadByte(); meshChunk.colors.Add(col); break; } } Vector3 currentVertex; // Handle triangles and triangle strips. switch (VertexPositionFormat) { case CoordinateFormat.NORMALIZED_8_BITS: currentVertex.X = r.ReadSByte() / 128.0f; currentVertex.Y = r.ReadSByte() / 128.0f; currentVertex.Z = r.ReadSByte() / 128.0f; meshChunk.vertices.Add(currentVertex); break; case CoordinateFormat.NORMALIZED_16_BITS: vertexIncreaseAmount = ((0x2 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x1)) & 0x1); stream.Seek(vertexIncreaseAmount, SeekOrigin.Current); currentVertex.X = (float)stream.ReadInt16() / 32768.0f; currentVertex.Y = (float)stream.ReadInt16() / 32768.0f; currentVertex.Z = (float)stream.ReadInt16() / 32768.0f; meshChunk.vertices.Add(currentVertex); break; case CoordinateFormat.FLOAT_32_BITS: vertexIncreaseAmount = ((0x4 - (Convert.ToInt32(stream.Position - vertexStartPos) & 0x3)) & 0x3); stream.Seek(vertexIncreaseAmount, SeekOrigin.Current); currentVertex.X = stream.ReadFloat(); currentVertex.Y = stream.ReadFloat(); currentVertex.Z = stream.ReadFloat(); meshChunk.vertices.Add(currentVertex); break; } stream.Seek(vertexStartPos + meshChunk.SectionInfo.VertexSize, SeekOrigin.Begin); if (flags.Primitive == PrimitiveType.PRIMITIVE_TRIANGLE) { meshChunk.Indices.Add(v); } } VertCnt = meshChunk.SectionInfo.VertexCount; pmo.Meshes.Add(meshChunk); // Find position of next data chunk. stream.Seek(positionAfterHeader + (meshChunk.SectionInfo.VertexCount * meshChunk.SectionInfo.VertexSize), SeekOrigin.Begin); stream.Seek(stream.Position % 4, SeekOrigin.Current); } }