internal override void WriteTypeData(BinaryWriterEx bw) { bw.WriteVector3(Position); bw.WriteSingle(Degree); }
private protected override void WriteTypeData(BinaryWriterEx bw) { bw.WriteVector3(Position); bw.WriteSingle(Degree); }
internal void Write(BinaryWriterEx bw, List <LayoutMember> layout, float uvFactor) { foreach (LayoutMember member in layout) { if (member.Semantic == LayoutSemantic.Position) { if (member.Type == LayoutType.Float3) { bw.WriteVector3(Position); } else if (member.Type == LayoutType.Float4) { bw.WriteVector3(Position); bw.WriteSingle(0); } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else if (member.Semantic == LayoutSemantic.BoneWeights) { if (member.Type == LayoutType.Byte4A) { for (int i = 0; i < 4; i++) { bw.WriteSByte((sbyte)Math.Round(BoneWeights[i] * 127)); } } else if (member.Type == LayoutType.Byte4C) { for (int i = 0; i < 4; i++) { bw.WriteByte((byte)Math.Round(BoneWeights[i] * 255)); } } else if (member.Type == LayoutType.UVPair) { for (int i = 0; i < 4; i++) { bw.WriteInt16((short)Math.Round(BoneWeights[i] * 32767)); } } else if (member.Type == LayoutType.Short4toFloat4A) { for (int i = 0; i < 4; i++) { bw.WriteInt16((short)Math.Round(BoneWeights[i] * 32767)); } } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else if (member.Semantic == LayoutSemantic.BoneIndices) { if (member.Type == LayoutType.Byte4B) { for (int i = 0; i < 4; i++) { bw.WriteByte((byte)BoneIndices[i]); } } else if (member.Type == LayoutType.ShortBoneIndices) { for (int i = 0; i < 4; i++) { bw.WriteUInt16((ushort)BoneIndices[i]); } } else if (member.Type == LayoutType.Byte4E) { for (int i = 0; i < 4; i++) { bw.WriteByte((byte)BoneIndices[i]); } } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else if (member.Semantic == LayoutSemantic.Normal) { if (member.Type == LayoutType.Float3) { bw.WriteVector3(Normal); } else if (member.Type == LayoutType.Float4) { bw.WriteVector3(Normal); bw.WriteSingle(NormalW); } else if (member.Type == LayoutType.Byte4A) { WriteByteNormXYZ(bw, Normal); bw.WriteByte((byte)NormalW); } else if (member.Type == LayoutType.Byte4B) { WriteByteNormXYZ(bw, Normal); bw.WriteByte((byte)NormalW); } else if (member.Type == LayoutType.Short2toFloat2) { bw.WriteByte((byte)NormalW); WriteSByteNormZYX(bw, Normal); } else if (member.Type == LayoutType.Byte4C) { WriteByteNormXYZ(bw, Normal); bw.WriteByte((byte)NormalW); } else if (member.Type == LayoutType.Short4toFloat4A) { WriteShortNormXYZ(bw, Normal); bw.WriteInt16((short)NormalW); } else if (member.Type == LayoutType.Short4toFloat4B) { WriteUShortNormXYZ(bw, Normal); bw.WriteInt16((short)NormalW); } else if (member.Type == LayoutType.Byte4E) { WriteByteNormXYZ(bw, Normal); bw.WriteByte((byte)NormalW); } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else if (member.Semantic == LayoutSemantic.UV) { Vector3 uv = uvQueue.Dequeue() * uvFactor; if (member.Type == LayoutType.Float2) { bw.WriteSingle(uv.X); bw.WriteSingle(uv.Y); } else if (member.Type == LayoutType.Float3) { bw.WriteVector3(uv); } else if (member.Type == LayoutType.Float4) { bw.WriteSingle(uv.X); bw.WriteSingle(uv.Y); uv = uvQueue.Dequeue() * uvFactor; bw.WriteSingle(uv.X); bw.WriteSingle(uv.Y); } else if (member.Type == LayoutType.Byte4A) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == LayoutType.Byte4B) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == LayoutType.Short2toFloat2) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == LayoutType.Byte4C) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == LayoutType.UV) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == LayoutType.UVPair) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); uv = uvQueue.Dequeue() * uvFactor; bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == LayoutType.Short4toFloat4B) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); bw.WriteInt16((short)Math.Round(uv.Z)); bw.WriteInt16(0); } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else if (member.Semantic == LayoutSemantic.Tangent) { Vector4 tangent = tangentQueue.Dequeue(); if (member.Type == LayoutType.Float4) { bw.WriteVector4(tangent); } else if (member.Type == LayoutType.Byte4A) { WriteByteNormXYZW(bw, tangent); } else if (member.Type == LayoutType.Byte4B) { WriteByteNormXYZW(bw, tangent); } else if (member.Type == LayoutType.Byte4C) { WriteByteNormXYZW(bw, tangent); } else if (member.Type == LayoutType.Short4toFloat4A) { WriteShortNormXYZW(bw, tangent); } else if (member.Type == LayoutType.Byte4E) { WriteByteNormXYZW(bw, tangent); } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else if (member.Semantic == LayoutSemantic.Bitangent) { if (member.Type == LayoutType.Byte4A) { WriteByteNormXYZW(bw, Bitangent); } else if (member.Type == LayoutType.Byte4B) { WriteByteNormXYZW(bw, Bitangent); } else if (member.Type == LayoutType.Byte4C) { WriteByteNormXYZW(bw, Bitangent); } else if (member.Type == LayoutType.Byte4E) { WriteByteNormXYZW(bw, Bitangent); } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else if (member.Semantic == LayoutSemantic.VertexColor) { VertexColor color = colorQueue.Dequeue(); if (member.Type == LayoutType.Float4) { color.WriteFloatRGBA(bw); } else if (member.Type == LayoutType.Byte4A) { color.WriteByteRGBA(bw); } else if (member.Type == LayoutType.Byte4C) { color.WriteByteRGBA(bw); } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } else { throw new NotImplementedException($"Write not implemented for {member.Type} {member.Semantic}."); } } }
internal override void Write(BinaryWriterEx bw, int id) { long start = bw.Position; bw.ReserveInt64("NameOffset"); bw.WriteUInt32((uint)Type); bw.WriteInt32(id); bw.WriteUInt32((uint)Shape.Type); bw.WriteVector3(Position); bw.WriteVector3(Rotation); bw.WriteInt32(Unk2C); bw.ReserveInt64("BaseDataOffset1"); bw.ReserveInt64("BaseDataOffset2"); bw.WriteInt32(-1); bw.WriteUInt32(MapStudioLayer); bw.ReserveInt64("ShapeDataOffset"); bw.ReserveInt64("BaseDataOffset3"); bw.ReserveInt64("TypeDataOffset"); bw.FillInt64("NameOffset", bw.Position - start); bw.WriteUTF16(MSB.ReambiguateName(Name), true); bw.Pad(4); bw.FillInt64("BaseDataOffset1", bw.Position - start); bw.WriteInt16((short)UnkA.Count); bw.WriteInt16s(UnkA); bw.Pad(4); bw.FillInt64("BaseDataOffset2", bw.Position - start); bw.WriteInt16((short)UnkB.Count); bw.WriteInt16s(UnkB); bw.Pad(8); if (Shape.HasShapeData) { bw.FillInt64("ShapeDataOffset", bw.Position - start); Shape.WriteShapeData(bw); } else { bw.FillInt64("ShapeDataOffset", 0); } bw.FillInt64("BaseDataOffset3", bw.Position - start); bw.WriteInt32(ActivationPartIndex); bw.WriteInt32(EntityID); if (HasTypeData) { if (Type == RegionType.Region23 || Type == RegionType.PartsGroup || Type == RegionType.AutoDrawGroup) { bw.Pad(8); } bw.FillInt64("TypeDataOffset", bw.Position - start); WriteTypeData(bw); } else { bw.FillInt64("TypeDataOffset", 0); } bw.Pad(8); }
internal void Write(BinaryWriterEx bw, BufferLayout layout, int vertexSize, int version) { var tangentQueue = new Queue <Vector4>(Tangents); var colorQueue = new Queue <Color>(Colors); var uvQueue = new Queue <Vector3>(UVs); float uvFactor = 1024; foreach (BufferLayout.Member member in layout) { switch (member.Semantic) { case BufferLayout.MemberSemantic.Position: if (member.Type == BufferLayout.MemberType.Float3) { bw.WriteVector3(Position); } else { throw new NotImplementedException(); } break; case BufferLayout.MemberSemantic.BoneWeights: if (member.Type == BufferLayout.MemberType.Byte4C) { for (int i = 0; i < 4; i++) { bw.WriteSByte((sbyte)Math.Round(BoneWeights[i] * sbyte.MaxValue)); } } else if (member.Type == BufferLayout.MemberType.Short4toFloat4A) { for (int i = 0; i < 4; i++) { bw.WriteInt16((short)Math.Round(BoneWeights[i] * short.MaxValue)); } } else { throw new NotImplementedException(); } break; case BufferLayout.MemberSemantic.BoneIndices: if (member.Type == BufferLayout.MemberType.Byte4B) { for (int i = 0; i < 4; i++) { bw.WriteByte((byte)BoneIndices[i]); } } else if (member.Type == BufferLayout.MemberType.ShortBoneIndices) { for (int i = 0; i < 4; i++) { bw.WriteUInt16((ushort)BoneIndices[i]); } } else if (member.Type == BufferLayout.MemberType.Byte4E) { for (int i = 0; i < 4; i++) { bw.WriteByte((byte)BoneIndices[i]); } } else { throw new NotImplementedException(); } break; case BufferLayout.MemberSemantic.Normal: if (member.Type == BufferLayout.MemberType.Float4) { bw.WriteVector4(Normal); } else if (member.Type == BufferLayout.MemberType.Byte4A) { bw.WriteByte((byte)Math.Round(Normal.X * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.Y * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.Z * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.W * 127 + 127)); } else if (member.Type == BufferLayout.MemberType.Byte4B) { bw.WriteByte((byte)Math.Round(Normal.X * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.Y * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.Z * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.W * 127 + 127)); } else if (member.Type == BufferLayout.MemberType.Byte4C) { bw.WriteByte((byte)Math.Round(Normal.X * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.Y * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.Z * 127 + 127)); bw.WriteByte((byte)Math.Round(Normal.W * 127 + 127)); } else if (member.Type == BufferLayout.MemberType.Short4toFloat4B) { bw.WriteInt16((short)Math.Round(Normal.X * 32767 + 32767)); bw.WriteInt16((short)Math.Round(Normal.Y * 32767 + 32767)); bw.WriteInt16((short)Math.Round(Normal.Z * 32767 + 32767)); bw.WriteInt16((short)Math.Round(Normal.W * 32767 + 32767)); } else { throw new NotImplementedException(); } break; case BufferLayout.MemberSemantic.UV: Vector3 uv = uvQueue.Dequeue() * uvFactor; if (member.Type == BufferLayout.MemberType.Float2) { bw.WriteSingle(uv.X); bw.WriteSingle(uv.Y); } else if (member.Type == BufferLayout.MemberType.Float3) { bw.WriteVector3(uv); } else if (member.Type == BufferLayout.MemberType.Byte4A) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == BufferLayout.MemberType.Byte4B) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == BufferLayout.MemberType.Short2toFloat2) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == BufferLayout.MemberType.Byte4C) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == BufferLayout.MemberType.UV) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else if (member.Type == BufferLayout.MemberType.UVPair) { bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); uv = uvQueue.Dequeue() * uvFactor; bw.WriteInt16((short)Math.Round(uv.X)); bw.WriteInt16((short)Math.Round(uv.Y)); } else { throw new NotImplementedException(); } break; case BufferLayout.MemberSemantic.Tangent: Vector4 tangent = tangentQueue.Dequeue(); if (member.Type == BufferLayout.MemberType.Byte4A) { bw.WriteByte((byte)Math.Round(tangent.X * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.Y * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.Z * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.W * 127 + 127)); } else if (member.Type == BufferLayout.MemberType.Byte4B) { bw.WriteByte((byte)Math.Round(tangent.X * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.Y * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.Z * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.W * 127 + 127)); } else if (member.Type == BufferLayout.MemberType.Byte4C) { bw.WriteByte((byte)Math.Round(tangent.X * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.Y * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.Z * 127 + 127)); bw.WriteByte((byte)Math.Round(tangent.W * 127 + 127)); } else { throw new NotImplementedException(); } break; case BufferLayout.MemberSemantic.UnknownVector4A: if (member.Type == BufferLayout.MemberType.Byte4B) { bw.WriteBytes(UnknownVector4); } else if (member.Type == BufferLayout.MemberType.Byte4C) { bw.WriteBytes(UnknownVector4); } else { throw new NotImplementedException(); } break; case BufferLayout.MemberSemantic.VertexColor: Color color = colorQueue.Dequeue(); if (member.Type == BufferLayout.MemberType.Float4) { bw.WriteSingle(color.R); bw.WriteSingle(color.G); bw.WriteSingle(color.B); bw.WriteSingle(color.A); } else if (member.Type == BufferLayout.MemberType.Byte4A) { bw.WriteByte((byte)Math.Round(color.A * 255)); bw.WriteByte((byte)Math.Round(color.R * 255)); bw.WriteByte((byte)Math.Round(color.G * 255)); bw.WriteByte((byte)Math.Round(color.B * 255)); } else if (member.Type == BufferLayout.MemberType.Byte4C) { bw.WriteByte((byte)Math.Round(color.R * 255)); bw.WriteByte((byte)Math.Round(color.G * 255)); bw.WriteByte((byte)Math.Round(color.B * 255)); bw.WriteByte((byte)Math.Round(color.A * 255)); } else { throw new NotImplementedException(); } break; default: throw new NotImplementedException(); } } }
/// <summary> /// Writes FLVER data to a BinaryWriterEx. /// </summary> internal override void Write(BinaryWriterEx bw) { bw.BigEndian = Header.BigEndian; bw.WriteASCII("FLVER\0"); bw.WriteASCII(Header.BigEndian ? "B\0" : "L\0"); bw.WriteInt32(Header.Version); bw.ReserveInt32("DataOffset"); bw.ReserveInt32("DataSize"); bw.WriteInt32(Dummies.Count); bw.WriteInt32(Materials.Count); bw.WriteInt32(Bones.Count); bw.WriteInt32(Meshes.Count); bw.WriteInt32(Meshes.Sum(m => m.VertexBuffers.Count)); bw.WriteVector3(Header.BoundingBoxMin); bw.WriteVector3(Header.BoundingBoxMax); int trueFaceCount = 0; int totalFaceCount = 0; foreach (Mesh mesh in Meshes) { bool allowPrimitiveRestarts = mesh.Vertices.Count < ushort.MaxValue; foreach (FaceSet faceSet in mesh.FaceSets) { faceSet.AddFaceCounts(allowPrimitiveRestarts, ref trueFaceCount, ref totalFaceCount); } } bw.WriteInt32(trueFaceCount); bw.WriteInt32(totalFaceCount); byte vertexIndicesSize = 0; if (Header.Version < 0x20013) { vertexIndicesSize = 16; foreach (Mesh mesh in Meshes) { foreach (FaceSet fs in mesh.FaceSets) { vertexIndicesSize = (byte)Math.Max(vertexIndicesSize, fs.GetVertexIndexSize()); } } } bw.WriteByte(vertexIndicesSize); bw.WriteBoolean(Header.Unicode); bw.WriteBoolean(Header.Unk4A); bw.WriteByte(0); bw.WriteInt32(Header.Unk4C); bw.WriteInt32(Meshes.Sum(m => m.FaceSets.Count)); bw.WriteInt32(BufferLayouts.Count); bw.WriteInt32(Materials.Sum(m => m.Textures.Count)); bw.WriteByte(Header.Unk5C); bw.WriteByte(Header.Unk5D); bw.WriteByte(0); bw.WriteByte(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(Header.Unk68); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); foreach (Dummy dummy in Dummies) { dummy.Write(bw, Header); } for (int i = 0; i < Materials.Count; i++) { Materials[i].Write(bw, i); } for (int i = 0; i < Bones.Count; i++) { Bones[i].Write(bw, i); } for (int i = 0; i < Meshes.Count; i++) { Meshes[i].Write(bw, i); } int faceSetIndex = 0; foreach (Mesh mesh in Meshes) { for (int i = 0; i < mesh.FaceSets.Count; i++) { int indexSize = vertexIndicesSize; if (indexSize == 0) { indexSize = mesh.FaceSets[i].GetVertexIndexSize(); } mesh.FaceSets[i].Write(bw, Header, indexSize, faceSetIndex + i); } faceSetIndex += mesh.FaceSets.Count; } int vertexBufferIndex = 0; foreach (Mesh mesh in Meshes) { for (int i = 0; i < mesh.VertexBuffers.Count; i++) { mesh.VertexBuffers[i].Write(bw, Header, vertexBufferIndex + i, i, BufferLayouts, mesh.Vertices.Count); } vertexBufferIndex += mesh.VertexBuffers.Count; } for (int i = 0; i < BufferLayouts.Count; i++) { BufferLayouts[i].Write(bw, i); } int textureIndex = 0; for (int i = 0; i < Materials.Count; i++) { Materials[i].WriteTextures(bw, i, textureIndex); textureIndex += Materials[i].Textures.Count; } if (Header.Version >= 0x2001A) { SekiroUnk.Write(bw); } bw.Pad(0x10); for (int i = 0; i < BufferLayouts.Count; i++) { BufferLayouts[i].WriteMembers(bw, i); } bw.Pad(0x10); for (int i = 0; i < Meshes.Count; i++) { Meshes[i].WriteBoundingBox(bw, i, Header); } bw.Pad(0x10); int boneIndicesStart = (int)bw.Position; for (int i = 0; i < Meshes.Count; i++) { Meshes[i].WriteBoneIndices(bw, i, boneIndicesStart); } bw.Pad(0x10); faceSetIndex = 0; for (int i = 0; i < Meshes.Count; i++) { bw.FillInt32($"MeshFaceSetIndices{i}", (int)bw.Position); for (int j = 0; j < Meshes[i].FaceSets.Count; j++) { bw.WriteInt32(faceSetIndex + j); } faceSetIndex += Meshes[i].FaceSets.Count; } bw.Pad(0x10); vertexBufferIndex = 0; for (int i = 0; i < Meshes.Count; i++) { bw.FillInt32($"MeshVertexBufferIndices{i}", (int)bw.Position); for (int j = 0; j < Meshes[i].VertexBuffers.Count; j++) { bw.WriteInt32(vertexBufferIndex + j); } vertexBufferIndex += Meshes[i].VertexBuffers.Count; } bw.Pad(0x10); var gxOffsets = new List <int>(); foreach (GXList gxList in GXLists) { gxOffsets.Add((int)bw.Position); gxList.Write(bw, Header); } for (int i = 0; i < Materials.Count; i++) { Materials[i].FillGXOffset(bw, i, gxOffsets); } bw.Pad(0x10); textureIndex = 0; for (int i = 0; i < Materials.Count; i++) { Material material = Materials[i]; material.WriteStrings(bw, Header, i); for (int j = 0; j < material.Textures.Count; j++) { material.Textures[j].WriteStrings(bw, Header, textureIndex + j); } textureIndex += material.Textures.Count; } bw.Pad(0x10); for (int i = 0; i < Bones.Count; i++) { Bones[i].WriteStrings(bw, Header, i); } int alignment = Header.Version <= 0x2000E ? 0x20 : 0x10; bw.Pad(alignment); if (Header.Version == 0x2000F || Header.Version == 0x20010) { bw.Pad(0x20); } int dataStart = (int)bw.Position; bw.FillInt32("DataOffset", dataStart); faceSetIndex = 0; vertexBufferIndex = 0; for (int i = 0; i < Meshes.Count; i++) { Mesh mesh = Meshes[i]; for (int j = 0; j < mesh.FaceSets.Count; j++) { int indexSize = vertexIndicesSize; if (indexSize == 0) { indexSize = mesh.FaceSets[j].GetVertexIndexSize(); } bw.Pad(alignment); mesh.FaceSets[j].WriteVertices(bw, indexSize, faceSetIndex + j, dataStart); } faceSetIndex += mesh.FaceSets.Count; foreach (FLVER.Vertex vertex in mesh.Vertices) { vertex.PrepareWrite(); } for (int j = 0; j < mesh.VertexBuffers.Count; j++) { bw.Pad(alignment); mesh.VertexBuffers[j].WriteBuffer(bw, vertexBufferIndex + j, BufferLayouts, mesh.Vertices, dataStart, Header); } foreach (FLVER.Vertex vertex in mesh.Vertices) { vertex.FinishWrite(); } vertexBufferIndex += mesh.VertexBuffers.Count; } bw.Pad(alignment); bw.FillInt32("DataSize", (int)bw.Position - dataStart); if (Header.Version == 0x2000F || Header.Version == 0x20010) { bw.Pad(0x20); } }
internal override void Write(BinaryWriterEx bw, int id) { base.Write(bw, id); bw.WriteVector3(Position); bw.WriteSingle(Degree); }
internal override void Write(BinaryWriterEx bw) { bw.BigEndian = BigEndian; bw.WriteInt32(1); bw.WriteInt32(Vertices.Count); bw.ReserveInt32("VertexOffset"); bw.WriteInt32(Triangles.Count); bw.ReserveInt32("TriangleOffset"); bw.ReserveInt32("RootBoxOffset"); bw.WriteInt32(0); bw.WriteInt32(Entities.Count); bw.ReserveInt32("EntityOffset"); for (int i = 0; i < 23; i++) { bw.WriteInt32(0); } bw.FillInt32("VertexOffset", (int)bw.Position); foreach (Vector3 vertex in Vertices) { bw.WriteVector3(vertex); } bw.FillInt32("TriangleOffset", (int)bw.Position); foreach (Triangle triangle in Triangles) { triangle.Write(bw); } var boxTriangleIndexOffsets = new Queue <int>(); void WriteBoxTriangleIndices(Box box) { if (box == null) { return; } WriteBoxTriangleIndices(box.ChildBox1); WriteBoxTriangleIndices(box.ChildBox2); WriteBoxTriangleIndices(box.ChildBox3); WriteBoxTriangleIndices(box.ChildBox4); if (box.TriangleIndices.Count == 0) { boxTriangleIndexOffsets.Enqueue(0); } else { boxTriangleIndexOffsets.Enqueue((int)bw.Position); bw.WriteInt32s(box.TriangleIndices); } } WriteBoxTriangleIndices(RootBox); int rootBoxOffset = RootBox.Write(bw, boxTriangleIndexOffsets); bw.FillInt32("RootBoxOffset", rootBoxOffset); var entityTriangleIndexOffsets = new List <int>(); foreach (Entity entity in Entities) { entityTriangleIndexOffsets.Add((int)bw.Position); bw.WriteInt32s(entity.TriangleIndices); } bw.FillInt32("EntityOffset", (int)bw.Position); for (int i = 0; i < Entities.Count; i++) { Entities[i].Write(bw, entityTriangleIndexOffsets[i]); } }