Skip to content

Commit

Permalink
Added mesh content pipeline.
Browse files Browse the repository at this point in the history
  • Loading branch information
RossNordby committed Jul 6, 2018
1 parent 710b949 commit d1d3b17
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 75 deletions.
42 changes: 30 additions & 12 deletions DemoContentBuilder/ContentBuilder.cs
Expand Up @@ -15,43 +15,61 @@ public struct ContentBuildResult
public string Message;
}

public struct ContentBuildInput
{
public string Path;
public ContentType Type;
}

public static class ContentBuilder
{
public static void BuildContent(string workingPath, string buildCachePath, string runtimeCachePath, List<string> fontPaths,
public static void BuildContent(string workingPath, string buildCachePath, string runtimeCachePath, List<ContentBuildInput> contentToBuild,
out List<ContentBuildResult> warnings, out List<ContentBuildResult> errors)
{
errors = new List<ContentBuildResult>();
warnings = new List<ContentBuildResult>();
bool newContentBuilt = false;
ContentBuildCacheIO.Load(buildCachePath, out var loadedBuildCache);
var newBuildCache = new Dictionary<string, ContentElement>();
foreach (var path in fontPaths)
foreach (var content in contentToBuild)
{
var currentTimeStamp = File.GetLastWriteTime(path).Ticks;
if (loadedBuildCache.TryGetValue(path, out var cachedContent) && currentTimeStamp == cachedContent.LastModifiedTimestamp)
var currentTimeStamp = File.GetLastWriteTime(content.Path).Ticks;
if (loadedBuildCache.TryGetValue(content.Path, out var cachedContent) && currentTimeStamp == cachedContent.LastModifiedTimestamp)
{
//We can just used the cached version of this content.
newBuildCache.Add(path, cachedContent);
Console.WriteLine($"Content up to date: {path}");
newBuildCache.Add(content.Path, cachedContent);
Console.WriteLine($"Content up to date: {content}");
}
else
{
//This is a new or modified content element, so we'll have to build it.
ContentElement newElement;
newElement.LastModifiedTimestamp = currentTimeStamp;
using (var stream = File.OpenRead(path))
using (var stream = File.OpenRead(content.Path))
{
//You could be a little more clever with these errors by letting builders report more specific problems. We can worry about that if this ever gets generalized.
try
{
newElement.Content = FontBuilder.Build(stream);
newBuildCache.Add(path, newElement);
//Just like the ContentArchive, we're being a little lazy- this isn't very extensible, but there are very few content types.
//If that changes, it would be pretty easy to open this up.
switch (content.Type)
{
case ContentType.Font:
newElement.Content = FontBuilder.Build(stream);
break;
case ContentType.Mesh:
newElement.Content = MeshBuilder.Build(stream);
break;
default:
throw new ArgumentException("Requested content type does not have a registered builder.");
}
newBuildCache.Add(content.Path, newElement);
newContentBuilt = true;
Console.WriteLine($"Content built: {path}");
Console.WriteLine($"Content built: {content}");
}
catch (Exception e)
{
errors.Add(new ContentBuildResult { File = path, Message = "Content build failed: " + e.Message });
//You could be a little more clever with these errors by letting builders report more specific problems. We can worry about that if this ever gets generalized.
errors.Add(new ContentBuildResult { File = content.Path, Message = "Content build failed: " + e.Message });
}
}
}
Expand Down
1 change: 1 addition & 0 deletions DemoContentBuilder/DemoContentBuilder.csproj
Expand Up @@ -33,6 +33,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CjClutter.ObjLoader" Version="1.0.0" />
<PackageReference Include="SharpDX" Version="4.0.1" />
<PackageReference Include="SharpDX.D3DCompiler" Version="4.0.1" />
<PackageReference Include="SharpFontStandard" Version="4.0.1" />
Expand Down
38 changes: 38 additions & 0 deletions DemoContentBuilder/MeshBuilder.cs
@@ -0,0 +1,38 @@
using DemoContentLoader;
using ObjLoader.Loader.Loaders;
using System.Collections.Generic;
using System.IO;
using System.Numerics;

namespace DemoContentBuilder
{
public static class MeshBuilder
{
public unsafe static MeshContent Build(Stream dataStream)
{
var result = new ObjLoaderFactory().Create().Load(dataStream);
var triangles = new List<TriangleContent>();
for (int i = 0; i < result.Groups.Count; ++i)
{
var group = result.Groups[i];
for (int j = 0; j < group.Faces.Count; ++j)
{
var face = group.Faces[j];
var a = result.Vertices[face[0].VertexIndex];
for (int k = 1; k < face.Count - 1; ++k)
{
var b = result.Vertices[face[k].VertexIndex];
var c = result.Vertices[face[k + 1].VertexIndex];
triangles.Add(new TriangleContent
{
A = new Vector3(a.X, a.Y, a.Z),
B = new Vector3(b.X, b.Y, b.Z),
C = new Vector3(c.X, c.Y, c.Z)
});
}
}
}
return new MeshContent(triangles.ToArray());
}
}
}
23 changes: 11 additions & 12 deletions DemoContentBuilder/ProjectBuilder.cs
@@ -1,4 +1,5 @@
using System;
using DemoContentLoader;
using System;
using System.Collections.Generic;
using System.IO;

Expand All @@ -22,17 +23,12 @@ public static string GetRelativePathFromDirectory(string path, string baseDirect

unsafe static void CollectContentPaths(string projectPath, out string workingPath,
out List<string> shaderPaths,
out List<string> fontPaths)
out List<ContentBuildInput> contentToBuild)
{
//NOTE:
//This project builder takes dependencies on a lot of things which really should not be depended upon.
//It would be nice to figure out a more solid way to do it. Is there are better way to query the dotnet build tools?
//A fallback option is to just create our own 'project'- just a little file with references alongside the csproj that triggered this build step.
//We would have complete control over the format.
projectPath = Path.GetFullPath(projectPath);
workingPath = Path.GetDirectoryName(projectPath);
shaderPaths = new List<string>();
fontPaths = new List<string>();
contentToBuild = new List<ContentBuildInput>();
try
{
using (var stream = new StreamReader(File.OpenRead(projectPath)))
Expand All @@ -54,7 +50,10 @@ public static string GetRelativePathFromDirectory(string path, string baseDirect
break;
case ".ttf":
case ".otf":
fontPaths.Add(path);
contentToBuild.Add(new ContentBuildInput { Path = path, Type = ContentType.Font });
break;
case ".obj":
contentToBuild.Add(new ContentBuildInput { Path = path, Type = ContentType.Mesh });
break;
}
}
Expand All @@ -69,7 +68,7 @@ public static string GetRelativePathFromDirectory(string path, string baseDirect
catch (Exception e)
{
Console.WriteLine($"{projectPath}: error: Content list read exception: {e.Message}");
}
}
}


Expand Down Expand Up @@ -116,7 +115,7 @@ public static void Main(string[] args)
}
foreach (var targetPath in targetPaths)
{
CollectContentPaths(targetPath, out var workingPath, out var shaderPaths, out var fontPaths);
CollectContentPaths(targetPath, out var workingPath, out var shaderPaths, out var contentPaths);
var cachePathStart = Path.Combine(workingPath, Path.GetFileNameWithoutExtension(targetPath));
//Shaders are stored a little differently than the rest of content. This is partially for legacy reasons.
//You could make the argument for bundling them together, but shaders do have some unique macro and dependency management that other kinds of content lack.
Expand All @@ -134,7 +133,7 @@ public static void Main(string[] args)
}
ContentBuilder.BuildContent(workingPath,
cachePathStart + ".contentbuildcache",
cachePathStart + ".contentarchive", fontPaths, out var contentWarnings, out var contentErrors);
cachePathStart + ".contentarchive", contentPaths, out var contentWarnings, out var contentErrors);
foreach (var error in contentErrors)
{
Console.WriteLine($"{error.File}: error: {error.Message}");
Expand Down
7 changes: 6 additions & 1 deletion DemoContentLoader/ContentArchive.cs
Expand Up @@ -8,7 +8,7 @@ namespace DemoContentLoader
public enum ContentType
{
Font = 1,
YargleBargle = 2
Mesh = 2
}
public interface IContent
{
Expand Down Expand Up @@ -42,6 +42,8 @@ public static IContent Load(ContentType type, BinaryReader reader)
{
case ContentType.Font:
return FontIO.Load(reader);
case ContentType.Mesh:
return MeshIO.Load(reader);
}
throw new ArgumentException($"Given content type {type} cannot be loaded; no loader is specified. Is the archive corrupted?");
}
Expand All @@ -53,6 +55,9 @@ public static void Save(IContent content, BinaryWriter writer)
case ContentType.Font:
FontIO.Save((FontContent)content, writer);
return;
case ContentType.Mesh:
MeshIO.Save((MeshContent)content, writer);
return;
}
throw new ArgumentException("Given content type cannot be saved; no archiver is specified.");
}
Expand Down
27 changes: 27 additions & 0 deletions DemoContentLoader/MeshContent.cs
@@ -0,0 +1,27 @@
using BepuUtilities;
using DemoContentLoader;
using System;
using System.Collections.Generic;
using System.Numerics;

namespace DemoContentLoader
{
public struct TriangleContent
{
public Vector3 A;
public Vector3 B;
public Vector3 C;
}

public class MeshContent : IContent
{
public TriangleContent[] Triangles;

public ContentType ContentType { get { return ContentType.Mesh; } }

public MeshContent(TriangleContent[] triangles)
{
Triangles = triangles;
}
}
}
52 changes: 52 additions & 0 deletions DemoContentLoader/MeshIO.cs
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;

namespace DemoContentLoader
{
public class MeshIO
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReadVector3(BinaryReader reader, out Vector3 v)
{
v = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}

public static MeshContent Load(BinaryReader reader)
{
var triangleCount = reader.ReadInt32();
var triangles = new TriangleContent[triangleCount];
for (int i = 0; i < triangleCount; ++i)
{
ref var triangle = ref triangles[i];
ReadVector3(reader, out triangle.A);
ReadVector3(reader, out triangle.B);
ReadVector3(reader, out triangle.C);
}
return new MeshContent(triangles);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Write(BinaryWriter writer, in Vector3 v)
{
writer.Write(v.X);
writer.Write(v.Y);
writer.Write(v.Z);
}

public static void Save(MeshContent content, BinaryWriter writer)
{
writer.Write(content.Triangles.Length);
for (int i = 0; i < content.Triangles.Length; ++i)
{
ref var triangle = ref content.Triangles[i];
Write(writer, triangle.A);
Write(writer, triangle.B);
Write(writer, triangle.C);
}
}
}
}

0 comments on commit d1d3b17

Please sign in to comment.