You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
5.0 KiB
137 lines
5.0 KiB
using System.Diagnostics.CodeAnalysis; |
|
using System.IO.Compression; |
|
using System.Numerics; |
|
using System.Text.Json; |
|
using Elements.Assets; |
|
using SharpGLTF.Materials; |
|
using SharpGLTF.Schema2; |
|
|
|
namespace Res2tf; |
|
|
|
public class ResonitePackage : IDisposable |
|
{ |
|
readonly ZipArchive _zip; |
|
|
|
public DataTree Tree { get; } |
|
public BsonValue Bson => Tree.Bson; |
|
public Dictionary<string, object> Assets { get; } = []; |
|
|
|
public ResonitePackage(string path) |
|
{ |
|
_zip = new ZipArchive(File.OpenRead(path)); |
|
|
|
using var mainRecord = _zip.GetEntry("R-Main.record")!.Open(); |
|
var mainDocument = JsonDocument.Parse(mainRecord); |
|
var mainAssetUri = mainDocument.RootElement.GetProperty("assetUri").GetString()!; |
|
|
|
Tree = new(ResolveToStream(mainAssetUri)); |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
_zip.Dispose(); |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
public byte[] ResolveToBytes(string uriString) |
|
{ |
|
using var stream = ResolveToStream(uriString, true); |
|
return ((MemoryStream)stream).ToArray(); |
|
} |
|
public Stream ResolveToStream(string uriString, bool copy = false) |
|
{ |
|
// URIs coming from the DataTree are gonna start with an '@' symbol, so just strip it. |
|
if (uriString.StartsWith('@')) uriString = uriString[1..]; |
|
|
|
var uri = new Uri(uriString); |
|
if (uri.Scheme != "packdb") throw new ArgumentException("Not a packdb URI"); |
|
if (!uri.AbsolutePath.StartsWith('/')) throw new ArgumentException("Not valid packdb URI"); |
|
|
|
var stream = _zip.GetEntry($"Assets{uri.AbsolutePath}")!.Open(); |
|
if (!copy) return stream; |
|
else using (stream) { |
|
var bytes = new MemoryStream(); |
|
stream.CopyTo(bytes); |
|
bytes.Seek(0, SeekOrigin.Begin); |
|
return bytes; |
|
} |
|
} |
|
|
|
public void ProcessAssets(ModelRoot model) |
|
{ |
|
// Process static assets first. |
|
var staticMesh = Tree.GetTypeId("[FrooxEngine]FrooxEngine.StaticMesh"); |
|
var staticTexture = Tree.GetTypeId("[FrooxEngine]FrooxEngine.StaticTexture2D"); |
|
foreach (var asset in Bson["Assets"].AsList()) { |
|
var type = (int)asset["Type"]; |
|
|
|
if (type == staticMesh) { |
|
var id = (string)asset["Data"]["ID"]; |
|
var uri = (string)asset["Data"]["URL"]["Data"]; |
|
var mesh = new MeshAsset(new MeshX(ResolveToStream(uri, true)), model); |
|
Assets[id] = mesh; |
|
} |
|
|
|
else if (type == staticTexture) { |
|
var id = (string)asset["Data"]["ID"]; |
|
var uri = (string)asset["Data"]["URL"]["Data"]; |
|
// For now, texture "asset" is just its URI, material will load it from there. |
|
Assets[id] = uri; |
|
} |
|
} |
|
|
|
var toonMaterial = Tree.GetTypeId("[FrooxEngine]FrooxEngine.XiexeToonMaterial"); |
|
var unlitMaterial = Tree.GetTypeId("[FrooxEngine]FrooxEngine.UnlitMaterial"); |
|
foreach (var asset in Bson["Assets"].AsList()) { |
|
var type = (int)asset["Type"]; |
|
|
|
if (type == toonMaterial) { |
|
var id = (string)asset["Data"]["ID"]; |
|
|
|
var color = ResoniteToColor(asset["Data"]["Color"]); |
|
|
|
var textureId = (string?)asset["Data"]["MainTexture"]["Data"]; |
|
var texturePath = (textureId != null) ? (string)Assets[textureId] : null; |
|
var texture = (texturePath != null) ? ResolveToBytes(texturePath) : null; |
|
|
|
var material = new MaterialBuilder() |
|
.WithDoubleSide(false) |
|
.WithBaseColor(color) |
|
.WithChannelImage(KnownChannel.BaseColor, texture); |
|
Assets[id] = material; |
|
} |
|
|
|
else if (type == unlitMaterial) { |
|
var id = (string)asset["Data"]["ID"]; |
|
|
|
var color = ResoniteToColor(asset["Data"]["TintColor"]); |
|
|
|
var textureId = (string)asset["Data"]["Texture"]["Data"]; |
|
var texturePath = (textureId != null) ? (string)Assets[textureId] : null; |
|
var texture = (texturePath != null) ? ResolveToBytes(texturePath) : null; |
|
|
|
var material = new MaterialBuilder() |
|
.WithDoubleSide(false) |
|
.WithBaseColor(color) |
|
.WithChannelImage(KnownChannel.BaseColor, texture); |
|
Assets[id] = material; |
|
} |
|
} |
|
} |
|
|
|
public bool TryGetAsset<TAsset>(string assetId, [NotNullWhen(true)] out TAsset? asset) |
|
{ |
|
if (Assets.TryGetValue(assetId, out var assetUntyped) && (assetUntyped is TAsset a)) |
|
{ asset = a; return true; } else { asset = default; return false; } |
|
} |
|
|
|
static Vector4 ResoniteToColor(BsonValue bson) |
|
{ |
|
var list = bson["Data"].AsList(); |
|
var r = Math.Clamp((float)list[0], 0.0f, 1.0f); |
|
var g = Math.Clamp((float)list[1], 0.0f, 1.0f); |
|
var b = Math.Clamp((float)list[2], 0.0f, 1.0f); |
|
var a = Math.Clamp((float)list[3], 0.0f, 1.0f); |
|
return new(r, g, b, a); |
|
} |
|
}
|
|
|