Skip to content

Commit

Permalink
Merge pull request #167 from MeltyPlayer/obj
Browse files Browse the repository at this point in the history
Optimized file generation in WavefrontWriter.
  • Loading branch information
vpenades committed Jan 15, 2023
2 parents 3fd1ea9 + d2123a0 commit 8a7da18
Showing 1 changed file with 67 additions and 62 deletions.
129 changes: 67 additions & 62 deletions src/SharpGLTF.Toolkit/IO/WavefrontWriter.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;

using SharpGLTF.Memory;
using SharpGLTF.Schema2;

using static System.FormattableString;
Expand Down Expand Up @@ -55,14 +57,13 @@ public void WriteFiles(string filePath)
{
Guard.NotNullOrEmpty(filePath, nameof(filePath));

var files = GetFiles(System.IO.Path.GetFileNameWithoutExtension(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);
}
}

Expand All @@ -81,17 +82,33 @@ public void WriteFiles(string filePath)
Guard.IsFalse(baseName.Any(c => char.IsWhiteSpace(c)), nameof(baseName), "Whitespace characters not allowed in filename");

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;
}

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
var images = materials
Expand All @@ -101,76 +118,81 @@ public void WriteFiles(string filePath)

bool firstImg = true;

var imageNameByImage = new Dictionary<MemoryImage, string>();
foreach (var img in images)
{
var imgName = firstImg
? $"{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;

imageNameByImage[img] = imgName;
}

// write materials

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}";

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
_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;
}

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 v in p.Vertices)
{
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 v in p.Vertices)
{
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)
{
Expand All @@ -179,50 +201,33 @@ private StringBuilder _GetGeometryContent(IReadOnlyDictionary<Material, string>
var uv = v.Material.TexCoord;
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;

foreach (var p in _Mesh.Primitives)
{
var mtl = materials[p.Material];

sb.AppendLine($"usemtl {mtl}");
sw.WriteLine($"usemtl {mtl}");

foreach (var t in p.Triangles)
{
var a = t.A + baseVertexIndex;
var b = t.B + 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;
}

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
Expand Down

0 comments on commit 8a7da18

Please sign in to comment.