Skip to content

Commit

Permalink
Merge pull request #44 from Golle/feature/multiple-manifests
Browse files Browse the repository at this point in the history
Feature/multiple manifests
  • Loading branch information
Golle authored Aug 27, 2022
2 parents c1025fb + 882c761 commit c6534c7
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 106 deletions.
18 changes: 18 additions & 0 deletions samples/Titan.Sandbox/assets/anothermanifest.tmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"order": 0,
"name": "AnotherManifest",
"textures": [
{
"path": "textures\\redsheet.png",
"type": "PNG",
"name": "redsheet"
},
{
"path": "textures\\seqoe_ui_light.png",
"type": "PNG",
"name": "seqoe_ui_light"
}
],
"models": [],
"materials": []
}
28 changes: 16 additions & 12 deletions samples/Titan.Sandbox/assets/sample_01.tmanifest
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
{
"name": "Sample Manifest 01",
"textures" :[
{
"path": "textures/bluesheet.png",
"type": "PNG"
}
],
"models" :[
{
"path": "models/pillar.geo"
}
]
"order": 0,
"name": "Sample Manifest 01",
"textures": [
{
"path": "textures/bluesheet.png",
"type": "PNG",
"name": null
}
],
"models": [
{
"path": "models/pillar.geo",
"name": null
}
],
"materials": []
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Input;
Expand Down Expand Up @@ -61,13 +62,7 @@ public CookAssetsViewModel(Window window, IDialogService? dialogService = null,
return;
}

//NOTE(Jens): add support for multiple manifests. can probably execute them either in parallel and show different tabs inside the window.
if (manifests.Length > 1)
{
await dialogService.MessageBox("Warning", $"We currently only support a single manifest per project. Only {manifests[0]} will be built.");
}

var args = $"run --project {GlobalConfiguration.PackagerProjectPath} -- package -m {manifests.First()} -o \"{_packagePath}\" -g \"{_generatedPath}\" {(_namespace != null ? $"-n {_namespace}" : string.Empty)}";
var args = $"run --project {GlobalConfiguration.PackagerProjectPath} -- package {string.Join(' ', manifests.Select(m => $"-m {m}"))} -o \"{_packagePath}\" -g \"{_generatedPath}\" {(_namespace != null ? $"-n {_namespace}" : string.Empty)}";
var dialog = new ExternalProcessWindow
{
DataContext = new ExternalProcessViewModel(new ExternalProcess("dotnet", args))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Background="Transparent"
ExtendClientAreaToDecorationsHint="True"
Title="SelectProjectWindow"
WindowStartupLocation="CenterOwner"
WindowStartupLocation="CenterScreen"
CanResize="False"
>
<Design.DataContext>
Expand Down
5 changes: 4 additions & 1 deletion tools/Titan.Tools.ManifestBuilder/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ private void SetWindowSize()
{
Width = windowSize.Width;
Height = windowSize.Height;
Position = new PixelPoint(windowSize.X, windowSize.Y);
if (windowSize.X >= 0 && windowSize.Y >= 0)
{
Position = new PixelPoint(windowSize.X, windowSize.Y);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion tools/Titan.Tools.Packager/PipelineContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public record PipelineContext
public bool Failed { get; init; }
public string? Reason { get; init; }
public string? OutputPath { get; init; }
public string? ManifestPath { get; init; }
public string?[] ManifestPaths { get; init; } = Array.Empty<string?>();
public string? Namespace { get; init; }
public string? GeneratedCodePath { get; init; }
}
188 changes: 104 additions & 84 deletions tools/Titan.Tools.Packager/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics.Metrics;
using System.Text;
using Titan.Assets.NewAssets;
using Titan.Core.Logging;
Expand All @@ -10,14 +11,14 @@
Logger.Start<ConsoleLogger>();

//NOTE(Jens): this is only used in debug. hardcoded paths
#if DEBUG
const string manifestFile = @"F:\Git\Titan\samples\Titan.Sandbox\assets\sample_01.tmanifest";
const string outputDir = @"F:\Git\Titan\samples\Titan.Sandbox\assets\bin\";
const string generatedOutputDir = @"F:\Git\Titan\samples\Titan.Sandbox";
const string @namespace = "Titan.Sandbox";
//#if DEBUG
//const string manifestFile = @"F:\Git\Titan\samples\Titan.Sandbox\assets\sample_01.tmanifest";
//const string outputDir = @"F:\Git\Titan\samples\Titan.Sandbox\assets\bin\";
//const string generatedOutputDir = @"F:\Git\Titan\samples\Titan.Sandbox";
//const string @namespace = "Titan.Sandbox";

args = $"package -m {manifestFile} -o {outputDir} -g {generatedOutputDir} -n {@namespace}".Split(' ');
#endif
//args = $"package -m {manifestFile} -o {outputDir} -g {generatedOutputDir} -n {@namespace}".Split(' ');
//#endif

try
{
Expand All @@ -27,8 +28,8 @@
.WithOption(new Option<PipelineContext>("manifest")
{
Alias = "m",
Description = "The absolute path to the manifest",
Callback = (context, path) => context with { ManifestPath = path },
Description = "The absolute path to the manifest (Multiple manifests are allowed)",
Callback = (context, path) => context with { ManifestPaths = context.ManifestPaths.Concat(new[] { path }).ToArray() },
RequiresArguments = true,
Validate = s => !string.IsNullOrWhiteSpace(s)
})
Expand Down Expand Up @@ -87,6 +88,9 @@






static async Task<PipelineContext> RunPipeline(PipelineContext context)
{
if (!ValidateContext(context))
Expand All @@ -95,84 +99,94 @@ static async Task<PipelineContext> RunPipeline(PipelineContext context)
}

var outputPath = context.OutputPath!;
var manifestFileName = Path.GetFileNameWithoutExtension(context.ManifestPath)!;
//NOTE(Jens): this can be separated into different threads later (if needed). Just the C# code gen that should be single threaded.

var manifest = await ReadManifest(context.ManifestPath);
if (manifest == null)
{
return context with { Failed = true, Reason = "Failed to get the manifest" };
}
Logger.Info($"Manifest: {manifest.Name}. Textures: {manifest.Textures.Count} Models: {manifest.Models.Count} Materials: {manifest.Materials.Count}");
var basePath = Path.GetDirectoryName(context.ManifestPath)!;
Logger.Info($"Manifests: {context.ManifestPaths.Length}");

List<(string Name, AssetDescriptor Descriptor)> assetDescriptors = new();
using ImageReader imageReader = new();

using PackageStream packageExporter = new(1 * 1024 * 1024 * 1024); // 1GB
// export images
for (var i = 0; i < context.ManifestPaths.Length; ++i)
{
using ImageReader imageReader = new();
foreach (var texture in manifest.Textures)
var (assetRegistryFilename, titanPakFilename) = GenerateFileNames(i);

var manifestPath = context.ManifestPaths[i];
//NOTE(Jens): this will create different pak files for each manifest. is this what we want?
using PackageStream packageExporter = new(1 * 1024 * 1024 * 1024); // 1GB
List<(string Name, AssetDescriptor Descriptor)> assetDescriptors = new();
var manifest = await ReadManifest(manifestPath);
if (manifest == null)
{
//NOTE(Jens): Load and convert images to DXGI_FORMAT
var path = Path.Combine(basePath, texture.Path);
var descriptor = ExportImage(path, imageReader, packageExporter);
if (descriptor == null)
return context with { Failed = true, Reason = "Failed to get the manifest" };
}
Logger.Info($"Manifest: {manifest.Name}. Textures: {manifest.Textures.Count} Models: {manifest.Models.Count} Materials: {manifest.Materials.Count}");
var basePath = Path.GetDirectoryName(manifestPath)!;
// export images
{
foreach (var texture in manifest.Textures)
{
return context with { Failed = true, Reason = $"Failed to export the image from path {path}" };
//NOTE(Jens): Load and convert images to DXGI_FORMAT
var path = Path.Combine(basePath, texture.Path);
var descriptor = ExportImage(path, imageReader, packageExporter);
if (descriptor == null)
{
return context with { Failed = true, Reason = $"Failed to export the image from path {path}" };
}
assetDescriptors.Add((texture.Name, descriptor.Value));
Logger.Info($"Image {texture.Name}({texture.Path}) completed");
}
assetDescriptors.Add((texture.Name, descriptor.Value));
Logger.Info($"Image {texture.Name}({texture.Path}) completed");
}
}

// export models
{
foreach (var model in manifest.Models)
// export models
{
Logger.Warning($"Exporting model {model.Name}. (model exporting has not been implemented yet.)");
foreach (var model in manifest.Models)
{
Logger.Warning($"Exporting model {model.Name}. (model exporting has not been implemented yet.)");
}
}
}

// export materials
{
foreach (var material in manifest.Materials)
{
Logger.Warning($"Exporting material {material.Name}. (material exporting has not been implemented yet.)");
}
}
var outputFile = Path.Combine(outputPath, titanPakFilename);
{
CreateIfNotExist(outputPath);
Logger.Info($"Writing the Titan Package to file {outputFile}");
await using var file = File.OpenWrite(outputFile);
file.SetLength((long)packageExporter.Offset);
file.Seek(0, SeekOrigin.Begin);
packageExporter.Export(file);
await file.FlushAsync();
Logger.Info($"{packageExporter.Offset} bytes written");
}

// export materials
{
foreach (var material in manifest.Materials)
if (string.IsNullOrWhiteSpace(context.GeneratedCodePath))
{
Logger.Warning("No generated code path specified, no index will be created.");
}
else
{
Logger.Warning($"Exporting material {material.Name}. (material exporting has not been implemented yet.)");
var fileContents = GenerateCSharpIndex(titanPakFilename, assetDescriptors, manifest.Name, context.Namespace);
var generatedFilePath = Path.Combine(context.GeneratedCodePath, assetRegistryFilename);
CreateIfNotExist(context.GeneratedCodePath);

Logger.Info($"Writing the generated code to {generatedFilePath}");
await using var writer = new StreamWriter(File.OpenWrite(generatedFilePath));
writer.BaseStream.SetLength(0);
await writer.WriteAsync(fileContents);
await writer.FlushAsync();
//await File.WriteAllTextAsync(generatedFilePath, fileContents);
}
}

return context;

var outputFile = Path.Combine(outputPath, $"{manifestFileName}.titanpak");
{
CreateIfNotExist(outputPath);
Logger.Info($"Writing the Titan Package to file {outputFile}");
await using var file = File.OpenWrite(outputFile);
file.SetLength((long)packageExporter.Offset);
file.Seek(0, SeekOrigin.Begin);
packageExporter.Export(file);
await file.FlushAsync();
Logger.Info($"{packageExporter.Offset} bytes written");
}

if (string.IsNullOrWhiteSpace(context.GeneratedCodePath))
{
Logger.Warning("No generated code path specified, no index will be created.");
}
else
{
var fileContents = GenerateCSharpIndex(outputFile, assetDescriptors, context.Namespace);
var generatedFilePath = Path.Combine(context.GeneratedCodePath, $"AssetRegistry{ToPropertyName(manifestFileName)}.cs");
CreateIfNotExist(context.GeneratedCodePath);

Logger.Info($"Writing the generated code to {generatedFilePath}");
await using var writer = new StreamWriter(File.OpenWrite(generatedFilePath));
await writer.WriteAsync(fileContents);
await writer.FlushAsync();
//await File.WriteAllTextAsync(generatedFilePath, fileContents);
}

return context;
static (string ManifestName, string PakFileName) GenerateFileNames(int index)
=> ($"AssetRegistry{++index,3:D3}.cs", $"data{index,3:D3}.titanpak");
}


Expand All @@ -185,12 +199,17 @@ static bool ValidateContext(PipelineContext context)
result = false;
}

if (string.IsNullOrWhiteSpace(context.ManifestPath))
if (context.ManifestPaths.Length == 0)
{
Logger.Error($"{nameof(PipelineContext.ManifestPath)} is null or empty.");
Logger.Error($"{nameof(PipelineContext.ManifestPaths)} is empty.");
result = false;
}

if (context.ManifestPaths.Any(string.IsNullOrWhiteSpace))
{
Logger.Error($"{nameof(PipelineContext.ManifestPaths)} has null or empty paths.");
result = false;
}
return result;
}

Expand Down Expand Up @@ -244,8 +263,10 @@ static bool ValidateContext(PipelineContext context)
}


