using System.Diagnostics; namespace Nerfed.Builder; public class Builder : IDisposable { private readonly Dictionary importers = new Dictionary(); private readonly RawFileImporter rawFileImporter; public Builder() { rawFileImporter = new RawFileImporter(); ShaderImporter shaderImporter = new ShaderImporter(); importers.Add(".vert", shaderImporter); // Vertex shader importers.Add(".frag", shaderImporter); // Fragment shader importers.Add(".tesc", shaderImporter); // Tessellation control shader importers.Add(".tese", shaderImporter); // Tessellation evaluation shader importers.Add(".geom", shaderImporter); // Geometry shader importers.Add(".comp", shaderImporter); // Compute shader } public void Run(BuildArgs args) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); CopyLibs(args.ProjectPath); List contentFiles = args.ContentFiles; string absContentPath = $"{args.ProjectPath}/{PathUtil.ContentFolderName}"; // If no files are provided, build all content. if (args.ContentFiles == null) { contentFiles = []; CollectAssetFiles(absContentPath, absContentPath, ref contentFiles); } if (contentFiles.Count > 0) { string importPath = $"{args.ProjectPath}/{PathUtil.ImportFolderName}"; ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = contentFiles.Count }; Parallel.ForEach(contentFiles, parallelOptions, relativeFile => { try { string inFile = $"{args.ProjectPath}/{PathUtil.ContentFolderName}/{relativeFile}"; if (!File.Exists(inFile)) { Console.Error.WriteLine($"Asset file '{relativeFile}' not found"); return; } string outFile = $"{importPath}/{relativeFile}{PathUtil.ImportedFileExtension}"; FileInfo inFileInfo = new FileInfo(inFile); FileInfo outFileInfo = new FileInfo(outFile); if (!FileUtil.IsNewer(inFileInfo, outFileInfo)) { // File has not changed since last build, no need to build this one. return; } string outDir = Path.GetDirectoryName(outFile); if (!Directory.Exists(outDir)) { Directory.CreateDirectory(outDir); } string ext = Path.GetExtension(inFile).ToLower(); if (importers.TryGetValue(ext, out IImporter importer)) { importer.Import(inFile, outFile); } else { rawFileImporter.Import(inFile, outFile); } Console.WriteLine(relativeFile); } catch (Exception e) { Console.Error.WriteLine($"Import error on asset '{relativeFile}': {e.Message}"); } }); } Console.WriteLine($"Build content completed in {stopwatch.Elapsed.TotalSeconds:F2} seconds"); } private void CopyLibs(string projectPath) { string libDir = $"{AppDomain.CurrentDomain.BaseDirectory}/../../Native/"; if (OperatingSystem.IsWindows()) { libDir += "x64"; } else if (OperatingSystem.IsLinux()) { libDir += "lib64"; } else if (OperatingSystem.IsMacOS()) { libDir += "osx"; } libDir = Path.GetFullPath(libDir); foreach (string libFile in Directory.EnumerateFiles(libDir)) { FileInfo srcFileInfo = new FileInfo(libFile); FileInfo dstFileInfo = new FileInfo($"{projectPath}/{PathUtil.BuildFolderName}/{Path.GetFileName(libFile)}"); if (FileUtil.IsNewer(srcFileInfo, dstFileInfo)) { FileUtil.Copy(srcFileInfo, dstFileInfo); } } } private void CollectAssetFiles(string assetDir, string dir, ref List files) { foreach (string file in Directory.EnumerateFiles(dir)) { if (Path.GetExtension(file).Equals(PathUtil.ImportFileExtension, StringComparison.CurrentCultureIgnoreCase)) { continue; } string relativeFile = file.Substring(assetDir.Length, file.Length - assetDir.Length); if (relativeFile[0] == Path.DirectorySeparatorChar || relativeFile[0] == Path.AltDirectorySeparatorChar) { relativeFile = relativeFile.Substring(1, relativeFile.Length - 1); } files.Add(relativeFile); } foreach (string subDir in Directory.EnumerateDirectories(dir)) { CollectAssetFiles(assetDir, subDir, ref files); } } public void Dispose() { } }