feat(meshopt): add typed enums and improve naming logic

Introduce SimplifyOptions and SimplifyVertexOptions enums for mesh simplification, replacing magic numbers with type-safe flags. Update MeshOptApi with strongly-typed wrapper methods. Refactor MeshletUtility to use new enums and nullable delegates, and fix stride calculation for pointer arithmetic.

Rename NamingConventions.GetMethodName to GetName, update name removal logic to use "$TBare", and add ALL_CAPS style for constants. Update config files to match new naming conventions and add ALL_CAPS constant rule for meshopt. Refactor BindingParser and related classes to support constant member kind. Apply minor bug fixes and code style improvements throughout.
This commit is contained in:
2026-03-20 15:17:38 +09:00
parent 4a98e44630
commit db0be367ef
10 changed files with 185 additions and 55 deletions

View File

@@ -276,7 +276,7 @@ public sealed class WrapperGeneratorEmitter
{
var func = routed.Function;
var nameOpts = routed.Apply.Opts?.name;
var methodName = naming.GetMethodName(func.Name, nameOpts, routed.TargetStructName);
var methodName = naming.GetName(func.Name, nameOpts, routed.TargetStructName);
// Build the parameter plan: for each native parameter, determine the public type
// and how to pass it to the Api call (applying remaps).

View File

@@ -18,7 +18,6 @@ public sealed class NativeStruct
public required bool IsList { get; init; }
public required bool IsPointerList { get; init; }
public string? ListElementType { get; init; }
public required bool IsElementLike { get; init; }
}
public sealed class NativeEnum
@@ -52,4 +51,5 @@ public enum NativeMemberKind
{
Field,
Property,
Constant,
}

View File

@@ -10,6 +10,7 @@ public sealed class BindingParser
{
public NativeLibrary Parse(string inputDirectory, WrapperConfig config)
{
var members = new List<NativeMember>();
var structs = new List<NativeStruct>();
var enums = new List<NativeEnum>();
var functions = new List<NativeFunction>();
@@ -33,18 +34,17 @@ public sealed class BindingParser
continue;
}
var members = ParseMembers(@struct);
var listInfo = TryMatchList(members);
var structMembers = ParseMembers(@struct);
var listInfo = TryMatchList(structMembers);
structs.Add(new NativeStruct
{
Name = @struct.Identifier.ValueText,
Namespace = namespaceName,
Members = members,
Members = structMembers,
IsList = listInfo.IsList,
IsPointerList = listInfo.IsPointerList,
ListElementType = listInfo.ListElementType,
IsElementLike = members.Any(static m => m.Name == "element" && m.TypeName == "ufbx_element"),
});
}

View File

