public static void EvaluateNormals(ModelMeshData mesh) { Vector3[] normals = new Vector3[mesh.Vertices.Count]; for (int i = 0; i != mesh.Vertices.Count; ++i ) normals[i] = Vector3.Zero; Action<int, int, int, int> ProcessTriangle = delegate(int partIdx, int idx1, int idx2, int idx3) { List<short> indices = mesh.MeshParts[partIdx].Indices; int[] idx = { indices[idx1], indices[idx2], indices[idx3] }; Vector3 side1 = mesh.Vertices[idx[1]] - mesh.Vertices[idx[0]]; Vector3 side2 = mesh.Vertices[idx[2]] - mesh.Vertices[idx[0]]; Vector3 norm = Vector3.Cross(side1, side2); norm.Normalize(); for (int i = 0 ; i != 3; ++i) normals[idx[i]] += norm; }; if (mesh.TriangleStrip) for (int j = 0; j != mesh.MeshParts.Count; ++j ) for (int i = 2; i < mesh.MeshParts[j].Indices.Count; ++i) ProcessTriangle(j, i - 2, i - 1, i); else for (int j = 0; j != mesh.MeshParts.Count; ++j) for (int i = 0; i < mesh.MeshParts[j].Indices.Count; i += 3) ProcessTriangle(j, i, i + 1, i + 2); for (int i = 0; i != normals.Length; ++i) { normals[i].Normalize(); normals[i] = normals[i]; } mesh.Normals = new List<Vector3>(normals); }
private void ParseStringSection(int size, SectionType parentType) { byte[] data = input.ReadBytes(size); if (parentType == SectionType.Texture) { int len = Array.IndexOf(data, (byte)0); if (len == -1) { len = data.Length; } if (len == 0) { return; } string textureName = Encoding.ASCII.GetString(data, 0, len); if (texturesFolder != null) { Texture2D texture = TexturesStorage.Instance.GetTexture(textureName, texturesFolder); if (texture != null) { ModelMeshData mesh = modelData.Meshes[modelData.Meshes.Count - 1]; Material material = mesh.Materials[mesh.Materials.Count - 1]; material.Texture = texture; } } } }
/// <summary> /// Обрабатывает секцию MaterialSplit (она же Bin Mesh PLG) /// Насколько я понял, эта секция нужна, когда отдельным кускам меша присваивается разный метариел (текстура), /// либо когда треугольники перечисляются не в виде TriangleList, а в виде TriangleStrip. /// /// Перезаписывает данные в последнем добавленном меше в modelData /// </summary> private void ParseMaterialSplit(int sectionSize) { int sectionEnd = (int)input.BaseStream.Position + sectionSize; // ParseMaterialSplit вызывается всегда после ParseGeometry, которая добавляет новый mesh в modelData ModelMeshData mesh = modelData.Meshes[modelData.Meshes.Count - 1]; int triangleStrip = input.ReadInt32(); int splitCount = input.ReadInt32(); mesh.SumIndicesCount = input.ReadInt32(); mesh.TriangleStrip = triangleStrip != 0; mesh.MeshParts.Clear(); // перезаписываем данные о треугольниках for (int j = 0; j < splitCount; ++j) { int localIndicesCount = input.ReadInt32(); int materialIdx = input.ReadInt32(); ModelMeshPartData meshPart = new ModelMeshPartData(localIndicesCount, materialIdx); for (int i = 0; i != localIndicesCount; ++i) { meshPart.Indices.Add((short)input.ReadInt32()); } mesh.MeshParts.Add(meshPart); } }
private void FillColors(ModelMeshData mesh, int verticesCount) { mesh.Colors = new List <Color>(verticesCount); for (int i = 0; i != verticesCount; ++i) { mesh.Colors.Add(new Color(0, 0, 0, 1)); } }
private static VertexBuffer CreateTexturedVertexBuffer(ModelMeshData mesh) { var vertices = new VertexPositionColorTexture[mesh.Vertices.Count]; for (var i = 0; i != mesh.Vertices.Count; ++i) vertices[i] = new VertexPositionColorTexture(mesh.Vertices[i], mesh.Colors[i], mesh.TextureCoords[i]); VertexBuffer vertexBuffer = new VertexBuffer(GraphicsDeviceHolder.Device, mesh.Vertices.Count * VertexPositionColorTexture.SizeInBytes, BufferUsage.WriteOnly); vertexBuffer.SetData(vertices); return vertexBuffer; }
private void ReadTextureCoords(ModelMeshData mesh, int verticesCount) { mesh.TextureCoords = new List <Vector2>(verticesCount); for (var i = 0; i != verticesCount; ++i) { float x = input.ReadSingle(); float y = input.ReadSingle(); mesh.TextureCoords.Add(new Vector2(x, y)); } }
private void ReadNormals(ModelMeshData mesh, int verticesCount) { mesh.Normals = new List <Vector3>(verticesCount); for (var i = 0; i != verticesCount; ++i) { var x = input.ReadSingle(); var y = input.ReadSingle(); // y and z coords are exchanged because of different coordinate system var z = input.ReadSingle(); mesh.Normals.Add(new Vector3(x, z, -y)); } }
private void ReadColors(ModelMeshData mesh, int verticesCount) { mesh.Colors = new List <Color>(verticesCount); for (int i = 0; i != verticesCount; ++i) { byte r = input.ReadByte(); byte g = input.ReadByte(); byte b = input.ReadByte(); byte a = input.ReadByte(); mesh.Colors.Add(new Color(r, g, b, a)); } }
/// <summary> /// Обрабатывает секцию Geometry /// Считывает вершины, треугольники (индексы вершин), нормали, текстурные координаты. /// </summary> private void ParseGeometry(DffVersion version) { ModelMeshData mesh = new ModelMeshData(); GeometrySectionFlags flags = (GeometrySectionFlags)input.ReadInt16(); input.BaseStream.Seek(sizeof(short), SeekOrigin.Current); // unknown int trianglesCount = input.ReadInt32(); int verticesCount = input.ReadInt32(); input.BaseStream.Seek(sizeof(int), SeekOrigin.Current); // morphTargetCount aka frameCount if (version < DffVersion.GTA_VC_2) { // geometry has lighting data. Ignoring it. // TODO :: we can use it for rendering!!! input.BaseStream.Seek(12, SeekOrigin.Current); } if ((flags & GeometrySectionFlags.MultipleTextureCoords) != GeometrySectionFlags.None) { Log.Instance.Print("Multiple TexCoords sets are provided but used only the first of it!", MessageType.Warning); } if ((flags & GeometrySectionFlags.HasColorInfo) != GeometrySectionFlags.None) { ReadColors(mesh, verticesCount); } else { FillColors(mesh, verticesCount); } if ((flags & GeometrySectionFlags.HasTextureCoords) != GeometrySectionFlags.None) { ReadTextureCoords(mesh, verticesCount); } ReadTriangles(mesh, trianglesCount); input.BaseStream.Seek(4 * sizeof(float), SeekOrigin.Current); // ignoring bounding sphere (x, y, z, radius) input.BaseStream.Seek(2 * sizeof(int), SeekOrigin.Current); // hasPosition, hasNormal (not used) ReadVertices(mesh, verticesCount); if ((flags & GeometrySectionFlags.HasNormalsInfo) != GeometrySectionFlags.None) { ReadNormals(mesh, verticesCount); } modelData.Meshes.Add(mesh); }
private static VertexBuffer CreateColoredVertexBuffer(ModelMeshData mesh) { var vertices = new VertexPositionColor[mesh.Vertices.Count]; for (var i = 0; i != mesh.Vertices.Count; ++i) { vertices[i] = new VertexPositionColor(mesh.Vertices[i], mesh.Colors[i]); } var vertexBuffer = new VertexBuffer(GraphicsDeviceHolder.Device, mesh.Vertices.Count * VertexPositionColor.SizeInBytes, BufferUsage.WriteOnly); vertexBuffer.SetData(vertices); return(vertexBuffer); }
private void ReadTriangles(ModelMeshData mesh, int trianglesCount) { ModelMeshPartData meshPart = new ModelMeshPartData(trianglesCount * 3, 0); mesh.SumIndicesCount = trianglesCount * 3; for (var i = 0; i != trianglesCount; ++i) { meshPart.Indices.Add(input.ReadInt16()); meshPart.Indices.Add(input.ReadInt16()); input.BaseStream.Seek(sizeof(short), SeekOrigin.Current); // skip index flags meshPart.Indices.Add(input.ReadInt16()); } mesh.MeshParts = new List <ModelMeshPartData>(); mesh.MeshParts.Add(meshPart); }
private static ModelMesh3D CreateModelMesh(ModelMeshData mesh) { bool textured = mesh.TextureCoords != null; var vertexBuffer = textured ? CreateTexturedVertexBuffer(mesh) : CreateColoredVertexBuffer(mesh); List <ModelMeshPart3D> meshParts3d; var indexBuffer = CreateIndexBuffer(mesh, out meshParts3d); return(new ModelMesh3D( new VertexDeclaration(GraphicsDeviceHolder.Device, textured? VertexPositionColorTexture.VertexElements : VertexPositionColor.VertexElements), vertexBuffer, indexBuffer, mesh.TriangleStrip, textured? VertexPositionColorTexture.SizeInBytes : VertexPositionColor.SizeInBytes, mesh.Materials, meshParts3d )); }
private static ModelMesh3D CreateModelMesh(ModelMeshData mesh) { bool textured = mesh.TextureCoords != null; var vertexBuffer = textured ? CreateTexturedVertexBuffer(mesh) : CreateColoredVertexBuffer(mesh); List<ModelMeshPart3D> meshParts3d; var indexBuffer = CreateIndexBuffer(mesh, out meshParts3d); return new ModelMesh3D( new VertexDeclaration(GraphicsDeviceHolder.Device, textured? VertexPositionColorTexture.VertexElements : VertexPositionColor.VertexElements), vertexBuffer, indexBuffer, mesh.TriangleStrip, textured? VertexPositionColorTexture.SizeInBytes : VertexPositionColor.SizeInBytes, mesh.Materials, meshParts3d ); }
private static IndexBuffer CreateIndexBuffer(ModelMeshData mesh, out List<ModelMeshPart3D> meshParts3d) { var indexBuffer = new IndexBuffer(GraphicsDeviceHolder.Device, mesh.SumIndicesCount * sizeof(short), BufferUsage.WriteOnly, IndexElementSize.SixteenBits); meshParts3d = new List<ModelMeshPart3D>(); int offset = 0; foreach (ModelMeshPartData part in mesh.MeshParts) { indexBuffer.SetData(offset * sizeof(short), part.Indices.ToArray(), 0, part.Indices.Count); meshParts3d.Add(new ModelMeshPart3D(offset, mesh.TriangleStrip ? part.Indices.Count - 2 : part.Indices.Count / 3, part.MaterialId)); offset += part.Indices.Count; } if (offset != mesh.SumIndicesCount) Utils.TerminateWithError("Incorrect total indices amount!"); return indexBuffer; }
private void ParseMaterial(int sectionSize) { input.BaseStream.Seek(4, SeekOrigin.Current); // unknown data byte[] color = input.ReadBytes(4); input.BaseStream.Seek(4, SeekOrigin.Current); // unknown data int textureCount = input.ReadInt32(); input.BaseStream.Seek(12, SeekOrigin.Current); // unknown data if (textureCount != 0 && textureCount != 1) { Log.Instance.Print("Material section: unexpected TexturesCount value: " + textureCount); } ModelMeshData mesh = modelData.Meshes[modelData.Meshes.Count - 1]; mesh.Materials.Add(new Material() { Color = new Color(color[0], color[1], color[2], color[3]) }); }
private static IndexBuffer CreateIndexBuffer(ModelMeshData mesh, out List <ModelMeshPart3D> meshParts3d) { var indexBuffer = new IndexBuffer(GraphicsDeviceHolder.Device, mesh.SumIndicesCount * sizeof(short), BufferUsage.WriteOnly, IndexElementSize.SixteenBits); meshParts3d = new List <ModelMeshPart3D>(); int offset = 0; foreach (ModelMeshPartData part in mesh.MeshParts) { indexBuffer.SetData(offset * sizeof(short), part.Indices.ToArray(), 0, part.Indices.Count); meshParts3d.Add(new ModelMeshPart3D(offset, mesh.TriangleStrip ? part.Indices.Count - 2 : part.Indices.Count / 3, part.MaterialId)); offset += part.Indices.Count; } if (offset != mesh.SumIndicesCount) { Utils.TerminateWithError("Incorrect total indices amount!"); } return(indexBuffer); }
private void ReadVertices(ModelMeshData mesh, int verticesCount) { mesh.Vertices = new List<Vector3>(verticesCount); for (var i = 0; i != verticesCount; ++i) { var x = input.ReadSingle(); var y = input.ReadSingle(); // y and z coords are exchanged because of different coordinate system var z = input.ReadSingle(); mesh.Vertices.Add(new Vector3(x, z, -y)); } }
private void ReadTriangles(ModelMeshData mesh, int trianglesCount) { ModelMeshPartData meshPart = new ModelMeshPartData(trianglesCount * 3, 0); mesh.SumIndicesCount = trianglesCount * 3; for (var i = 0; i != trianglesCount; ++i) { meshPart.Indices.Add(input.ReadInt16()); meshPart.Indices.Add(input.ReadInt16()); input.BaseStream.Seek(sizeof(short), SeekOrigin.Current); // skip index flags meshPart.Indices.Add(input.ReadInt16()); } mesh.MeshParts = new List<ModelMeshPartData>(); mesh.MeshParts.Add(meshPart); }
private void ReadTextureCoords(ModelMeshData mesh, int verticesCount) { mesh.TextureCoords = new List<Vector2>(verticesCount); for (var i = 0; i != verticesCount; ++i) { float x = input.ReadSingle(); float y = input.ReadSingle(); mesh.TextureCoords.Add(new Vector2(x, y)); } }
private void ReadColors(ModelMeshData mesh, int verticesCount) { mesh.Colors = new List<Color>(verticesCount); for (int i = 0; i != verticesCount; ++i) { byte r = input.ReadByte(); byte g = input.ReadByte(); byte b = input.ReadByte(); byte a = input.ReadByte(); mesh.Colors.Add(new Color(r, g, b, a)); } }
/// <summary> /// Обрабатывает секцию Geometry /// Считывает вершины, треугольники (индексы вершин), нормали, текстурные координаты. /// </summary> private void ParseGeometry(DffVersion version) { ModelMeshData mesh = new ModelMeshData(); GeometrySectionFlags flags = (GeometrySectionFlags)input.ReadInt16(); input.BaseStream.Seek(sizeof(short), SeekOrigin.Current); // unknown int trianglesCount = input.ReadInt32(); int verticesCount = input.ReadInt32(); input.BaseStream.Seek(sizeof(int), SeekOrigin.Current); // morphTargetCount aka frameCount if (version < DffVersion.GTA_VC_2) { // geometry has lighting data. Ignoring it. // TODO :: we can use it for rendering!!! input.BaseStream.Seek(12, SeekOrigin.Current); } if ((flags & GeometrySectionFlags.MultipleTextureCoords) != GeometrySectionFlags.None) Log.Instance.Print("Multiple TexCoords sets are provided but used only the first of it!", MessageType.Warning); if ((flags & GeometrySectionFlags.HasColorInfo) != GeometrySectionFlags.None) ReadColors(mesh, verticesCount); else FillColors(mesh, verticesCount); if ((flags & GeometrySectionFlags.HasTextureCoords) != GeometrySectionFlags.None) ReadTextureCoords(mesh, verticesCount); ReadTriangles(mesh, trianglesCount); input.BaseStream.Seek(4 * sizeof(float), SeekOrigin.Current); // ignoring bounding sphere (x, y, z, radius) input.BaseStream.Seek(2 * sizeof(int), SeekOrigin.Current); // hasPosition, hasNormal (not used) ReadVertices(mesh, verticesCount); if ((flags & GeometrySectionFlags.HasNormalsInfo) != GeometrySectionFlags.None) ReadNormals(mesh, verticesCount); modelData.Meshes.Add(mesh); }
private void FillColors(ModelMeshData mesh, int verticesCount) { mesh.Colors = new List<Color>(verticesCount); for (int i = 0; i != verticesCount; ++i) mesh.Colors.Add(new Color(0, 0, 0, 1)); }