/// <summary> /// Read surface data<para/> /// Чтение поверхностей /// </summary> /// <param name="f">Stream<para/>Поток</param> /// <param name="numGeoms">Surface count<para/>Количество поверхностей</param> void ReadGeometry(BinaryReader f, int numGeoms) { Surfaces = new Geometry[numGeoms]; for (int i = 0; i < numGeoms; i++) { Geometry g = new Geometry(); // Header data // Заголовок ChunkHeader h = ReadHeader(f); if (h.Type != ChunkType.Geometry) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } h = ReadHeader(f); if (h.Type != ChunkType.Struct) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } // Surface flags // Флаги поверхности g.Flags = f.ReadUInt16(); // Texture coordinates // Текстурные координаты int texCoords = f.ReadByte(); if ((g.Flags & (int)GeometryFlags.TextureCoords) > 0) { texCoords = 1; } bool nativeGeom = f.ReadBoolean(); int triCount = f.ReadInt32(); int vertCount = f.ReadInt32(); f.BaseStream.Position += 4; // Skip some vertex data // Пропуск некоторой информации if (h.Version < 0x1003) { f.BaseStream.Position += 12; } if (!nativeGeom) { // Vertex colors // Цвета вершин if ((g.Flags & (int)GeometryFlags.Colors) > 0) { g.Colors = f.ReadBytes(vertCount * 4); for (int ci = 3; ci < g.Colors.Length; ci += 4) { if (g.Colors[ci] < 255) { g.HasAlpha = true; break; } } } // First texcoord set // Первый набор текстурных координат if ((g.Flags & (int)GeometryFlags.TextureCoords) > 0) { g.TextureCoords = new float[vertCount * 2]; for (int c = 0; c < g.TextureCoords.Length; c++) { g.TextureCoords[c] = f.ReadSingle(); } } // Second coord set // Второй набор координат if ((g.Flags & (int)GeometryFlags.SecondTexCoords) > 0) { for (int cd = 0; cd < texCoords; cd++) { float[] coords = new float[vertCount * 2]; for (int c = 0; c < coords.Length; c++) { coords[c] = f.ReadSingle(); } if (cd == 0) { g.TextureCoords = coords; } else if (cd == 1) { g.SecondTextureCoords = coords; } } } // Indices // Вершинные индексы g.Indices = new ushort[triCount * 4]; for (int c = 0; c < g.Indices.Length; c++) { g.Indices[c] = f.ReadUInt16(); } } // Bounding sphere // Сфера для отсечения g.SpherePos = new float[3]; for (int c = 0; c < 3; c++) { g.SpherePos[c] = f.ReadSingle(); } g.SphereRadius = f.ReadSingle(); // Skipping vertex flags // Пропускаем флаги вершин f.BaseStream.Position += 8; if (!nativeGeom) { // Reading vertex positions // Чтение позиций вершин g.Vertices = new float[vertCount * 3]; for (int v = 0; v < g.Vertices.Length; v++) { g.Vertices[v] = f.ReadSingle(); } // Reading normals // Чтение нормалей if ((g.Flags & (int)GeometryFlags.Normals) > 0) { g.Normals = new float[vertCount * 3]; for (int vn = 0; vn < vertCount * 3; vn++) { g.Normals[vn] = f.ReadSingle(); } } } // Reading materials // Чтение материалов h = ReadHeader(f); if (h.Type != ChunkType.MaterialList) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } h = ReadHeader(f); if (h.Type != ChunkType.Struct) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } // Material count // Количество материалов int materialNum = f.ReadInt32(); f.BaseStream.Position += (materialNum * 4); // Reading materials // Чтение материалов ReadMaterials(g, f, materialNum); // Reading extension // Чтение расширения h = ReadHeader(f); if (h.Type != ChunkType.Extension) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } if (h.Size > 0) { int ops = (int)(f.BaseStream.Position + h.Size); while (true) { if (f.BaseStream.Position >= ops) { break; } h = ReadHeader(f); switch (h.Type) { // Binary mesh case ChunkType.BinMesh: int splitMode = f.ReadInt32(); if (splitMode != 0 && splitMode != 1) { throw new Exception("[ModelFile] Unknown splitting mode: " + splitMode); } int numSplits = f.ReadInt32(); f.BaseStream.Position += 4; g.Binary = new BinaryMesh[numSplits]; bool hasData = h.Size > 12 + numSplits * 8; for (int sp = 0; sp < numSplits; sp++) { int numInds = f.ReadInt32(); int matIndex = f.ReadInt32(); BinaryMesh bn = new BinaryMesh(); bn.BinaryMaterial = g.Materials[matIndex]; bn.Mode = (SplitMode)splitMode; if (hasData) { bn.Indices = new ushort[numInds]; for (int vr = 0; vr < numInds; vr++) { bn.Indices[vr] = (ushort)f.ReadUInt32(); } } g.Binary[sp] = bn; } break; // Skinning info // Данные скиннинга case ChunkType.Skin: // Number of bones // Количество костей g.BoneCount = f.ReadByte(); int usedBones = f.ReadByte(); // Number of bones per single vertex // Количество костей на одну вершинуы g.MaxBonesPerVertex = f.ReadByte(); f.BaseStream.Position += 1; if (usedBones > 0) { f.BaseStream.Position += usedBones; } // Indices and weights // Индексы и веса int numVerts = g.Vertices.Length / 3; g.Bones = f.ReadBytes(numVerts * 4); g.Weights = new float[numVerts * 4]; for (int b = 0; b < g.Weights.Length; b++) { g.Weights[b] = f.ReadSingle(); } // Skip matrices - we build our own // Пропускаем матрицы - мы построим свои if (g.MaxBonesPerVertex == 0) { f.BaseStream.Position += 4 * g.BoneCount; } f.BaseStream.Position += g.BoneCount * 64; // Skipping zeroes // Пропуск нескольких нулей if (usedBones > 0) { f.BaseStream.Position += 12; } break; default: f.BaseStream.Position += h.Size; break; } } } Surfaces[i] = g; } }
/// <summary> /// Read surface data<para/> /// Чтение поверхностей /// </summary> /// <param name="f">Stream<para/>Поток</param> /// <param name="numGeoms">Surface count<para/>Количество поверхностей</param> void ReadGeometry(BinaryReader f, int numGeoms) { Surfaces = new Geometry[numGeoms]; for (int i = 0; i < numGeoms; i++) { Geometry g = new Geometry(); // Header data // Заголовок ChunkHeader h = ReadHeader(f); if (h.Type != ChunkType.Geometry) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } h = ReadHeader(f); if (h.Type != ChunkType.Struct) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } // Surface flags // Флаги поверхности g.Flags = f.ReadUInt16(); // Texture coordinates // Текстурные координаты int texCoords = f.ReadByte(); if ((g.Flags & (int)GeometryFlags.TextureCoords) > 0) { texCoords = 1; } bool nativeGeom = f.ReadBoolean(); int triCount = f.ReadInt32(); int vertCount = f.ReadInt32(); f.BaseStream.Position += 4; // Skip some vertex data // Пропуск некоторой информации if (h.Version<0x1003) { f.BaseStream.Position += 12; } if (!nativeGeom) { // Vertex colors // Цвета вершин if ((g.Flags & (int)GeometryFlags.Colors) > 0) { g.Colors = f.ReadBytes(vertCount * 4); for (int ci = 3; ci < g.Colors.Length; ci+=4) { if (g.Colors[ci]<255) { g.HasAlpha = true; break; } } } // First texcoord set // Первый набор текстурных координат if ((g.Flags & (int)GeometryFlags.TextureCoords) > 0) { g.TextureCoords = new float[vertCount * 2]; for (int c = 0; c < g.TextureCoords.Length; c++) { g.TextureCoords[c] = f.ReadSingle(); } } // Second coord set // Второй набор координат if ((g.Flags & (int)GeometryFlags.SecondTexCoords) > 0) { for (int cd = 0; cd < texCoords; cd++) { float[] coords = new float[vertCount * 2]; for (int c = 0; c < coords.Length; c++) { coords[c] = f.ReadSingle(); } if (cd == 0) { g.TextureCoords = coords; } else if (cd == 1) { g.SecondTextureCoords = coords; } } } // Indices // Вершинные индексы g.Indices = new ushort[triCount * 4]; for (int c = 0; c < g.Indices.Length; c++) { g.Indices[c] = f.ReadUInt16(); } } // Bounding sphere // Сфера для отсечения g.SpherePos = new float[3]; for (int c = 0; c < 3; c++) { g.SpherePos[c] = f.ReadSingle(); } g.SphereRadius = f.ReadSingle(); // Skipping vertex flags // Пропускаем флаги вершин f.BaseStream.Position += 8; if (!nativeGeom) { // Reading vertex positions // Чтение позиций вершин g.Vertices = new float[vertCount * 3]; for (int v = 0; v < g.Vertices.Length; v++) { g.Vertices[v] = f.ReadSingle(); } // Reading normals // Чтение нормалей if ((g.Flags & (int)GeometryFlags.Normals) > 0) { g.Normals = new float[vertCount * 3]; for (int vn = 0; vn < vertCount * 3; vn++) { g.Normals[vn] = f.ReadSingle(); } } } // Reading materials // Чтение материалов h = ReadHeader(f); if (h.Type != ChunkType.MaterialList) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } h = ReadHeader(f); if (h.Type != ChunkType.Struct) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } // Material count // Количество материалов int materialNum = f.ReadInt32(); f.BaseStream.Position += (materialNum * 4); // Reading materials // Чтение материалов ReadMaterials(g, f, materialNum); // Reading extension // Чтение расширения h = ReadHeader(f); if (h.Type != ChunkType.Extension) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } if (h.Size > 0) { int ops = (int)(f.BaseStream.Position + h.Size); while (true) { if (f.BaseStream.Position >= ops) { break; } h = ReadHeader(f); switch (h.Type) { // Binary mesh case ChunkType.BinMesh: int splitMode = f.ReadInt32(); if (splitMode != 0 && splitMode != 1) { throw new Exception("[ModelFile] Unknown splitting mode: " + splitMode); } int numSplits = f.ReadInt32(); f.BaseStream.Position += 4; g.Binary = new BinaryMesh[numSplits]; bool hasData = h.Size > 12 + numSplits * 8; for (int sp = 0; sp < numSplits; sp++) { int numInds = f.ReadInt32(); int matIndex = f.ReadInt32(); BinaryMesh bn = new BinaryMesh(); bn.BinaryMaterial = g.Materials[matIndex]; bn.Mode = (SplitMode)splitMode; if (hasData) { bn.Indices = new ushort[numInds]; for (int vr = 0; vr < numInds; vr++) { bn.Indices[vr] = (ushort)f.ReadUInt32(); } } g.Binary[sp] = bn; } break; // Skinning info // Данные скиннинга case ChunkType.Skin: // Number of bones // Количество костей g.BoneCount = f.ReadByte(); int usedBones = f.ReadByte(); // Number of bones per single vertex // Количество костей на одну вершинуы g.MaxBonesPerVertex = f.ReadByte(); f.BaseStream.Position += 1; if (usedBones>0) { f.BaseStream.Position += usedBones; } // Indices and weights // Индексы и веса int numVerts = g.Vertices.Length / 3; g.Bones = f.ReadBytes(numVerts * 4); g.Weights = new float[numVerts * 4]; for (int b = 0; b < g.Weights.Length; b++) { g.Weights[b] = f.ReadSingle(); } // Skip matrices - we build our own // Пропускаем матрицы - мы построим свои if (g.MaxBonesPerVertex == 0) { f.BaseStream.Position += 4 * g.BoneCount; } f.BaseStream.Position += g.BoneCount * 64; // Skipping zeroes // Пропуск нескольких нулей if (usedBones>0) { f.BaseStream.Position += 12; } break; default: f.BaseStream.Position += h.Size; break; } } } Surfaces[i] = g; } }