|
@@ -1,10 +1,12 @@
|
|
|
using System;
|
|
using System;
|
|
|
using System.Collections;
|
|
using System.Collections;
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
|
|
+using System.IO;
|
|
|
using System.Linq;
|
|
using System.Linq;
|
|
|
using System.Numerics;
|
|
using System.Numerics;
|
|
|
using System.Text;
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
+using SharpGLTF.Memory;
|
|
|
using SharpGLTF.Schema2;
|
|
using SharpGLTF.Schema2;
|
|
|
|
|
|
|
|
using static System.FormattableString;
|
|
using static System.FormattableString;
|
|
@@ -55,14 +57,13 @@ namespace SharpGLTF.IO
|
|
|
{
|
|
{
|
|
|
Guard.NotNullOrEmpty(filePath, nameof(filePath));
|
|
Guard.NotNullOrEmpty(filePath, nameof(filePath));
|
|
|
|
|
|
|
|
- var files = GetFiles(System.IO.Path.GetFileNameWithoutExtension(filePath));
|
|
|
|
|
-
|
|
|
|
|
var dir = System.IO.Path.GetDirectoryName(filePath);
|
|
var dir = System.IO.Path.GetDirectoryName(filePath);
|
|
|
|
|
|
|
|
- foreach (var f in files)
|
|
|
|
|
|
|
+ foreach (var fileNameAndGenerator in _GetFileGenerators(System.IO.Path.GetFileNameWithoutExtension(filePath)))
|
|
|
{
|
|
{
|
|
|
- var fpath = System.IO.Path.Combine(dir, f.Key);
|
|
|
|
|
- System.IO.File.WriteAllBytes(fpath, f.Value.ToArray());
|
|
|
|
|
|
|
+ var fpath = System.IO.Path.Combine(dir, fileNameAndGenerator.Key);
|
|
|
|
|
+ using var fs = File.OpenWrite(fpath);
|
|
|
|
|
+ fileNameAndGenerator.Value(fs);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -81,17 +82,33 @@ namespace SharpGLTF.IO
|
|
|
Guard.IsFalse(baseName.Any(c => char.IsWhiteSpace(c)), nameof(baseName), "Whitespace characters not allowed in filename");
|
|
Guard.IsFalse(baseName.Any(c => char.IsWhiteSpace(c)), nameof(baseName), "Whitespace characters not allowed in filename");
|
|
|
|
|
|
|
|
var files = new Dictionary<String, BYTES>();
|
|
var files = new Dictionary<String, BYTES>();
|
|
|
|
|
+ foreach (var fileNameAndGenerator in _GetFileGenerators(baseName))
|
|
|
|
|
+ {
|
|
|
|
|
+ using var mem = new MemoryStream();
|
|
|
|
|
+ fileNameAndGenerator.Value(mem);
|
|
|
|
|
|
|
|
- var materials = _WriteMaterials(files, baseName, _Mesh.Primitives.Select(item => item.Material));
|
|
|
|
|
-
|
|
|
|
|
- var geocontent = _GetGeometryContent(materials, baseName + ".mtl");
|
|
|
|
|
|
|
+ mem.TryGetBuffer(out var bytes);
|
|
|
|
|
|
|
|
- _WriteTextContent(files, baseName + ".obj", geocontent);
|
|
|
|
|
|
|
+ files[fileNameAndGenerator.Key] = bytes;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return files;
|
|
return files;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static IReadOnlyDictionary<Material, string> _WriteMaterials(IDictionary<String, BYTES> files, string baseName, IEnumerable<Material> materials)
|
|
|
|
|
|
|
+ private IReadOnlyDictionary<String, Action<Stream>> _GetFileGenerators(string baseName)
|
|
|
|
|
+ {
|
|
|
|
|
+ Guard.IsFalse(baseName.Any(c => char.IsWhiteSpace(c)), nameof(baseName), "Whitespace characters not allowed in filename");
|
|
|
|
|
+
|
|
|
|
|
+ var fileGenerators = new Dictionary<String, Action<Stream>>();
|
|
|
|
|
+
|
|
|
|
|
+ var materials = _GetMaterialFileGenerator(fileGenerators, baseName, _Mesh.Primitives.Select(item => item.Material));
|
|
|
|
|
+
|
|
|
|
|
+ fileGenerators[baseName + ".obj"] = fs => _GetGeometryContent(new StreamWriter(fs), materials, baseName + ".mtl");
|
|
|
|
|
+
|
|
|
|
|
+ return fileGenerators;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static IReadOnlyDictionary<Material, string> _GetMaterialFileGenerator(IDictionary<String, Action<Stream>> fileGenerators, string baseName, IEnumerable<Material> materials)
|
|
|
{
|
|
{
|
|
|
// write all image files
|
|
// write all image files
|
|
|
var images = materials
|
|
var images = materials
|
|
@@ -101,76 +118,81 @@ namespace SharpGLTF.IO
|
|
|
|
|
|
|
|
bool firstImg = true;
|
|
bool firstImg = true;
|
|
|
|
|
|
|
|
|
|
+ var imageNameByImage = new Dictionary<MemoryImage, string>();
|
|
|
foreach (var img in images)
|
|
foreach (var img in images)
|
|
|
{
|
|
{
|
|
|
var imgName = firstImg
|
|
var imgName = firstImg
|
|
|
? $"{baseName}.{img.FileExtension}"
|
|
? $"{baseName}.{img.FileExtension}"
|
|
|
- : $"{baseName}_{files.Count}.{img.FileExtension}";
|
|
|
|
|
|
|
+ : $"{baseName}_{fileGenerators.Count}.{img.FileExtension}";
|
|
|
|
|
|
|
|
- files[imgName] = new BYTES(img.Content.ToArray());
|
|
|
|
|
|
|
+ fileGenerators[imgName] = fs => {
|
|
|
|
|
+ var bytes = img.Content.ToArray();
|
|
|
|
|
+ fs.Write(bytes, 0, bytes.Length);
|
|
|
|
|
+ };
|
|
|
firstImg = false;
|
|
firstImg = false;
|
|
|
|
|
+
|
|
|
|
|
+ imageNameByImage[img] = imgName;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// write materials
|
|
// write materials
|
|
|
|
|
|
|
|
var mmap = new Dictionary<Material, string>();
|
|
var mmap = new Dictionary<Material, string>();
|
|
|
-
|
|
|
|
|
- var sb = new StringBuilder();
|
|
|
|
|
-
|
|
|
|
|
- foreach (var m in materials)
|
|
|
|
|
|
|
+ foreach (var m in materials)
|
|
|
{
|
|
{
|
|
|
mmap[m] = $"Material_{mmap.Count}";
|
|
mmap[m] = $"Material_{mmap.Count}";
|
|
|
-
|
|
|
|
|
- sb.AppendLine($"newmtl {mmap[m]}");
|
|
|
|
|
- sb.AppendLine("illum 2");
|
|
|
|
|
- sb.AppendLine(Invariant($"Ka {m.DiffuseColor.X} {m.DiffuseColor.Y} {m.DiffuseColor.Z}"));
|
|
|
|
|
- sb.AppendLine(Invariant($"Kd {m.DiffuseColor.X} {m.DiffuseColor.Y} {m.DiffuseColor.Z}"));
|
|
|
|
|
- sb.AppendLine(Invariant($"Ks {m.SpecularColor.X} {m.SpecularColor.Y} {m.SpecularColor.Z}"));
|
|
|
|
|
-
|
|
|
|
|
- if (m.DiffuseTexture.IsValid)
|
|
|
|
|
- {
|
|
|
|
|
- var imgName = files.FirstOrDefault(kvp => new Memory.MemoryImage(kvp.Value) == m.DiffuseTexture ).Key;
|
|
|
|
|
- sb.AppendLine($"map_Kd {imgName}");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- sb.AppendLine();
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// write material library
|
|
// write material library
|
|
|
- _WriteTextContent(files, baseName + ".mtl", sb);
|
|
|
|
|
|
|
+ fileGenerators[baseName + ".mtl"] = fs =>
|
|
|
|
|
+ {
|
|
|
|
|
+ var sw = new StreamWriter(fs);
|
|
|
|
|
+ foreach (var m in materials)
|
|
|
|
|
+ {
|
|
|
|
|
+ sw.WriteLine($"newmtl {mmap[m]}");
|
|
|
|
|
+ sw.WriteLine("illum 2");
|
|
|
|
|
+ sw.WriteLine(Invariant($"Ka {m.DiffuseColor.X} {m.DiffuseColor.Y} {m.DiffuseColor.Z}"));
|
|
|
|
|
+ sw.WriteLine(Invariant($"Kd {m.DiffuseColor.X} {m.DiffuseColor.Y} {m.DiffuseColor.Z}"));
|
|
|
|
|
+ sw.WriteLine(Invariant($"Ks {m.SpecularColor.X} {m.SpecularColor.Y} {m.SpecularColor.Z}"));
|
|
|
|
|
+
|
|
|
|
|
+ if (m.DiffuseTexture.IsValid) {
|
|
|
|
|
+ var imgName = imageNameByImage[m.DiffuseTexture];
|
|
|
|
|
+ sw.WriteLine($"map_Kd {imgName}");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ sw.WriteLine();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
return mmap;
|
|
return mmap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private StringBuilder _GetGeometryContent(IReadOnlyDictionary<Material, string> materials, string mtlLib)
|
|
|
|
|
|
|
+ private void _GetGeometryContent(StreamWriter sw, IReadOnlyDictionary<Material, string> materials, string mtlLib)
|
|
|
{
|
|
{
|
|
|
- var sb = new StringBuilder();
|
|
|
|
|
-
|
|
|
|
|
- sb.AppendLine($"mtllib {mtlLib}");
|
|
|
|
|
|
|
+ sw.WriteLine($"mtllib {mtlLib}");
|
|
|
|
|
|
|
|
- sb.AppendLine();
|
|
|
|
|
|
|
+ sw.WriteLine();
|
|
|
|
|
|
|
|
foreach (var p in _Mesh.Primitives)
|
|
foreach (var p in _Mesh.Primitives)
|
|
|
{
|
|
{
|
|
|
foreach (var v in p.Vertices)
|
|
foreach (var v in p.Vertices)
|
|
|
{
|
|
{
|
|
|
var pos = v.Position;
|
|
var pos = v.Position;
|
|
|
- sb.AppendLine(Invariant($"v {pos.X} {pos.Y} {pos.Z}"));
|
|
|
|
|
|
|
+ sw.WriteLine(Invariant($"v {pos.X} {pos.Y} {pos.Z}"));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- sb.AppendLine();
|
|
|
|
|
|
|
+ sw.WriteLine();
|
|
|
|
|
|
|
|
foreach (var p in _Mesh.Primitives)
|
|
foreach (var p in _Mesh.Primitives)
|
|
|
{
|
|
{
|
|
|
foreach (var v in p.Vertices)
|
|
foreach (var v in p.Vertices)
|
|
|
{
|
|
{
|
|
|
var nrm = v.Geometry.Normal;
|
|
var nrm = v.Geometry.Normal;
|
|
|
- sb.AppendLine(Invariant($"vn {nrm.X} {nrm.Y} {nrm.Z}"));
|
|
|
|
|
|
|
+ sw.WriteLine(Invariant($"vn {nrm.X} {nrm.Y} {nrm.Z}"));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- sb.AppendLine();
|
|
|
|
|
|
|
+ sw.WriteLine();
|
|
|
|
|
|
|
|
foreach (var p in _Mesh.Primitives)
|
|
foreach (var p in _Mesh.Primitives)
|
|
|
{
|
|
{
|
|
@@ -179,13 +201,13 @@ namespace SharpGLTF.IO
|
|
|
var uv = v.Material.TexCoord;
|
|
var uv = v.Material.TexCoord;
|
|
|
uv.Y = 1 - uv.Y;
|
|
uv.Y = 1 - uv.Y;
|
|
|
|
|
|
|
|
- sb.AppendLine(Invariant($"vt {uv.X} {uv.Y}"));
|
|
|
|
|
|
|
+ sw.WriteLine(Invariant($"vt {uv.X} {uv.Y}"));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- sb.AppendLine();
|
|
|
|
|
|
|
+ sw.WriteLine();
|
|
|
|
|
|
|
|
- sb.AppendLine("g default");
|
|
|
|
|
|
|
+ sw.WriteLine("g default");
|
|
|
|
|
|
|
|
var baseVertexIndex = 1;
|
|
var baseVertexIndex = 1;
|
|
|
|
|
|
|
@@ -193,7 +215,7 @@ namespace SharpGLTF.IO
|
|
|
{
|
|
{
|
|
|
var mtl = materials[p.Material];
|
|
var mtl = materials[p.Material];
|
|
|
|
|
|
|
|
- sb.AppendLine($"usemtl {mtl}");
|
|
|
|
|
|
|
+ sw.WriteLine($"usemtl {mtl}");
|
|
|
|
|
|
|
|
foreach (var t in p.Triangles)
|
|
foreach (var t in p.Triangles)
|
|
|
{
|
|
{
|
|
@@ -201,28 +223,11 @@ namespace SharpGLTF.IO
|
|
|
var b = t.B + baseVertexIndex;
|
|
var b = t.B + baseVertexIndex;
|
|
|
var c = t.C + baseVertexIndex;
|
|
var c = t.C + baseVertexIndex;
|
|
|
|
|
|
|
|
- sb.AppendLine(Invariant($"f {a}/{a}/{a} {b}/{b}/{b} {c}/{c}/{c}"));
|
|
|
|
|
|
|
+ sw.WriteLine(Invariant($"f {a}/{a}/{a} {b}/{b}/{b} {c}/{c}/{c}"));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
baseVertexIndex += p.Vertices.Count;
|
|
baseVertexIndex += p.Vertices.Count;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- return sb;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private static void _WriteTextContent(IDictionary<string, BYTES> files, string fileName, StringBuilder sb)
|
|
|
|
|
- {
|
|
|
|
|
- using (var mem = new System.IO.MemoryStream())
|
|
|
|
|
- {
|
|
|
|
|
- using (var tex = new System.IO.StreamWriter(mem))
|
|
|
|
|
- {
|
|
|
|
|
- tex.Write(sb.ToString());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- mem.TryGetBuffer(out BYTES content);
|
|
|
|
|
-
|
|
|
|
|
- files[fileName] = content;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
#endregion
|