Files
tooll3--t3/Editor/UiModel/Exporting/PlayerExporter.ExportInfo.cs
2026-07-13 13:13:17 +08:00

180 lines
7.1 KiB
C#

#nullable enable
using System.IO;
using System.Threading;
using T3.Core.Model;
using T3.Core.Operator;
using T3.Core.Resource;
using T3.Core.Resource.Assets;
using T3.Core.Settings;
using ShaderCompiler = T3.Core.Resource.ShaderCompiling.ShaderCompiler;
namespace T3.Editor.UiModel.Exporting;
internal static partial class PlayerExporter
{
private sealed class ExportData
{
public IReadOnlyCollection<AssetExportItem> ExportItems => _exportItems;
private readonly HashSet<Symbol> _symbols = [];
private readonly HashSet<Instance> _collectedInstances = [];
/** Packages with code */
public IEnumerable<SymbolPackage> SymbolPackages => _symbolPackages.Keys;
private readonly Dictionary<SymbolPackage, List<Symbol>> _symbolPackages = new();
/** Packages including used assets */
public readonly HashSet<IResourcePackage> AssetPackages = [];
private readonly HashSet<AssetExportItem> _exportItems = [];
public bool TryAddInstance(Instance instance) => _collectedInstances.Add(instance);
public void TryAddExportAsset(in AssetExportItem exportItem)
{
_exportItems.Add(exportItem);
}
/// <summary>
/// Collect <see cref="Symbol"/> and its <see cref="SymbolPackage"/>.
/// </summary>
public bool TryAddSymbol(Symbol symbol)
{
Console.WriteLine("Including symbol: " + symbol.Name);
if(!_symbols.Add(symbol))
return false;
var package = symbol.SymbolPackage;
if (!_symbolPackages.TryGetValue(package, out var symbols))
{
symbols = [];
_symbolPackages.Add(package, symbols);
}
symbols.Add(symbol);
foreach(var child in symbol.Children.Values)
{
TryAddSymbol(child.Symbol);
}
return true;
}
public void PrintInfo()
{
Log.Info($"Collected {_collectedInstances.Count} instances for export in {_symbols.Count} different symbols:");
foreach (var resourcePath in ExportItems)
{
Log.Debug($" {resourcePath}");
}
}
/// <summary>
/// Collect <see cref="Asset"/> and its <see cref="SymbolPackage"/>
/// </summary>
public bool TryAddSharedAsset(Asset asset)
{
var relativePathInResourceFolder = Path.GetRelativePath(asset.Package.AssetsFolder, asset.FullPath);
TryAddExportAsset(new AssetExportItem(asset.Package.RootNamespace, relativePathInResourceFolder, asset.FullPath));
// Include related font textures
if (asset.Address.EndsWith(".fnt", StringComparison.OrdinalIgnoreCase))
{
var absolutePathPng = asset.FullPath.Replace(".fnt", ".png");
var relativePathInResourceFolderPng = relativePathInResourceFolder.Replace(".fnt", ".png");
TryAddExportAsset(new AssetExportItem(asset.Package.RootNamespace,
relativePathInResourceFolderPng,
absolutePathPng));
}
// Search and include for shader includes
if (asset.Address.EndsWith(".hlsl", StringComparison.OrdinalIgnoreCase))
{
var shaderText = File.ReadAllText(asset.FullPath);
foreach (var includePath in ShaderCompiler.GetIncludesFrom(shaderText))
{
if (!ShaderCompiler.TryResolveSharedIncludeAsset(includePath, out var includeAsset))
continue;
var relativePathInResourceFolder2 = Path.GetRelativePath(includeAsset.Package.AssetsFolder, includeAsset.FullPath);
TryAddExportAsset(new AssetExportItem(includeAsset.Package.RootNamespace,
relativePathInResourceFolder2,
includeAsset.FullPath));
}
}
AssetPackages.Add(asset.Package);
return true;
}
public bool ContainsSymbolId(Guid symbolId)
{
foreach (var symbol in _symbols)
{
if (symbol.Id == symbolId)
return true;
}
return false;
}
}
private sealed class AssetExportItem(string? packageRootNamespace, string relativePathInResourcesFolder, string absolutePath)
{
private readonly string? _packageRootNamespace = packageRootNamespace;
private readonly string _relativePathInResourcesFolder = relativePathInResourcesFolder;
private readonly string _absolutePath = absolutePath;
// equality operators
public static bool operator ==(AssetExportItem left, AssetExportItem right) => left._absolutePath == right._absolutePath;
public static bool operator !=(AssetExportItem left, AssetExportItem right) => left._absolutePath != right._absolutePath;
public override int GetHashCode() => _absolutePath.GetHashCode();
public override bool Equals(object? obj) => obj is AssetExportItem other && other == this;
public override string ToString() => $"\"{_relativePathInResourcesFolder}\" (\"{_absolutePath}\")";
private bool TryCopyTo(string exportDir, ref int successCount)
{
//var targetPath = Path.Combine(exportDir, resourcePath.RelativePath);
var targetPath = GetTargetPathDir(exportDir);
var success = TryCopyFile(_absolutePath, targetPath);
// Use bit operations to et successInt to 0 on failure
Interlocked.And(ref successCount, Convert.ToInt32(success));
if (!success)
{
Log.Error($"Failed to copy resource file for export: {_absolutePath}");
return false;
}
return true;
}
private string GetTargetPathDir(string exportDir)
{
if (_packageRootNamespace != null)
{
return Path.Combine(exportDir, FileLocations.OperatorsSubFolder,
_packageRootNamespace,
FileLocations.AssetsSubfolder,
_relativePathInResourcesFolder);
}
return Path.Combine(exportDir, _relativePathInResourcesFolder);
}
public static bool TryCopyItems(IEnumerable<AssetExportItem> exportItems, string exportDir)
{
var successInt = Convert.ToInt32(true);
exportItems
.AsParallel()
.ForAll(item => item.TryCopyTo(exportDir, ref successInt));
return Convert.ToBoolean(successInt);
}
}
}