mirror of
https://gitea.tendokyu.moe/Kayori/Medusa.net.git
synced 2026-09-27 17:28:01 +03:00
- Refactor handler dispatch to use a parsed GameModel instead of raw model strings (Handler.cs, HandlerService.cs, new GameModel.cs) - Add a Unity asset-extraction subsystem (Abstractions/Extractors/Unity) - Add WriteEventLogHandler, XrpcDouble serialization type - Rename EaCoin*Handler -> *EaCoinHandler for naming consistency - Add PIN/username update and forgot/reset password flows, with a console-logging IEmailSender until real email is wired up - Add a settings page and a theming pass (theme tokens, tailwind config) - Add plugin nav/route/UI-manifest frontend types
79 lines
3.4 KiB
C#
79 lines
3.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Abstractions.Extractors.Unity.Services;
|
|
using AssetsTools.NET;
|
|
|
|
namespace Abstractions.Extractors.Unity;
|
|
|
|
/// <summary>
|
|
/// Exports mesh geometry to an .obj file. AssetsTools.NET's raw typetree doesn't decode Unity's
|
|
/// compressed GPU vertex buffer format, so this only writes real geometry for meshes old enough to
|
|
/// still carry flat m_Vertices/m_Triangles fields. Anything using the modern packed m_VertexData
|
|
/// layout falls through to a plain typetree dump instead.
|
|
/// </summary>
|
|
public sealed class MeshExtractor : IAssetExtractor
|
|
{
|
|
public IReadOnlyCollection<string> HandledTypes { get; } = ["Mesh"];
|
|
|
|
public async Task<bool> ExtractAsync(ExtractionContext context, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var baseField = context.BaseField;
|
|
var verticesField = baseField.Get("m_Vertices");
|
|
var trianglesField = baseField.Get("m_Triangles");
|
|
|
|
var vertices = verticesField.IsDummy ? [] : ReadVector3Array(verticesField);
|
|
var triangles = trianglesField.IsDummy ? [] : BundleLoader.ArrayElements(trianglesField).Select(f => f.AsUInt).ToList();
|
|
|
|
var meshData = new Dictionary<string, object?>
|
|
{
|
|
["name"] = baseField.Get("m_Name") is { IsDummy: false } n ? n.AsString : "Unknown",
|
|
["vertex_count"] = vertices.Count,
|
|
["triangle_count"] = triangles.Count / 3,
|
|
["submesh_count"] = BundleLoader.ArrayElements(baseField.Get("m_SubMeshes")).Count(),
|
|
["blend_shapes"] = BundleLoader.ArrayElements(baseField.Get("m_Shapes").Get("shapes")).Count(),
|
|
["bone_count"] = BundleLoader.ArrayElements(baseField.Get("m_BoneNameHashes")).Count(),
|
|
};
|
|
|
|
if (vertices.Count > 0)
|
|
{
|
|
var obj = new StringBuilder();
|
|
obj.AppendLine($"# Mesh: {meshData["name"]}");
|
|
obj.AppendLine($"# Vertices: {vertices.Count}");
|
|
foreach (var v in vertices)
|
|
obj.AppendLine(FormattableString.Invariant($"v {v.X:F6} {v.Y:F6} {v.Z:F6}"));
|
|
|
|
for (var i = 0; i + 2 < triangles.Count; i += 3)
|
|
obj.AppendLine($"f {triangles[i] + 1} {triangles[i + 1] + 1} {triangles[i + 2] + 1}");
|
|
|
|
await ObjectSerializer.WriteTextAsync(obj.ToString(), context.OutputPathNoExtension + ".obj", cancellationToken);
|
|
}
|
|
|
|
await ObjectSerializer.WriteJsonAsync(meshData, context.OutputPathNoExtension + "_info.json", cancellationToken);
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
return await FallbackExtractor.WriteTypeTreeDumpAsync(context, cancellationToken);
|
|
}
|
|
}
|
|
|
|
private static List<(float X, float Y, float Z)> ReadVector3Array(AssetTypeValueField verticesField)
|
|
{
|
|
var result = new List<(float, float, float)>();
|
|
foreach (var v in BundleLoader.ArrayElements(verticesField))
|
|
{
|
|
var x = v.Get("x") is { IsDummy: false } xf ? xf.AsFloat : 0f;
|
|
var y = v.Get("y") is { IsDummy: false } yf ? yf.AsFloat : 0f;
|
|
var z = v.Get("z") is { IsDummy: false } zf ? zf.AsFloat : 0f;
|
|
result.Add((x, y, z));
|
|
}
|
|
return result;
|
|
}
|
|
}
|