feat(bindings): update C# wrappers for meshopt, nvtt, ufbx

Refactor and regenerate native C# bindings for Ghost.MeshOptimizer, Ghost.Nvtt, and Ghost.Ufbx to match updated native APIs and improve usability.
- Replace meshoptimizer.dll with newer version.
- Move meshoptimizer functions to static methods in partial class; add new meshlet, simplification, quantization features.
- Remove enum wrappers in favor of constants; delete meshopt_Allocator.cs.
- Regenerate native wrappers with PascalCase naming, XML doc comments, and aggressive inlining.
- Implement IDisposable for resource structs; update configs for naming, documentation, and method mapping.
- Update user code to use new wrapper classes and method names.
- Improve documentation and comments for clarity.

BREAKING CHANGE: API surface changes, wrapper class and method names updated, enum wrappers removed, custom allocator deleted.
This commit is contained in:
2026-03-17 00:19:54 +09:00
parent 9bae3e647e
commit e831b71a79
62 changed files with 3820 additions and 1285 deletions

View File

@@ -215,7 +215,7 @@ public sealed class WrapperGeneratorEmitter
resolver.TryGetBindingStructName(func.Parameters[0].TypeName),
"RETURN_TYPE" =>
resolver.TryGetBindingStructName(func.ReturnType),
_ => null,
_ => targetTypeRule,
};
}
@@ -283,7 +283,11 @@ public sealed class WrapperGeneratorEmitter
var plan = BuildParameterPlan(func, config, routed);
// Comment showing the source function.
writer.WriteLine($"// From: {func.Name}({string.Join(", ", func.Parameters.Select(static p => p.TypeName))})");
writer.WriteLine("/// <summary>");
writer.WriteLine($"/// From: <see cref=\"Api.{func.Name}({string.Join(", ", func.Parameters.Select(static p => p.TypeName))})\" />");
writer.WriteLine("/// </summary>");
writer.WriteLine("[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");
// Signature
var staticModifier = routed.IsInstance ? "" : "static ";

View File

@@ -63,6 +63,45 @@ public sealed class NamingConventions
name = TrimUnderscores(name);
}
var style = nameOpts.style as string;
if (!string.IsNullOrEmpty(style))
{
if (string.Equals(style, "PascalCase", StringComparison.OrdinalIgnoreCase))
{
int counter = 0;
Span<char> nameSpan = stackalloc char[name.Length];
for (int i = 0; i < name.Length; i++)
{
if (i == 0)
{
nameSpan[counter] = char.ToUpperInvariant(name[i]);
counter++;
continue;
}
if (name[i] == '_')
{
while (name[i] == '_' && i < name.Length)
{
i++;
}
nameSpan[counter] = char.ToUpperInvariant(name[i]);
counter++;
continue;
}
nameSpan[counter] = name[i];
counter++;
}
name = nameSpan[..counter].ToString();
}
}
return name;
}

View File

@@ -0,0 +1,69 @@
{
"libraryName": "meshoptimizer",
"nativeNamespace": "Ghost.MeshOptimizer",
"outputNamespace": "Ghost.MeshOptimizer",
"nativeTypePrefix": "meshopt_",
"skipTypes": [
"NativeAnnotationAttribute",
"NativeTypeNameAttribute"
],
"skipFunctions": [],
"actions": [
{
// Instance method: first param is T* of a known binding struct → method on T
"comment": "First param is T* of a known binding struct → instance method on T",
"filter": "EXTERN_API",
"conditions": [ "SELF_PTR" ],
"targetType": "FIRST_PARAM_TYPE",
"apply": {
"type": "INSTANCE_METHOD",
"opts": {
"removeFirstParam": true,
"passAs": "($TSelf*)System.Runtime.CompilerServices.Unsafe.AsPointer(ref this)",
// Change "nvttCreateSurface" to "Create"
"name": {
"remove": [
"PREFIX",
"NO_PREFIX($TSelf)" // NO_PREFIX(NvttSurface) will change "NvttSurface" to "Surface", the prefix is determined by the "nativeTypePrefix" field at the top level of this config
],
"style": "PascalCase"
}
}
}
},
{
// Static method: first param is NOT a known struct pointer, but return type is T* → static on T
"comment": "Return type is T* of a known binding struct → static method on T",
"filter": "EXTERN_API",
"conditions": [ "FIRST_PARAM_OTHER_TYPE", "RETURN_BINDING_TYPE" ],
"targetType": "RETURN_TYPE",
"apply": {
"type": "STATIC_METHOD",
"opts": {
"name": {
"remove": [
"PREFIX",
"NO_PREFIX($TSelf)"
],
"style": "PascalCase"
}
}
}
},
{
"filter": "EXTERN_API",
"targetType": "NvttApi",
"apply": {
"type": "STATIC_METHOD",
"opts": {
"name": {
"remove": [
"PREFIX"
],
"style": "PascalCase"
}
}
}
}
]
}

View File

@@ -91,6 +91,20 @@
}
}
}
},
{
"filter": "EXTERN_API",
"targetType": "NvttApi",
"apply": {
"type": "STATIC_METHOD",
"opts": {
"name": {
"remove": [
"PREFIX"
]
}
}
}
}
]
}

View File

@@ -28,6 +28,31 @@
],
"actions": [
{
// Dispose pattern: void return + T* param + name matches .*free_<Struct> → IDisposable.Dispose on T
"comment": "Dispose pattern: void return + T* param → Dispose method on T",
"filter": "EXTERN_API",
"conditions": [ "VOID_RETURN", "SELF_PTR", "NAME_CONDITION(ufbx_free_$TBare)" ],
"targetType": "FIRST_PARAM_TYPE",
"apply": [
{
"type": "INSTANCE_METHOD",
"opts": {
"removeFirstParam": true,
"passAs": "($TSelf*)System.Runtime.CompilerServices.Unsafe.AsPointer(ref this)",
"name": {
"set": "Dispose"
}
}
},
{
"type": "INHERITANCE",
"opts": {
"baseType": [ "System.IDisposable" ]
}
}
]
},
{
// Instance method: first param is T* of a known binding struct → method on T
"comment": "First param is T* of a known binding struct → instance method on T",
@@ -44,7 +69,8 @@
"remove": [
"PREFIX",
"NO_PREFIX($TSelf)" // NO_PREFIX(ufbx_scene) will output "scene" change "free_scene" to "free", the prefix is determined by the "nativeTypePrefix" field at the top level of this config
]
],
"style": "PascalCase"
}
}
}
@@ -62,7 +88,23 @@
"remove": [
"PREFIX",
"NO_PREFIX($TSelf)"
]
],
"style": "PascalCase"
}
}
}
},
{
"filter": "EXTERN_API",
"targetType": "UfbxApi",
"apply": {
"type": "STATIC_METHOD",
"opts": {
"name": {
"remove": [
"PREFIX"
],
"style": "PascalCase"
}
}
}