@@ -17,13 +17,13 @@ public sealed class NamingConventions
///
/// Supported remove tokens:
/// "PREFIX" — strip the config's NativeTypePrefix from the start (e.g. "nvtt", "ufbx_")
/// "NO_PREFIX($TSelf)" — strip the target struct name minus its type prefix from the start,
/// "$TBare" — strip the target struct name minus its type prefix from the start,
/// case-insensitively (e.g. NvttSurface → "Surface" stripped from "SurfaceWidth")
///
/// nameOpts is the dynamic opts.name object from JSON (may be null).
/// If no nameOpts are provided, the name is returned with only the library prefix stripped.
/// </summary>
public string GetMethodName(string nativeFunctionName, dynamic? nameOpts, string targetStructName)
public string GetName(string nativeFunctionName, dynamic? nameOpts, string targetStructName)
{
var name = nativeFunctionName;
@@ -47,13 +47,8 @@ public sealed class NamingConventions
{
name = StripPrefixIgnoreCase(name, _config.NativeTypePrefix);
}
else if (token.StartsWith("NO_PREFIX(", StringComparison.Ordinal) && token.EndsWith(')'))
else if (string.Equals(token, "$TBare", StringComparison.Ordinal))
{
// Extract $TSelf — it's the literal token "NO_PREFIX($TSelf)", so the struct name
// is resolved from the targetStructName argument passed in.
// Strip the config prefix from the struct name to get the "bare" part.
// Try prefix first, then suffix (handles both nvtt "SurfaceWidth"→"Width"
// and ufbx "free_scene"→"free_" styles).
var bareStructName = StripPrefixIgnoreCase(targetStructName, _config.NativeTypePrefix);
// Remove directly, the name maybe nvttSetOutputOptionsOutputHeader, if we only remove prefix and suffix, OutputOptions in the middle will be ignored, so we remove the bare struct name directly, case-insensitively.
@@ -66,7 +61,7 @@ public sealed class NamingConventions
var style = nameOpts.style as string;
if (!string.IsNullOrEmpty(style))
{
if (string.Equals(style, "PascalCase", StringComparison.OrdinalIgnoreCase))
if (string.Equals(style, "PascalCase", StringComparison.Ordinal))
{
int counter = 0;
Span<char> nameSpan = stackalloc char[name.Length];
@@ -83,10 +78,10 @@ public sealed class NamingConventions
if (name[i] == '_')
{
while (name[i] == '_' && i < name.Length)
do
{
i++;
}
} while (i < name.Length && name[i] == '_');
nameSpan[counter] = char.ToUpperInvariant(name[i]);
counter++;
@@ -98,6 +93,54 @@ public sealed class NamingConventions
counter++;
}
name = nameSpan[..counter].ToString();
}
else if (string.Equals(style, "ALL_CAPS", StringComparison.Ordinal))
{
int counter = 0;
Span<char> nameSpan = stackalloc char[name.Length * 2]; // Worst case, every character is uppercase and followed by an underscore.
for (int i = 0; i < name.Length; i++)
{
// ___ to _
if (name[i] == '_')
{
while (i + 1 < name.Length && name[i + 1] == '_')
{
i++;
}
nameSpan[counter] = '_';
counter++;
continue;
}
// AbC to AB_C
if (i > 0 && char.IsUpper(name[i]) && char.IsLower(name[i - 1]))
{
nameSpan[counter] = '_';
counter++;
}
// ABC to ABC
while (i < name.Length && char.IsUpper(name[i]))
{
nameSpan[counter] = name[i];
counter++;
i++;
}
if (i == name.Length)
{
break;
}
nameSpan[counter] = char.ToUpperInvariant(name[i]);
counter++;
}
name = nameSpan[..counter].ToString();
}
}

View File

@@ -24,7 +24,7 @@
"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
"$TBare" // 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"
}
@@ -43,7 +43,7 @@
"name": {
"remove": [
"PREFIX",
"NO_PREFIX($TSelf)"
"$TBare"
],
"style": "PascalCase"
}
@@ -64,6 +64,21 @@
}
}
}
},
{
"filter": "CONST",
"targetType": "MeshOptApi",
"apply": {
"type": "CONST",
"opts": {
"name": {
"remove": [
"PREFIX"
],
"style": "ALL_CAPS"
}
}
}
}
]
}

View File

@@ -68,7 +68,7 @@
"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
"$TBare" // NO_PREFIX(NvttSurface) will change "NvttSurface" to "Surface", the prefix is determined by the "nativeTypePrefix" field at the top level of this config
]
}
}
@@ -86,21 +86,7 @@
"name": {
"remove": [
"PREFIX",
"NO_PREFIX($TSelf)"
]
}
}
}
},
{
"filter": "EXTERN_API",
"targetType": "NvttApi",
"apply": {
"type": "STATIC_METHOD",
"opts": {
"name": {
"remove": [
"PREFIX"
"$TBare"
]
}
}

View File

@@ -68,7 +68,7 @@
"name": {
"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
"$TBare" // 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"
}
@@ -87,7 +87,7 @@
"name": {
"remove": [
"PREFIX",
"NO_PREFIX($TSelf)"
"$TBare"
],
"style": "PascalCase"
}