static string GenerateCSharpIndex(string packageFile, IReadOnlyList<(string Name, AssetDescriptor descriptor)> descriptors, string? @namespace)
static string GenerateCSharpIndex(string packageFile, IReadOnlyList<(string Name, AssetDescriptor descriptor)> descriptors, string manifestName, string? @namespace)
{
//NOTE(Jens): use a name that can be compiled in C#
manifestName = ToPropertyName(manifestName);
var unnamedCount = 0;
var builder = new StringBuilder();
builder.AppendLine($"// This is a generated file from {typeof(Program).Assembly.FullName}");
Expand All @@ -260,11 +281,15 @@ static string GenerateCSharpIndex(string packageFile, IReadOnlyList<(string Name
.AppendLine("public static partial class AssetRegistry")
.AppendLine("{");

builder.AppendLine($"\tpublic const string PackageFile =\"{Path.GetFileName(packageFile)}\"; // This wont work for multiple manifests.");
builder.AppendLine($"\tpublic static class {manifestName}")
.AppendLine("\t{");

builder.AppendLine($"\t\tpublic const string PackageFile =\"{Path.GetFileName(packageFile)}\";");

builder
.AppendLine("\tpublic static partial class Textures")
.AppendLine("\t{");
.AppendLine("\t\tpublic static partial class Textures")
.AppendLine("\t\t{");

foreach (var (name, descriptor) in descriptors)
{
string propertyName;
Expand All @@ -278,12 +303,13 @@ static string GenerateCSharpIndex(string packageFile, IReadOnlyList<(string Name
propertyName = ToPropertyName(name);
}

builder.AppendLine($"\t\t\tpublic static readonly {typeof(AssetDescriptor).FullName} {propertyName} = {DescriptorToString(descriptor)}");
}
builder.AppendLine("\t\t}")
.AppendLine("\t}")
.AppendLine("}");


builder.AppendLine($"\t\tpublic static readonly {typeof(AssetDescriptor).FullName} {propertyName} = {DescriptorToString(descriptor)}");
}
builder.AppendLine("\t}");
builder.AppendLine("}");
builder.Replace("\t", new string(' ', 4));
return builder.ToString();
static string DescriptorToString(in AssetDescriptor descriptor) =>
Expand All @@ -297,10 +323,6 @@ static string DescriptorToString(in AssetDescriptor descriptor) =>

static string ToPropertyName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
Logger.Warning("Mi");
}
Span<char> buffer = stackalloc char[name.Length];
var count = 0;
if (!char.IsLetter(name[0]))
Expand All @@ -324,8 +346,6 @@ static string ToPropertyName(string name)
return new string(buffer[..count]);
}



static void CreateIfNotExist(string folder)
{
if (!Directory.Exists(folder))
Expand Down

0 comments on commit c6534c7

Please sign in to comment.