initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
39
modules/mono/glue/GodotSharp/.editorconfig
Normal file
39
modules/mono/glue/GodotSharp/.editorconfig
Normal file
@@ -0,0 +1,39 @@
|
||||
# This file should only contain severity override to diagnostics, in order to make generated and
|
||||
# interop code compilation readable. We want to limit the scope of suppression as much as possible.
|
||||
|
||||
[**/Generated/**.cs]
|
||||
# IDE1006: Naming rule violation
|
||||
dotnet_diagnostic.IDE1006.severity = none
|
||||
# CA1062: Validate parameter is non-null before using it
|
||||
# Useful for generated code, as it disables nullable
|
||||
dotnet_diagnostic.CA1062.severity = error
|
||||
# CA1069: Enums should not have duplicate values
|
||||
dotnet_diagnostic.CA1069.severity = none
|
||||
# CA1707: Identifiers should not contain underscores
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
# CA1708: Identifiers should differ by more than case
|
||||
dotnet_diagnostic.CA1708.severity = none
|
||||
# CA1711: Identifiers should not have incorrect suffix
|
||||
# Disable warning for suffixes like EventHandler, Flags, Enum, etc.
|
||||
dotnet_diagnostic.CA1711.severity = none
|
||||
# CA1716: Identifiers should not match keywords
|
||||
# This is suppressed, because it will report `@event` as well as `event`
|
||||
dotnet_diagnostic.CA1716.severity = none
|
||||
# CA1720: Identifiers should not contain type names
|
||||
dotnet_diagnostic.CA1720.severity = none
|
||||
# CS1591: Missing XML comment for publicly visible type or member
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
# CS1573: Parameter has no matching param tag in the XML comment
|
||||
dotnet_diagnostic.CS1573.severity = none
|
||||
# TODO: Temporary change to not pollute the warnings, but this denotes with ou doc generation
|
||||
# CS1734: XML comment on '' has a paramref tag for '', but there is no parameter by that name
|
||||
dotnet_diagnostic.CS1734.severity = none
|
||||
|
||||
[GodotSharp/Core/NativeInterop/**.cs]
|
||||
# CA1720: Identifiers should not contain type names
|
||||
dotnet_diagnostic.CA1720.severity = none
|
||||
|
||||
[GodotSharp/Core/**.cs]
|
||||
# CS1591: Missing XML comment for publicly visible type or member
|
||||
# TODO: Temporary change to not pollute the warnings, but we need to document public APIs
|
||||
dotnet_diagnostic.CS1591.severity = suggestion
|
||||
3
modules/mono/glue/GodotSharp/.gitignore
vendored
Normal file
3
modules/mono/glue/GodotSharp/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Generated Godot API sources directories
|
||||
GodotSharp/Generated
|
||||
GodotSharpEditor/Generated
|
||||
@@ -0,0 +1,5 @@
|
||||
<assembly name="System.Runtime.InteropServices">
|
||||
<member name="T:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute">
|
||||
<attribute ctor="M:JetBrains.Annotations.MeansImplicitUseAttribute.#ctor" />
|
||||
</member>
|
||||
</assembly>
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Godot.SourceGenerators.Internal;
|
||||
|
||||
internal readonly struct CallbacksData
|
||||
{
|
||||
public CallbacksData(INamedTypeSymbol nativeTypeSymbol, INamedTypeSymbol funcStructSymbol)
|
||||
{
|
||||
NativeTypeSymbol = nativeTypeSymbol;
|
||||
FuncStructSymbol = funcStructSymbol;
|
||||
Methods = NativeTypeSymbol.GetMembers()
|
||||
.Where(symbol => symbol is IMethodSymbol { IsPartialDefinition: true })
|
||||
.Cast<IMethodSymbol>()
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
public INamedTypeSymbol NativeTypeSymbol { get; }
|
||||
|
||||
public INamedTypeSymbol FuncStructSymbol { get; }
|
||||
|
||||
public ImmutableArray<IMethodSymbol> Methods { get; }
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Godot.SourceGenerators.Internal;
|
||||
|
||||
internal static class Common
|
||||
{
|
||||
public static void ReportNonPartialUnmanagedCallbacksClass(
|
||||
GeneratorExecutionContext context,
|
||||
ClassDeclarationSyntax cds, INamedTypeSymbol symbol
|
||||
)
|
||||
{
|
||||
string message =
|
||||
"Missing partial modifier on declaration of type '" +
|
||||
$"{symbol.FullQualifiedNameOmitGlobal()}' which has attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
|
||||
|
||||
string description = $"{message}. Classes with attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' " +
|
||||
"must be declared with the partial modifier.";
|
||||
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0001",
|
||||
title: message,
|
||||
messageFormat: message,
|
||||
category: "Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true,
|
||||
description),
|
||||
cds.GetLocation(),
|
||||
cds.SyntaxTree.FilePath));
|
||||
}
|
||||
|
||||
public static void ReportNonPartialUnmanagedCallbacksOuterClass(
|
||||
GeneratorExecutionContext context,
|
||||
TypeDeclarationSyntax outerTypeDeclSyntax
|
||||
)
|
||||
{
|
||||
var outerSymbol = context.Compilation
|
||||
.GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
|
||||
.GetDeclaredSymbol(outerTypeDeclSyntax);
|
||||
|
||||
string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
|
||||
namedTypeSymbol.FullQualifiedNameOmitGlobal() :
|
||||
"type not found";
|
||||
|
||||
string message =
|
||||
$"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
|
||||
$"which contains one or more subclasses with attribute " +
|
||||
$"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
|
||||
|
||||
string description = $"{message}. Classes with attribute " +
|
||||
$"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' and their " +
|
||||
"containing types must be declared with the partial modifier.";
|
||||
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0002",
|
||||
title: message,
|
||||
messageFormat: message,
|
||||
category: "Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true,
|
||||
description),
|
||||
outerTypeDeclSyntax.GetLocation(),
|
||||
outerTypeDeclSyntax.SyntaxTree.FilePath));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Godot.SourceGenerators.Internal;
|
||||
|
||||
internal static class ExtensionMethods
|
||||
{
|
||||
public static AttributeData? GetGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
|
||||
=> symbol.GetAttributes()
|
||||
.FirstOrDefault(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false);
|
||||
|
||||
private static bool HasGenerateUnmanagedCallbacksAttribute(
|
||||
this ClassDeclarationSyntax cds, Compilation compilation,
|
||||
out INamedTypeSymbol? symbol
|
||||
)
|
||||
{
|
||||
var sm = compilation.GetSemanticModel(cds.SyntaxTree);
|
||||
|
||||
var classTypeSymbol = sm.GetDeclaredSymbol(cds);
|
||||
if (classTypeSymbol == null)
|
||||
{
|
||||
symbol = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!classTypeSymbol.GetAttributes()
|
||||
.Any(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false))
|
||||
{
|
||||
symbol = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
symbol = classTypeSymbol;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
|
||||
=> symbol.FullQualifiedNameOmitGlobal() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
|
||||
|
||||
public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses(
|
||||
this IEnumerable<ClassDeclarationSyntax> source,
|
||||
Compilation compilation
|
||||
)
|
||||
{
|
||||
foreach (var cds in source)
|
||||
{
|
||||
if (cds.HasGenerateUnmanagedCallbacksAttribute(compilation, out var symbol))
|
||||
yield return (cds, symbol!);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsNested(this TypeDeclarationSyntax cds)
|
||||
=> cds.Parent is TypeDeclarationSyntax;
|
||||
|
||||
public static bool IsPartial(this TypeDeclarationSyntax cds)
|
||||
=> cds.Modifiers.Any(SyntaxKind.PartialKeyword);
|
||||
|
||||
public static bool AreAllOuterTypesPartial(
|
||||
this TypeDeclarationSyntax cds,
|
||||
out TypeDeclarationSyntax? typeMissingPartial
|
||||
)
|
||||
{
|
||||
SyntaxNode? outerSyntaxNode = cds.Parent;
|
||||
|
||||
while (outerSyntaxNode is TypeDeclarationSyntax outerTypeDeclSyntax)
|
||||
{
|
||||
if (!outerTypeDeclSyntax.IsPartial())
|
||||
{
|
||||
typeMissingPartial = outerTypeDeclSyntax;
|
||||
return false;
|
||||
}
|
||||
|
||||
outerSyntaxNode = outerSyntaxNode.Parent;
|
||||
}
|
||||
|
||||
typeMissingPartial = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetDeclarationKeyword(this INamedTypeSymbol namedTypeSymbol)
|
||||
{
|
||||
string? keyword = namedTypeSymbol.DeclaringSyntaxReferences
|
||||
.OfType<TypeDeclarationSyntax>().FirstOrDefault()?
|
||||
.Keyword.Text;
|
||||
|
||||
return keyword ?? namedTypeSymbol.TypeKind switch
|
||||
{
|
||||
TypeKind.Interface => "interface",
|
||||
TypeKind.Struct => "struct",
|
||||
_ => "class"
|
||||
};
|
||||
}
|
||||
|
||||
private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
|
||||
SymbolDisplayFormat.FullyQualifiedFormat
|
||||
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
|
||||
|
||||
private static SymbolDisplayFormat FullyQualifiedFormatIncludeGlobal { get; } =
|
||||
SymbolDisplayFormat.FullyQualifiedFormat
|
||||
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Included);
|
||||
|
||||
public static string FullQualifiedNameOmitGlobal(this ITypeSymbol symbol)
|
||||
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
|
||||
|
||||
public static string FullQualifiedNameOmitGlobal(this INamespaceSymbol namespaceSymbol)
|
||||
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
|
||||
|
||||
public static string FullQualifiedNameIncludeGlobal(this ITypeSymbol symbol)
|
||||
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatIncludeGlobal);
|
||||
|
||||
public static string FullQualifiedNameIncludeGlobal(this INamespaceSymbol namespaceSymbol)
|
||||
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatIncludeGlobal);
|
||||
|
||||
public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
|
||||
=> qualifiedName
|
||||
// AddSource() doesn't support @ prefix
|
||||
.Replace("@", "")
|
||||
// AddSource() doesn't support angle brackets
|
||||
.Replace("<", "(Of ")
|
||||
.Replace(">", ")");
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Godot.SourceGenerators.Internal;
|
||||
|
||||
internal static class GeneratorClasses
|
||||
{
|
||||
public const string GenerateUnmanagedCallbacksAttr = "Godot.SourceGenerators.Internal.GenerateUnmanagedCallbacksAttribute";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,478 @@
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Godot.SourceGenerators.Internal;
|
||||
|
||||
[Generator]
|
||||
public class UnmanagedCallbacksGenerator : ISourceGenerator
|
||||
{
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForPostInitialization(ctx => { GenerateAttribute(ctx); });
|
||||
}
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
INamedTypeSymbol[] unmanagedCallbacksClasses = context
|
||||
.Compilation.SyntaxTrees
|
||||
.SelectMany(tree =>
|
||||
tree.GetRoot().DescendantNodes()
|
||||
.OfType<ClassDeclarationSyntax>()
|
||||
.SelectUnmanagedCallbacksClasses(context.Compilation)
|
||||
// Report and skip non-partial classes
|
||||
.Where(x =>
|
||||
{
|
||||
if (x.cds.IsPartial())
|
||||
{
|
||||
if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
|
||||
{
|
||||
Common.ReportNonPartialUnmanagedCallbacksOuterClass(context, typeMissingPartial!);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common.ReportNonPartialUnmanagedCallbacksClass(context, x.cds, x.symbol);
|
||||
return false;
|
||||
})
|
||||
.Select(x => x.symbol)
|
||||
)
|
||||
.Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
|
||||
.ToArray();
|
||||
|
||||
foreach (var symbol in unmanagedCallbacksClasses)
|
||||
{
|
||||
var attr = symbol.GetGenerateUnmanagedCallbacksAttribute();
|
||||
if (attr == null || attr.ConstructorArguments.Length != 1)
|
||||
{
|
||||
// TODO: Report error or throw exception, this is an invalid case and should never be reached
|
||||
System.Diagnostics.Debug.Fail("FAILED!");
|
||||
continue;
|
||||
}
|
||||
|
||||
var funcStructType = (INamedTypeSymbol?)attr.ConstructorArguments[0].Value;
|
||||
if (funcStructType == null)
|
||||
{
|
||||
// TODO: Report error or throw exception, this is an invalid case and should never be reached
|
||||
System.Diagnostics.Debug.Fail("FAILED!");
|
||||
continue;
|
||||
}
|
||||
|
||||
var data = new CallbacksData(symbol, funcStructType);
|
||||
GenerateInteropMethodImplementations(context, data);
|
||||
GenerateUnmanagedCallbacksStruct(context, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateAttribute(GeneratorPostInitializationContext context)
|
||||
{
|
||||
string source = @"using System;
|
||||
|
||||
namespace Godot.SourceGenerators.Internal
|
||||
{
|
||||
internal class GenerateUnmanagedCallbacksAttribute : Attribute
|
||||
{
|
||||
public Type FuncStructType { get; }
|
||||
|
||||
public GenerateUnmanagedCallbacksAttribute(Type funcStructType)
|
||||
{
|
||||
FuncStructType = funcStructType;
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
context.AddSource("GenerateUnmanagedCallbacksAttribute.generated",
|
||||
SourceText.From(source, Encoding.UTF8));
|
||||
}
|
||||
|
||||
private void GenerateInteropMethodImplementations(GeneratorExecutionContext context, CallbacksData data)
|
||||
{
|
||||
var symbol = data.NativeTypeSymbol;
|
||||
|
||||
INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
|
||||
string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
|
||||
namespaceSymbol.FullQualifiedNameOmitGlobal() :
|
||||
string.Empty;
|
||||
bool hasNamespace = classNs.Length != 0;
|
||||
bool isInnerClass = symbol.ContainingType != null;
|
||||
|
||||
var source = new StringBuilder();
|
||||
var methodSource = new StringBuilder();
|
||||
var methodCallArguments = new StringBuilder();
|
||||
var methodSourceAfterCall = new StringBuilder();
|
||||
|
||||
source.Append(
|
||||
@"using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.Bridge;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
|
||||
|
||||
");
|
||||
|
||||
if (hasNamespace)
|
||||
{
|
||||
source.Append("namespace ");
|
||||
source.Append(classNs);
|
||||
source.Append("\n{\n");
|
||||
}
|
||||
|
||||
if (isInnerClass)
|
||||
{
|
||||
var containingType = symbol.ContainingType;
|
||||
AppendPartialContainingTypeDeclarations(containingType);
|
||||
|
||||
void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
|
||||
{
|
||||
if (containingType == null)
|
||||
return;
|
||||
|
||||
AppendPartialContainingTypeDeclarations(containingType.ContainingType);
|
||||
|
||||
source.Append("partial ");
|
||||
source.Append(containingType.GetDeclarationKeyword());
|
||||
source.Append(" ");
|
||||
source.Append(containingType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
|
||||
source.Append("\n{\n");
|
||||
}
|
||||
}
|
||||
|
||||
source.Append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
|
||||
source.Append($"unsafe partial class {symbol.Name}\n");
|
||||
source.Append("{\n");
|
||||
source.Append($" private static {data.FuncStructSymbol.FullQualifiedNameIncludeGlobal()} _unmanagedCallbacks;\n\n");
|
||||
|
||||
foreach (var callback in data.Methods)
|
||||
{
|
||||
methodSource.Clear();
|
||||
methodCallArguments.Clear();
|
||||
methodSourceAfterCall.Clear();
|
||||
|
||||
source.Append(" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]\n");
|
||||
source.Append($" {SyntaxFacts.GetText(callback.DeclaredAccessibility)} ");
|
||||
|
||||
if (callback.IsStatic)
|
||||
source.Append("static ");
|
||||
|
||||
source.Append("partial ");
|
||||
source.Append(callback.ReturnType.FullQualifiedNameIncludeGlobal());
|
||||
source.Append(' ');
|
||||
source.Append(callback.Name);
|
||||
source.Append('(');
|
||||
|
||||
for (int i = 0; i < callback.Parameters.Length; i++)
|
||||
{
|
||||
var parameter = callback.Parameters[i];
|
||||
|
||||
AppendRefKind(source, parameter.RefKind, parameter.ScopedKind);
|
||||
source.Append(' ');
|
||||
source.Append(parameter.Type.FullQualifiedNameIncludeGlobal());
|
||||
source.Append(' ');
|
||||
source.Append(parameter.Name);
|
||||
|
||||
if (parameter.RefKind == RefKind.Out)
|
||||
{
|
||||
// Only assign default if the parameter won't be passed by-ref or copied later.
|
||||
if (IsGodotInteropStruct(parameter.Type))
|
||||
methodSource.Append($" {parameter.Name} = default;\n");
|
||||
}
|
||||
|
||||
if (IsByRefParameter(parameter))
|
||||
{
|
||||
if (IsGodotInteropStruct(parameter.Type))
|
||||
{
|
||||
methodSource.Append(" ");
|
||||
AppendCustomUnsafeAsPointer(methodSource, parameter, out string varName);
|
||||
methodCallArguments.Append(varName);
|
||||
}
|
||||
else if (parameter.Type.IsValueType)
|
||||
{
|
||||
methodSource.Append(" ");
|
||||
AppendCopyToStackAndGetPointer(methodSource, parameter, out string varName);
|
||||
methodCallArguments.Append($"&{varName}");
|
||||
|
||||
if (parameter.RefKind is RefKind.Out or RefKind.Ref)
|
||||
{
|
||||
methodSourceAfterCall.Append($" {parameter.Name} = {varName};\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's a by-ref param and we can't get the pointer
|
||||
// just pass it by-ref and let it be pinned.
|
||||
AppendRefKind(methodCallArguments, parameter.RefKind, parameter.ScopedKind)
|
||||
.Append(' ')
|
||||
.Append(parameter.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
methodCallArguments.Append(parameter.Name);
|
||||
}
|
||||
|
||||
if (i < callback.Parameters.Length - 1)
|
||||
{
|
||||
source.Append(", ");
|
||||
methodCallArguments.Append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
source.Append(")\n");
|
||||
source.Append(" {\n");
|
||||
|
||||
source.Append(methodSource);
|
||||
source.Append(" ");
|
||||
|
||||
if (!callback.ReturnsVoid)
|
||||
{
|
||||
if (methodSourceAfterCall.Length != 0)
|
||||
source.Append($"{callback.ReturnType.FullQualifiedNameIncludeGlobal()} ret = ");
|
||||
else
|
||||
source.Append("return ");
|
||||
}
|
||||
|
||||
source.Append($"_unmanagedCallbacks.{callback.Name}(");
|
||||
source.Append(methodCallArguments);
|
||||
source.Append(");\n");
|
||||
|
||||
if (methodSourceAfterCall.Length != 0)
|
||||
{
|
||||
source.Append(methodSourceAfterCall);
|
||||
|
||||
if (!callback.ReturnsVoid)
|
||||
source.Append(" return ret;\n");
|
||||
}
|
||||
|
||||
source.Append(" }\n\n");
|
||||
}
|
||||
|
||||
source.Append("}\n");
|
||||
|
||||
if (isInnerClass)
|
||||
{
|
||||
var containingType = symbol.ContainingType;
|
||||
|
||||
while (containingType != null)
|
||||
{
|
||||
source.Append("}\n"); // outer class
|
||||
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNamespace)
|
||||
source.Append("\n}");
|
||||
|
||||
source.Append("\n\n#pragma warning restore CA1707\n");
|
||||
|
||||
context.AddSource($"{data.NativeTypeSymbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()}.generated",
|
||||
SourceText.From(source.ToString(), Encoding.UTF8));
|
||||
}
|
||||
|
||||
private void GenerateUnmanagedCallbacksStruct(GeneratorExecutionContext context, CallbacksData data)
|
||||
{
|
||||
var symbol = data.FuncStructSymbol;
|
||||
|
||||
INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
|
||||
string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
|
||||
namespaceSymbol.FullQualifiedNameOmitGlobal() :
|
||||
string.Empty;
|
||||
bool hasNamespace = classNs.Length != 0;
|
||||
bool isInnerClass = symbol.ContainingType != null;
|
||||
|
||||
var source = new StringBuilder();
|
||||
|
||||
source.Append(
|
||||
@"using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
|
||||
|
||||
");
|
||||
if (hasNamespace)
|
||||
{
|
||||
source.Append("namespace ");
|
||||
source.Append(classNs);
|
||||
source.Append("\n{\n");
|
||||
}
|
||||
|
||||
if (isInnerClass)
|
||||
{
|
||||
var containingType = symbol.ContainingType;
|
||||
AppendPartialContainingTypeDeclarations(containingType);
|
||||
|
||||
void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
|
||||
{
|
||||
if (containingType == null)
|
||||
return;
|
||||
|
||||
AppendPartialContainingTypeDeclarations(containingType.ContainingType);
|
||||
|
||||
source.Append("partial ");
|
||||
source.Append(containingType.GetDeclarationKeyword());
|
||||
source.Append(" ");
|
||||
source.Append(containingType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
|
||||
source.Append("\n{\n");
|
||||
}
|
||||
}
|
||||
|
||||
source.Append("[StructLayout(LayoutKind.Sequential)]\n");
|
||||
source.Append($"unsafe partial struct {symbol.Name}\n{{\n");
|
||||
|
||||
foreach (var callback in data.Methods)
|
||||
{
|
||||
source.Append(" ");
|
||||
source.Append(callback.DeclaredAccessibility == Accessibility.Public ? "public " : "internal ");
|
||||
|
||||
source.Append("delegate* unmanaged<");
|
||||
|
||||
foreach (var parameter in callback.Parameters)
|
||||
{
|
||||
if (IsByRefParameter(parameter))
|
||||
{
|
||||
if (IsGodotInteropStruct(parameter.Type) || parameter.Type.IsValueType)
|
||||
{
|
||||
AppendPointerType(source, parameter.Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's a by-ref param and we can't get the pointer
|
||||
// just pass it by-ref and let it be pinned.
|
||||
AppendRefKind(source, parameter.RefKind, parameter.ScopedKind)
|
||||
.Append(' ')
|
||||
.Append(parameter.Type.FullQualifiedNameIncludeGlobal());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
source.Append(parameter.Type.FullQualifiedNameIncludeGlobal());
|
||||
}
|
||||
|
||||
source.Append(", ");
|
||||
}
|
||||
|
||||
source.Append(callback.ReturnType.FullQualifiedNameIncludeGlobal());
|
||||
source.Append($"> {callback.Name};\n");
|
||||
}
|
||||
|
||||
source.Append("}\n");
|
||||
|
||||
if (isInnerClass)
|
||||
{
|
||||
var containingType = symbol.ContainingType;
|
||||
|
||||
while (containingType != null)
|
||||
{
|
||||
source.Append("}\n"); // outer class
|
||||
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNamespace)
|
||||
source.Append("}\n");
|
||||
|
||||
source.Append("\n#pragma warning restore CA1707\n");
|
||||
|
||||
context.AddSource($"{symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()}.generated",
|
||||
SourceText.From(source.ToString(), Encoding.UTF8));
|
||||
}
|
||||
|
||||
private static bool IsGodotInteropStruct(ITypeSymbol type) =>
|
||||
_godotInteropStructs.Contains(type.FullQualifiedNameOmitGlobal());
|
||||
|
||||
private static bool IsByRefParameter(IParameterSymbol parameter) =>
|
||||
parameter.RefKind is RefKind.In or RefKind.Out or RefKind.Ref;
|
||||
|
||||
private static StringBuilder AppendRefKind(StringBuilder source, RefKind refKind, ScopedKind scopedKind)
|
||||
{
|
||||
return (refKind, scopedKind) switch
|
||||
{
|
||||
(RefKind.Out, _) => source.Append("out"),
|
||||
(RefKind.In, ScopedKind.ScopedRef) => source.Append("scoped in"),
|
||||
(RefKind.In, _) => source.Append("in"),
|
||||
(RefKind.Ref, ScopedKind.ScopedRef) => source.Append("scoped ref"),
|
||||
(RefKind.Ref, _) => source.Append("ref"),
|
||||
_ => source,
|
||||
};
|
||||
}
|
||||
|
||||
private static void AppendPointerType(StringBuilder source, ITypeSymbol type)
|
||||
{
|
||||
source.Append(type.FullQualifiedNameIncludeGlobal());
|
||||
source.Append('*');
|
||||
}
|
||||
|
||||
private static void AppendCustomUnsafeAsPointer(StringBuilder source, IParameterSymbol parameter,
|
||||
out string varName)
|
||||
{
|
||||
varName = $"{parameter.Name}_ptr";
|
||||
|
||||
AppendPointerType(source, parameter.Type);
|
||||
source.Append(' ');
|
||||
source.Append(varName);
|
||||
source.Append(" = ");
|
||||
|
||||
source.Append('(');
|
||||
AppendPointerType(source, parameter.Type);
|
||||
source.Append(')');
|
||||
|
||||
if (parameter.RefKind == RefKind.In)
|
||||
source.Append("CustomUnsafe.ReadOnlyRefAsPointer(in ");
|
||||
else
|
||||
source.Append("CustomUnsafe.AsPointer(ref ");
|
||||
|
||||
source.Append(parameter.Name);
|
||||
|
||||
source.Append(");\n");
|
||||
}
|
||||
|
||||
private static void AppendCopyToStackAndGetPointer(StringBuilder source, IParameterSymbol parameter,
|
||||
out string varName)
|
||||
{
|
||||
varName = $"{parameter.Name}_copy";
|
||||
|
||||
source.Append(parameter.Type.FullQualifiedNameIncludeGlobal());
|
||||
source.Append(' ');
|
||||
source.Append(varName);
|
||||
if (parameter.RefKind is RefKind.In or RefKind.Ref)
|
||||
{
|
||||
source.Append(" = ");
|
||||
source.Append(parameter.Name);
|
||||
}
|
||||
|
||||
source.Append(";\n");
|
||||
}
|
||||
|
||||
private static readonly string[] _godotInteropStructs =
|
||||
{
|
||||
"Godot.NativeInterop.godot_ref",
|
||||
"Godot.NativeInterop.godot_variant_call_error",
|
||||
"Godot.NativeInterop.godot_variant",
|
||||
"Godot.NativeInterop.godot_string",
|
||||
"Godot.NativeInterop.godot_string_name",
|
||||
"Godot.NativeInterop.godot_node_path",
|
||||
"Godot.NativeInterop.godot_signal",
|
||||
"Godot.NativeInterop.godot_callable",
|
||||
"Godot.NativeInterop.godot_array",
|
||||
"Godot.NativeInterop.godot_dictionary",
|
||||
"Godot.NativeInterop.godot_packed_byte_array",
|
||||
"Godot.NativeInterop.godot_packed_int32_array",
|
||||
"Godot.NativeInterop.godot_packed_int64_array",
|
||||
"Godot.NativeInterop.godot_packed_float32_array",
|
||||
"Godot.NativeInterop.godot_packed_float64_array",
|
||||
"Godot.NativeInterop.godot_packed_string_array",
|
||||
"Godot.NativeInterop.godot_packed_vector2_array",
|
||||
"Godot.NativeInterop.godot_packed_vector3_array",
|
||||
"Godot.NativeInterop.godot_packed_vector4_array",
|
||||
"Godot.NativeInterop.godot_packed_color_array",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<!-- To generate the .runtimeconfig.json file-->
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<RollForward>LatestMajor</RollForward>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotSharp\GodotSharp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
292
modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
Normal file
292
modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using Godot.Bridge;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace GodotPlugins
|
||||
{
|
||||
public static class Main
|
||||
{
|
||||
// IMPORTANT:
|
||||
// Keeping strong references to the AssemblyLoadContext (our PluginLoadContext) prevents
|
||||
// it from being unloaded. To avoid issues, we wrap the reference in this class, and mark
|
||||
// all the methods that access it as non-inlineable. This way we prevent local references
|
||||
// (either real or introduced by the JIT) to escape the scope of these methods due to
|
||||
// inlining, which could keep the AssemblyLoadContext alive while trying to unload.
|
||||
private sealed class PluginLoadContextWrapper
|
||||
{
|
||||
private PluginLoadContext? _pluginLoadContext;
|
||||
private readonly WeakReference _weakReference;
|
||||
|
||||
private PluginLoadContextWrapper(PluginLoadContext pluginLoadContext, WeakReference weakReference)
|
||||
{
|
||||
_pluginLoadContext = pluginLoadContext;
|
||||
_weakReference = weakReference;
|
||||
}
|
||||
|
||||
public string? AssemblyLoadedPath
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
get => _pluginLoadContext?.AssemblyLoadedPath;
|
||||
}
|
||||
|
||||
public bool IsCollectible
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
// if _pluginLoadContext is null we already started unloading, so it was collectible
|
||||
get => _pluginLoadContext?.IsCollectible ?? true;
|
||||
}
|
||||
|
||||
public bool IsAlive
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
get => _weakReference.IsAlive;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static (Assembly, PluginLoadContextWrapper) CreateAndLoadFromAssemblyName(
|
||||
AssemblyName assemblyName,
|
||||
string pluginPath,
|
||||
ICollection<string> sharedAssemblies,
|
||||
AssemblyLoadContext mainLoadContext,
|
||||
bool isCollectible
|
||||
)
|
||||
{
|
||||
var context = new PluginLoadContext(pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
|
||||
var reference = new WeakReference(context, trackResurrection: true);
|
||||
var wrapper = new PluginLoadContextWrapper(context, reference);
|
||||
var assembly = context.LoadFromAssemblyName(assemblyName);
|
||||
return (assembly, wrapper);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal void Unload()
|
||||
{
|
||||
_pluginLoadContext?.Unload();
|
||||
_pluginLoadContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<AssemblyName> SharedAssemblies = new();
|
||||
private static readonly Assembly CoreApiAssembly = typeof(global::Godot.GodotObject).Assembly;
|
||||
private static Assembly? _editorApiAssembly;
|
||||
private static PluginLoadContextWrapper? _projectLoadContext;
|
||||
private static bool _editorHint = false;
|
||||
|
||||
private static readonly AssemblyLoadContext MainLoadContext =
|
||||
AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
|
||||
AssemblyLoadContext.Default;
|
||||
|
||||
private static DllImportResolver? _dllImportResolver;
|
||||
|
||||
// Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later.
|
||||
[UnmanagedCallersOnly]
|
||||
// ReSharper disable once UnusedMember.Local
|
||||
private static unsafe godot_bool InitializeFromEngine(IntPtr godotDllHandle, godot_bool editorHint,
|
||||
PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks,
|
||||
IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
_editorHint = editorHint.ToBool();
|
||||
|
||||
_dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
|
||||
|
||||
SharedAssemblies.Add(CoreApiAssembly.GetName());
|
||||
NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver);
|
||||
|
||||
AlcReloadCfg.Configure(alcReloadEnabled: _editorHint);
|
||||
NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
|
||||
|
||||
if (_editorHint)
|
||||
{
|
||||
_editorApiAssembly = Assembly.Load("GodotSharpEditor");
|
||||
SharedAssemblies.Add(_editorApiAssembly.GetName());
|
||||
NativeLibrary.SetDllImportResolver(_editorApiAssembly, _dllImportResolver);
|
||||
}
|
||||
|
||||
*pluginsCallbacks = new()
|
||||
{
|
||||
LoadProjectAssemblyCallback = &LoadProjectAssembly,
|
||||
LoadToolsAssemblyCallback = &LoadToolsAssembly,
|
||||
UnloadProjectPluginCallback = &UnloadProjectPlugin,
|
||||
};
|
||||
|
||||
*managedCallbacks = ManagedCallbacks.Create();
|
||||
|
||||
return godot_bool.True;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Error.WriteLine(e);
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct PluginsCallbacks
|
||||
{
|
||||
public unsafe delegate* unmanaged<char*, godot_string*, godot_bool> LoadProjectAssemblyCallback;
|
||||
public unsafe delegate* unmanaged<char*, IntPtr, int, IntPtr> LoadToolsAssemblyCallback;
|
||||
public unsafe delegate* unmanaged<godot_bool> UnloadProjectPluginCallback;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath, godot_string* outLoadedAssemblyPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_projectLoadContext != null)
|
||||
return godot_bool.True; // Already loaded
|
||||
|
||||
string assemblyPath = new(nAssemblyPath);
|
||||
|
||||
(var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
|
||||
|
||||
string loadedAssemblyPath = _projectLoadContext.AssemblyLoadedPath ?? assemblyPath;
|
||||
*outLoadedAssemblyPath = Marshaling.ConvertStringToNative(loadedAssemblyPath);
|
||||
|
||||
ScriptManagerBridge.LookupScriptsInAssembly(projectAssembly);
|
||||
|
||||
return godot_bool.True;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Error.WriteLine(e);
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath,
|
||||
IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
string assemblyPath = new(nAssemblyPath);
|
||||
|
||||
if (_editorApiAssembly == null)
|
||||
throw new InvalidOperationException("The Godot editor API assembly is not loaded.");
|
||||
|
||||
var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: false);
|
||||
|
||||
NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
|
||||
|
||||
var method = assembly.GetType("GodotTools.GodotSharpEditor")?
|
||||
.GetMethod("InternalCreateInstance",
|
||||
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
throw new MissingMethodException("GodotTools.GodotSharpEditor",
|
||||
"InternalCreateInstance");
|
||||
}
|
||||
|
||||
return (IntPtr?)method
|
||||
.Invoke(null, new object[] { unmanagedCallbacks, unmanagedCallbacksSize })
|
||||
?? IntPtr.Zero;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Error.WriteLine(e);
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath, bool isCollectible)
|
||||
{
|
||||
string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
|
||||
|
||||
var sharedAssemblies = new List<string>();
|
||||
|
||||
foreach (var sharedAssembly in SharedAssemblies)
|
||||
{
|
||||
string? sharedAssemblyName = sharedAssembly.Name;
|
||||
if (sharedAssemblyName != null)
|
||||
sharedAssemblies.Add(sharedAssemblyName);
|
||||
}
|
||||
|
||||
return PluginLoadContextWrapper.CreateAndLoadFromAssemblyName(
|
||||
new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext, isCollectible);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static godot_bool UnloadProjectPlugin()
|
||||
{
|
||||
try
|
||||
{
|
||||
return UnloadPlugin(ref _projectLoadContext).ToGodotBool();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.Error.WriteLine(e);
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool UnloadPlugin(ref PluginLoadContextWrapper? pluginLoadContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (pluginLoadContext == null)
|
||||
return true;
|
||||
|
||||
if (!pluginLoadContext.IsCollectible)
|
||||
{
|
||||
Console.Error.WriteLine("Cannot unload a non-collectible assembly load context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLine("Unloading assembly load context...");
|
||||
|
||||
pluginLoadContext.Unload();
|
||||
|
||||
int startTimeMs = Environment.TickCount;
|
||||
bool takingTooLong = false;
|
||||
|
||||
while (pluginLoadContext.IsAlive)
|
||||
{
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
if (!pluginLoadContext.IsAlive)
|
||||
break;
|
||||
|
||||
int elapsedTimeMs = Environment.TickCount - startTimeMs;
|
||||
|
||||
if (!takingTooLong && elapsedTimeMs >= 200)
|
||||
{
|
||||
takingTooLong = true;
|
||||
|
||||
// TODO: How to log from GodotPlugins? (delegate pointer?)
|
||||
Console.Error.WriteLine("Assembly unloading is taking longer than expected...");
|
||||
}
|
||||
else if (elapsedTimeMs >= 1000)
|
||||
{
|
||||
// TODO: How to log from GodotPlugins? (delegate pointer?)
|
||||
Console.Error.WriteLine(
|
||||
"Failed to unload assemblies. Possible causes: Strong GC handles, running threads, etc.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Assembly load context unloaded successfully.");
|
||||
|
||||
pluginLoadContext = null;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO: How to log exceptions from GodotPlugins? (delegate pointer?)
|
||||
Console.Error.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace GodotPlugins
|
||||
{
|
||||
public class PluginLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private readonly AssemblyDependencyResolver _resolver;
|
||||
private readonly ICollection<string> _sharedAssemblies;
|
||||
private readonly AssemblyLoadContext _mainLoadContext;
|
||||
|
||||
public string? AssemblyLoadedPath { get; private set; }
|
||||
|
||||
public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies,
|
||||
AssemblyLoadContext mainLoadContext, bool isCollectible)
|
||||
: base(isCollectible)
|
||||
{
|
||||
_resolver = new AssemblyDependencyResolver(pluginPath);
|
||||
_sharedAssemblies = sharedAssemblies;
|
||||
_mainLoadContext = mainLoadContext;
|
||||
|
||||
if (string.IsNullOrEmpty(AppContext.BaseDirectory))
|
||||
{
|
||||
// See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs#L17-L35
|
||||
// but Assembly.Location is unavailable, because we load assemblies from memory.
|
||||
string? baseDirectory = Path.GetDirectoryName(pluginPath);
|
||||
if (baseDirectory != null)
|
||||
{
|
||||
if (!Path.EndsInDirectorySeparator(baseDirectory))
|
||||
baseDirectory += Path.DirectorySeparatorChar;
|
||||
// This SetData call effectively sets AppContext.BaseDirectory
|
||||
// See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.cs#L21-L25
|
||||
AppDomain.CurrentDomain.SetData("APP_CONTEXT_BASE_DIRECTORY", baseDirectory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: How to log from GodotPlugins? (delegate pointer?)
|
||||
Console.Error.WriteLine("Failed to set AppContext.BaseDirectory. Dynamic loading of libraries may fail.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
{
|
||||
if (assemblyName.Name == null)
|
||||
return null;
|
||||
|
||||
if (_sharedAssemblies.Contains(assemblyName.Name))
|
||||
return _mainLoadContext.LoadFromAssemblyName(assemblyName);
|
||||
|
||||
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
|
||||
if (assemblyPath != null)
|
||||
{
|
||||
AssemblyLoadedPath = assemblyPath;
|
||||
|
||||
// Load in memory to prevent locking the file
|
||||
using var assemblyFile = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb");
|
||||
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
return LoadFromStream(assemblyFile, pdbFile);
|
||||
}
|
||||
|
||||
return LoadFromStream(assemblyFile);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||
{
|
||||
string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
|
||||
if (libraryPath != null)
|
||||
return LoadUnmanagedDllFromPath(libraryPath);
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
modules/mono/glue/GodotSharp/GodotSharp.sln
Normal file
43
modules/mono/glue/GodotSharp/GodotSharp.sln
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.9.34728.123
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\GodotSharp.csproj", "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotPlugins", "GodotPlugins\GodotPlugins.csproj", "{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{7749662B-E30C-419A-B745-13852573360A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {371B0F03-042D-45FD-A270-F3141F2480CD}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
8
modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
Normal file
8
modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
Normal file
@@ -0,0 +1,8 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GC/@EntryIndexedValue">GC</s:String>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=alcs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gdextension/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=godotsharp/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=icall/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=quat/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=vcall/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
248
modules/mono/glue/GodotSharp/GodotSharp/Compat.cs
Normal file
248
modules/mono/glue/GodotSharp/GodotSharp/Compat.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
// This file contains methods that existed in a previous version of Godot in ClassDB but were removed
|
||||
// or their method signature has changed so they are no longer generated by bindings_generator.
|
||||
// These methods are provided to avoid breaking binary compatibility.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Godot;
|
||||
|
||||
#pragma warning disable CS1734 // XML comment on 'X' has a paramref tag for 'Y', but there is no parameter by that name.
|
||||
// TODO: This is currently disabled because of https://github.com/dotnet/roslyn/issues/52904
|
||||
#pragma warning disable IDE0040 // Add accessibility modifiers.
|
||||
|
||||
partial class AnimationNode
|
||||
{
|
||||
/// <inheritdoc cref="BlendInput(int, double, bool, bool, float, FilterAction, bool, bool)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public double BlendInput(int inputIndex, double time, bool seek, bool isExternalSeeking, float blend, FilterAction filter, bool sync)
|
||||
{
|
||||
return BlendInput(inputIndex, time, seek, isExternalSeeking, blend, filter, sync, testOnly: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BlendNode(StringName, AnimationNode, double, bool, bool, float, FilterAction, bool, bool)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public double BlendNode(StringName name, AnimationNode node, double time, bool seek, bool isExternalSeeking, float blend, FilterAction filter, bool sync)
|
||||
{
|
||||
return BlendNode(name, node, time, seek, isExternalSeeking, blend, filter, sync, testOnly: false);
|
||||
}
|
||||
}
|
||||
|
||||
partial class AnimationPlayer
|
||||
{
|
||||
/// <inheritdoc cref="AnimationMixer.CallbackModeMethod"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public AnimationMethodCallMode MethodCallMode
|
||||
{
|
||||
get => (AnimationMethodCallMode)CallbackModeMethod;
|
||||
set => CallbackModeMethod = (AnimationCallbackModeMethod)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="AnimationMixer.Active"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool PlaybackActive
|
||||
{
|
||||
get => Active;
|
||||
set => Active = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="AnimationMixer.CallbackModeProcess"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public AnimationProcessCallback PlaybackProcessMode
|
||||
{
|
||||
get => (AnimationProcessCallback)CallbackModeProcess;
|
||||
set => CallbackModeProcess = (AnimationCallbackModeProcess)value;
|
||||
}
|
||||
}
|
||||
|
||||
partial class AnimationTree
|
||||
{
|
||||
/// <inheritdoc cref="AnimationMixer.CallbackModeProcess"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public AnimationProcessCallback ProcessCallback
|
||||
{
|
||||
get => (AnimationProcessCallback)CallbackModeProcess;
|
||||
set => CallbackModeProcess = (AnimationCallbackModeProcess)value;
|
||||
}
|
||||
}
|
||||
|
||||
partial class CodeEdit
|
||||
{
|
||||
/// <inheritdoc cref="AddCodeCompletionOption(CodeCompletionKind, string, string, Nullable{Color}, Resource, Variant, int)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void AddCodeCompletionOption(CodeCompletionKind type, string displayText, string insertText, Nullable<Color> textColor, Resource icon, Nullable<Variant> value)
|
||||
{
|
||||
AddCodeCompletionOption(type, displayText, insertText, textColor, icon, value, location: 1024);
|
||||
}
|
||||
}
|
||||
|
||||
partial class Geometry3D
|
||||
{
|
||||
/// <inheritdoc cref="SegmentIntersectsConvex(Vector3, Vector3, Godot.Collections.Array{Plane})"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static Vector3[] SegmentIntersectsConvex(Vector3 from, Vector3 to, Godot.Collections.Array planes)
|
||||
{
|
||||
return SegmentIntersectsConvex(from, to, new Godot.Collections.Array<Plane>(planes));
|
||||
}
|
||||
}
|
||||
|
||||
partial class GraphEdit
|
||||
{
|
||||
/// <inheritdoc cref="ShowArrangeButton"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool ArrangeNodesButtonHidden
|
||||
{
|
||||
get => !ShowArrangeButton;
|
||||
set => ShowArrangeButton = !value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetMenuHBox()"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public HBoxContainer GetZoomHBox()
|
||||
{
|
||||
return GetMenuHBox();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="SnappingDistance"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int SnapDistance
|
||||
{
|
||||
get => SnappingDistance;
|
||||
set => SnappingDistance = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="SnappingEnabled"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool UseSnap
|
||||
{
|
||||
get => SnappingEnabled;
|
||||
set => SnappingEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
partial class GraphNode
|
||||
{
|
||||
/// <inheritdoc cref="GraphElement.DeleteRequest"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public event Action CloseRequest
|
||||
{
|
||||
add => DeleteRequest += value;
|
||||
remove => DeleteRequest -= value;
|
||||
}
|
||||
}
|
||||
partial class ImporterMesh
|
||||
{
|
||||
/// <inheritdoc cref="AddSurface(Mesh.PrimitiveType, Godot.Collections.Array, Godot.Collections.Array{Godot.Collections.Array}, Godot.Collections.Dictionary, Material, string, ulong)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void AddSurface(Mesh.PrimitiveType primitive, Godot.Collections.Array arrays, Godot.Collections.Array<Godot.Collections.Array> blendShapes, Godot.Collections.Dictionary lods, Material material, string name, uint flags)
|
||||
{
|
||||
AddSurface(primitive, arrays, blendShapes, lods, material, name, (ulong)flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
partial class MeshInstance3D
|
||||
{
|
||||
/// <inheritdoc cref="CreateMultipleConvexCollisions(MeshConvexDecompositionSettings)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void CreateMultipleConvexCollisions()
|
||||
{
|
||||
CreateMultipleConvexCollisions(settings: null);
|
||||
}
|
||||
}
|
||||
|
||||
partial class Node3D
|
||||
{
|
||||
/// <inheritdoc cref="LookAt(Vector3, Nullable{Vector3}, bool)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void LookAt(Vector3 target, Nullable<Vector3> up)
|
||||
{
|
||||
LookAt(target, up, useModelFront: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="LookAtFromPosition(Vector3, Vector3, Nullable{Vector3}, bool)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void LookAtFromPosition(Vector3 position, Vector3 target, Nullable<Vector3> up)
|
||||
{
|
||||
LookAtFromPosition(position, target, up, useModelFront: false);
|
||||
}
|
||||
}
|
||||
|
||||
partial class RenderingDevice
|
||||
{
|
||||
/// <inheritdoc cref="DrawListBegin(Rid, InitialAction, FinalAction, InitialAction, FinalAction, Color[], float, uint, Nullable{Rect2}, Godot.Collections.Array{Rid})"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public long DrawListBegin(Rid framebuffer, InitialAction initialColorAction, FinalAction finalColorAction, InitialAction initialDepthAction, FinalAction finalDepthAction, Color[] clearColorValues, float clearDepth, uint clearStencil, Nullable<Rect2> region, Godot.Collections.Array storageTextures)
|
||||
{
|
||||
return DrawListBegin(framebuffer, initialColorAction, finalColorAction, initialDepthAction, finalDepthAction, clearColorValues, clearDepth, clearStencil, region, new Godot.Collections.Array<Rid>(storageTextures));
|
||||
}
|
||||
}
|
||||
|
||||
partial class RichTextLabel
|
||||
{
|
||||
/// <inheritdoc cref="PushList(int, ListType, bool, string)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void PushList(int level, ListType type, bool capitalize)
|
||||
{
|
||||
PushList(level, type, capitalize, bullet: "•");
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="PushParagraph(HorizontalAlignment, TextDirection, string, TextServer.StructuredTextParser, TextServer.JustificationFlag, float[])"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void PushParagraph(HorizontalAlignment alignment, TextDirection baseDirection, string language, TextServer.StructuredTextParser stParser)
|
||||
{
|
||||
PushParagraph(alignment, baseDirection, language, stParser, TextServer.JustificationFlag.WordBound | TextServer.JustificationFlag.Kashida | TextServer.JustificationFlag.SkipLastLine | TextServer.JustificationFlag.DoNotSkipSingleLine);
|
||||
}
|
||||
}
|
||||
|
||||
partial class SurfaceTool
|
||||
{
|
||||
/// <inheritdoc cref="AddTriangleFan(Vector3[], Vector2[], Color[], Vector2[], Vector3[], Godot.Collections.Array{Plane})"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void AddTriangleFan(Vector3[] vertices, Vector2[] uvs, Color[] colors, Vector2[] uv2S, Vector3[] normals, Godot.Collections.Array tangents)
|
||||
{
|
||||
AddTriangleFan(vertices, uvs, colors, uv2S, normals, new Godot.Collections.Array<Plane>(tangents));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Commit(ArrayMesh, ulong)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public ArrayMesh Commit(ArrayMesh existing, uint flags)
|
||||
{
|
||||
return Commit(existing, (ulong)flags);
|
||||
}
|
||||
}
|
||||
|
||||
partial class TileMap
|
||||
{
|
||||
/// <summary>
|
||||
/// The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int CellQuadrantSize
|
||||
{
|
||||
get => RenderingQuadrantSize;
|
||||
set => RenderingQuadrantSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
partial class Tree
|
||||
{
|
||||
/// <inheritdoc cref="EditSelected(bool)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool EditSelected()
|
||||
{
|
||||
return EditSelected(forceEdit: false);
|
||||
}
|
||||
}
|
||||
|
||||
partial class UndoRedo
|
||||
{
|
||||
/// <inheritdoc cref="CreateAction(string, MergeMode, bool)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void CreateAction(string name, MergeMode mergeMode)
|
||||
{
|
||||
CreateAction(name, mergeMode, backwardUndoOps: false);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore CS1734
|
||||
754
modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
Normal file
754
modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
Normal file
@@ -0,0 +1,754 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Axis-Aligned Bounding Box. AABB consists of a position, a size, and
|
||||
/// several utility functions. It is typically used for fast overlap tests.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Aabb : IEquatable<Aabb>
|
||||
{
|
||||
private Vector3 _position;
|
||||
private Vector3 _size;
|
||||
|
||||
/// <summary>
|
||||
/// Beginning corner. Typically has values lower than <see cref="End"/>.
|
||||
/// </summary>
|
||||
/// <value>Directly uses a private field.</value>
|
||||
public Vector3 Position
|
||||
{
|
||||
readonly get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive.
|
||||
/// If the size is negative, you can use <see cref="Abs"/> to fix it.
|
||||
/// </summary>
|
||||
/// <value>Directly uses a private field.</value>
|
||||
public Vector3 Size
|
||||
{
|
||||
readonly get { return _size; }
|
||||
set { _size = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ending corner. This is calculated as <see cref="Position"/> plus
|
||||
/// <see cref="Size"/>. Setting this value will change the size.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Getting is equivalent to <paramref name="value"/> = <see cref="Position"/> + <see cref="Size"/>,
|
||||
/// setting is equivalent to <see cref="Size"/> = <paramref name="value"/> - <see cref="Position"/>
|
||||
/// </value>
|
||||
public Vector3 End
|
||||
{
|
||||
readonly get { return _position + _size; }
|
||||
set { _size = value - _position; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The volume of this <see cref="Aabb"/>.
|
||||
/// See also <see cref="HasVolume"/>.
|
||||
/// </summary>
|
||||
public readonly real_t Volume
|
||||
{
|
||||
get { return _size.X * _size.Y * _size.Z; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an <see cref="Aabb"/> with equivalent position and size, modified so that
|
||||
/// the most-negative corner is the origin and the size is positive.
|
||||
/// </summary>
|
||||
/// <returns>The modified <see cref="Aabb"/>.</returns>
|
||||
public readonly Aabb Abs()
|
||||
{
|
||||
Vector3 end = End;
|
||||
Vector3 topLeft = end.Min(_position);
|
||||
return new Aabb(topLeft, _size.Abs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the center of the <see cref="Aabb"/>, which is equal
|
||||
/// to <see cref="Position"/> + (<see cref="Size"/> / 2).
|
||||
/// </summary>
|
||||
/// <returns>The center.</returns>
|
||||
public readonly Vector3 GetCenter()
|
||||
{
|
||||
return _position + (_size * 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this <see cref="Aabb"/> completely encloses another one.
|
||||
/// </summary>
|
||||
/// <param name="with">The other <see cref="Aabb"/> that may be enclosed.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not this <see cref="Aabb"/> encloses <paramref name="with"/>.
|
||||
/// </returns>
|
||||
public readonly bool Encloses(Aabb with)
|
||||
{
|
||||
Vector3 srcMin = _position;
|
||||
Vector3 srcMax = _position + _size;
|
||||
Vector3 dstMin = with._position;
|
||||
Vector3 dstMax = with._position + with._size;
|
||||
|
||||
return srcMin.X <= dstMin.X &&
|
||||
srcMax.X >= dstMax.X &&
|
||||
srcMin.Y <= dstMin.Y &&
|
||||
srcMax.Y >= dstMax.Y &&
|
||||
srcMin.Z <= dstMin.Z &&
|
||||
srcMax.Z >= dstMax.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this <see cref="Aabb"/> expanded to include a given point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to include.</param>
|
||||
/// <returns>The expanded <see cref="Aabb"/>.</returns>
|
||||
public readonly Aabb Expand(Vector3 point)
|
||||
{
|
||||
Vector3 begin = _position;
|
||||
Vector3 end = _position + _size;
|
||||
|
||||
if (point.X < begin.X)
|
||||
{
|
||||
begin.X = point.X;
|
||||
}
|
||||
if (point.Y < begin.Y)
|
||||
{
|
||||
begin.Y = point.Y;
|
||||
}
|
||||
if (point.Z < begin.Z)
|
||||
{
|
||||
begin.Z = point.Z;
|
||||
}
|
||||
|
||||
if (point.X > end.X)
|
||||
{
|
||||
end.X = point.X;
|
||||
}
|
||||
if (point.Y > end.Y)
|
||||
{
|
||||
end.Y = point.Y;
|
||||
}
|
||||
if (point.Z > end.Z)
|
||||
{
|
||||
end.Z = point.Z;
|
||||
}
|
||||
|
||||
return new Aabb(begin, end - begin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of one of the 8 endpoints of the <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <param name="idx">Which endpoint to get.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="idx"/> is less than 0 or greater than 7.
|
||||
/// </exception>
|
||||
/// <returns>An endpoint of the <see cref="Aabb"/>.</returns>
|
||||
public readonly Vector3 GetEndpoint(int idx)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0:
|
||||
return new Vector3(_position.X, _position.Y, _position.Z);
|
||||
case 1:
|
||||
return new Vector3(_position.X, _position.Y, _position.Z + _size.Z);
|
||||
case 2:
|
||||
return new Vector3(_position.X, _position.Y + _size.Y, _position.Z);
|
||||
case 3:
|
||||
return new Vector3(_position.X, _position.Y + _size.Y, _position.Z + _size.Z);
|
||||
case 4:
|
||||
return new Vector3(_position.X + _size.X, _position.Y, _position.Z);
|
||||
case 5:
|
||||
return new Vector3(_position.X + _size.X, _position.Y, _position.Z + _size.Z);
|
||||
case 6:
|
||||
return new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z);
|
||||
case 7:
|
||||
return new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z + _size.Z);
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(idx),
|
||||
$"Index is {idx}, but a value from 0 to 7 is expected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the normalized longest axis of the <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <returns>A vector representing the normalized longest axis of the <see cref="Aabb"/>.</returns>
|
||||
public readonly Vector3 GetLongestAxis()
|
||||
{
|
||||
var axis = new Vector3(1f, 0f, 0f);
|
||||
real_t maxSize = _size.X;
|
||||
|
||||
if (_size.Y > maxSize)
|
||||
{
|
||||
axis = new Vector3(0f, 1f, 0f);
|
||||
maxSize = _size.Y;
|
||||
}
|
||||
|
||||
if (_size.Z > maxSize)
|
||||
{
|
||||
axis = new Vector3(0f, 0f, 1f);
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Vector3.Axis"/> index for which axis is longest.</returns>
|
||||
public readonly Vector3.Axis GetLongestAxisIndex()
|
||||
{
|
||||
var axis = Vector3.Axis.X;
|
||||
real_t maxSize = _size.X;
|
||||
|
||||
if (_size.Y > maxSize)
|
||||
{
|
||||
axis = Vector3.Axis.Y;
|
||||
maxSize = _size.Y;
|
||||
}
|
||||
|
||||
if (_size.Z > maxSize)
|
||||
{
|
||||
axis = Vector3.Axis.Z;
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the scalar length of the longest axis of the <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <returns>The scalar length of the longest axis of the <see cref="Aabb"/>.</returns>
|
||||
public readonly real_t GetLongestAxisSize()
|
||||
{
|
||||
real_t maxSize = _size.X;
|
||||
|
||||
if (_size.Y > maxSize)
|
||||
maxSize = _size.Y;
|
||||
|
||||
if (_size.Z > maxSize)
|
||||
maxSize = _size.Z;
|
||||
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the normalized shortest axis of the <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <returns>A vector representing the normalized shortest axis of the <see cref="Aabb"/>.</returns>
|
||||
public readonly Vector3 GetShortestAxis()
|
||||
{
|
||||
var axis = new Vector3(1f, 0f, 0f);
|
||||
real_t maxSize = _size.X;
|
||||
|
||||
if (_size.Y < maxSize)
|
||||
{
|
||||
axis = new Vector3(0f, 1f, 0f);
|
||||
maxSize = _size.Y;
|
||||
}
|
||||
|
||||
if (_size.Z < maxSize)
|
||||
{
|
||||
axis = new Vector3(0f, 0f, 1f);
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Vector3.Axis"/> index for which axis is shortest.</returns>
|
||||
public readonly Vector3.Axis GetShortestAxisIndex()
|
||||
{
|
||||
var axis = Vector3.Axis.X;
|
||||
real_t maxSize = _size.X;
|
||||
|
||||
if (_size.Y < maxSize)
|
||||
{
|
||||
axis = Vector3.Axis.Y;
|
||||
maxSize = _size.Y;
|
||||
}
|
||||
|
||||
if (_size.Z < maxSize)
|
||||
{
|
||||
axis = Vector3.Axis.Z;
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the scalar length of the shortest axis of the <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <returns>The scalar length of the shortest axis of the <see cref="Aabb"/>.</returns>
|
||||
public readonly real_t GetShortestAxisSize()
|
||||
{
|
||||
real_t maxSize = _size.X;
|
||||
|
||||
if (_size.Y < maxSize)
|
||||
maxSize = _size.Y;
|
||||
|
||||
if (_size.Z < maxSize)
|
||||
maxSize = _size.Z;
|
||||
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the support point in a given direction.
|
||||
/// This is useful for collision detection algorithms.
|
||||
/// </summary>
|
||||
/// <param name="dir">The direction to find support for.</param>
|
||||
/// <returns>A vector representing the support.</returns>
|
||||
public readonly Vector3 GetSupport(Vector3 dir)
|
||||
{
|
||||
Vector3 support = _position;
|
||||
if (dir.X > 0.0f)
|
||||
{
|
||||
support.X += _size.X;
|
||||
}
|
||||
if (dir.Y > 0.0f)
|
||||
{
|
||||
support.Y += _size.Y;
|
||||
}
|
||||
if (dir.Z > 0.0f)
|
||||
{
|
||||
support.Z += _size.Z;
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Aabb"/> grown a given amount of units towards all the sides.
|
||||
/// </summary>
|
||||
/// <param name="by">The amount to grow by.</param>
|
||||
/// <returns>The grown <see cref="Aabb"/>.</returns>
|
||||
public readonly Aabb Grow(real_t by)
|
||||
{
|
||||
Aabb res = this;
|
||||
|
||||
res._position.X -= by;
|
||||
res._position.Y -= by;
|
||||
res._position.Z -= by;
|
||||
res._size.X += 2.0f * by;
|
||||
res._size.Y += 2.0f * by;
|
||||
res._size.Z += 2.0f * by;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Aabb"/> contains a point,
|
||||
/// or <see langword="false"/> otherwise.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to check.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> contains <paramref name="point"/>.
|
||||
/// </returns>
|
||||
public readonly bool HasPoint(Vector3 point)
|
||||
{
|
||||
if (point.X < _position.X)
|
||||
return false;
|
||||
if (point.Y < _position.Y)
|
||||
return false;
|
||||
if (point.Z < _position.Z)
|
||||
return false;
|
||||
if (point.X > _position.X + _size.X)
|
||||
return false;
|
||||
if (point.Y > _position.Y + _size.Y)
|
||||
return false;
|
||||
if (point.Z > _position.Z + _size.Z)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Aabb"/>
|
||||
/// has a surface or a length, and <see langword="false"/>
|
||||
/// if the <see cref="Aabb"/> is empty (all components
|
||||
/// of <see cref="Size"/> are zero or negative).
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> has surface.
|
||||
/// </returns>
|
||||
public readonly bool HasSurface()
|
||||
{
|
||||
return _size.X > 0.0f || _size.Y > 0.0f || _size.Z > 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Aabb"/> has
|
||||
/// area, and <see langword="false"/> if the <see cref="Aabb"/>
|
||||
/// is linear, empty, or has a negative <see cref="Size"/>.
|
||||
/// See also <see cref="Volume"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> has volume.
|
||||
/// </returns>
|
||||
public readonly bool HasVolume()
|
||||
{
|
||||
return _size.X > 0.0f && _size.Y > 0.0f && _size.Z > 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the intersection of this <see cref="Aabb"/> and <paramref name="with"/>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other <see cref="Aabb"/>.</param>
|
||||
/// <returns>The clipped <see cref="Aabb"/>.</returns>
|
||||
public readonly Aabb Intersection(Aabb with)
|
||||
{
|
||||
Vector3 srcMin = _position;
|
||||
Vector3 srcMax = _position + _size;
|
||||
Vector3 dstMin = with._position;
|
||||
Vector3 dstMax = with._position + with._size;
|
||||
|
||||
Vector3 min, max;
|
||||
|
||||
if (srcMin.X > dstMax.X || srcMax.X < dstMin.X)
|
||||
{
|
||||
return new Aabb();
|
||||
}
|
||||
|
||||
min.X = srcMin.X > dstMin.X ? srcMin.X : dstMin.X;
|
||||
max.X = srcMax.X < dstMax.X ? srcMax.X : dstMax.X;
|
||||
|
||||
if (srcMin.Y > dstMax.Y || srcMax.Y < dstMin.Y)
|
||||
{
|
||||
return new Aabb();
|
||||
}
|
||||
|
||||
min.Y = srcMin.Y > dstMin.Y ? srcMin.Y : dstMin.Y;
|
||||
max.Y = srcMax.Y < dstMax.Y ? srcMax.Y : dstMax.Y;
|
||||
|
||||
if (srcMin.Z > dstMax.Z || srcMax.Z < dstMin.Z)
|
||||
{
|
||||
return new Aabb();
|
||||
}
|
||||
|
||||
min.Z = srcMin.Z > dstMin.Z ? srcMin.Z : dstMin.Z;
|
||||
max.Z = srcMax.Z < dstMax.Z ? srcMax.Z : dstMax.Z;
|
||||
|
||||
return new Aabb(min, max - min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Aabb"/> overlaps with <paramref name="with"/>
|
||||
/// (i.e. they have at least one point in common).
|
||||
/// </summary>
|
||||
/// <param name="with">The other <see cref="Aabb"/> to check for intersections with.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not they are intersecting.
|
||||
/// </returns>
|
||||
public readonly bool Intersects(Aabb with)
|
||||
{
|
||||
if (_position.X >= with._position.X + with._size.X)
|
||||
return false;
|
||||
if (_position.X + _size.X <= with._position.X)
|
||||
return false;
|
||||
if (_position.Y >= with._position.Y + with._size.Y)
|
||||
return false;
|
||||
if (_position.Y + _size.Y <= with._position.Y)
|
||||
return false;
|
||||
if (_position.Z >= with._position.Z + with._size.Z)
|
||||
return false;
|
||||
if (_position.Z + _size.Z <= with._position.Z)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Aabb"/> is on both sides of <paramref name="plane"/>.
|
||||
/// </summary>
|
||||
/// <param name="plane">The <see cref="Plane"/> to check for intersection.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> intersects the <see cref="Plane"/>.
|
||||
/// </returns>
|
||||
public readonly bool IntersectsPlane(Plane plane)
|
||||
{
|
||||
ReadOnlySpan<Vector3> points =
|
||||
[
|
||||
new Vector3(_position.X, _position.Y, _position.Z),
|
||||
new Vector3(_position.X, _position.Y, _position.Z + _size.Z),
|
||||
new Vector3(_position.X, _position.Y + _size.Y, _position.Z),
|
||||
new Vector3(_position.X, _position.Y + _size.Y, _position.Z + _size.Z),
|
||||
new Vector3(_position.X + _size.X, _position.Y, _position.Z),
|
||||
new Vector3(_position.X + _size.X, _position.Y, _position.Z + _size.Z),
|
||||
new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z),
|
||||
new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z + _size.Z)
|
||||
];
|
||||
|
||||
bool over = false;
|
||||
bool under = false;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (plane.DistanceTo(points[i]) > 0)
|
||||
{
|
||||
over = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
under = true;
|
||||
}
|
||||
}
|
||||
|
||||
return under && over;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Aabb"/> intersects
|
||||
/// the line segment between <paramref name="from"/> and <paramref name="to"/>.
|
||||
/// </summary>
|
||||
/// <param name="from">The start of the line segment.</param>
|
||||
/// <param name="to">The end of the line segment.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> intersects the line segment.
|
||||
/// </returns>
|
||||
public readonly bool IntersectsSegment(Vector3 from, Vector3 to)
|
||||
{
|
||||
real_t min = 0f;
|
||||
real_t max = 1f;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
real_t segFrom = from[i];
|
||||
real_t segTo = to[i];
|
||||
real_t boxBegin = _position[i];
|
||||
real_t boxEnd = boxBegin + _size[i];
|
||||
real_t cmin, cmax;
|
||||
|
||||
if (segFrom < segTo)
|
||||
{
|
||||
if (segFrom > boxEnd || segTo < boxBegin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
real_t length = segTo - segFrom;
|
||||
cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f;
|
||||
cmax = segTo > boxEnd ? (boxEnd - segFrom) / length : 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segTo > boxEnd || segFrom < boxBegin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
real_t length = segTo - segFrom;
|
||||
cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f;
|
||||
cmax = segTo < boxBegin ? (boxBegin - segFrom) / length : 1f;
|
||||
}
|
||||
|
||||
if (cmin > min)
|
||||
{
|
||||
min = cmin;
|
||||
}
|
||||
|
||||
if (cmax < max)
|
||||
{
|
||||
max = cmax;
|
||||
}
|
||||
if (max < min)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this <see cref="Aabb"/> is finite, by calling
|
||||
/// <see cref="Mathf.IsFinite(real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>Whether this vector is finite or not.</returns>
|
||||
public readonly bool IsFinite()
|
||||
{
|
||||
return _position.IsFinite() && _size.IsFinite();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a larger <see cref="Aabb"/> that contains this <see cref="Aabb"/> and <paramref name="with"/>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other <see cref="Aabb"/>.</param>
|
||||
/// <returns>The merged <see cref="Aabb"/>.</returns>
|
||||
public readonly Aabb Merge(Aabb with)
|
||||
{
|
||||
Vector3 beg1 = _position;
|
||||
Vector3 beg2 = with._position;
|
||||
var end1 = new Vector3(_size.X, _size.Y, _size.Z) + beg1;
|
||||
var end2 = new Vector3(with._size.X, with._size.Y, with._size.Z) + beg2;
|
||||
|
||||
var min = new Vector3(
|
||||
beg1.X < beg2.X ? beg1.X : beg2.X,
|
||||
beg1.Y < beg2.Y ? beg1.Y : beg2.Y,
|
||||
beg1.Z < beg2.Z ? beg1.Z : beg2.Z
|
||||
);
|
||||
|
||||
var max = new Vector3(
|
||||
end1.X > end2.X ? end1.X : end2.X,
|
||||
end1.Y > end2.Y ? end1.Y : end2.Y,
|
||||
end1.Z > end2.Z ? end1.Z : end2.Z
|
||||
);
|
||||
|
||||
return new Aabb(min, max - min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an <see cref="Aabb"/> from a position and size.
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="size">The size, typically positive.</param>
|
||||
public Aabb(Vector3 position, Vector3 size)
|
||||
{
|
||||
_position = position;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an <see cref="Aabb"/> from a <paramref name="position"/>,
|
||||
/// <paramref name="width"/>, <paramref name="height"/>, and <paramref name="depth"/>.
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="width">The width, typically positive.</param>
|
||||
/// <param name="height">The height, typically positive.</param>
|
||||
/// <param name="depth">The depth, typically positive.</param>
|
||||
public Aabb(Vector3 position, real_t width, real_t height, real_t depth)
|
||||
{
|
||||
_position = position;
|
||||
_size = new Vector3(width, height, depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an <see cref="Aabb"/> from <paramref name="x"/>,
|
||||
/// <paramref name="y"/>, <paramref name="z"/>, and <paramref name="size"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The position's X coordinate.</param>
|
||||
/// <param name="y">The position's Y coordinate.</param>
|
||||
/// <param name="z">The position's Z coordinate.</param>
|
||||
/// <param name="size">The size, typically positive.</param>
|
||||
public Aabb(real_t x, real_t y, real_t z, Vector3 size)
|
||||
{
|
||||
_position = new Vector3(x, y, z);
|
||||
_size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an <see cref="Aabb"/> from <paramref name="x"/>,
|
||||
/// <paramref name="y"/>, <paramref name="z"/>, <paramref name="width"/>,
|
||||
/// <paramref name="height"/>, and <paramref name="depth"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The position's X coordinate.</param>
|
||||
/// <param name="y">The position's Y coordinate.</param>
|
||||
/// <param name="z">The position's Z coordinate.</param>
|
||||
/// <param name="width">The width, typically positive.</param>
|
||||
/// <param name="height">The height, typically positive.</param>
|
||||
/// <param name="depth">The depth, typically positive.</param>
|
||||
public Aabb(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth)
|
||||
{
|
||||
_position = new Vector3(x, y, z);
|
||||
_size = new Vector3(width, height, depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the AABBs are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left AABB.</param>
|
||||
/// <param name="right">The right AABB.</param>
|
||||
/// <returns>Whether or not the AABBs are exactly equal.</returns>
|
||||
public static bool operator ==(Aabb left, Aabb right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the AABBs are not equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left AABB.</param>
|
||||
/// <param name="right">The right AABB.</param>
|
||||
/// <returns>Whether or not the AABBs are not equal.</returns>
|
||||
public static bool operator !=(Aabb left, Aabb right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the AABB is exactly equal
|
||||
/// to the given object (<paramref name="obj"/>).
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with.</param>
|
||||
/// <returns>Whether or not the AABB and the object are equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Aabb other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the AABBs are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="other">The other AABB.</param>
|
||||
/// <returns>Whether or not the AABBs are exactly equal.</returns>
|
||||
public readonly bool Equals(Aabb other)
|
||||
{
|
||||
return _position == other._position && _size == other._size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this AABB and <paramref name="other"/> are approximately equal,
|
||||
/// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
|
||||
/// </summary>
|
||||
/// <param name="other">The other AABB to compare.</param>
|
||||
/// <returns>Whether or not the AABBs structures are approximately equal.</returns>
|
||||
public readonly bool IsEqualApprox(Aabb other)
|
||||
{
|
||||
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Aabb"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this AABB.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_position, _size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Aabb"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this AABB.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Aabb"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this AABB.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"{_position.ToString(format)}, {_size.ToString(format)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
1881
modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
Normal file
1881
modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute that determines that the assembly contains Godot scripts and, optionally, the
|
||||
/// collection of types that implement scripts; otherwise, retrieving the types requires lookup.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public sealed class AssemblyHasScriptsAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// If the Godot scripts contained in the assembly require lookup
|
||||
/// and can't rely on <see cref="ScriptTypes"/>.
|
||||
/// </summary>
|
||||
[MemberNotNullWhen(false, nameof(ScriptTypes))]
|
||||
public bool RequiresLookup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The collection of types that implement a Godot script.
|
||||
/// </summary>
|
||||
public Type[]? ScriptTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new AssemblyHasScriptsAttribute instance
|
||||
/// that requires lookup to get the Godot scripts.
|
||||
/// </summary>
|
||||
public AssemblyHasScriptsAttribute()
|
||||
{
|
||||
RequiresLookup = true;
|
||||
ScriptTypes = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new AssemblyHasScriptsAttribute instance
|
||||
/// that includes the Godot script types and requires no lookup.
|
||||
/// </summary>
|
||||
/// <param name="scriptTypes">The collection of types that implement a Godot script.</param>
|
||||
public AssemblyHasScriptsAttribute(Type[] scriptTypes)
|
||||
{
|
||||
RequiresLookup = false;
|
||||
ScriptTypes = scriptTypes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports the annotated member as a property of the Godot Object.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class ExportAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional hint that determines how the property should be handled by the editor.
|
||||
/// </summary>
|
||||
public PropertyHint Hint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional string that can contain additional metadata for the <see cref="Hint"/>.
|
||||
/// </summary>
|
||||
public string HintString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new ExportAttribute Instance.
|
||||
/// </summary>
|
||||
/// <param name="hint">The hint for the exported property.</param>
|
||||
/// <param name="hintString">A string that may contain additional metadata for the hint.</param>
|
||||
public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "")
|
||||
{
|
||||
Hint = hint;
|
||||
HintString = hintString;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Define a new category for the following exported properties. This helps to organize properties in the Inspector dock.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class ExportCategoryAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the category.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Define a new category for the following exported properties.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the category.</param>
|
||||
public ExportCategoryAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Define a new group for the following exported properties. This helps to organize properties in the Inspector dock.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class ExportGroupAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the group.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If provided, the prefix that all properties must have to be considered part of the group.
|
||||
/// </summary>
|
||||
public string? Prefix { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Define a new group for the following exported properties.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the group.</param>
|
||||
/// <param name="prefix">If provided, the group would make group to only consider properties that have this prefix.</param>
|
||||
public ExportGroupAttribute(string name, string prefix = "")
|
||||
{
|
||||
Name = name;
|
||||
Prefix = prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class ExportSubgroupAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the subgroup.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If provided, the prefix that all properties must have to be considered part of the subgroup.
|
||||
/// </summary>
|
||||
public string? Prefix { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the subgroup.</param>
|
||||
/// <param name="prefix">If provided, the subgroup would make group to only consider properties that have this prefix.</param>
|
||||
public ExportSubgroupAttribute(string name, string prefix = "")
|
||||
{
|
||||
Name = name;
|
||||
Prefix = prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports the annotated <see cref="Callable"/> as a clickable button.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class ExportToolButtonAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The label of the button.
|
||||
/// </summary>
|
||||
public string Text { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If defined, used to fetch an icon for the button via <see cref="Control.GetThemeIcon"/>,
|
||||
/// from the <code>EditorIcons</code> theme type.
|
||||
/// </summary>
|
||||
public string? Icon { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Exports the annotated <see cref="Callable"/> as a clickable button.
|
||||
/// </summary>
|
||||
/// <param name="text">The label of the button.</param>
|
||||
public ExportToolButtonAttribute(string text)
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes the target class as a global script class to Godot Engine.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class GlobalClassAttribute : Attribute { }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute that specifies the engine class name when it's not the same
|
||||
/// as the generated C# class name. This allows introspection code to find
|
||||
/// the name associated with the class. If the attribute is not present,
|
||||
/// the C# class name can be used instead.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class GodotClassNameAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Original engine class name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
public GodotClassNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a custom icon for representing this class in the Godot Editor.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class IconAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// File path to a custom icon for representing this class in the Godot Editor.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Specify the custom icon that represents the class.
|
||||
/// </summary>
|
||||
/// <param name="path">File path to the custom icon.</param>
|
||||
public IconAttribute(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute that restricts generic type parameters to be only types
|
||||
/// that can be marshaled from/to a <see cref="Variant"/>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.GenericParameter)]
|
||||
public sealed class MustBeVariantAttribute : Attribute { }
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute that changes the RPC mode for the annotated <c>method</c> to the given <see cref="Mode"/>,
|
||||
/// optionally specifying the <see cref="TransferMode"/> and <see cref="TransferChannel"/> (on supported peers).
|
||||
/// See <see cref="MultiplayerApi.RpcMode"/> and <see cref="MultiplayerPeer.TransferModeEnum"/>.
|
||||
/// By default, methods are not exposed to networking (and RPCs).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class RpcAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// RPC mode for the annotated method.
|
||||
/// </summary>
|
||||
public MultiplayerApi.RpcMode Mode { get; } = MultiplayerApi.RpcMode.Disabled;
|
||||
|
||||
/// <summary>
|
||||
/// If the method will also be called locally; otherwise, it is only called remotely.
|
||||
/// </summary>
|
||||
public bool CallLocal { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Transfer mode for the annotated method.
|
||||
/// </summary>
|
||||
public MultiplayerPeer.TransferModeEnum TransferMode { get; init; } = MultiplayerPeer.TransferModeEnum.Reliable;
|
||||
|
||||
/// <summary>
|
||||
/// Transfer channel for the annotated mode.
|
||||
/// </summary>
|
||||
public int TransferChannel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="RpcAttribute"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="mode">The RPC mode to use.</param>
|
||||
public RpcAttribute(MultiplayerApi.RpcMode mode = MultiplayerApi.RpcMode.Authority)
|
||||
{
|
||||
Mode = mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute that contains the path to the object's script.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public sealed class ScriptPathAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// File path to the script.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new ScriptPathAttribute instance.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path to the script</param>
|
||||
public ScriptPathAttribute(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Delegate)]
|
||||
public sealed class SignalAttribute : Attribute { }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class ToolAttribute : Attribute { }
|
||||
}
|
||||
1160
modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
Normal file
1160
modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
namespace Godot.Bridge;
|
||||
|
||||
public static class AlcReloadCfg
|
||||
{
|
||||
private static bool _configured;
|
||||
|
||||
public static void Configure(bool alcReloadEnabled)
|
||||
{
|
||||
if (_configured)
|
||||
return;
|
||||
|
||||
_configured = true;
|
||||
|
||||
IsAlcReloadingEnabled = alcReloadEnabled;
|
||||
}
|
||||
|
||||
internal static bool IsAlcReloadingEnabled;
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot.Bridge
|
||||
{
|
||||
internal static class CSharpInstanceBridge
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe godot_bool Call(IntPtr godotObjectGCHandle, godot_string_name* method,
|
||||
godot_variant** args, int argCount, godot_variant_call_error* refCallError, godot_variant* ret)
|
||||
{
|
||||
try
|
||||
{
|
||||
var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (godotObject == null)
|
||||
{
|
||||
*ret = default;
|
||||
(*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL;
|
||||
return godot_bool.False;
|
||||
}
|
||||
|
||||
bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method),
|
||||
new NativeVariantPtrArgs(args, argCount), out godot_variant retValue);
|
||||
|
||||
if (!methodInvoked)
|
||||
{
|
||||
*ret = default;
|
||||
// This is important, as it tells Object::call that no method was called.
|
||||
// Otherwise, it would prevent Object::call from calling native methods.
|
||||
(*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD;
|
||||
return godot_bool.False;
|
||||
}
|
||||
|
||||
*ret = retValue;
|
||||
return godot_bool.True;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*ret = default;
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (godotObject == null)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if (godotObject.SetGodotClassPropertyValue(CustomUnsafe.AsRef(name), CustomUnsafe.AsRef(value)))
|
||||
{
|
||||
return godot_bool.True;
|
||||
}
|
||||
|
||||
if (!godotObject.HasGodotClassMethod(GodotObject.MethodName._Set.NativeValue.DangerousSelfRef))
|
||||
{
|
||||
return godot_bool.False;
|
||||
}
|
||||
|
||||
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
|
||||
|
||||
Variant valueManaged = Variant.CreateCopyingBorrowed(*value);
|
||||
|
||||
return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name,
|
||||
godot_variant* outRet)
|
||||
{
|
||||
try
|
||||
{
|
||||
var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (godotObject == null)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
// Properties
|
||||
if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue))
|
||||
{
|
||||
*outRet = outRetValue;
|
||||
return godot_bool.True;
|
||||
}
|
||||
|
||||
// Signals
|
||||
if (godotObject.HasGodotClassSignal(CustomUnsafe.AsRef(name)))
|
||||
{
|
||||
godot_signal signal = new godot_signal(NativeFuncs.godotsharp_string_name_new_copy(*name), godotObject.GetInstanceId());
|
||||
*outRet = VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue(signal);
|
||||
return godot_bool.True;
|
||||
}
|
||||
|
||||
// Methods
|
||||
if (godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(name)))
|
||||
{
|
||||
godot_callable method = new godot_callable(NativeFuncs.godotsharp_string_name_new_copy(*name), godotObject.GetInstanceId());
|
||||
*outRet = VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(method);
|
||||
return godot_bool.True;
|
||||
}
|
||||
|
||||
if (!godotObject.HasGodotClassMethod(GodotObject.MethodName._Get.NativeValue.DangerousSelfRef))
|
||||
{
|
||||
return godot_bool.False;
|
||||
}
|
||||
|
||||
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
|
||||
|
||||
Variant ret = godotObject._Get(nameManaged);
|
||||
|
||||
if (ret.VariantType == Variant.Type.Nil)
|
||||
{
|
||||
*outRet = default;
|
||||
return godot_bool.False;
|
||||
}
|
||||
|
||||
*outRet = ret.CopyNativeVariant();
|
||||
return godot_bool.True;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*outRet = default;
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull)
|
||||
{
|
||||
try
|
||||
{
|
||||
var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (okIfNull.ToBool())
|
||||
godotObject?.Dispose();
|
||||
else
|
||||
godotObject!.Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var self = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (self == null)
|
||||
{
|
||||
*outRes = default;
|
||||
*outValid = godot_bool.False;
|
||||
return;
|
||||
}
|
||||
|
||||
var resultStr = self.ToString();
|
||||
|
||||
if (resultStr == null)
|
||||
{
|
||||
*outRes = default;
|
||||
*outValid = godot_bool.False;
|
||||
return;
|
||||
}
|
||||
|
||||
*outRes = Marshaling.ConvertStringToNative(resultStr);
|
||||
*outValid = godot_bool.True;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*outRes = default;
|
||||
*outValid = godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe godot_bool HasMethodUnknownParams(IntPtr godotObjectGCHandle, godot_string_name* method)
|
||||
{
|
||||
try
|
||||
{
|
||||
var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (godotObject == null)
|
||||
return godot_bool.False;
|
||||
|
||||
return godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(method)).ToGodotBool();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void SerializeState(
|
||||
IntPtr godotObjectGCHandle,
|
||||
godot_dictionary* propertiesState,
|
||||
godot_dictionary* signalEventsState
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (godotObject == null)
|
||||
return;
|
||||
|
||||
// Call OnBeforeSerialize
|
||||
|
||||
// ReSharper disable once SuspiciousTypeConversion.Global
|
||||
if (godotObject is ISerializationListener serializationListener)
|
||||
serializationListener.OnBeforeSerialize();
|
||||
|
||||
// Save instance state
|
||||
|
||||
using var info = GodotSerializationInfo.CreateCopyingBorrowed(
|
||||
*propertiesState, *signalEventsState);
|
||||
|
||||
godotObject.SaveGodotObjectData(info);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void DeserializeState(
|
||||
IntPtr godotObjectGCHandle,
|
||||
godot_dictionary* propertiesState,
|
||||
godot_dictionary* signalEventsState
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
|
||||
|
||||
if (godotObject == null)
|
||||
return;
|
||||
|
||||
// Restore instance state
|
||||
|
||||
using var info = GodotSerializationInfo.CreateCopyingBorrowed(
|
||||
*propertiesState, *signalEventsState);
|
||||
|
||||
godotObject.RestoreGodotObjectData(info);
|
||||
|
||||
// Call OnAfterDeserialize
|
||||
|
||||
// ReSharper disable once SuspiciousTypeConversion.Global
|
||||
if (godotObject is ISerializationListener serializationListener)
|
||||
serializationListener.OnAfterDeserialize();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot.Bridge
|
||||
{
|
||||
internal static class GCHandleBridge
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void FreeGCHandle(IntPtr gcHandlePtr)
|
||||
{
|
||||
try
|
||||
{
|
||||
CustomGCHandle.Free(GCHandle.FromIntPtr(gcHandlePtr));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true, if releasing the provided handle is necessary for assembly unloading to succeed.
|
||||
// This check is not perfect and only intended to prevent things in GodotTools from being reloaded.
|
||||
[UnmanagedCallersOnly]
|
||||
internal static godot_bool GCHandleIsTargetCollectible(IntPtr gcHandlePtr)
|
||||
{
|
||||
try
|
||||
{
|
||||
var target = GCHandle.FromIntPtr(gcHandlePtr).Target;
|
||||
|
||||
if (target is Delegate @delegate)
|
||||
return DelegateUtils.IsDelegateCollectible(@delegate).ToGodotBool();
|
||||
|
||||
return target.GetType().IsCollectible.ToGodotBool();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
return godot_bool.True;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot.Bridge;
|
||||
|
||||
public sealed class GodotSerializationInfo : IDisposable
|
||||
{
|
||||
private readonly Collections.Dictionary _properties;
|
||||
private readonly Collections.Dictionary _signalEvents;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_properties?.Dispose();
|
||||
_signalEvents?.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private GodotSerializationInfo(in godot_dictionary properties, in godot_dictionary signalEvents)
|
||||
{
|
||||
_properties = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(properties);
|
||||
_signalEvents = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(signalEvents);
|
||||
}
|
||||
|
||||
internal static GodotSerializationInfo CreateCopyingBorrowed(
|
||||
in godot_dictionary properties, in godot_dictionary signalEvents)
|
||||
{
|
||||
return new(NativeFuncs.godotsharp_dictionary_new_copy(properties),
|
||||
NativeFuncs.godotsharp_dictionary_new_copy(signalEvents));
|
||||
}
|
||||
|
||||
public void AddProperty(StringName name, Variant value)
|
||||
{
|
||||
_properties[name] = value;
|
||||
}
|
||||
|
||||
public bool TryGetProperty(StringName name, out Variant value)
|
||||
{
|
||||
return _properties.TryGetValue(name, out value);
|
||||
}
|
||||
|
||||
public void AddSignalEventDelegate(StringName name, Delegate eventDelegate)
|
||||
{
|
||||
var serializedData = new Collections.Array();
|
||||
|
||||
if (DelegateUtils.TrySerializeDelegate(eventDelegate, serializedData))
|
||||
{
|
||||
_signalEvents[name] = serializedData;
|
||||
}
|
||||
else if (OS.IsStdOutVerbose())
|
||||
{
|
||||
Console.WriteLine($"Failed to serialize event signal delegate: {name}");
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetSignalEventDelegate<T>(StringName name, [MaybeNullWhen(false)] out T value)
|
||||
where T : Delegate
|
||||
{
|
||||
if (_signalEvents.TryGetValue(name, out Variant serializedData))
|
||||
{
|
||||
if (DelegateUtils.TryDeserializeDelegate(serializedData.AsGodotArray(), out var eventDelegate))
|
||||
{
|
||||
value = eventDelegate as T;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine($"Cannot cast the deserialized event signal delegate: {name}. " +
|
||||
$"Expected '{typeof(T).FullName}'; got '{eventDelegate.GetType().FullName}'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (OS.IsStdOutVerbose())
|
||||
{
|
||||
Console.WriteLine($"Failed to deserialize event signal delegate: {name}");
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot.Bridge
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct ManagedCallbacks
|
||||
{
|
||||
// @formatter:off
|
||||
public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback;
|
||||
public delegate* unmanaged<IntPtr, void*, godot_variant**, int, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
|
||||
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
|
||||
public delegate* unmanaged<IntPtr, int> DelegateUtils_DelegateHash;
|
||||
public delegate* unmanaged<IntPtr, godot_bool*, int> DelegateUtils_GetArgumentCount;
|
||||
public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
|
||||
public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
|
||||
public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
|
||||
public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
|
||||
public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
|
||||
public delegate* unmanaged<godot_string*, godot_string*, godot_string*, godot_bool*, godot_bool*, godot_string*, void> ScriptManagerBridge_GetGlobalClassName;
|
||||
public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
|
||||
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
|
||||
public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge;
|
||||
public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
|
||||
public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
|
||||
public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
|
||||
public delegate* unmanaged<IntPtr, godot_csharp_type_info*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
|
||||
public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
|
||||
public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
|
||||
public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_variant_call_error*, godot_variant*, godot_bool> ScriptManagerBridge_CallStatic;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_variant_call_error*, godot_variant*, godot_bool> CSharpInstanceBridge_Call;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Set;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Get;
|
||||
public delegate* unmanaged<IntPtr, godot_bool, void> CSharpInstanceBridge_CallDispose;
|
||||
public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, void> CSharpInstanceBridge_CallToString;
|
||||
public delegate* unmanaged<IntPtr, godot_string_name*, godot_bool> CSharpInstanceBridge_HasMethodUnknownParams;
|
||||
public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_SerializeState;
|
||||
public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_DeserializeState;
|
||||
public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle;
|
||||
public delegate* unmanaged<IntPtr, godot_bool> GCHandleBridge_GCHandleIsTargetCollectible;
|
||||
public delegate* unmanaged<void*, void> DebuggingUtils_GetCurrentStackInfo;
|
||||
public delegate* unmanaged<void> DisposablesTracker_OnGodotShuttingDown;
|
||||
public delegate* unmanaged<godot_bool, void> GD_OnCoreApiAssemblyLoaded;
|
||||
// @formatter:on
|
||||
|
||||
public static ManagedCallbacks Create()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
// @formatter:off
|
||||
SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback,
|
||||
DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs,
|
||||
DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals,
|
||||
DelegateUtils_DelegateHash = &DelegateUtils.DelegateHash,
|
||||
DelegateUtils_GetArgumentCount = &DelegateUtils.GetArgumentCount,
|
||||
DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle,
|
||||
DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle,
|
||||
ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
|
||||
ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding,
|
||||
ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance,
|
||||
ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
|
||||
ScriptManagerBridge_GetGlobalClassName = &ScriptManagerBridge.GetGlobalClassName,
|
||||
ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
|
||||
ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
|
||||
ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
|
||||
ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge,
|
||||
ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath,
|
||||
ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge,
|
||||
ScriptManagerBridge_TryReloadRegisteredScriptWithClass = &ScriptManagerBridge.TryReloadRegisteredScriptWithClass,
|
||||
ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo,
|
||||
ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType,
|
||||
ScriptManagerBridge_GetPropertyInfoList = &ScriptManagerBridge.GetPropertyInfoList,
|
||||
ScriptManagerBridge_GetPropertyDefaultValues = &ScriptManagerBridge.GetPropertyDefaultValues,
|
||||
ScriptManagerBridge_CallStatic = &ScriptManagerBridge.CallStatic,
|
||||
CSharpInstanceBridge_Call = &CSharpInstanceBridge.Call,
|
||||
CSharpInstanceBridge_Set = &CSharpInstanceBridge.Set,
|
||||
CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get,
|
||||
CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose,
|
||||
CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString,
|
||||
CSharpInstanceBridge_HasMethodUnknownParams = &CSharpInstanceBridge.HasMethodUnknownParams,
|
||||
CSharpInstanceBridge_SerializeState = &CSharpInstanceBridge.SerializeState,
|
||||
CSharpInstanceBridge_DeserializeState = &CSharpInstanceBridge.DeserializeState,
|
||||
GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle,
|
||||
GCHandleBridge_GCHandleIsTargetCollectible = &GCHandleBridge.GCHandleIsTargetCollectible,
|
||||
DebuggingUtils_GetCurrentStackInfo = &DebuggingUtils.GetCurrentStackInfo,
|
||||
DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown,
|
||||
GD_OnCoreApiAssemblyLoaded = &GD.OnCoreApiAssemblyLoaded,
|
||||
// @formatter:on
|
||||
};
|
||||
}
|
||||
|
||||
public static void Create(IntPtr outManagedCallbacks)
|
||||
=> *(ManagedCallbacks*)outManagedCallbacks = Create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Godot.Bridge;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public readonly struct MethodInfo
|
||||
{
|
||||
public StringName Name { get; init; }
|
||||
public PropertyInfo ReturnVal { get; init; }
|
||||
public MethodFlags Flags { get; init; }
|
||||
public int Id { get; init; } = 0;
|
||||
public List<PropertyInfo>? Arguments { get; init; }
|
||||
public List<Variant>? DefaultArguments { get; init; }
|
||||
|
||||
public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags,
|
||||
List<PropertyInfo>? arguments, List<Variant>? defaultArguments)
|
||||
{
|
||||
Name = name;
|
||||
ReturnVal = returnVal;
|
||||
Flags = flags;
|
||||
Arguments = arguments;
|
||||
DefaultArguments = defaultArguments;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Godot.Bridge;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public readonly struct PropertyInfo
|
||||
{
|
||||
public Variant.Type Type { get; init; }
|
||||
public StringName Name { get; init; }
|
||||
public PropertyHint Hint { get; init; }
|
||||
public string HintString { get; init; }
|
||||
public PropertyUsageFlags Usage { get; init; }
|
||||
public StringName? ClassName { get; init; }
|
||||
public bool Exported { get; init; }
|
||||
|
||||
public PropertyInfo(Variant.Type type, StringName name, PropertyHint hint, string hintString,
|
||||
PropertyUsageFlags usage, bool exported)
|
||||
: this(type, name, hint, hintString, usage, className: null, exported) { }
|
||||
|
||||
public PropertyInfo(Variant.Type type, StringName name, PropertyHint hint, string hintString,
|
||||
PropertyUsageFlags usage, StringName? className, bool exported)
|
||||
{
|
||||
Type = type;
|
||||
Name = name;
|
||||
Hint = hint;
|
||||
HintString = hintString;
|
||||
Usage = usage;
|
||||
ClassName = className;
|
||||
Exported = exported;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Godot.Bridge;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public static partial class ScriptManagerBridge
|
||||
{
|
||||
[SuppressMessage("Design", "CA1001", MessageId = "Types that own disposable fields should be disposable",
|
||||
Justification = "Not applicable. The class functions as a persistent singleton.")]
|
||||
private class ScriptTypeBiMap
|
||||
{
|
||||
public readonly ReaderWriterLockSlim ReadWriteLock = new(LockRecursionPolicy.SupportsRecursion);
|
||||
private System.Collections.Generic.Dictionary<IntPtr, Type> _scriptTypeMap = new();
|
||||
private System.Collections.Generic.Dictionary<Type, IntPtr> _typeScriptMap = new();
|
||||
|
||||
public void Add(IntPtr scriptPtr, Type scriptType)
|
||||
{
|
||||
// TODO: What if this is called while unloading a load context, but after we already did cleanup in preparation for unloading?
|
||||
|
||||
Debug.Assert(!scriptType.IsGenericTypeDefinition, $"A generic type definition must never be added to the script type map. Type: {scriptType}.");
|
||||
|
||||
_scriptTypeMap.Add(scriptPtr, scriptType);
|
||||
_typeScriptMap.Add(scriptType, scriptPtr);
|
||||
|
||||
if (AlcReloadCfg.IsAlcReloadingEnabled)
|
||||
{
|
||||
AddTypeForAlcReloading(scriptType);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(IntPtr scriptPtr)
|
||||
{
|
||||
if (_scriptTypeMap.Remove(scriptPtr, out Type? scriptType))
|
||||
_ = _typeScriptMap.Remove(scriptType);
|
||||
}
|
||||
|
||||
public bool RemoveByScriptType(Type scriptType, out IntPtr scriptPtr)
|
||||
{
|
||||
if (_typeScriptMap.Remove(scriptType, out scriptPtr))
|
||||
return _scriptTypeMap.Remove(scriptPtr);
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Type GetScriptType(IntPtr scriptPtr) => _scriptTypeMap[scriptPtr];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetScriptType(IntPtr scriptPtr, [MaybeNullWhen(false)] out Type scriptType) =>
|
||||
_scriptTypeMap.TryGetValue(scriptPtr, out scriptType);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetScriptPtr(Type scriptType, out IntPtr scriptPtr) =>
|
||||
_typeScriptMap.TryGetValue(scriptType, out scriptPtr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsScriptRegistered(IntPtr scriptPtr) => _scriptTypeMap.ContainsKey(scriptPtr);
|
||||
}
|
||||
|
||||
private class PathScriptTypeBiMap
|
||||
{
|
||||
private System.Collections.Generic.Dictionary<string, Type> _pathTypeMap = new();
|
||||
private System.Collections.Generic.Dictionary<Type, string> _typePathMap = new();
|
||||
|
||||
public System.Collections.Generic.Dictionary<string, Type>.KeyCollection Paths => _pathTypeMap.Keys;
|
||||
|
||||
public void Add(string scriptPath, Type scriptType)
|
||||
{
|
||||
_pathTypeMap.Add(scriptPath, scriptType);
|
||||
|
||||
// Due to partial classes, more than one file can point to the same type, so
|
||||
// there could be duplicate keys in this case. We only add a type as key once.
|
||||
_typePathMap.TryAdd(scriptType, scriptPath);
|
||||
}
|
||||
|
||||
public void RemoveByScriptType(Type scriptType)
|
||||
{
|
||||
foreach (var pair in _pathTypeMap
|
||||
.Where(p => p.Value == scriptType).ToArray())
|
||||
{
|
||||
_pathTypeMap.Remove(pair.Key);
|
||||
}
|
||||
|
||||
_typePathMap.Remove(scriptType);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetScriptType(string scriptPath, [MaybeNullWhen(false)] out Type scriptType) =>
|
||||
// This must never return true for a generic type definition, we only consider script types
|
||||
// the types that can be attached to a Node/Resource (non-generic or constructed generic types).
|
||||
_pathTypeMap.TryGetValue(scriptPath, out scriptType) && !scriptType.IsGenericTypeDefinition;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetScriptPath(Type scriptType, [MaybeNullWhen(false)] out string scriptPath)
|
||||
{
|
||||
if (scriptType.IsGenericTypeDefinition)
|
||||
{
|
||||
// This must never return true for a generic type definition, we only consider script types
|
||||
// the types that can be attached to a Node/Resource (non-generic or constructed generic types).
|
||||
scriptPath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return _typePathMap.TryGetValue(scriptType, out scriptPath);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetGenericTypeDefinitionPath(Type genericTypeDefinition, [MaybeNullWhen(false)] out string scriptPath)
|
||||
{
|
||||
Debug.Assert(genericTypeDefinition.IsGenericTypeDefinition);
|
||||
return _typePathMap.TryGetValue(genericTypeDefinition, out scriptPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
204
modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
Normal file
204
modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Callable is a first class object which can be held in variables and passed to functions.
|
||||
/// It represents a given method in an Object, and is typically used for signal callbacks.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public void PrintArgs(object ar1, object arg2, object arg3 = null)
|
||||
/// {
|
||||
/// GD.PrintS(arg1, arg2, arg3);
|
||||
/// }
|
||||
///
|
||||
/// public void Test()
|
||||
/// {
|
||||
/// // This Callable object will call the PrintArgs method defined above.
|
||||
/// Callable callable = new Callable(this, nameof(PrintArgs));
|
||||
/// callable.Call("hello", "world"); // Prints "hello world null".
|
||||
/// callable.Call(Vector2.Up, 42, callable); // Prints "(0, -1) 42 Node(Node.cs)::PrintArgs".
|
||||
/// callable.Call("invalid"); // Invalid call, should have at least 2 arguments.
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public readonly partial struct Callable
|
||||
{
|
||||
private readonly GodotObject _target;
|
||||
private readonly StringName _method;
|
||||
private readonly Delegate _delegate;
|
||||
private readonly unsafe delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> _trampoline;
|
||||
|
||||
/// <summary>
|
||||
/// Object that contains the method.
|
||||
/// </summary>
|
||||
public GodotObject Target => _target;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the method that will be called.
|
||||
/// </summary>
|
||||
public StringName Method => _method;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate of the method that will be called.
|
||||
/// </summary>
|
||||
public Delegate Delegate => _delegate;
|
||||
|
||||
/// <summary>
|
||||
/// Trampoline function pointer for dynamically invoking <see cref="Callable.Delegate"/>.
|
||||
/// </summary>
|
||||
public unsafe delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> Trampoline
|
||||
=> _trampoline;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Callable"/> for the method called <paramref name="method"/>
|
||||
/// in the specified <paramref name="target"/>.
|
||||
/// </summary>
|
||||
/// <param name="target">Object that contains the method.</param>
|
||||
/// <param name="method">Name of the method that will be called.</param>
|
||||
public unsafe Callable(GodotObject target, StringName method)
|
||||
{
|
||||
_target = target;
|
||||
_method = method;
|
||||
_delegate = null;
|
||||
_trampoline = null;
|
||||
}
|
||||
|
||||
private unsafe Callable(Delegate @delegate,
|
||||
delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> trampoline)
|
||||
{
|
||||
_target = @delegate?.Target as GodotObject;
|
||||
_method = null;
|
||||
_delegate = @delegate;
|
||||
_trampoline = trampoline;
|
||||
}
|
||||
|
||||
private const int VarArgsSpanThreshold = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Calls the method represented by this <see cref="Callable"/>.
|
||||
/// Arguments can be passed and should match the method's signature.
|
||||
/// </summary>
|
||||
/// <param name="args">Arguments that will be passed to the method call.</param>
|
||||
/// <returns>The value returned by the method.</returns>
|
||||
public unsafe Variant Call(params Variant[] args)
|
||||
{
|
||||
using godot_callable callable = Marshaling.ConvertCallableToNative(this);
|
||||
|
||||
int argc = args.Length;
|
||||
|
||||
Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
|
||||
stackalloc godot_variant.movable[VarArgsSpanThreshold] :
|
||||
new godot_variant.movable[argc];
|
||||
|
||||
Span<IntPtr> argsSpan = argc <= VarArgsSpanThreshold ?
|
||||
stackalloc IntPtr[VarArgsSpanThreshold] :
|
||||
new IntPtr[argc];
|
||||
|
||||
fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
|
||||
fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
|
||||
{
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
varargs[i] = (godot_variant)args[i].NativeVar;
|
||||
argsPtr[i] = new IntPtr(&varargs[i]);
|
||||
}
|
||||
|
||||
godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
|
||||
(godot_variant**)argsPtr, argc, out godot_variant_call_error vcall_error);
|
||||
ExceptionUtils.DebugCheckCallError(callable, (godot_variant**)argsPtr, argc, vcall_error);
|
||||
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the method represented by this <see cref="Callable"/> in deferred mode, i.e. during the idle frame.
|
||||
/// Arguments can be passed and should match the method's signature.
|
||||
/// </summary>
|
||||
/// <param name="args">Arguments that will be passed to the method call.</param>
|
||||
public unsafe void CallDeferred(params Variant[] args)
|
||||
{
|
||||
using godot_callable callable = Marshaling.ConvertCallableToNative(this);
|
||||
|
||||
int argc = args.Length;
|
||||
|
||||
Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
|
||||
stackalloc godot_variant.movable[VarArgsSpanThreshold] :
|
||||
new godot_variant.movable[argc];
|
||||
|
||||
Span<IntPtr> argsSpan = argc <= VarArgsSpanThreshold ?
|
||||
stackalloc IntPtr[VarArgsSpanThreshold] :
|
||||
new IntPtr[argc];
|
||||
|
||||
fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
|
||||
fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
|
||||
{
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
varargs[i] = (godot_variant)args[i].NativeVar;
|
||||
argsPtr[i] = new IntPtr(&varargs[i]);
|
||||
}
|
||||
|
||||
NativeFuncs.godotsharp_callable_call_deferred(callable, (godot_variant**)argsPtr, argc);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Constructs a new <see cref="Callable"/> using the <paramref name="trampoline"/>
|
||||
/// function pointer to dynamically invoke the given <paramref name="delegate"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The parameters passed to the <paramref name="trampoline"/> function are:
|
||||
/// </para>
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// <term>delegateObj</term>
|
||||
/// <description>The given <paramref name="delegate"/>, upcast to <see cref="object"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>args</term>
|
||||
/// <description>Array of <see cref="godot_variant"/> arguments.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>ret</term>
|
||||
/// <description>Return value of type <see cref="godot_variant"/>.</description>
|
||||
/// </item>
|
||||
///</list>
|
||||
/// <para>
|
||||
/// The delegate should be downcast to a more specific delegate type before invoking.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Usage example:
|
||||
///
|
||||
/// <code>
|
||||
/// static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
/// {
|
||||
/// if (args.Count != 1)
|
||||
/// throw new ArgumentException($"Callable expected {1} argument but received {args.Count}.");
|
||||
///
|
||||
/// TResult res = ((Func<int, string>)delegateObj)(
|
||||
/// VariantConversionCallbacks.GetToManagedCallback<int>()(args[0])
|
||||
/// );
|
||||
///
|
||||
/// ret = VariantConversionCallbacks.GetToVariantCallback<string>()(res);
|
||||
/// }
|
||||
///
|
||||
/// var callable = Callable.CreateWithUnsafeTrampoline((int num) => "foo" + num.ToString(), &Trampoline);
|
||||
/// var res = (string)callable.Call(10);
|
||||
/// Console.WriteLine(res);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="delegate">Delegate method that will be called.</param>
|
||||
/// <param name="trampoline">Trampoline function pointer for invoking the delegate.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe Callable CreateWithUnsafeTrampoline(Delegate @delegate,
|
||||
delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> trampoline)
|
||||
=> new(@delegate, trampoline);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public readonly partial struct Callable
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void ThrowIfArgCountMismatch(NativeVariantPtrArgs args, int countExpected,
|
||||
[CallerArgumentExpression("args")] string? paramName = null)
|
||||
{
|
||||
if (countExpected != args.Count)
|
||||
ThrowArgCountMismatch(countExpected, args.Count, paramName);
|
||||
|
||||
static void ThrowArgCountMismatch(int countExpected, int countReceived, string? paramName)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Invalid argument count for invoking callable." +
|
||||
$" Expected {countExpected} argument(s), received {countReceived}.",
|
||||
paramName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Callable"/> for the given <paramref name="action"/>.
|
||||
/// </summary>
|
||||
/// <param name="action">Action method that will be called.</param>
|
||||
public static unsafe Callable From(
|
||||
Action action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 0);
|
||||
|
||||
((Action)delegateObj)();
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0>(
|
||||
Action<T0> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 1);
|
||||
|
||||
((Action<T0>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1>(
|
||||
Action<T0, T1> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 2);
|
||||
|
||||
((Action<T0, T1>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2>(
|
||||
Action<T0, T1, T2> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 3);
|
||||
|
||||
((Action<T0, T1, T2>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3>(
|
||||
Action<T0, T1, T2, T3> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 4);
|
||||
|
||||
((Action<T0, T1, T2, T3>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4>(
|
||||
Action<T0, T1, T2, T3, T4> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 5);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5>(
|
||||
Action<T0, T1, T2, T3, T4, T5> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 6);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6>(
|
||||
Action<T0, T1, T2, T3, T4, T5, T6> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 7);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5, T6>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7>(
|
||||
Action<T0, T1, T2, T3, T4, T5, T6, T7> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 8);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5, T6, T7>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From(Action)"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8>(
|
||||
Action<T0, T1, T2, T3, T4, T5, T6, T7, T8> action
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 9);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5, T6, T7, T8>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7]),
|
||||
VariantUtils.ConvertTo<T8>(args[8])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(action, &Trampoline);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Callable"/> for the given <paramref name="func"/>.
|
||||
/// </summary>
|
||||
/// <param name="func">Action method that will be called.</param>
|
||||
public static unsafe Callable From<[MustBeVariant] TResult>(
|
||||
Func<TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 0);
|
||||
|
||||
TResult res = ((Func<TResult>)delegateObj)();
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] TResult>(
|
||||
Func<T0, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 1);
|
||||
|
||||
TResult res = ((Func<T0, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 2);
|
||||
|
||||
TResult res = ((Func<T0, T1, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, T2, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 3);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, T2, T3, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 4);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, T2, T3, T4, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 5);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, T2, T3, T4, T5, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 6);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, T2, T3, T4, T5, T6, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 7);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 8);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="From{TResult}(Func{TResult})"/>
|
||||
public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8, [MustBeVariant] TResult>(
|
||||
Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult> func
|
||||
)
|
||||
{
|
||||
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
{
|
||||
ThrowIfArgCountMismatch(args, 9);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>)delegateObj)(
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7]),
|
||||
VariantUtils.ConvertTo<T8>(args[8])
|
||||
);
|
||||
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
}
|
||||
}
|
||||
1388
modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
Normal file
1388
modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
Normal file
File diff suppressed because it is too large
Load Diff
311
modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
Normal file
311
modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
Normal file
@@ -0,0 +1,311 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains color constants created from standardized color names.
|
||||
/// The standardized color set is based on the X11 and .NET color names.
|
||||
/// </summary>
|
||||
public static class Colors
|
||||
{
|
||||
// Color names and values are derived from core/math/color_names.inc
|
||||
internal static readonly FrozenDictionary<string, Color> NamedColors = new Dictionary<string, Color> {
|
||||
{ "ALICEBLUE", Colors.AliceBlue },
|
||||
{ "ANTIQUEWHITE", Colors.AntiqueWhite },
|
||||
{ "AQUA", Colors.Aqua },
|
||||
{ "AQUAMARINE", Colors.Aquamarine },
|
||||
{ "AZURE", Colors.Azure },
|
||||
{ "BEIGE", Colors.Beige },
|
||||
{ "BISQUE", Colors.Bisque },
|
||||
{ "BLACK", Colors.Black },
|
||||
{ "BLANCHEDALMOND", Colors.BlanchedAlmond },
|
||||
{ "BLUE", Colors.Blue },
|
||||
{ "BLUEVIOLET", Colors.BlueViolet },
|
||||
{ "BROWN", Colors.Brown },
|
||||
{ "BURLYWOOD", Colors.Burlywood },
|
||||
{ "CADETBLUE", Colors.CadetBlue },
|
||||
{ "CHARTREUSE", Colors.Chartreuse },
|
||||
{ "CHOCOLATE", Colors.Chocolate },
|
||||
{ "CORAL", Colors.Coral },
|
||||
{ "CORNFLOWERBLUE", Colors.CornflowerBlue },
|
||||
{ "CORNSILK", Colors.Cornsilk },
|
||||
{ "CRIMSON", Colors.Crimson },
|
||||
{ "CYAN", Colors.Cyan },
|
||||
{ "DARKBLUE", Colors.DarkBlue },
|
||||
{ "DARKCYAN", Colors.DarkCyan },
|
||||
{ "DARKGOLDENROD", Colors.DarkGoldenrod },
|
||||
{ "DARKGRAY", Colors.DarkGray },
|
||||
{ "DARKGREEN", Colors.DarkGreen },
|
||||
{ "DARKKHAKI", Colors.DarkKhaki },
|
||||
{ "DARKMAGENTA", Colors.DarkMagenta },
|
||||
{ "DARKOLIVEGREEN", Colors.DarkOliveGreen },
|
||||
{ "DARKORANGE", Colors.DarkOrange },
|
||||
{ "DARKORCHID", Colors.DarkOrchid },
|
||||
{ "DARKRED", Colors.DarkRed },
|
||||
{ "DARKSALMON", Colors.DarkSalmon },
|
||||
{ "DARKSEAGREEN", Colors.DarkSeaGreen },
|
||||
{ "DARKSLATEBLUE", Colors.DarkSlateBlue },
|
||||
{ "DARKSLATEGRAY", Colors.DarkSlateGray },
|
||||
{ "DARKTURQUOISE", Colors.DarkTurquoise },
|
||||
{ "DARKVIOLET", Colors.DarkViolet },
|
||||
{ "DEEPPINK", Colors.DeepPink },
|
||||
{ "DEEPSKYBLUE", Colors.DeepSkyBlue },
|
||||
{ "DIMGRAY", Colors.DimGray },
|
||||
{ "DODGERBLUE", Colors.DodgerBlue },
|
||||
{ "FIREBRICK", Colors.Firebrick },
|
||||
{ "FLORALWHITE", Colors.FloralWhite },
|
||||
{ "FORESTGREEN", Colors.ForestGreen },
|
||||
{ "FUCHSIA", Colors.Fuchsia },
|
||||
{ "GAINSBORO", Colors.Gainsboro },
|
||||
{ "GHOSTWHITE", Colors.GhostWhite },
|
||||
{ "GOLD", Colors.Gold },
|
||||
{ "GOLDENROD", Colors.Goldenrod },
|
||||
{ "GRAY", Colors.Gray },
|
||||
{ "GREEN", Colors.Green },
|
||||
{ "GREENYELLOW", Colors.GreenYellow },
|
||||
{ "HONEYDEW", Colors.Honeydew },
|
||||
{ "HOTPINK", Colors.HotPink },
|
||||
{ "INDIANRED", Colors.IndianRed },
|
||||
{ "INDIGO", Colors.Indigo },
|
||||
{ "IVORY", Colors.Ivory },
|
||||
{ "KHAKI", Colors.Khaki },
|
||||
{ "LAVENDER", Colors.Lavender },
|
||||
{ "LAVENDERBLUSH", Colors.LavenderBlush },
|
||||
{ "LAWNGREEN", Colors.LawnGreen },
|
||||
{ "LEMONCHIFFON", Colors.LemonChiffon },
|
||||
{ "LIGHTBLUE", Colors.LightBlue },
|
||||
{ "LIGHTCORAL", Colors.LightCoral },
|
||||
{ "LIGHTCYAN", Colors.LightCyan },
|
||||
{ "LIGHTGOLDENROD", Colors.LightGoldenrod },
|
||||
{ "LIGHTGRAY", Colors.LightGray },
|
||||
{ "LIGHTGREEN", Colors.LightGreen },
|
||||
{ "LIGHTPINK", Colors.LightPink },
|
||||
{ "LIGHTSALMON", Colors.LightSalmon },
|
||||
{ "LIGHTSEAGREEN", Colors.LightSeaGreen },
|
||||
{ "LIGHTSKYBLUE", Colors.LightSkyBlue },
|
||||
{ "LIGHTSLATEGRAY", Colors.LightSlateGray },
|
||||
{ "LIGHTSTEELBLUE", Colors.LightSteelBlue },
|
||||
{ "LIGHTYELLOW", Colors.LightYellow },
|
||||
{ "LIME", Colors.Lime },
|
||||
{ "LIMEGREEN", Colors.LimeGreen },
|
||||
{ "LINEN", Colors.Linen },
|
||||
{ "MAGENTA", Colors.Magenta },
|
||||
{ "MAROON", Colors.Maroon },
|
||||
{ "MEDIUMAQUAMARINE", Colors.MediumAquamarine },
|
||||
{ "MEDIUMBLUE", Colors.MediumBlue },
|
||||
{ "MEDIUMORCHID", Colors.MediumOrchid },
|
||||
{ "MEDIUMPURPLE", Colors.MediumPurple },
|
||||
{ "MEDIUMSEAGREEN", Colors.MediumSeaGreen },
|
||||
{ "MEDIUMSLATEBLUE", Colors.MediumSlateBlue },
|
||||
{ "MEDIUMSPRINGGREEN", Colors.MediumSpringGreen },
|
||||
{ "MEDIUMTURQUOISE", Colors.MediumTurquoise },
|
||||
{ "MEDIUMVIOLETRED", Colors.MediumVioletRed },
|
||||
{ "MIDNIGHTBLUE", Colors.MidnightBlue },
|
||||
{ "MINTCREAM", Colors.MintCream },
|
||||
{ "MISTYROSE", Colors.MistyRose },
|
||||
{ "MOCCASIN", Colors.Moccasin },
|
||||
{ "NAVAJOWHITE", Colors.NavajoWhite },
|
||||
{ "NAVYBLUE", Colors.NavyBlue },
|
||||
{ "OLDLACE", Colors.OldLace },
|
||||
{ "OLIVE", Colors.Olive },
|
||||
{ "OLIVEDRAB", Colors.OliveDrab },
|
||||
{ "ORANGE", Colors.Orange },
|
||||
{ "ORANGERED", Colors.OrangeRed },
|
||||
{ "ORCHID", Colors.Orchid },
|
||||
{ "PALEGOLDENROD", Colors.PaleGoldenrod },
|
||||
{ "PALEGREEN", Colors.PaleGreen },
|
||||
{ "PALETURQUOISE", Colors.PaleTurquoise },
|
||||
{ "PALEVIOLETRED", Colors.PaleVioletRed },
|
||||
{ "PAPAYAWHIP", Colors.PapayaWhip },
|
||||
{ "PEACHPUFF", Colors.PeachPuff },
|
||||
{ "PERU", Colors.Peru },
|
||||
{ "PINK", Colors.Pink },
|
||||
{ "PLUM", Colors.Plum },
|
||||
{ "POWDERBLUE", Colors.PowderBlue },
|
||||
{ "PURPLE", Colors.Purple },
|
||||
{ "REBECCAPURPLE", Colors.RebeccaPurple },
|
||||
{ "RED", Colors.Red },
|
||||
{ "ROSYBROWN", Colors.RosyBrown },
|
||||
{ "ROYALBLUE", Colors.RoyalBlue },
|
||||
{ "SADDLEBROWN", Colors.SaddleBrown },
|
||||
{ "SALMON", Colors.Salmon },
|
||||
{ "SANDYBROWN", Colors.SandyBrown },
|
||||
{ "SEAGREEN", Colors.SeaGreen },
|
||||
{ "SEASHELL", Colors.Seashell },
|
||||
{ "SIENNA", Colors.Sienna },
|
||||
{ "SILVER", Colors.Silver },
|
||||
{ "SKYBLUE", Colors.SkyBlue },
|
||||
{ "SLATEBLUE", Colors.SlateBlue },
|
||||
{ "SLATEGRAY", Colors.SlateGray },
|
||||
{ "SNOW", Colors.Snow },
|
||||
{ "SPRINGGREEN", Colors.SpringGreen },
|
||||
{ "STEELBLUE", Colors.SteelBlue },
|
||||
{ "TAN", Colors.Tan },
|
||||
{ "TEAL", Colors.Teal },
|
||||
{ "THISTLE", Colors.Thistle },
|
||||
{ "TOMATO", Colors.Tomato },
|
||||
{ "TRANSPARENT", Colors.Transparent },
|
||||
{ "TURQUOISE", Colors.Turquoise },
|
||||
{ "VIOLET", Colors.Violet },
|
||||
{ "WEBGRAY", Colors.WebGray },
|
||||
{ "WEBGREEN", Colors.WebGreen },
|
||||
{ "WEBMAROON", Colors.WebMaroon },
|
||||
{ "WEBPURPLE", Colors.WebPurple },
|
||||
{ "WHEAT", Colors.Wheat },
|
||||
{ "WHITE", Colors.White },
|
||||
{ "WHITESMOKE", Colors.WhiteSmoke },
|
||||
{ "YELLOW", Colors.Yellow },
|
||||
{ "YELLOWGREEN", Colors.YellowGreen },
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
#pragma warning disable CS1591 // Disable warning: "Missing XML comment for publicly visible type or member"
|
||||
public static Color AliceBlue => new Color(0xF0F8FFFF);
|
||||
public static Color AntiqueWhite => new Color(0xFAEBD7FF);
|
||||
public static Color Aqua => new Color(0x00FFFFFF);
|
||||
public static Color Aquamarine => new Color(0x7FFFD4FF);
|
||||
public static Color Azure => new Color(0xF0FFFFFF);
|
||||
public static Color Beige => new Color(0xF5F5DCFF);
|
||||
public static Color Bisque => new Color(0xFFE4C4FF);
|
||||
public static Color Black => new Color(0x000000FF);
|
||||
public static Color BlanchedAlmond => new Color(0xFFEBCDFF);
|
||||
public static Color Blue => new Color(0x0000FFFF);
|
||||
public static Color BlueViolet => new Color(0x8A2BE2FF);
|
||||
public static Color Brown => new Color(0xA52A2AFF);
|
||||
public static Color Burlywood => new Color(0xDEB887FF);
|
||||
public static Color CadetBlue => new Color(0x5F9EA0FF);
|
||||
public static Color Chartreuse => new Color(0x7FFF00FF);
|
||||
public static Color Chocolate => new Color(0xD2691EFF);
|
||||
public static Color Coral => new Color(0xFF7F50FF);
|
||||
public static Color CornflowerBlue => new Color(0x6495EDFF);
|
||||
public static Color Cornsilk => new Color(0xFFF8DCFF);
|
||||
public static Color Crimson => new Color(0xDC143CFF);
|
||||
public static Color Cyan => new Color(0x00FFFFFF);
|
||||
public static Color DarkBlue => new Color(0x00008BFF);
|
||||
public static Color DarkCyan => new Color(0x008B8BFF);
|
||||
public static Color DarkGoldenrod => new Color(0xB8860BFF);
|
||||
public static Color DarkGray => new Color(0xA9A9A9FF);
|
||||
public static Color DarkGreen => new Color(0x006400FF);
|
||||
public static Color DarkKhaki => new Color(0xBDB76BFF);
|
||||
public static Color DarkMagenta => new Color(0x8B008BFF);
|
||||
public static Color DarkOliveGreen => new Color(0x556B2FFF);
|
||||
public static Color DarkOrange => new Color(0xFF8C00FF);
|
||||
public static Color DarkOrchid => new Color(0x9932CCFF);
|
||||
public static Color DarkRed => new Color(0x8B0000FF);
|
||||
public static Color DarkSalmon => new Color(0xE9967AFF);
|
||||
public static Color DarkSeaGreen => new Color(0x8FBC8FFF);
|
||||
public static Color DarkSlateBlue => new Color(0x483D8BFF);
|
||||
public static Color DarkSlateGray => new Color(0x2F4F4FFF);
|
||||
public static Color DarkTurquoise => new Color(0x00CED1FF);
|
||||
public static Color DarkViolet => new Color(0x9400D3FF);
|
||||
public static Color DeepPink => new Color(0xFF1493FF);
|
||||
public static Color DeepSkyBlue => new Color(0x00BFFFFF);
|
||||
public static Color DimGray => new Color(0x696969FF);
|
||||
public static Color DodgerBlue => new Color(0x1E90FFFF);
|
||||
public static Color Firebrick => new Color(0xB22222FF);
|
||||
public static Color FloralWhite => new Color(0xFFFAF0FF);
|
||||
public static Color ForestGreen => new Color(0x228B22FF);
|
||||
public static Color Fuchsia => new Color(0xFF00FFFF);
|
||||
public static Color Gainsboro => new Color(0xDCDCDCFF);
|
||||
public static Color GhostWhite => new Color(0xF8F8FFFF);
|
||||
public static Color Gold => new Color(0xFFD700FF);
|
||||
public static Color Goldenrod => new Color(0xDAA520FF);
|
||||
public static Color Gray => new Color(0xBEBEBEFF);
|
||||
public static Color Green => new Color(0x00FF00FF);
|
||||
public static Color GreenYellow => new Color(0xADFF2FFF);
|
||||
public static Color Honeydew => new Color(0xF0FFF0FF);
|
||||
public static Color HotPink => new Color(0xFF69B4FF);
|
||||
public static Color IndianRed => new Color(0xCD5C5CFF);
|
||||
public static Color Indigo => new Color(0x4B0082FF);
|
||||
public static Color Ivory => new Color(0xFFFFF0FF);
|
||||
public static Color Khaki => new Color(0xF0E68CFF);
|
||||
public static Color Lavender => new Color(0xE6E6FAFF);
|
||||
public static Color LavenderBlush => new Color(0xFFF0F5FF);
|
||||
public static Color LawnGreen => new Color(0x7CFC00FF);
|
||||
public static Color LemonChiffon => new Color(0xFFFACDFF);
|
||||
public static Color LightBlue => new Color(0xADD8E6FF);
|
||||
public static Color LightCoral => new Color(0xF08080FF);
|
||||
public static Color LightCyan => new Color(0xE0FFFFFF);
|
||||
public static Color LightGoldenrod => new Color(0xFAFAD2FF);
|
||||
public static Color LightGray => new Color(0xD3D3D3FF);
|
||||
public static Color LightGreen => new Color(0x90EE90FF);
|
||||
public static Color LightPink => new Color(0xFFB6C1FF);
|
||||
public static Color LightSalmon => new Color(0xFFA07AFF);
|
||||
public static Color LightSeaGreen => new Color(0x20B2AAFF);
|
||||
public static Color LightSkyBlue => new Color(0x87CEFAFF);
|
||||
public static Color LightSlateGray => new Color(0x778899FF);
|
||||
public static Color LightSteelBlue => new Color(0xB0C4DEFF);
|
||||
public static Color LightYellow => new Color(0xFFFFE0FF);
|
||||
public static Color Lime => new Color(0x00FF00FF);
|
||||
public static Color LimeGreen => new Color(0x32CD32FF);
|
||||
public static Color Linen => new Color(0xFAF0E6FF);
|
||||
public static Color Magenta => new Color(0xFF00FFFF);
|
||||
public static Color Maroon => new Color(0xB03060FF);
|
||||
public static Color MediumAquamarine => new Color(0x66CDAAFF);
|
||||
public static Color MediumBlue => new Color(0x0000CDFF);
|
||||
public static Color MediumOrchid => new Color(0xBA55D3FF);
|
||||
public static Color MediumPurple => new Color(0x9370DBFF);
|
||||
public static Color MediumSeaGreen => new Color(0x3CB371FF);
|
||||
public static Color MediumSlateBlue => new Color(0x7B68EEFF);
|
||||
public static Color MediumSpringGreen => new Color(0x00FA9AFF);
|
||||
public static Color MediumTurquoise => new Color(0x48D1CCFF);
|
||||
public static Color MediumVioletRed => new Color(0xC71585FF);
|
||||
public static Color MidnightBlue => new Color(0x191970FF);
|
||||
public static Color MintCream => new Color(0xF5FFFAFF);
|
||||
public static Color MistyRose => new Color(0xFFE4E1FF);
|
||||
public static Color Moccasin => new Color(0xFFE4B5FF);
|
||||
public static Color NavajoWhite => new Color(0xFFDEADFF);
|
||||
public static Color NavyBlue => new Color(0x000080FF);
|
||||
public static Color OldLace => new Color(0xFDF5E6FF);
|
||||
public static Color Olive => new Color(0x808000FF);
|
||||
public static Color OliveDrab => new Color(0x6B8E23FF);
|
||||
public static Color Orange => new Color(0xFFA500FF);
|
||||
public static Color OrangeRed => new Color(0xFF4500FF);
|
||||
public static Color Orchid => new Color(0xDA70D6FF);
|
||||
public static Color PaleGoldenrod => new Color(0xEEE8AAFF);
|
||||
public static Color PaleGreen => new Color(0x98FB98FF);
|
||||
public static Color PaleTurquoise => new Color(0xAFEEEEFF);
|
||||
public static Color PaleVioletRed => new Color(0xDB7093FF);
|
||||
public static Color PapayaWhip => new Color(0xFFEFD5FF);
|
||||
public static Color PeachPuff => new Color(0xFFDAB9FF);
|
||||
public static Color Peru => new Color(0xCD853FFF);
|
||||
public static Color Pink => new Color(0xFFC0CBFF);
|
||||
public static Color Plum => new Color(0xDDA0DDFF);
|
||||
public static Color PowderBlue => new Color(0xB0E0E6FF);
|
||||
public static Color Purple => new Color(0xA020F0FF);
|
||||
public static Color RebeccaPurple => new Color(0x663399FF);
|
||||
public static Color Red => new Color(0xFF0000FF);
|
||||
public static Color RosyBrown => new Color(0xBC8F8FFF);
|
||||
public static Color RoyalBlue => new Color(0x4169E1FF);
|
||||
public static Color SaddleBrown => new Color(0x8B4513FF);
|
||||
public static Color Salmon => new Color(0xFA8072FF);
|
||||
public static Color SandyBrown => new Color(0xF4A460FF);
|
||||
public static Color SeaGreen => new Color(0x2E8B57FF);
|
||||
public static Color Seashell => new Color(0xFFF5EEFF);
|
||||
public static Color Sienna => new Color(0xA0522DFF);
|
||||
public static Color Silver => new Color(0xC0C0C0FF);
|
||||
public static Color SkyBlue => new Color(0x87CEEBFF);
|
||||
public static Color SlateBlue => new Color(0x6A5ACDFF);
|
||||
public static Color SlateGray => new Color(0x708090FF);
|
||||
public static Color Snow => new Color(0xFFFAFAFF);
|
||||
public static Color SpringGreen => new Color(0x00FF7FFF);
|
||||
public static Color SteelBlue => new Color(0x4682B4FF);
|
||||
public static Color Tan => new Color(0xD2B48CFF);
|
||||
public static Color Teal => new Color(0x008080FF);
|
||||
public static Color Thistle => new Color(0xD8BFD8FF);
|
||||
public static Color Tomato => new Color(0xFF6347FF);
|
||||
public static Color Transparent => new Color(0xFFFFFF00);
|
||||
public static Color Turquoise => new Color(0x40E0D0FF);
|
||||
public static Color Violet => new Color(0xEE82EEFF);
|
||||
public static Color WebGray => new Color(0x808080FF);
|
||||
public static Color WebGreen => new Color(0x008000FF);
|
||||
public static Color WebMaroon => new Color(0x800000FF);
|
||||
public static Color WebPurple => new Color(0x800080FF);
|
||||
public static Color Wheat => new Color(0xF5DEB3FF);
|
||||
public static Color White => new Color(0xFFFFFFFF);
|
||||
public static Color WhiteSmoke => new Color(0xF5F5F5FF);
|
||||
public static Color Yellow => new Color(0xFFFF00FF);
|
||||
public static Color YellowGreen => new Color(0x9ACD32FF);
|
||||
#pragma warning restore CS1591
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using Godot.Bridge;
|
||||
|
||||
namespace Godot;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a GCHandle that becomes weak when unloading the assembly load context, without having
|
||||
/// to manually replace the GCHandle. This hides all the complexity of releasing strong GC handles
|
||||
/// to allow the assembly load context to unload properly.
|
||||
///
|
||||
/// Internally, a strong CustomGCHandle actually contains a weak GCHandle, while the actual strong
|
||||
/// reference is stored in a static table.
|
||||
/// </summary>
|
||||
public static class CustomGCHandle
|
||||
{
|
||||
// ConditionalWeakTable uses DependentHandle, so it stores weak references.
|
||||
// Having the assembly load context as key won't prevent it from unloading.
|
||||
private static ConditionalWeakTable<AssemblyLoadContext, object?> _alcsBeingUnloaded = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool IsAlcBeingUnloaded(AssemblyLoadContext alc) => _alcsBeingUnloaded.TryGetValue(alc, out _);
|
||||
|
||||
private static ConcurrentDictionary<
|
||||
AssemblyLoadContext,
|
||||
ConcurrentDictionary<GCHandle, object>
|
||||
> _strongReferencesByAlc = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void OnAlcUnloading(AssemblyLoadContext alc)
|
||||
{
|
||||
_alcsBeingUnloaded.Add(alc, null);
|
||||
|
||||
if (_strongReferencesByAlc.TryRemove(alc, out var strongReferences))
|
||||
{
|
||||
strongReferences.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GCHandle AllocStrong(object value)
|
||||
=> AllocStrong(value, value.GetType());
|
||||
|
||||
public static GCHandle AllocStrong(object value, Type valueType)
|
||||
{
|
||||
if (AlcReloadCfg.IsAlcReloadingEnabled)
|
||||
{
|
||||
var alc = AssemblyLoadContext.GetLoadContext(valueType.Assembly);
|
||||
|
||||
if (alc != null)
|
||||
{
|
||||
var weakHandle = GCHandle.Alloc(value, GCHandleType.Weak);
|
||||
|
||||
if (!IsAlcBeingUnloaded(alc))
|
||||
{
|
||||
var strongReferences = _strongReferencesByAlc.GetOrAdd(alc,
|
||||
static alc =>
|
||||
{
|
||||
alc.Unloading += OnAlcUnloading;
|
||||
return new();
|
||||
});
|
||||
strongReferences.TryAdd(weakHandle, value);
|
||||
}
|
||||
|
||||
return weakHandle;
|
||||
}
|
||||
}
|
||||
|
||||
return GCHandle.Alloc(value, GCHandleType.Normal);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GCHandle AllocWeak(object value) => GCHandle.Alloc(value, GCHandleType.Weak);
|
||||
|
||||
public static void Free(GCHandle handle)
|
||||
{
|
||||
if (AlcReloadCfg.IsAlcReloadingEnabled)
|
||||
{
|
||||
var target = handle.Target;
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
var alc = AssemblyLoadContext.GetLoadContext(target.GetType().Assembly);
|
||||
|
||||
if (alc != null && _strongReferencesByAlc.TryGetValue(alc, out var strongReferences))
|
||||
_ = strongReferences.TryRemove(handle, out _);
|
||||
}
|
||||
}
|
||||
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
73
modules/mono/glue/GodotSharp/GodotSharp/Core/DebugView.cs
Normal file
73
modules/mono/glue/GodotSharp/GodotSharp/Core/DebugView.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Godot.Collections
|
||||
{
|
||||
internal sealed class ArrayDebugView<T>
|
||||
{
|
||||
private readonly IList<T> _array;
|
||||
|
||||
public ArrayDebugView(IList<T> array)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(array);
|
||||
|
||||
_array = array;
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
||||
public T[] Items
|
||||
{
|
||||
get
|
||||
{
|
||||
var items = new T[_array.Count];
|
||||
_array.CopyTo(items, 0);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DictionaryDebugView<TKey, TValue>
|
||||
{
|
||||
private readonly IDictionary<TKey, TValue> _dictionary;
|
||||
|
||||
public DictionaryDebugView(IDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dictionary);
|
||||
|
||||
_dictionary = dictionary;
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
||||
public DictionaryKeyItemDebugView<TKey, TValue>[] Items
|
||||
{
|
||||
get
|
||||
{
|
||||
var items = new KeyValuePair<TKey, TValue>[_dictionary.Count];
|
||||
var views = new DictionaryKeyItemDebugView<TKey, TValue>[_dictionary.Count];
|
||||
_dictionary.CopyTo(items, 0);
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
views[i] = new DictionaryKeyItemDebugView<TKey, TValue>(items[i]);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{Value}", Name = "[{Key}]")]
|
||||
internal readonly struct DictionaryKeyItemDebugView<TKey, TValue>
|
||||
{
|
||||
public DictionaryKeyItemDebugView(KeyValuePair<TKey, TValue> keyValue)
|
||||
{
|
||||
Key = keyValue.Key;
|
||||
Value = keyValue.Value;
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
public TKey Key { get; }
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
public TValue Value { get; }
|
||||
}
|
||||
}
|
||||
231
modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
Normal file
231
modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
internal static class DebuggingUtils
|
||||
{
|
||||
private static void AppendTypeName(this StringBuilder sb, Type type)
|
||||
{
|
||||
// Use the C# type keyword for built-in types.
|
||||
// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types
|
||||
if (type == typeof(void))
|
||||
sb.Append("void");
|
||||
else if (type == typeof(bool))
|
||||
sb.Append("bool");
|
||||
else if (type == typeof(byte))
|
||||
sb.Append("byte");
|
||||
else if (type == typeof(sbyte))
|
||||
sb.Append("sbyte");
|
||||
else if (type == typeof(char))
|
||||
sb.Append("char");
|
||||
else if (type == typeof(decimal))
|
||||
sb.Append("decimal");
|
||||
else if (type == typeof(double))
|
||||
sb.Append("double");
|
||||
else if (type == typeof(float))
|
||||
sb.Append("float");
|
||||
else if (type == typeof(int))
|
||||
sb.Append("int");
|
||||
else if (type == typeof(uint))
|
||||
sb.Append("uint");
|
||||
else if (type == typeof(nint))
|
||||
sb.Append("nint");
|
||||
else if (type == typeof(nuint))
|
||||
sb.Append("nuint");
|
||||
else if (type == typeof(long))
|
||||
sb.Append("long");
|
||||
else if (type == typeof(ulong))
|
||||
sb.Append("ulong");
|
||||
else if (type == typeof(short))
|
||||
sb.Append("short");
|
||||
else if (type == typeof(ushort))
|
||||
sb.Append("ushort");
|
||||
else if (type == typeof(object))
|
||||
sb.Append("object");
|
||||
else if (type == typeof(string))
|
||||
sb.Append("string");
|
||||
else
|
||||
sb.Append(type);
|
||||
}
|
||||
|
||||
internal static void InstallTraceListener()
|
||||
{
|
||||
Trace.Listeners.Clear();
|
||||
Trace.Listeners.Add(new GodotTraceListener());
|
||||
}
|
||||
|
||||
#pragma warning disable IDE1006 // Naming rule violation
|
||||
// ReSharper disable once InconsistentNaming
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal ref struct godot_stack_info
|
||||
{
|
||||
public godot_string File;
|
||||
public godot_string Func;
|
||||
public int Line;
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal ref struct godot_stack_info_vector
|
||||
{
|
||||
private IntPtr _writeProxy;
|
||||
private unsafe godot_stack_info* _ptr;
|
||||
|
||||
public readonly unsafe godot_stack_info* Elements
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _ptr;
|
||||
}
|
||||
|
||||
public void Resize(int size)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(size);
|
||||
|
||||
var err = NativeFuncs.godotsharp_stack_info_vector_resize(ref this, size);
|
||||
if (err != Error.Ok)
|
||||
throw new InvalidOperationException("Failed to resize vector. Error code is: " + err.ToString());
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
if (_ptr == null)
|
||||
return;
|
||||
NativeFuncs.godotsharp_stack_info_vector_destroy(ref this);
|
||||
_ptr = null;
|
||||
}
|
||||
}
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
internal static unsafe StackFrame? GetCurrentStackFrame(int skipFrames = 0)
|
||||
{
|
||||
// We skip 2 frames:
|
||||
// The first skipped frame is the current method.
|
||||
// The second skipped frame is a method in NativeInterop.NativeFuncs.
|
||||
var stackTrace = new StackTrace(skipFrames: 2 + skipFrames, fNeedFileInfo: true);
|
||||
return stackTrace.GetFrame(0);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void GetCurrentStackInfo(void* destVector)
|
||||
{
|
||||
try
|
||||
{
|
||||
var vector = (godot_stack_info_vector*)destVector;
|
||||
|
||||
// We skip 2 frames:
|
||||
// The first skipped frame is the current method.
|
||||
// The second skipped frame is a method in NativeInterop.NativeFuncs.
|
||||
var stackTrace = new StackTrace(skipFrames: 2, fNeedFileInfo: true);
|
||||
int frameCount = stackTrace.FrameCount;
|
||||
|
||||
if (frameCount == 0)
|
||||
return;
|
||||
|
||||
vector->Resize(frameCount);
|
||||
|
||||
int i = 0;
|
||||
foreach (StackFrame frame in stackTrace.GetFrames())
|
||||
{
|
||||
var method = frame.GetMethod();
|
||||
|
||||
if (method is MethodInfo methodInfo && methodInfo.IsDefined(typeof(StackTraceHiddenAttribute)))
|
||||
{
|
||||
// Skip methods marked hidden from the stack trace.
|
||||
continue;
|
||||
}
|
||||
|
||||
string? fileName = frame.GetFileName();
|
||||
int fileLineNumber = frame.GetFileLineNumber();
|
||||
|
||||
GetStackFrameMethodDecl(frame, out string methodDecl);
|
||||
|
||||
godot_stack_info* stackInfo = &vector->Elements[i];
|
||||
|
||||
// Assign directly to element in Vector. This way we don't need to worry
|
||||
// about disposal if an exception is thrown. The Vector takes care of it.
|
||||
stackInfo->File = Marshaling.ConvertStringToNative(fileName);
|
||||
stackInfo->Func = Marshaling.ConvertStringToNative(methodDecl);
|
||||
stackInfo->Line = fileLineNumber;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Resize the vector again in case we skipped some frames.
|
||||
vector->Resize(i);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void GetStackFrameMethodDecl(StackFrame frame, out string methodDecl)
|
||||
{
|
||||
MethodBase? methodBase = frame.GetMethod();
|
||||
|
||||
if (methodBase == null)
|
||||
{
|
||||
methodDecl = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (methodBase is MethodInfo methodInfo)
|
||||
{
|
||||
sb.AppendTypeName(methodInfo.ReturnType);
|
||||
sb.Append(' ');
|
||||
}
|
||||
|
||||
sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>");
|
||||
sb.Append('.');
|
||||
sb.Append(methodBase.Name);
|
||||
|
||||
if (methodBase.IsGenericMethod)
|
||||
{
|
||||
Type[] genericParams = methodBase.GetGenericArguments();
|
||||
|
||||
sb.Append('<');
|
||||
|
||||
for (int j = 0; j < genericParams.Length; j++)
|
||||
{
|
||||
if (j > 0)
|
||||
sb.Append(", ");
|
||||
|
||||
sb.AppendTypeName(genericParams[j]);
|
||||
}
|
||||
|
||||
sb.Append('>');
|
||||
}
|
||||
|
||||
sb.Append('(');
|
||||
|
||||
bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0;
|
||||
|
||||
ParameterInfo[] parameter = methodBase.GetParameters();
|
||||
|
||||
for (int i = 0; i < parameter.Length; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append(", ");
|
||||
|
||||
if (i == parameter.Length - 1 && varArgs)
|
||||
sb.Append("params ");
|
||||
|
||||
sb.AppendTypeName(parameter[i].ParameterType);
|
||||
}
|
||||
|
||||
sb.Append(')');
|
||||
|
||||
methodDecl = sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
898
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
Normal file
898
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
Normal file
@@ -0,0 +1,898 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
internal static class DelegateUtils
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
|
||||
{
|
||||
try
|
||||
{
|
||||
var @delegateA = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleA).Target;
|
||||
var @delegateB = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleB).Target;
|
||||
return (@delegateA! == @delegateB!).ToGodotBool();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static int DelegateHash(IntPtr delegateGCHandle)
|
||||
{
|
||||
try
|
||||
{
|
||||
var @delegate = (Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target;
|
||||
return @delegate?.GetHashCode() ?? 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe int GetArgumentCount(IntPtr delegateGCHandle, godot_bool* outIsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var @delegate = (Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target;
|
||||
int? argCount = @delegate?.Method?.GetParameters().Length;
|
||||
if (argCount is null)
|
||||
{
|
||||
*outIsValid = godot_bool.False;
|
||||
return 0;
|
||||
}
|
||||
*outIsValid = godot_bool.True;
|
||||
return argCount.Value;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*outIsValid = godot_bool.False;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, void* trampoline,
|
||||
godot_variant** args, int argc, godot_variant* outRet)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (trampoline == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(trampoline),
|
||||
"Cannot dynamically invoke delegate because the trampoline is null.");
|
||||
}
|
||||
|
||||
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
|
||||
var trampolineFn = (delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void>)trampoline;
|
||||
|
||||
trampolineFn(@delegate, new NativeVariantPtrArgs(args, argc), out godot_variant ret);
|
||||
|
||||
*outRet = ret;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*outRet = default;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).
|
||||
|
||||
private enum TargetKind : uint
|
||||
{
|
||||
Static,
|
||||
GodotObject,
|
||||
CompilerGenerated
|
||||
}
|
||||
|
||||
internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
|
||||
{
|
||||
if (@delegate is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (@delegate is MulticastDelegate multicastDelegate)
|
||||
{
|
||||
bool someDelegatesSerialized = false;
|
||||
|
||||
Delegate[] invocationList = multicastDelegate.GetInvocationList();
|
||||
|
||||
if (invocationList.Length > 1)
|
||||
{
|
||||
var multiCastData = new Collections.Array();
|
||||
|
||||
foreach (Delegate oneDelegate in invocationList)
|
||||
someDelegatesSerialized |= TrySerializeDelegate(oneDelegate, multiCastData);
|
||||
|
||||
if (!someDelegatesSerialized)
|
||||
return false;
|
||||
|
||||
serializedData.Add(multiCastData);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (TrySerializeSingleDelegate(@delegate, out byte[]? buffer))
|
||||
{
|
||||
serializedData.Add((Span<byte>)buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TrySerializeSingleDelegate(Delegate @delegate, [MaybeNullWhen(false)] out byte[] buffer)
|
||||
{
|
||||
buffer = null;
|
||||
|
||||
object? target = @delegate.Target;
|
||||
|
||||
switch (target)
|
||||
{
|
||||
case null:
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write((ulong)TargetKind.Static);
|
||||
|
||||
SerializeType(writer, @delegate.GetType());
|
||||
|
||||
if (!TrySerializeMethodInfo(writer, @delegate.Method))
|
||||
return false;
|
||||
|
||||
buffer = stream.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case GodotObject godotObject:
|
||||
{
|
||||
if (!GodotObject.IsInstanceValid(godotObject))
|
||||
{
|
||||
// If the delegate's target has been freed we can't serialize it.
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write((ulong)TargetKind.GodotObject);
|
||||
// ReSharper disable once RedundantCast
|
||||
writer.Write((ulong)godotObject.GetInstanceId());
|
||||
|
||||
SerializeType(writer, @delegate.GetType());
|
||||
|
||||
if (!TrySerializeMethodInfo(writer, @delegate.Method))
|
||||
return false;
|
||||
|
||||
buffer = stream.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
Type targetType = target.GetType();
|
||||
|
||||
if (targetType.IsDefined(typeof(CompilerGeneratedAttribute), true))
|
||||
{
|
||||
// Compiler generated. Probably a closure. Try to serialize it.
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write((ulong)TargetKind.CompilerGenerated);
|
||||
SerializeType(writer, targetType);
|
||||
|
||||
SerializeType(writer, @delegate.GetType());
|
||||
|
||||
if (!TrySerializeMethodInfo(writer, @delegate.Method))
|
||||
return false;
|
||||
|
||||
FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
writer.Write(fields.Length);
|
||||
|
||||
foreach (FieldInfo field in fields)
|
||||
{
|
||||
Type fieldType = field.FieldType;
|
||||
|
||||
Variant.Type variantType = GD.TypeToVariantType(fieldType);
|
||||
|
||||
if (variantType == Variant.Type.Nil)
|
||||
return false;
|
||||
|
||||
static byte[] VarToBytes(in godot_variant var)
|
||||
{
|
||||
NativeFuncs.godotsharp_var_to_bytes(var, godot_bool.True, out var varBytes);
|
||||
using (varBytes)
|
||||
return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
|
||||
}
|
||||
|
||||
writer.Write(field.Name);
|
||||
|
||||
var fieldValue = field.GetValue(target);
|
||||
using var fieldValueVariant = RuntimeTypeConversionHelper.ConvertToVariant(fieldValue);
|
||||
byte[] valueBuffer = VarToBytes(fieldValueVariant);
|
||||
writer.Write(valueBuffer.Length);
|
||||
writer.Write(valueBuffer);
|
||||
}
|
||||
|
||||
buffer = stream.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
|
||||
{
|
||||
SerializeType(writer, methodInfo.DeclaringType);
|
||||
|
||||
writer.Write(methodInfo.Name);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (methodInfo.IsPublic)
|
||||
flags |= (int)BindingFlags.Public;
|
||||
else
|
||||
flags |= (int)BindingFlags.NonPublic;
|
||||
|
||||
if (methodInfo.IsStatic)
|
||||
flags |= (int)BindingFlags.Static;
|
||||
else
|
||||
flags |= (int)BindingFlags.Instance;
|
||||
|
||||
writer.Write(flags);
|
||||
|
||||
Type returnType = methodInfo.ReturnType;
|
||||
bool hasReturn = methodInfo.ReturnType != typeof(void);
|
||||
|
||||
writer.Write(hasReturn);
|
||||
if (hasReturn)
|
||||
SerializeType(writer, returnType);
|
||||
|
||||
ParameterInfo[] parameters = methodInfo.GetParameters();
|
||||
|
||||
writer.Write(parameters.Length);
|
||||
|
||||
if (parameters.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
SerializeType(writer, parameters[i].ParameterType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void SerializeType(BinaryWriter writer, Type? type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
int genericArgumentsCount = -1;
|
||||
writer.Write(genericArgumentsCount);
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
Type genericTypeDef = type.GetGenericTypeDefinition();
|
||||
Type[] genericArgs = type.GetGenericArguments();
|
||||
|
||||
int genericArgumentsCount = genericArgs.Length;
|
||||
writer.Write(genericArgumentsCount);
|
||||
|
||||
writer.Write(genericTypeDef.Assembly.GetName().Name ?? "");
|
||||
writer.Write(genericTypeDef.FullName ?? genericTypeDef.ToString());
|
||||
|
||||
for (int i = 0; i < genericArgs.Length; i++)
|
||||
SerializeType(writer, genericArgs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int genericArgumentsCount = 0;
|
||||
writer.Write(genericArgumentsCount);
|
||||
|
||||
writer.Write(type.Assembly.GetName().Name ?? "");
|
||||
writer.Write(type.FullName ?? type.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe godot_bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle,
|
||||
godot_array* nSerializedData)
|
||||
{
|
||||
try
|
||||
{
|
||||
var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
|
||||
|
||||
var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
|
||||
|
||||
return TrySerializeDelegate(@delegate, serializedData)
|
||||
.ToGodotBool();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe godot_bool TryDeserializeDelegateWithGCHandle(godot_array* nSerializedData,
|
||||
IntPtr* delegateGCHandle)
|
||||
{
|
||||
try
|
||||
{
|
||||
var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
|
||||
|
||||
if (TryDeserializeDelegate(serializedData, out Delegate? @delegate))
|
||||
{
|
||||
*delegateGCHandle = GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(@delegate));
|
||||
return godot_bool.True;
|
||||
}
|
||||
else
|
||||
{
|
||||
*delegateGCHandle = IntPtr.Zero;
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*delegateGCHandle = default;
|
||||
return godot_bool.False;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryDeserializeDelegate(Collections.Array serializedData,
|
||||
[MaybeNullWhen(false)] out Delegate @delegate)
|
||||
{
|
||||
@delegate = null;
|
||||
|
||||
if (serializedData.Count == 1)
|
||||
{
|
||||
var elem = serializedData[0].Obj;
|
||||
|
||||
if (elem == null)
|
||||
return false;
|
||||
|
||||
if (elem is Collections.Array multiCastData)
|
||||
return TryDeserializeDelegate(multiCastData, out @delegate);
|
||||
|
||||
return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
|
||||
}
|
||||
|
||||
var delegates = new List<Delegate>(serializedData.Count);
|
||||
|
||||
foreach (Variant variantElem in serializedData)
|
||||
{
|
||||
var elem = variantElem.Obj;
|
||||
|
||||
if (elem == null)
|
||||
continue;
|
||||
|
||||
if (elem is Collections.Array multiCastData)
|
||||
{
|
||||
if (TryDeserializeDelegate(multiCastData, out Delegate? oneDelegate))
|
||||
delegates.Add(oneDelegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryDeserializeSingleDelegate((byte[])elem, out Delegate? oneDelegate))
|
||||
delegates.Add(oneDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
if (delegates.Count <= 0)
|
||||
return false;
|
||||
|
||||
@delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray())!;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryDeserializeSingleDelegate(byte[] buffer, [MaybeNullWhen(false)] out Delegate @delegate)
|
||||
{
|
||||
@delegate = null;
|
||||
|
||||
using (var stream = new MemoryStream(buffer, writable: false))
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
var targetKind = (TargetKind)reader.ReadUInt64();
|
||||
|
||||
switch (targetKind)
|
||||
{
|
||||
case TargetKind.Static:
|
||||
{
|
||||
Type? delegateType = DeserializeType(reader);
|
||||
if (delegateType == null)
|
||||
return false;
|
||||
|
||||
if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
|
||||
return false;
|
||||
|
||||
@delegate = Delegate.CreateDelegate(delegateType, null, methodInfo, throwOnBindFailure: false);
|
||||
|
||||
if (@delegate == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
case TargetKind.GodotObject:
|
||||
{
|
||||
ulong objectId = reader.ReadUInt64();
|
||||
GodotObject? godotObject = GodotObject.InstanceFromId(objectId);
|
||||
if (godotObject == null)
|
||||
return false;
|
||||
|
||||
Type? delegateType = DeserializeType(reader);
|
||||
if (delegateType == null)
|
||||
return false;
|
||||
|
||||
if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
|
||||
return false;
|
||||
|
||||
@delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo,
|
||||
throwOnBindFailure: false);
|
||||
|
||||
if (@delegate == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
case TargetKind.CompilerGenerated:
|
||||
{
|
||||
Type? targetType = DeserializeType(reader);
|
||||
if (targetType == null)
|
||||
return false;
|
||||
|
||||
Type? delegateType = DeserializeType(reader);
|
||||
if (delegateType == null)
|
||||
return false;
|
||||
|
||||
if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
|
||||
return false;
|
||||
|
||||
int fieldCount = reader.ReadInt32();
|
||||
|
||||
object recreatedTarget = Activator.CreateInstance(targetType)!;
|
||||
|
||||
for (int i = 0; i < fieldCount; i++)
|
||||
{
|
||||
string name = reader.ReadString();
|
||||
int valueBufferLength = reader.ReadInt32();
|
||||
byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
|
||||
|
||||
FieldInfo? fieldInfo = targetType.GetField(name,
|
||||
BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
var variantValue = GD.BytesToVarWithObjects(valueBuffer);
|
||||
object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType(
|
||||
(godot_variant)variantValue.NativeVar, fieldInfo.FieldType);
|
||||
fieldInfo.SetValue(recreatedTarget, managedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo,
|
||||
throwOnBindFailure: false);
|
||||
|
||||
if (@delegate == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryDeserializeMethodInfo(BinaryReader reader,
|
||||
[MaybeNullWhen(false)] out MethodInfo methodInfo)
|
||||
{
|
||||
methodInfo = null;
|
||||
|
||||
Type? declaringType = DeserializeType(reader);
|
||||
|
||||
if (declaringType == null)
|
||||
return false;
|
||||
|
||||
string methodName = reader.ReadString();
|
||||
|
||||
BindingFlags flags = (BindingFlags)reader.ReadInt32();
|
||||
|
||||
bool hasReturn = reader.ReadBoolean();
|
||||
Type? returnType = hasReturn ? DeserializeType(reader) : typeof(void);
|
||||
|
||||
int parametersCount = reader.ReadInt32();
|
||||
var parameterTypes = parametersCount == 0 ? Type.EmptyTypes : new Type[parametersCount];
|
||||
|
||||
for (int i = 0; i < parametersCount; i++)
|
||||
{
|
||||
Type? parameterType = DeserializeType(reader);
|
||||
if (parameterType == null)
|
||||
return false;
|
||||
parameterTypes[i] = parameterType;
|
||||
}
|
||||
|
||||
#pragma warning disable REFL045 // These flags are insufficient to match any members
|
||||
// TODO: Suppressing invalid warning, remove when issue is fixed
|
||||
// https://github.com/DotNetAnalyzers/ReflectionAnalyzers/issues/209
|
||||
methodInfo = declaringType.GetMethod(methodName, flags, null, parameterTypes, null);
|
||||
#pragma warning restore REFL045
|
||||
return methodInfo != null && methodInfo.ReturnType == returnType;
|
||||
}
|
||||
|
||||
private static Type? DeserializeType(BinaryReader reader)
|
||||
{
|
||||
int genericArgumentsCount = reader.ReadInt32();
|
||||
|
||||
if (genericArgumentsCount == -1)
|
||||
return null;
|
||||
|
||||
string assemblyName = reader.ReadString();
|
||||
|
||||
if (assemblyName.Length == 0)
|
||||
{
|
||||
GD.PushError($"Missing assembly name of type when attempting to deserialize delegate");
|
||||
return null;
|
||||
}
|
||||
|
||||
string typeFullName = reader.ReadString();
|
||||
var type = ReflectionUtils.FindTypeInLoadedAssemblies(assemblyName, typeFullName);
|
||||
|
||||
if (type == null)
|
||||
return null; // Type not found
|
||||
|
||||
if (genericArgumentsCount != 0)
|
||||
{
|
||||
var genericArgumentTypes = new Type[genericArgumentsCount];
|
||||
|
||||
for (int i = 0; i < genericArgumentsCount; i++)
|
||||
{
|
||||
Type? genericArgumentType = DeserializeType(reader);
|
||||
if (genericArgumentType == null)
|
||||
return null;
|
||||
genericArgumentTypes[i] = genericArgumentType;
|
||||
}
|
||||
|
||||
type = type.MakeGenericType(genericArgumentTypes);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
// Returns true, if unloading the delegate is necessary for assembly unloading to succeed.
|
||||
// This check is not perfect and only intended to prevent things in GodotTools from being reloaded.
|
||||
internal static bool IsDelegateCollectible(Delegate @delegate)
|
||||
{
|
||||
if (@delegate.GetType().IsCollectible)
|
||||
return true;
|
||||
|
||||
if (@delegate is MulticastDelegate multicastDelegate)
|
||||
{
|
||||
Delegate[] invocationList = multicastDelegate.GetInvocationList();
|
||||
|
||||
if (invocationList.Length > 1)
|
||||
{
|
||||
foreach (Delegate oneDelegate in invocationList)
|
||||
if (IsDelegateCollectible(oneDelegate))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (@delegate.Method.IsCollectible)
|
||||
return true;
|
||||
|
||||
object? target = @delegate.Target;
|
||||
|
||||
if (target is not null && target.GetType().IsCollectible)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static class RuntimeTypeConversionHelper
|
||||
{
|
||||
public static godot_variant ConvertToVariant(object? obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return default;
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case bool @bool:
|
||||
return VariantUtils.CreateFrom(@bool);
|
||||
case char @char:
|
||||
return VariantUtils.CreateFrom(@char);
|
||||
case sbyte int8:
|
||||
return VariantUtils.CreateFrom(int8);
|
||||
case short int16:
|
||||
return VariantUtils.CreateFrom(int16);
|
||||
case int int32:
|
||||
return VariantUtils.CreateFrom(int32);
|
||||
case long int64:
|
||||
return VariantUtils.CreateFrom(int64);
|
||||
case byte uint8:
|
||||
return VariantUtils.CreateFrom(uint8);
|
||||
case ushort uint16:
|
||||
return VariantUtils.CreateFrom(uint16);
|
||||
case uint uint32:
|
||||
return VariantUtils.CreateFrom(uint32);
|
||||
case ulong uint64:
|
||||
return VariantUtils.CreateFrom(uint64);
|
||||
case float @float:
|
||||
return VariantUtils.CreateFrom(@float);
|
||||
case double @double:
|
||||
return VariantUtils.CreateFrom(@double);
|
||||
case Vector2 vector2:
|
||||
return VariantUtils.CreateFrom(vector2);
|
||||
case Vector2I vector2I:
|
||||
return VariantUtils.CreateFrom(vector2I);
|
||||
case Rect2 rect2:
|
||||
return VariantUtils.CreateFrom(rect2);
|
||||
case Rect2I rect2I:
|
||||
return VariantUtils.CreateFrom(rect2I);
|
||||
case Transform2D transform2D:
|
||||
return VariantUtils.CreateFrom(transform2D);
|
||||
case Vector3 vector3:
|
||||
return VariantUtils.CreateFrom(vector3);
|
||||
case Vector3I vector3I:
|
||||
return VariantUtils.CreateFrom(vector3I);
|
||||
case Vector4 vector4:
|
||||
return VariantUtils.CreateFrom(vector4);
|
||||
case Vector4I vector4I:
|
||||
return VariantUtils.CreateFrom(vector4I);
|
||||
case Basis basis:
|
||||
return VariantUtils.CreateFrom(basis);
|
||||
case Quaternion quaternion:
|
||||
return VariantUtils.CreateFrom(quaternion);
|
||||
case Transform3D transform3D:
|
||||
return VariantUtils.CreateFrom(transform3D);
|
||||
case Projection projection:
|
||||
return VariantUtils.CreateFrom(projection);
|
||||
case Aabb aabb:
|
||||
return VariantUtils.CreateFrom(aabb);
|
||||
case Color color:
|
||||
return VariantUtils.CreateFrom(color);
|
||||
case Plane plane:
|
||||
return VariantUtils.CreateFrom(plane);
|
||||
case Callable callable:
|
||||
return VariantUtils.CreateFrom(callable);
|
||||
case Signal signal:
|
||||
return VariantUtils.CreateFrom(signal);
|
||||
case string @string:
|
||||
return VariantUtils.CreateFrom(@string);
|
||||
case byte[] byteArray:
|
||||
return VariantUtils.CreateFrom(byteArray);
|
||||
case int[] int32Array:
|
||||
return VariantUtils.CreateFrom(int32Array);
|
||||
case long[] int64Array:
|
||||
return VariantUtils.CreateFrom(int64Array);
|
||||
case float[] floatArray:
|
||||
return VariantUtils.CreateFrom(floatArray);
|
||||
case double[] doubleArray:
|
||||
return VariantUtils.CreateFrom(doubleArray);
|
||||
case string[] stringArray:
|
||||
return VariantUtils.CreateFrom(stringArray);
|
||||
case Vector2[] vector2Array:
|
||||
return VariantUtils.CreateFrom(vector2Array);
|
||||
case Vector3[] vector3Array:
|
||||
return VariantUtils.CreateFrom(vector3Array);
|
||||
case Color[] colorArray:
|
||||
return VariantUtils.CreateFrom(colorArray);
|
||||
case StringName[] stringNameArray:
|
||||
return VariantUtils.CreateFrom(stringNameArray);
|
||||
case NodePath[] nodePathArray:
|
||||
return VariantUtils.CreateFrom(nodePathArray);
|
||||
case Rid[] ridArray:
|
||||
return VariantUtils.CreateFrom(ridArray);
|
||||
case GodotObject[] godotObjectArray:
|
||||
return VariantUtils.CreateFrom(godotObjectArray);
|
||||
case StringName stringName:
|
||||
return VariantUtils.CreateFrom(stringName);
|
||||
case NodePath nodePath:
|
||||
return VariantUtils.CreateFrom(nodePath);
|
||||
case Rid rid:
|
||||
return VariantUtils.CreateFrom(rid);
|
||||
case Collections.Dictionary godotDictionary:
|
||||
return VariantUtils.CreateFrom(godotDictionary);
|
||||
case Collections.Array godotArray:
|
||||
return VariantUtils.CreateFrom(godotArray);
|
||||
case Variant variant:
|
||||
return VariantUtils.CreateFrom(variant);
|
||||
case GodotObject godotObject:
|
||||
return VariantUtils.CreateFrom(godotObject);
|
||||
case Enum @enum:
|
||||
return VariantUtils.CreateFrom(Convert.ToInt64(@enum, CultureInfo.InvariantCulture));
|
||||
case Collections.IGenericGodotDictionary godotDictionary:
|
||||
return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary);
|
||||
case Collections.IGenericGodotArray godotArray:
|
||||
return VariantUtils.CreateFrom(godotArray.UnderlyingArray);
|
||||
}
|
||||
|
||||
GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
|
||||
obj.GetType().FullName + ".");
|
||||
return new godot_variant();
|
||||
}
|
||||
|
||||
private delegate object? ConvertToSystemObjectFunc(in godot_variant managed);
|
||||
|
||||
private static readonly System.Collections.Generic.Dictionary<Type, ConvertToSystemObjectFunc>
|
||||
_toSystemObjectFuncByType = new()
|
||||
{
|
||||
[typeof(bool)] = (in godot_variant variant) => VariantUtils.ConvertTo<bool>(variant),
|
||||
[typeof(char)] = (in godot_variant variant) => VariantUtils.ConvertTo<char>(variant),
|
||||
[typeof(sbyte)] = (in godot_variant variant) => VariantUtils.ConvertTo<sbyte>(variant),
|
||||
[typeof(short)] = (in godot_variant variant) => VariantUtils.ConvertTo<short>(variant),
|
||||
[typeof(int)] = (in godot_variant variant) => VariantUtils.ConvertTo<int>(variant),
|
||||
[typeof(long)] = (in godot_variant variant) => VariantUtils.ConvertTo<long>(variant),
|
||||
[typeof(byte)] = (in godot_variant variant) => VariantUtils.ConvertTo<byte>(variant),
|
||||
[typeof(ushort)] = (in godot_variant variant) => VariantUtils.ConvertTo<ushort>(variant),
|
||||
[typeof(uint)] = (in godot_variant variant) => VariantUtils.ConvertTo<uint>(variant),
|
||||
[typeof(ulong)] = (in godot_variant variant) => VariantUtils.ConvertTo<ulong>(variant),
|
||||
[typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo<float>(variant),
|
||||
[typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo<double>(variant),
|
||||
[typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2>(variant),
|
||||
[typeof(Vector2I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2I>(variant),
|
||||
[typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2>(variant),
|
||||
[typeof(Rect2I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2I>(variant),
|
||||
[typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform2D>(variant),
|
||||
[typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3>(variant),
|
||||
[typeof(Vector3I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3I>(variant),
|
||||
[typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo<Basis>(variant),
|
||||
[typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo<Quaternion>(variant),
|
||||
[typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform3D>(variant),
|
||||
[typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4>(variant),
|
||||
[typeof(Vector4I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4I>(variant),
|
||||
[typeof(Aabb)] = (in godot_variant variant) => VariantUtils.ConvertTo<Aabb>(variant),
|
||||
[typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo<Color>(variant),
|
||||
[typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo<Plane>(variant),
|
||||
[typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo<Callable>(variant),
|
||||
[typeof(Signal)] = (in godot_variant variant) => VariantUtils.ConvertTo<Signal>(variant),
|
||||
[typeof(string)] = (in godot_variant variant) => VariantUtils.ConvertTo<string>(variant),
|
||||
[typeof(byte[])] = (in godot_variant variant) => VariantUtils.ConvertTo<byte[]>(variant),
|
||||
[typeof(int[])] = (in godot_variant variant) => VariantUtils.ConvertTo<int[]>(variant),
|
||||
[typeof(long[])] = (in godot_variant variant) => VariantUtils.ConvertTo<long[]>(variant),
|
||||
[typeof(float[])] = (in godot_variant variant) => VariantUtils.ConvertTo<float[]>(variant),
|
||||
[typeof(double[])] = (in godot_variant variant) => VariantUtils.ConvertTo<double[]>(variant),
|
||||
[typeof(string[])] = (in godot_variant variant) => VariantUtils.ConvertTo<string[]>(variant),
|
||||
[typeof(Vector2[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2[]>(variant),
|
||||
[typeof(Vector3[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3[]>(variant),
|
||||
[typeof(Color[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Color[]>(variant),
|
||||
[typeof(StringName[])] =
|
||||
(in godot_variant variant) => VariantUtils.ConvertTo<StringName[]>(variant),
|
||||
[typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath[]>(variant),
|
||||
[typeof(Rid[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Rid[]>(variant),
|
||||
[typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName>(variant),
|
||||
[typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath>(variant),
|
||||
[typeof(Rid)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rid>(variant),
|
||||
[typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) =>
|
||||
VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant),
|
||||
[typeof(Godot.Collections.Array)] =
|
||||
(in godot_variant variant) => VariantUtils.ConvertTo<Godot.Collections.Array>(variant),
|
||||
[typeof(Variant)] = (in godot_variant variant) => VariantUtils.ConvertTo<Variant>(variant),
|
||||
};
|
||||
|
||||
public static object? ConvertToObjectOfType(in godot_variant variant, Type type)
|
||||
{
|
||||
if (_toSystemObjectFuncByType.TryGetValue(type, out var func))
|
||||
return func(variant);
|
||||
|
||||
if (typeof(GodotObject).IsAssignableFrom(type))
|
||||
return VariantUtils.ConvertTo<GodotObject>(variant);
|
||||
|
||||
if (typeof(GodotObject[]).IsAssignableFrom(type))
|
||||
{
|
||||
static GodotObject[] ConvertToSystemArrayOfGodotObject(in godot_array nativeArray, Type type)
|
||||
{
|
||||
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_array_new_copy(nativeArray));
|
||||
|
||||
int length = array.Count;
|
||||
var ret = (GodotObject[])Activator.CreateInstance(type, length)!;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
ret[i] = array[i].AsGodotObject();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
using var godotArray = NativeFuncs.godotsharp_variant_as_array(variant);
|
||||
return ConvertToSystemArrayOfGodotObject(godotArray, type);
|
||||
}
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
var enumUnderlyingType = type.GetEnumUnderlyingType();
|
||||
|
||||
switch (Type.GetTypeCode(enumUnderlyingType))
|
||||
{
|
||||
case TypeCode.SByte:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToInt8(variant));
|
||||
case TypeCode.Int16:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToInt16(variant));
|
||||
case TypeCode.Int32:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToInt32(variant));
|
||||
case TypeCode.Int64:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToInt64(variant));
|
||||
case TypeCode.Byte:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToUInt8(variant));
|
||||
case TypeCode.UInt16:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToUInt16(variant));
|
||||
case TypeCode.UInt32:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToUInt32(variant));
|
||||
case TypeCode.UInt64:
|
||||
return Enum.ToObject(type, VariantUtils.ConvertToUInt64(variant));
|
||||
default:
|
||||
{
|
||||
GD.PushError(
|
||||
"Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
|
||||
type.FullName + " : " + enumUnderlyingType.FullName + ".");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericTypeDef = type.GetGenericTypeDefinition();
|
||||
|
||||
if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>))
|
||||
{
|
||||
var ctor = type.GetConstructor(new[] { typeof(Godot.Collections.Dictionary) });
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException("Dictionary constructor not found");
|
||||
|
||||
return ctor.Invoke(new object?[]
|
||||
{
|
||||
VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant)
|
||||
});
|
||||
}
|
||||
|
||||
if (genericTypeDef == typeof(Godot.Collections.Array<>))
|
||||
{
|
||||
var ctor = type.GetConstructor(new[] { typeof(Godot.Collections.Array) });
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException("Array constructor not found");
|
||||
|
||||
return ctor.Invoke(new object?[]
|
||||
{
|
||||
VariantUtils.ConvertTo<Godot.Collections.Array>(variant)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
GD.PushError($"Attempted to convert Variant to unsupported type. Name: {type.FullName}.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
969
modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
Normal file
969
modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
Normal file
@@ -0,0 +1,969 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot.NativeInterop;
|
||||
using System.Diagnostics;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper around Godot's Dictionary class, a dictionary of Variant
|
||||
/// typed elements allocated in the engine in C++. Useful when
|
||||
/// interfacing with the engine.
|
||||
/// </summary>
|
||||
[DebuggerTypeProxy(typeof(DictionaryDebugView<Variant, Variant>))]
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
public sealed class Dictionary :
|
||||
IDictionary<Variant, Variant>,
|
||||
IReadOnlyDictionary<Variant, Variant>,
|
||||
IDisposable
|
||||
{
|
||||
internal godot_dictionary.movable NativeValue;
|
||||
|
||||
private WeakReference<IDisposable>? _weakReferenceToSelf;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new empty <see cref="Dictionary"/>.
|
||||
/// </summary>
|
||||
public Dictionary()
|
||||
{
|
||||
NativeValue = (godot_dictionary.movable)NativeFuncs.godotsharp_dictionary_new();
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
|
||||
}
|
||||
|
||||
private Dictionary(godot_dictionary nativeValueToOwn)
|
||||
{
|
||||
NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ?
|
||||
nativeValueToOwn :
|
||||
NativeFuncs.godotsharp_dictionary_new());
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
|
||||
}
|
||||
|
||||
// Explicit name to make it very clear
|
||||
internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn)
|
||||
=> new Dictionary(nativeValueToOwn);
|
||||
|
||||
~Dictionary()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this <see cref="Dictionary"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Dispose(bool disposing)
|
||||
{
|
||||
// Always dispose `NativeValue` even if disposing is true
|
||||
NativeValue.DangerousSelfRef.Dispose();
|
||||
|
||||
if (_weakReferenceToSelf != null)
|
||||
{
|
||||
DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Dictionary"/>.
|
||||
/// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed:
|
||||
/// all nested arrays and dictionaries are duplicated and will not be shared with
|
||||
/// the original dictionary. If <see langword="false"/>, a shallow copy is made and
|
||||
/// references to the original nested arrays and dictionaries are kept, so that
|
||||
/// modifying a sub-array or dictionary in the copy will also impact those
|
||||
/// referenced in the source dictionary. Note that any <see cref="GodotObject"/> derived
|
||||
/// elements will be shallow copied regardless of the <paramref name="deep"/>
|
||||
/// setting.
|
||||
/// </summary>
|
||||
/// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
|
||||
/// <returns>A new Godot Dictionary.</returns>
|
||||
public Dictionary Duplicate(bool deep = false)
|
||||
{
|
||||
godot_dictionary newDictionary;
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_duplicate(ref self, deep.ToGodotBool(), out newDictionary);
|
||||
return CreateTakingOwnershipOfDisposableValue(newDictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds entries from <paramref name="dictionary"/> to this dictionary.
|
||||
/// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/>
|
||||
/// is <see langword="true"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <param name="dictionary">Dictionary to copy entries from.</param>
|
||||
/// <param name="overwrite">If duplicate keys should be copied over as well.</param>
|
||||
public void Merge(Dictionary dictionary, bool overwrite = false)
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
var other = (godot_dictionary)dictionary.NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_merge(ref self, in other, overwrite.ToGodotBool());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this <see cref="Dictionary"/> against the <paramref name="other"/>
|
||||
/// <see cref="Dictionary"/> recursively. Returns <see langword="true"/> if the
|
||||
/// two dictionaries contain the same keys and values. The order of the entries
|
||||
/// does not matter.
|
||||
/// otherwise.
|
||||
/// </summary>
|
||||
/// <param name="other">The other dictionary to compare against.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the dictionaries contain the same keys and values,
|
||||
/// <see langword="false"/> otherwise.
|
||||
/// </returns>
|
||||
public bool RecursiveEqual(Dictionary other)
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
var otherVariant = (godot_dictionary)other.NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_recursive_equal(ref self, otherVariant).ToBool();
|
||||
}
|
||||
|
||||
// IDictionary
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of keys in this <see cref="Dictionary"/>.
|
||||
/// </summary>
|
||||
public ICollection<Variant> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
godot_array keysArray;
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
|
||||
return Array.CreateTakingOwnershipOfDisposableValue(keysArray);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of elements in this <see cref="Dictionary"/>.
|
||||
/// </summary>
|
||||
public ICollection<Variant> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
godot_array valuesArray;
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
|
||||
return Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Keys => Keys;
|
||||
|
||||
IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Values => Values;
|
||||
|
||||
private (Array keys, Array values, int count) GetKeyValuePairs()
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
|
||||
godot_array keysArray;
|
||||
NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
|
||||
var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray);
|
||||
|
||||
godot_array valuesArray;
|
||||
NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
|
||||
var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
|
||||
|
||||
int count = NativeFuncs.godotsharp_dictionary_count(ref self);
|
||||
|
||||
return (keys, values, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value at the given <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The property is assigned and the dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <exception cref="KeyNotFoundException">
|
||||
/// The property is retrieved and an entry for <paramref name="key"/>
|
||||
/// does not exist in the dictionary.
|
||||
/// </exception>
|
||||
/// <value>The value at the given <paramref name="key"/>.</value>
|
||||
public Variant this[Variant key]
|
||||
{
|
||||
get
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
|
||||
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
(godot_variant)key.NativeVar, out godot_variant value).ToBool())
|
||||
{
|
||||
return Variant.CreateTakingOwnershipOfDisposableValue(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_set_value(ref self,
|
||||
(godot_variant)key.NativeVar, (godot_variant)value.NativeVar);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an value <paramref name="value"/> at key <paramref name="key"/>
|
||||
/// to this <see cref="Dictionary"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// An entry for <paramref name="key"/> already exists in the dictionary.
|
||||
/// </exception>
|
||||
/// <param name="key">The key at which to add the value.</param>
|
||||
/// <param name="value">The value to add.</param>
|
||||
public void Add(Variant key, Variant value)
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
var variantKey = (godot_variant)key.NativeVar;
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
|
||||
if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
|
||||
throw new ArgumentException("An element with the same key already exists.", nameof(key));
|
||||
|
||||
godot_variant variantValue = (godot_variant)value.NativeVar;
|
||||
NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<Variant, Variant>>.Add(KeyValuePair<Variant, Variant> item)
|
||||
=> Add(item.Key, item.Value);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the dictionary, removing all entries from it.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
public void Clear()
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_clear(ref self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this <see cref="Dictionary"/> contains the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to look for.</param>
|
||||
/// <returns>Whether or not this dictionary contains the given key.</returns>
|
||||
public bool ContainsKey(Variant key)
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool();
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<Variant, Variant>>.Contains(KeyValuePair<Variant, Variant> item)
|
||||
{
|
||||
godot_variant variantKey = (godot_variant)item.Key.NativeVar;
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
||||
using (retValue)
|
||||
{
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
godot_variant variantValue = (godot_variant)item.Value.NativeVar;
|
||||
return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an element from this <see cref="Dictionary"/> by key.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <param name="key">The key of the element to remove.</param>
|
||||
public bool Remove(Variant key)
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool();
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<Variant, Variant>>.Remove(KeyValuePair<Variant, Variant> item)
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
godot_variant variantKey = (godot_variant)item.Key.NativeVar;
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
||||
using (retValue)
|
||||
{
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
godot_variant variantValue = (godot_variant)item.Value.NativeVar;
|
||||
if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
|
||||
{
|
||||
return NativeFuncs.godotsharp_dictionary_remove_key(
|
||||
ref self, variantKey).ToBool();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of elements in this <see cref="Dictionary"/>.
|
||||
/// This is also known as the size or length of the dictionary.
|
||||
/// </summary>
|
||||
/// <returns>The number of elements.</returns>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_count(ref self);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the dictionary is read-only.
|
||||
/// See <see cref="MakeReadOnly"/>.
|
||||
/// </summary>
|
||||
public bool IsReadOnly => NativeValue.DangerousSelfRef.IsReadOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the <see cref="Dictionary"/> read-only, i.e. disabled modying of the
|
||||
/// dictionary's elements. Does not apply to nested content, e.g. content of
|
||||
/// nested dictionaries.
|
||||
/// </summary>
|
||||
public void MakeReadOnly()
|
||||
{
|
||||
if (IsReadOnly)
|
||||
{
|
||||
// Avoid interop call when the dictionary is already read-only.
|
||||
return;
|
||||
}
|
||||
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_make_read_only(ref self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value for the given <paramref name="key"/> in the dictionary.
|
||||
/// Returns <see langword="true"/> if an entry for the given key exists in
|
||||
/// the dictionary; otherwise, returns <see langword="false"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the element to get.</param>
|
||||
/// <param name="value">The value at the given <paramref name="key"/>.</param>
|
||||
/// <returns>If an entry was found for the given <paramref name="key"/>.</returns>
|
||||
public bool TryGetValue(Variant key, out Variant value)
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
(godot_variant)key.NativeVar, out godot_variant retValue).ToBool();
|
||||
|
||||
value = found ? Variant.CreateTakingOwnershipOfDisposableValue(retValue) : default;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the elements of this <see cref="Dictionary"/> to the given untyped
|
||||
/// <see cref="KeyValuePair{TKey, TValue}"/> array, starting at the given index.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// The <paramref name="array"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="arrayIndex"/> is less than 0 or greater than the array's size.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// The destination array was not long enough.
|
||||
/// </exception>
|
||||
/// <param name="array">The array to copy to.</param>
|
||||
/// <param name="arrayIndex">The index to start at.</param>
|
||||
void ICollection<KeyValuePair<Variant, Variant>>.CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
|
||||
|
||||
if (arrayIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex),
|
||||
"Number was less than the array's lower bound in the first dimension.");
|
||||
|
||||
var (keys, values, count) = GetKeyValuePairs();
|
||||
|
||||
if (array.Length < (arrayIndex + count))
|
||||
throw new ArgumentException(
|
||||
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[arrayIndex] = new(keys[i], values[i]);
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// IEnumerable
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator for this <see cref="Dictionary"/>.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator.</returns>
|
||||
public IEnumerator<KeyValuePair<Variant, Variant>> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
yield return GetKeyValuePair(i);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
private KeyValuePair<Variant, Variant> GetKeyValuePair(int index)
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
|
||||
out godot_variant key,
|
||||
out godot_variant value);
|
||||
return new KeyValuePair<Variant, Variant>(Variant.CreateTakingOwnershipOfDisposableValue(key),
|
||||
Variant.CreateTakingOwnershipOfDisposableValue(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Dictionary"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this dictionary.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_to_string(ref self, out godot_string str);
|
||||
using (str)
|
||||
return Marshaling.ConvertStringToManaged(str);
|
||||
}
|
||||
|
||||
private void ThrowIfReadOnly()
|
||||
{
|
||||
if (IsReadOnly)
|
||||
{
|
||||
throw new InvalidOperationException("Dictionary instance is read-only.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IGenericGodotDictionary
|
||||
{
|
||||
public Dictionary UnderlyingDictionary { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Typed wrapper around Godot's Dictionary class, a dictionary of <typeparamref name="TKey"/>
|
||||
/// and <typeparamref name="TValue"/> annotated, Variant typed elements allocated in the engine in C++.
|
||||
/// Useful when interfacing with the engine. Otherwise prefer .NET collections
|
||||
/// such as <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
|
||||
/// <remarks>
|
||||
/// While the elements are statically annotated to <typeparamref name="TKey"/> and <typeparamref name="TValue"/>,
|
||||
/// the underlying dictionary still stores <see cref="Variant"/>, which has the same memory footprint per element
|
||||
/// as an untyped <see cref="Dictionary"/>.
|
||||
/// </remarks>
|
||||
[DebuggerTypeProxy(typeof(DictionaryDebugView<,>))]
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
[SuppressMessage("Design", "CA1001", MessageId = "Types that own disposable fields should be disposable",
|
||||
Justification = "Known issue. Requires explicit refcount management to not dispose untyped collections.")]
|
||||
public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> :
|
||||
IDictionary<TKey, TValue>,
|
||||
IReadOnlyDictionary<TKey, TValue>,
|
||||
IGenericGodotDictionary
|
||||
{
|
||||
private static godot_variant ToVariantFunc(scoped in Dictionary<TKey, TValue> godotDictionary) =>
|
||||
VariantUtils.CreateFromDictionary(godotDictionary);
|
||||
|
||||
private static Dictionary<TKey, TValue> FromVariantFunc(in godot_variant variant) =>
|
||||
VariantUtils.ConvertToDictionary<TKey, TValue>(variant);
|
||||
|
||||
private void SetTypedForUnderlyingDictionary()
|
||||
{
|
||||
Marshaling.GetTypedCollectionParameterInfo<TKey>(out var keyVariantType, out var keyClassName, out var keyScriptRef);
|
||||
Marshaling.GetTypedCollectionParameterInfo<TValue>(out var valueVariantType, out var valueClassName, out var valueScriptRef);
|
||||
|
||||
var self = (godot_dictionary)NativeValue;
|
||||
|
||||
using (keyScriptRef)
|
||||
using (valueScriptRef)
|
||||
{
|
||||
NativeFuncs.godotsharp_dictionary_set_typed(
|
||||
ref self,
|
||||
(uint)keyVariantType,
|
||||
keyClassName,
|
||||
keyScriptRef,
|
||||
(uint)valueVariantType,
|
||||
valueClassName,
|
||||
valueScriptRef);
|
||||
}
|
||||
}
|
||||
|
||||
static unsafe Dictionary()
|
||||
{
|
||||
VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.ToVariantCb = ToVariantFunc;
|
||||
VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.FromVariantCb = FromVariantFunc;
|
||||
}
|
||||
|
||||
private readonly Dictionary _underlyingDict;
|
||||
|
||||
Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict;
|
||||
|
||||
internal ref godot_dictionary.movable NativeValue
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref _underlyingDict.NativeValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new empty <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new Godot Dictionary.</returns>
|
||||
public Dictionary()
|
||||
{
|
||||
_underlyingDict = new Dictionary();
|
||||
SetTypedForUnderlyingDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// The <paramref name="dictionary"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <param name="dictionary">The dictionary to construct from.</param>
|
||||
/// <returns>A new Godot Dictionary.</returns>
|
||||
public Dictionary(IDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dictionary);
|
||||
|
||||
_underlyingDict = new Dictionary();
|
||||
SetTypedForUnderlyingDictionary();
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
|
||||
Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// The <paramref name="dictionary"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <param name="dictionary">The dictionary to construct from.</param>
|
||||
/// <returns>A new Godot Dictionary.</returns>
|
||||
public Dictionary(Dictionary dictionary)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dictionary);
|
||||
|
||||
_underlyingDict = dictionary;
|
||||
}
|
||||
|
||||
// Explicit name to make it very clear
|
||||
internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(
|
||||
godot_dictionary nativeValueToOwn)
|
||||
=> new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
|
||||
|
||||
/// <summary>
|
||||
/// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
|
||||
/// </summary>
|
||||
/// <param name="from">The typed dictionary to convert.</param>
|
||||
/// <returns>A new Godot Dictionary, or <see langword="null"/> if <see paramref="from"/> was null.</returns>
|
||||
[return: NotNullIfNotNull("from")]
|
||||
public static explicit operator Dictionary?(Dictionary<TKey, TValue>? from)
|
||||
{
|
||||
return from?._underlyingDict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed:
|
||||
/// all nested arrays and dictionaries are duplicated and will not be shared with
|
||||
/// the original dictionary. If <see langword="false"/>, a shallow copy is made and
|
||||
/// references to the original nested arrays and dictionaries are kept, so that
|
||||
/// modifying a sub-array or dictionary in the copy will also impact those
|
||||
/// referenced in the source dictionary. Note that any <see cref="GodotObject"/> derived
|
||||
/// elements will be shallow copied regardless of the <paramref name="deep"/>
|
||||
/// setting.
|
||||
/// </summary>
|
||||
/// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
|
||||
/// <returns>A new Godot Dictionary.</returns>
|
||||
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
|
||||
{
|
||||
return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds entries from <paramref name="dictionary"/> to this dictionary.
|
||||
/// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/>
|
||||
/// is <see langword="true"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <param name="dictionary">Dictionary to copy entries from.</param>
|
||||
/// <param name="overwrite">If duplicate keys should be copied over as well.</param>
|
||||
public void Merge(Dictionary<TKey, TValue> dictionary, bool overwrite = false)
|
||||
{
|
||||
_underlyingDict.Merge(dictionary._underlyingDict, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this <see cref="Dictionary{TKey, TValue}"/> against the <paramref name="other"/>
|
||||
/// <see cref="Dictionary{TKey, TValue}"/> recursively. Returns <see langword="true"/> if the
|
||||
/// two dictionaries contain the same keys and values. The order of the entries does not matter.
|
||||
/// otherwise.
|
||||
/// </summary>
|
||||
/// <param name="other">The other dictionary to compare against.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the dictionaries contain the same keys and values,
|
||||
/// <see langword="false"/> otherwise.
|
||||
/// </returns>
|
||||
public bool RecursiveEqual(Dictionary<TKey, TValue> other)
|
||||
{
|
||||
return _underlyingDict.RecursiveEqual(other._underlyingDict);
|
||||
}
|
||||
|
||||
// IDictionary<TKey, TValue>
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value at the given <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The property is assigned and the dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <exception cref="KeyNotFoundException">
|
||||
/// The property is retrieved and an entry for <paramref name="key"/>
|
||||
/// does not exist in the dictionary.
|
||||
/// </exception>
|
||||
/// <value>The value at the given <paramref name="key"/>.</value>
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
|
||||
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant value).ToBool())
|
||||
{
|
||||
using (value)
|
||||
return VariantUtils.ConvertTo<TValue>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
using var variantValue = VariantUtils.CreateFrom(value);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_set_value(ref self,
|
||||
variantKey, variantValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of keys in this <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
public ICollection<TKey> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
godot_array keyArray;
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray);
|
||||
return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of elements in this <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
godot_array valuesArray;
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
|
||||
return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
|
||||
|
||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
|
||||
|
||||
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
|
||||
{
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
|
||||
out godot_variant key,
|
||||
out godot_variant value);
|
||||
using (key)
|
||||
using (value)
|
||||
{
|
||||
return new KeyValuePair<TKey, TValue>(
|
||||
VariantUtils.ConvertTo<TKey>(key),
|
||||
VariantUtils.ConvertTo<TValue>(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an object <paramref name="value"/> at key <paramref name="key"/>
|
||||
/// to this <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// An element with the same <paramref name="key"/> already exists.
|
||||
/// </exception>
|
||||
/// <param name="key">The key at which to add the object.</param>
|
||||
/// <param name="value">The object to add.</param>
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
|
||||
if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
|
||||
throw new ArgumentException("An element with the same key already exists.", nameof(key));
|
||||
|
||||
using var variantValue = VariantUtils.CreateFrom(value);
|
||||
NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this <see cref="Dictionary{TKey, TValue}"/> contains the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to look for.</param>
|
||||
/// <returns>Whether or not this dictionary contains the given key.</returns>
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
/// <param name="key">The key of the element to remove.</param>
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value for the given <paramref name="key"/> in the dictionary.
|
||||
/// Returns <see langword="true"/> if an entry for the given key exists in
|
||||
/// the dictionary; otherwise, returns <see langword="false"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the element to get.</param>
|
||||
/// <param name="value">The value at the given <paramref name="key"/>.</param>
|
||||
/// <returns>If an entry was found for the given <paramref name="key"/>.</returns>
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
{
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
||||
using (retValue)
|
||||
value = found ? VariantUtils.ConvertTo<TValue>(retValue) : default;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// ICollection<KeyValuePair<TKey, TValue>>
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of elements in this <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// This is also known as the size or length of the dictionary.
|
||||
/// </summary>
|
||||
/// <returns>The number of elements.</returns>
|
||||
public int Count => _underlyingDict.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the dictionary is read-only.
|
||||
/// See <see cref="MakeReadOnly"/>.
|
||||
/// </summary>
|
||||
public bool IsReadOnly => _underlyingDict.IsReadOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Makes the <see cref="Dictionary{TKey, TValue}"/> read-only, i.e. disabled
|
||||
/// modying of the dictionary's elements. Does not apply to nested content,
|
||||
/// e.g. content of nested dictionaries.
|
||||
/// </summary>
|
||||
public void MakeReadOnly()
|
||||
{
|
||||
_underlyingDict.MakeReadOnly();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
=> Add(item.Key, item.Value);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the dictionary, removing all entries from it.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The dictionary is read-only.
|
||||
/// </exception>
|
||||
public void Clear() => _underlyingDict.Clear();
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
using var variantKey = VariantUtils.CreateFrom(item.Key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
||||
using (retValue)
|
||||
{
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
using var variantValue = VariantUtils.CreateFrom(item.Value);
|
||||
return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the elements of this <see cref="Dictionary{TKey, TValue}"/> to the given
|
||||
/// untyped C# array, starting at the given index.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// The <paramref name="array"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="arrayIndex"/> is less than 0 or greater than the array's size.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// The destination array was not long enough.
|
||||
/// </exception>
|
||||
/// <param name="array">The array to copy to.</param>
|
||||
/// <param name="arrayIndex">The index to start at.</param>
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
|
||||
|
||||
if (arrayIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex),
|
||||
"Number was less than the array's lower bound in the first dimension.");
|
||||
|
||||
int count = Count;
|
||||
|
||||
if (array.Length < (arrayIndex + count))
|
||||
throw new ArgumentException(
|
||||
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[arrayIndex] = GetKeyValuePair(i);
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
|
||||
using var variantKey = VariantUtils.CreateFrom(item.Key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
||||
using (retValue)
|
||||
{
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
using var variantValue = VariantUtils.CreateFrom(item.Value);
|
||||
if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
|
||||
{
|
||||
return NativeFuncs.godotsharp_dictionary_remove_key(
|
||||
ref self, variantKey).ToBool();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// IEnumerable<KeyValuePair<TKey, TValue>>
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator for this <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator.</returns>
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
yield return GetKeyValuePair(i);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this dictionary.</returns>
|
||||
public override string ToString() => _underlyingDict.ToString();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.CreateFrom(from);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Dictionary<TKey, TValue>(Variant from) =>
|
||||
from.AsGodotDictionary<TKey, TValue>();
|
||||
|
||||
private void ThrowIfReadOnly()
|
||||
{
|
||||
if (IsReadOnly)
|
||||
{
|
||||
throw new InvalidOperationException("Dictionary instance is read-only.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
Normal file
19
modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public static class Dispatcher
|
||||
{
|
||||
internal static GodotTaskScheduler DefaultGodotTaskScheduler;
|
||||
|
||||
internal static void InitializeDefaultGodotTaskScheduler()
|
||||
{
|
||||
DefaultGodotTaskScheduler?.Dispose();
|
||||
DefaultGodotTaskScheduler = new GodotTaskScheduler();
|
||||
}
|
||||
|
||||
public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
internal static class DisposablesTracker
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void OnGodotShuttingDown()
|
||||
{
|
||||
try
|
||||
{
|
||||
OnGodotShuttingDownImpl();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnGodotShuttingDownImpl()
|
||||
{
|
||||
bool isStdoutVerbose;
|
||||
|
||||
try
|
||||
{
|
||||
isStdoutVerbose = OS.IsStdOutVerbose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// OS singleton already disposed. Maybe OnUnloading was called twice.
|
||||
isStdoutVerbose = false;
|
||||
}
|
||||
|
||||
if (isStdoutVerbose)
|
||||
GD.Print("Unloading: Disposing tracked instances...");
|
||||
|
||||
// Dispose Godot Objects first, and only then dispose other disposables
|
||||
// like StringName, NodePath, Godot.Collections.Array/Dictionary, etc.
|
||||
// The Godot Object Dispose() method may need any of the later instances.
|
||||
|
||||
foreach (WeakReference<GodotObject> item in GodotObjectInstances.Keys)
|
||||
{
|
||||
if (item.TryGetTarget(out GodotObject? self))
|
||||
self.Dispose();
|
||||
}
|
||||
|
||||
foreach (WeakReference<IDisposable> item in OtherInstances.Keys)
|
||||
{
|
||||
if (item.TryGetTarget(out IDisposable? self))
|
||||
self.Dispose();
|
||||
}
|
||||
|
||||
if (isStdoutVerbose)
|
||||
GD.Print("Unloading: Finished disposing tracked instances.");
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<WeakReference<GodotObject>, byte> GodotObjectInstances { get; } =
|
||||
new();
|
||||
|
||||
private static ConcurrentDictionary<WeakReference<IDisposable>, byte> OtherInstances { get; } =
|
||||
new();
|
||||
|
||||
public static WeakReference<GodotObject> RegisterGodotObject(GodotObject godotObject)
|
||||
{
|
||||
var weakReferenceToSelf = new WeakReference<GodotObject>(godotObject);
|
||||
GodotObjectInstances.TryAdd(weakReferenceToSelf, 0);
|
||||
return weakReferenceToSelf;
|
||||
}
|
||||
|
||||
public static WeakReference<IDisposable> RegisterDisposable(IDisposable disposable)
|
||||
{
|
||||
var weakReferenceToSelf = new WeakReference<IDisposable>(disposable);
|
||||
OtherInstances.TryAdd(weakReferenceToSelf, 0);
|
||||
return weakReferenceToSelf;
|
||||
}
|
||||
|
||||
public static void UnregisterGodotObject(GodotObject godotObject, WeakReference<GodotObject> weakReferenceToSelf)
|
||||
{
|
||||
if (!GodotObjectInstances.TryRemove(weakReferenceToSelf, out _))
|
||||
throw new ArgumentException("Godot Object not registered.", nameof(weakReferenceToSelf));
|
||||
}
|
||||
|
||||
public static void UnregisterDisposable(WeakReference<IDisposable> weakReference)
|
||||
{
|
||||
if (!OtherInstances.TryRemove(weakReference, out _))
|
||||
throw new ArgumentException("Disposable not registered.", nameof(weakReference));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public partial class GodotObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the <see cref="GodotObject"/> that corresponds to <paramref name="instanceId"/>.
|
||||
/// All Objects have a unique instance ID. See also <see cref="GetInstanceId"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public partial class MyNode : Node
|
||||
/// {
|
||||
/// public string Foo { get; set; } = "bar";
|
||||
///
|
||||
/// public override void _Ready()
|
||||
/// {
|
||||
/// ulong id = GetInstanceId();
|
||||
/// var inst = (MyNode)InstanceFromId(Id);
|
||||
/// GD.Print(inst.Foo); // Prints bar
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="instanceId">Instance ID of the Object to retrieve.</param>
|
||||
/// <returns>The <see cref="GodotObject"/> instance.</returns>
|
||||
public static GodotObject? InstanceFromId(ulong instanceId)
|
||||
{
|
||||
return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="GodotObject"/> that corresponds
|
||||
/// to <paramref name="id"/> is a valid object (e.g. has not been deleted from
|
||||
/// memory). All Objects have a unique instance ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The Object ID to check.</param>
|
||||
/// <returns>If the instance with the given ID is a valid object.</returns>
|
||||
public static bool IsInstanceIdValid(ulong id)
|
||||
{
|
||||
return IsInstanceValid(InstanceFromId(id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if <paramref name="instance"/> is a
|
||||
/// valid <see cref="GodotObject"/> (e.g. has not been deleted from memory).
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance to check.</param>
|
||||
/// <returns>If the instance is a valid object.</returns>
|
||||
public static bool IsInstanceValid([NotNullWhen(true)] GodotObject? instance)
|
||||
{
|
||||
return instance != null && instance.NativeInstance != IntPtr.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a weak reference to an object, or <see langword="null"/>
|
||||
/// if the argument is invalid.
|
||||
/// A weak reference to an object is not enough to keep the object alive:
|
||||
/// when the only remaining references to a referent are weak references,
|
||||
/// garbage collection is free to destroy the referent and reuse its memory
|
||||
/// for something else. However, until the object is actually destroyed the
|
||||
/// weak reference may return the object even if there are no strong references
|
||||
/// to it.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Godot.WeakRef"/> reference to the object or <see langword="null"/>.
|
||||
/// </returns>
|
||||
public static WeakRef? WeakRef(GodotObject? obj)
|
||||
{
|
||||
if (!IsInstanceValid(obj))
|
||||
return null;
|
||||
|
||||
NativeFuncs.godotsharp_weakref(GetPtr(obj), out godot_ref weakRef);
|
||||
using (weakRef)
|
||||
{
|
||||
if (weakRef.IsNull)
|
||||
return null;
|
||||
|
||||
return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef.Reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public partial class Node
|
||||
{
|
||||
/// <summary>
|
||||
/// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from
|
||||
/// the current node) or an absolute path (in the scene tree) to a node. If the path
|
||||
/// does not exist, a <see langword="null"/> instance is returned and an error
|
||||
/// is logged. Attempts to access methods on the return value will result in an
|
||||
/// "Attempt to call <method> on a null instance." error.
|
||||
/// Note: Fetching absolute paths only works when the node is inside the scene tree
|
||||
/// (see <see cref="IsInsideTree"/>).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Example: Assume your current node is Character and the following tree:
|
||||
/// <code>
|
||||
/// /root
|
||||
/// /root/Character
|
||||
/// /root/Character/Sword
|
||||
/// /root/Character/Backpack/Dagger
|
||||
/// /root/MyGame
|
||||
/// /root/Swamp/Alligator
|
||||
/// /root/Swamp/Mosquito
|
||||
/// /root/Swamp/Goblin
|
||||
/// </code>
|
||||
/// Possible paths are:
|
||||
/// <code>
|
||||
/// GetNode("Sword");
|
||||
/// GetNode("Backpack/Dagger");
|
||||
/// GetNode("../Swamp/Alligator");
|
||||
/// GetNode("/root/MyGame");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="GetNodeOrNull{T}(NodePath)"/>
|
||||
/// <param name="path">The path to the node to fetch.</param>
|
||||
/// <exception cref="InvalidCastException">
|
||||
/// The fetched node can't be casted to the given type <typeparamref name="T"/>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The <see cref="Node"/> at the given <paramref name="path"/>.
|
||||
/// </returns>
|
||||
public T GetNode<T>(NodePath path) where T : class
|
||||
{
|
||||
return (T)(object)GetNode(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Similar to <see cref="GetNode"/>, but does not log an error if <paramref name="path"/>
|
||||
/// does not point to a valid <see cref="Node"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Example: Assume your current node is Character and the following tree:
|
||||
/// <code>
|
||||
/// /root
|
||||
/// /root/Character
|
||||
/// /root/Character/Sword
|
||||
/// /root/Character/Backpack/Dagger
|
||||
/// /root/MyGame
|
||||
/// /root/Swamp/Alligator
|
||||
/// /root/Swamp/Mosquito
|
||||
/// /root/Swamp/Goblin
|
||||
/// </code>
|
||||
/// Possible paths are:
|
||||
/// <code>
|
||||
/// GetNode("Sword");
|
||||
/// GetNode("Backpack/Dagger");
|
||||
/// GetNode("../Swamp/Alligator");
|
||||
/// GetNode("/root/MyGame");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="GetNode{T}(NodePath)"/>
|
||||
/// <param name="path">The path to the node to fetch.</param>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The <see cref="Node"/> at the given <paramref name="path"/>, or <see langword="null"/> if not found.
|
||||
/// </returns>
|
||||
public T GetNodeOrNull<T>(NodePath path) where T : class
|
||||
{
|
||||
return GetNodeOrNull(path) as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a child node by its index (see <see cref="GetChildCount"/>).
|
||||
/// This method is often used for iterating all children of a node.
|
||||
/// Negative indices access the children from the last one.
|
||||
/// To access a child node via its name, use <see cref="GetNode"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetChildOrNull{T}(int, bool)"/>
|
||||
/// <param name="idx">Child index.</param>
|
||||
/// <param name="includeInternal">
|
||||
/// If <see langword="false"/>, internal children are skipped (see <c>internal</c>
|
||||
/// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>).
|
||||
/// </param>
|
||||
/// <exception cref="InvalidCastException">
|
||||
/// The fetched node can't be casted to the given type <typeparamref name="T"/>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The child <see cref="Node"/> at the given index <paramref name="idx"/>.
|
||||
/// </returns>
|
||||
public T GetChild<T>(int idx, bool includeInternal = false) where T : class
|
||||
{
|
||||
return (T)(object)GetChild(idx, includeInternal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a child node by its index (see <see cref="GetChildCount"/>).
|
||||
/// This method is often used for iterating all children of a node.
|
||||
/// Negative indices access the children from the last one.
|
||||
/// To access a child node via its name, use <see cref="GetNode"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetChild{T}(int, bool)"/>
|
||||
/// <param name="idx">Child index.</param>
|
||||
/// <param name="includeInternal">
|
||||
/// If <see langword="false"/>, internal children are skipped (see <c>internal</c>
|
||||
/// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>).
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The child <see cref="Node"/> at the given index <paramref name="idx"/>, or <see langword="null"/> if not found.
|
||||
/// </returns>
|
||||
public T GetChildOrNull<T>(int idx, bool includeInternal = false) where T : class
|
||||
{
|
||||
int count = GetChildCount(includeInternal);
|
||||
return idx >= -count && idx < count ? GetChild(idx, includeInternal) as T : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The node owner. A node can have any other node as owner (as long as it is
|
||||
/// a valid parent, grandparent, etc. ascending in the tree). When saving a
|
||||
/// node (using <see cref="PackedScene"/>), all the nodes it owns will be saved
|
||||
/// with it. This allows for the creation of complex <see cref="SceneTree"/>s,
|
||||
/// with instancing and subinstancing.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetOwnerOrNull{T}"/>
|
||||
/// <exception cref="InvalidCastException">
|
||||
/// The fetched node can't be casted to the given type <typeparamref name="T"/>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The owner <see cref="Node"/>.
|
||||
/// </returns>
|
||||
public T GetOwner<T>() where T : class
|
||||
{
|
||||
return (T)(object)Owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The node owner. A node can have any other node as owner (as long as it is
|
||||
/// a valid parent, grandparent, etc. ascending in the tree). When saving a
|
||||
/// node (using <see cref="PackedScene"/>), all the nodes it owns will be saved
|
||||
/// with it. This allows for the creation of complex <see cref="SceneTree"/>s,
|
||||
/// with instancing and subinstancing.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetOwner{T}"/>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The owner <see cref="Node"/>, or <see langword="null"/> if there is no owner.
|
||||
/// </returns>
|
||||
public T GetOwnerOrNull<T>() where T : class
|
||||
{
|
||||
return Owner as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parent node of the current node, or a <see langword="null"/> instance
|
||||
/// if the node lacks a parent.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetParentOrNull{T}"/>
|
||||
/// <exception cref="InvalidCastException">
|
||||
/// The fetched node can't be casted to the given type <typeparamref name="T"/>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The parent <see cref="Node"/>.
|
||||
/// </returns>
|
||||
public T GetParent<T>() where T : class
|
||||
{
|
||||
return (T)(object)GetParent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parent node of the current node, or a <see langword="null"/> instance
|
||||
/// if the node lacks a parent.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetParent{T}"/>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>
|
||||
/// The parent <see cref="Node"/>, or <see langword="null"/> if the node has no parent.
|
||||
/// </returns>
|
||||
public T GetParentOrNull<T>() where T : class
|
||||
{
|
||||
return GetParent() as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public partial class PackedScene
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates the scene's node hierarchy, erroring on failure.
|
||||
/// Triggers child scene instantiation(s). Triggers a
|
||||
/// <see cref="Node.NotificationSceneInstantiated"/> notification on the root node.
|
||||
/// </summary>
|
||||
/// <seealso cref="InstantiateOrNull{T}(GenEditState)"/>
|
||||
/// <exception cref="InvalidCastException">
|
||||
/// The instantiated node can't be casted to the given type <typeparamref name="T"/>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>The instantiated scene.</returns>
|
||||
public T Instantiate<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class
|
||||
{
|
||||
return (T)(object)Instantiate(editState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates the scene's node hierarchy, returning <see langword="null"/> on failure.
|
||||
/// Triggers child scene instantiation(s). Triggers a
|
||||
/// <see cref="Node.NotificationSceneInstantiated"/> notification on the root node.
|
||||
/// </summary>
|
||||
/// <seealso cref="Instantiate{T}(GenEditState)"/>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
|
||||
/// <returns>The instantiated scene.</returns>
|
||||
public T InstantiateOrNull<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class
|
||||
{
|
||||
return Instantiate(editState) as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public static partial class ResourceLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads a resource at the given <paramref name="path"/>, caching the result
|
||||
/// for further access.
|
||||
/// The registered <see cref="ResourceFormatLoader"/> instances are queried sequentially
|
||||
/// to find the first one which can handle the file's extension, and then attempt
|
||||
/// loading. If loading fails, the remaining ResourceFormatLoaders are also attempted.
|
||||
/// An optional <paramref name="typeHint"/> can be used to further specify the
|
||||
/// <see cref="Resource"/> type that should be handled by the <see cref="ResourceFormatLoader"/>.
|
||||
/// Anything that inherits from <see cref="Resource"/> can be used as a type hint,
|
||||
/// for example <see cref="Image"/>.
|
||||
/// The <paramref name="cacheMode"/> property defines whether and how the cache should
|
||||
/// be used or updated when loading the resource. See <see cref="CacheMode"/> for details.
|
||||
/// Returns an empty resource if no <see cref="ResourceFormatLoader"/> could handle the file.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidCastException">
|
||||
/// The loaded resource can't be casted to the given type <typeparamref name="T"/>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Resource"/>.</typeparam>
|
||||
public static T Load<T>(string path, string typeHint = null, CacheMode cacheMode = CacheMode.Reuse) where T : class
|
||||
{
|
||||
return (T)(object)Load(path, typeHint, cacheMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
734
modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
Normal file
734
modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
Normal file
@@ -0,0 +1,734 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Godot's global functions.
|
||||
/// </summary>
|
||||
public static partial class GD
|
||||
{
|
||||
/// <summary>
|
||||
/// Decodes a byte array back to a <see cref="Variant"/> value, without decoding objects.
|
||||
/// Note: If you need object deserialization, see <see cref="BytesToVarWithObjects"/>.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param>
|
||||
/// <returns>The decoded <see cref="Variant"/>.</returns>
|
||||
public static Variant BytesToVar(Span<byte> bytes)
|
||||
{
|
||||
using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
|
||||
NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.False, out godot_variant ret);
|
||||
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a byte array back to a <see cref="Variant"/> value. Decoding objects is allowed.
|
||||
/// Warning: Deserialized object can contain code which gets executed. Do not use this
|
||||
/// option if the serialized object comes from untrusted sources to avoid potential security
|
||||
/// threats (remote code execution).
|
||||
/// </summary>
|
||||
/// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param>
|
||||
/// <returns>The decoded <see cref="Variant"/>.</returns>
|
||||
public static Variant BytesToVarWithObjects(Span<byte> bytes)
|
||||
{
|
||||
using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
|
||||
NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.True, out godot_variant ret);
|
||||
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts <paramref name="what"/> to <paramref name="type"/> in the best way possible.
|
||||
/// The <paramref name="type"/> parameter uses the <see cref="Variant.Type"/> values.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// Variant a = new Godot.Collections.Array { 4, 2.5, 1.2 };
|
||||
/// GD.Print(a.VariantType == Variant.Type.Array); // Prints true
|
||||
///
|
||||
/// var b = GD.Convert(a, Variant.Type.PackedByteArray);
|
||||
/// GD.Print(b); // Prints [4, 2, 1]
|
||||
/// GD.Print(b.VariantType == Variant.Type.Array); // Prints false
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns>
|
||||
public static Variant Convert(Variant what, Variant.Type type)
|
||||
{
|
||||
NativeFuncs.godotsharp_convert((godot_variant)what.NativeVar, (int)type, out godot_variant ret);
|
||||
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the integer hash of the passed <paramref name="var"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Print(GD.Hash("a")); // Prints 177670
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="var">Variable that will be hashed.</param>
|
||||
/// <returns>Hash of the variable passed.</returns>
|
||||
public static int Hash(Variant var)
|
||||
{
|
||||
return NativeFuncs.godotsharp_hash((godot_variant)var.NativeVar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a resource from the filesystem located at <paramref name="path"/>.
|
||||
/// The resource is loaded on the method call (unless it's referenced already
|
||||
/// elsewhere, e.g. in another script or in the scene), which might cause slight delay,
|
||||
/// especially when loading scenes. To avoid unnecessary delays when loading something
|
||||
/// multiple times, either store the resource in a variable.
|
||||
///
|
||||
/// Note: Resource paths can be obtained by right-clicking on a resource in the FileSystem
|
||||
/// dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
|
||||
///
|
||||
/// Important: The path must be absolute, a local path will just return <see langword="null"/>.
|
||||
/// This method is a simplified version of <see cref="ResourceLoader.Load"/>, which can be used
|
||||
/// for more advanced scenarios.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Load a scene called main located in the root of the project directory and cache it in a variable.
|
||||
/// var main = GD.Load("res://main.tscn"); // main will contain a PackedScene resource.
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="path">Path of the <see cref="Resource"/> to load.</param>
|
||||
/// <returns>The loaded <see cref="Resource"/>.</returns>
|
||||
public static Resource Load(string path)
|
||||
{
|
||||
return ResourceLoader.Load(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a resource from the filesystem located at <paramref name="path"/>.
|
||||
/// The resource is loaded on the method call (unless it's referenced already
|
||||
/// elsewhere, e.g. in another script or in the scene), which might cause slight delay,
|
||||
/// especially when loading scenes. To avoid unnecessary delays when loading something
|
||||
/// multiple times, either store the resource in a variable.
|
||||
///
|
||||
/// Note: Resource paths can be obtained by right-clicking on a resource in the FileSystem
|
||||
/// dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
|
||||
///
|
||||
/// Important: The path must be absolute, a local path will just return <see langword="null"/>.
|
||||
/// This method is a simplified version of <see cref="ResourceLoader.Load"/>, which can be used
|
||||
/// for more advanced scenarios.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Load a scene called main located in the root of the project directory and cache it in a variable.
|
||||
/// var main = GD.Load<PackedScene>("res://main.tscn"); // main will contain a PackedScene resource.
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="path">Path of the <see cref="Resource"/> to load.</param>
|
||||
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Resource"/>.</typeparam>
|
||||
public static T Load<T>(string path) where T : class
|
||||
{
|
||||
return ResourceLoader.Load<T>(path);
|
||||
}
|
||||
|
||||
private static string AppendPrintParams(object[] parameters)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
sb.Append(parameters[i]?.ToString() ?? "null");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string AppendPrintParams(char separator, object[] parameters)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
sb.Append(separator);
|
||||
sb.Append(parameters[i]?.ToString() ?? "null");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints a message to the console.
|
||||
///
|
||||
/// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
|
||||
/// to print error and warning messages instead of <see cref="Print(string)"/>.
|
||||
/// This distinguishes them from print messages used for debugging purposes,
|
||||
/// while also displaying a stack trace when an error or warning is printed.
|
||||
/// </summary>
|
||||
/// <param name="what">Message that will be printed.</param>
|
||||
public static void Print(string what)
|
||||
{
|
||||
using var godotStr = Marshaling.ConvertStringToNative(what);
|
||||
NativeFuncs.godotsharp_print(godotStr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts one or more arguments of any type to string in the best way possible
|
||||
/// and prints them to the console.
|
||||
///
|
||||
/// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/>
|
||||
/// to print error and warning messages instead of <see cref="Print(object[])"/>.
|
||||
/// This distinguishes them from print messages used for debugging purposes,
|
||||
/// while also displaying a stack trace when an error or warning is printed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var a = new Godot.Collections.Array { 1, 2, 3 };
|
||||
/// GD.Print("a", "b", a); // Prints ab[1, 2, 3]
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that will be printed.</param>
|
||||
public static void Print(params object[] what)
|
||||
{
|
||||
Print(AppendPrintParams(what));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints a message to the console.
|
||||
/// The following BBCode tags are supported: b, i, u, s, indent, code, url, center,
|
||||
/// right, color, bgcolor, fgcolor.
|
||||
/// Color tags only support named colors such as <c>red</c>, not hexadecimal color codes.
|
||||
/// Unsupported tags will be left as-is in standard output.
|
||||
/// When printing to standard output, the supported subset of BBCode is converted to
|
||||
/// ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes
|
||||
/// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary
|
||||
/// across terminal emulators, especially for italic and strikethrough.
|
||||
///
|
||||
/// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
|
||||
/// to print error and warning messages instead of <see cref="Print(string)"/> or
|
||||
/// <see cref="PrintRich(string)"/>.
|
||||
/// This distinguishes them from print messages used for debugging purposes,
|
||||
/// while also displaying a stack trace when an error or warning is printed.
|
||||
/// </summary>
|
||||
/// <param name="what">Message that will be printed.</param>
|
||||
public static void PrintRich(string what)
|
||||
{
|
||||
using var godotStr = Marshaling.ConvertStringToNative(what);
|
||||
NativeFuncs.godotsharp_print_rich(godotStr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts one or more arguments of any type to string in the best way possible
|
||||
/// and prints them to the console.
|
||||
/// The following BBCode tags are supported: b, i, u, s, indent, code, url, center,
|
||||
/// right, color, bgcolor, fgcolor.
|
||||
/// Color tags only support named colors such as <c>red</c>, not hexadecimal color codes.
|
||||
/// Unsupported tags will be left as-is in standard output.
|
||||
/// When printing to standard output, the supported subset of BBCode is converted to
|
||||
/// ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes
|
||||
/// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary
|
||||
/// across terminal emulators, especially for italic and strikethrough.
|
||||
///
|
||||
/// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/>
|
||||
/// to print error and warning messages instead of <see cref="Print(object[])"/> or
|
||||
/// <see cref="PrintRich(object[])"/>.
|
||||
/// This distinguishes them from print messages used for debugging purposes,
|
||||
/// while also displaying a stack trace when an error or warning is printed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PrintRich("[code][b]Hello world![/b][/code]"); // Prints out: [b]Hello world![/b]
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that will be printed.</param>
|
||||
public static void PrintRich(params object[] what)
|
||||
{
|
||||
PrintRich(AppendPrintParams(what));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints a message to standard error line.
|
||||
/// </summary>
|
||||
/// <param name="what">Message that will be printed.</param>
|
||||
public static void PrintErr(string what)
|
||||
{
|
||||
using var godotStr = Marshaling.ConvertStringToNative(what);
|
||||
NativeFuncs.godotsharp_printerr(godotStr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints one or more arguments to strings in the best way possible to standard error line.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PrintErr("prints to stderr");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that will be printed.</param>
|
||||
public static void PrintErr(params object[] what)
|
||||
{
|
||||
PrintErr(AppendPrintParams(what));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints a message to the OS terminal.
|
||||
/// Unlike <see cref="Print(string)"/>, no newline is added at the end.
|
||||
/// </summary>
|
||||
/// <param name="what">Message that will be printed.</param>
|
||||
public static void PrintRaw(string what)
|
||||
{
|
||||
using var godotStr = Marshaling.ConvertStringToNative(what);
|
||||
NativeFuncs.godotsharp_printraw(godotStr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints one or more arguments to strings in the best way possible to the OS terminal.
|
||||
/// Unlike <see cref="Print(object[])"/>, no newline is added at the end.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PrintRaw("A");
|
||||
/// GD.PrintRaw("B");
|
||||
/// GD.PrintRaw("C");
|
||||
/// // Prints ABC to terminal
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that will be printed.</param>
|
||||
public static void PrintRaw(params object[] what)
|
||||
{
|
||||
PrintRaw(AppendPrintParams(what));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints one or more arguments to the console with a space between each argument.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PrintS("A", "B", "C"); // Prints A B C
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that will be printed.</param>
|
||||
public static void PrintS(params object[] what)
|
||||
{
|
||||
string message = AppendPrintParams(' ', what);
|
||||
using var godotStr = Marshaling.ConvertStringToNative(message);
|
||||
NativeFuncs.godotsharp_prints(godotStr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints one or more arguments to the console with a tab between each argument.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PrintT("A", "B", "C"); // Prints A B C
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that will be printed.</param>
|
||||
public static void PrintT(params object[] what)
|
||||
{
|
||||
string message = AppendPrintParams('\t', what);
|
||||
using var godotStr = Marshaling.ConvertStringToNative(message);
|
||||
NativeFuncs.godotsharp_printt(godotStr);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
private static void ErrPrintError(string message, godot_error_handler_type type = godot_error_handler_type.ERR_HANDLER_ERROR)
|
||||
{
|
||||
// Skip 1 frame to avoid current method.
|
||||
var stackFrame = DebuggingUtils.GetCurrentStackFrame(skipFrames: 1);
|
||||
string callerFilePath = ProjectSettings.LocalizePath(stackFrame.GetFileName());
|
||||
DebuggingUtils.GetStackFrameMethodDecl(stackFrame, out string callerName);
|
||||
int callerLineNumber = stackFrame.GetFileLineNumber();
|
||||
|
||||
using godot_string messageStr = Marshaling.ConvertStringToNative(message);
|
||||
using godot_string callerNameStr = Marshaling.ConvertStringToNative(callerName);
|
||||
using godot_string callerFilePathStr = Marshaling.ConvertStringToNative(callerFilePath);
|
||||
NativeFuncs.godotsharp_err_print_error(callerNameStr, callerFilePathStr, callerLineNumber, messageStr, p_type: type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes an error message to Godot's built-in debugger and to the OS terminal.
|
||||
///
|
||||
/// Note: Errors printed this way will not pause project execution.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PushError("test error"); // Prints "test error" to debugger and terminal as error call
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="message">Error message.</param>
|
||||
public static void PushError(string message)
|
||||
{
|
||||
ErrPrintError(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes an error message to Godot's built-in debugger and to the OS terminal.
|
||||
///
|
||||
/// Note: Errors printed this way will not pause project execution.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that form the error message.</param>
|
||||
public static void PushError(params object[] what)
|
||||
{
|
||||
ErrPrintError(AppendPrintParams(what));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a warning message to Godot's built-in debugger and to the OS terminal.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="message">Warning message.</param>
|
||||
public static void PushWarning(string message)
|
||||
{
|
||||
ErrPrintError(message, type: godot_error_handler_type.ERR_HANDLER_WARNING);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a warning message to Godot's built-in debugger and to the OS terminal.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="what">Arguments that form the warning message.</param>
|
||||
public static void PushWarning(params object[] what)
|
||||
{
|
||||
ErrPrintError(AppendPrintParams(what), type: godot_error_handler_type.ERR_HANDLER_WARNING);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random floating point value between <c>0.0</c> and <c>1.0</c> (inclusive).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Randf(); // Returns e.g. 0.375671
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>A random <see langword="float"/> number.</returns>
|
||||
public static float Randf()
|
||||
{
|
||||
return NativeFuncs.godotsharp_randf();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a normally-distributed pseudo-random floating point value
|
||||
/// using Box-Muller transform with the specified <pararmref name="mean"/>
|
||||
/// and a standard <paramref name="deviation"/>.
|
||||
/// This is also called Gaussian distribution.
|
||||
/// </summary>
|
||||
/// <returns>A random normally-distributed <see langword="float"/> number.</returns>
|
||||
public static double Randfn(double mean, double deviation)
|
||||
{
|
||||
return NativeFuncs.godotsharp_randfn(mean, deviation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random unsigned 32-bit integer.
|
||||
/// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c>
|
||||
/// (where N is smaller than 2^32).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Randi(); // Returns random integer between 0 and 2^32 - 1
|
||||
/// GD.Randi() % 20; // Returns random integer between 0 and 19
|
||||
/// GD.Randi() % 100; // Returns random integer between 0 and 99
|
||||
/// GD.Randi() % 100 + 1; // Returns random integer between 1 and 100
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>A random <see langword="uint"/> number.</returns>
|
||||
public static uint Randi()
|
||||
{
|
||||
return NativeFuncs.godotsharp_randi();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Randomizes the seed (or the internal state) of the random number generator.
|
||||
/// The current implementation uses a number based on the device's time.
|
||||
///
|
||||
/// Note: This method is called automatically when the project is run.
|
||||
/// If you need to fix the seed to have consistent, reproducible results,
|
||||
/// use <see cref="Seed(ulong)"/> to initialize the random number generator.
|
||||
/// </summary>
|
||||
public static void Randomize()
|
||||
{
|
||||
NativeFuncs.godotsharp_randomize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random floating point value between <paramref name="from"/>
|
||||
/// and <paramref name="to"/> (inclusive).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.RandRange(0.0, 20.5); // Returns e.g. 7.45315
|
||||
/// GD.RandRange(-10.0, 10.0); // Returns e.g. -3.844535
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>A random <see langword="double"/> number inside the given range.</returns>
|
||||
public static double RandRange(double from, double to)
|
||||
{
|
||||
return NativeFuncs.godotsharp_randf_range(from, to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random signed 32-bit integer between <paramref name="from"/>
|
||||
/// and <paramref name="to"/> (inclusive). If <paramref name="to"/> is lesser than
|
||||
/// <paramref name="from"/>, they are swapped.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.RandRange(0, 1); // Returns either 0 or 1
|
||||
/// GD.RandRange(-10, 1000); // Returns random integer between -10 and 1000
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>A random <see langword="int"/> number inside the given range.</returns>
|
||||
public static int RandRange(int from, int to)
|
||||
{
|
||||
return NativeFuncs.godotsharp_randi_range(from, to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a <paramref name="seed"/>, returns a randomized <see langword="uint"/>
|
||||
/// value. The <paramref name="seed"/> may be modified.
|
||||
/// Passing the same <paramref name="seed"/> consistently returns the same value.
|
||||
///
|
||||
/// Note: "Seed" here refers to the internal state of the pseudo random number
|
||||
/// generator, currently implemented as a 64 bit integer.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var a = GD.RandFromSeed(4);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="seed">
|
||||
/// Seed to use to generate the random number.
|
||||
/// If a different seed is used, its value will be modified.
|
||||
/// </param>
|
||||
/// <returns>A random <see langword="uint"/> number.</returns>
|
||||
public static uint RandFromSeed(ref ulong seed)
|
||||
{
|
||||
return NativeFuncs.godotsharp_rand_from_seed(seed, out seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="IEnumerable{T}"/> that iterates from
|
||||
/// <c>0</c> (inclusive) to <paramref name="end"/> (exclusive)
|
||||
/// in steps of <c>1</c>.
|
||||
/// </summary>
|
||||
/// <param name="end">The last index.</param>
|
||||
public static IEnumerable<int> Range(int end)
|
||||
{
|
||||
return Range(0, end, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="IEnumerable{T}"/> that iterates from
|
||||
/// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive)
|
||||
/// in steps of <c>1</c>.
|
||||
/// </summary>
|
||||
/// <param name="start">The first index.</param>
|
||||
/// <param name="end">The last index.</param>
|
||||
public static IEnumerable<int> Range(int start, int end)
|
||||
{
|
||||
return Range(start, end, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="IEnumerable{T}"/> that iterates from
|
||||
/// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive)
|
||||
/// in steps of <paramref name="step"/>.
|
||||
/// The argument <paramref name="step"/> can be negative, but not <c>0</c>.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="step"/> is 0.
|
||||
/// </exception>
|
||||
/// <param name="start">The first index.</param>
|
||||
/// <param name="end">The last index.</param>
|
||||
/// <param name="step">The amount by which to increment the index on each iteration.</param>
|
||||
public static IEnumerable<int> Range(int start, int end, int step)
|
||||
{
|
||||
if (step == 0)
|
||||
throw new ArgumentException("step cannot be 0.", nameof(step));
|
||||
|
||||
if (end < start && step > 0)
|
||||
yield break;
|
||||
|
||||
if (end > start && step < 0)
|
||||
yield break;
|
||||
|
||||
if (step > 0)
|
||||
{
|
||||
for (int i = start; i < end; i += step)
|
||||
yield return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = start; i > end; i += step)
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets seed for the random number generator to <paramref name="seed"/>.
|
||||
/// Setting the seed manually can ensure consistent, repeatable results for
|
||||
/// most random functions.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// ulong mySeed = (ulong)GD.Hash("Godot Rocks");
|
||||
/// GD.Seed(mySeed);
|
||||
/// var a = GD.Randf() + GD.Randi();
|
||||
/// GD.Seed(mySeed);
|
||||
/// var b = GD.Randf() + GD.Randi();
|
||||
/// // a and b are now identical
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="seed">Seed that will be used.</param>
|
||||
public static void Seed(ulong seed)
|
||||
{
|
||||
NativeFuncs.godotsharp_seed(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/>
|
||||
/// to the original value.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string a = "{ \"a\": 1, \"b\": 2 }"; // a is a string
|
||||
/// var b = GD.StrToVar(a).AsGodotDictionary(); // b is a Dictionary
|
||||
/// GD.Print(b["a"]); // Prints 1
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="str">String that will be converted to Variant.</param>
|
||||
/// <returns>The decoded <c>Variant</c>.</returns>
|
||||
public static Variant StrToVar(string str)
|
||||
{
|
||||
using var godotStr = Marshaling.ConvertStringToNative(str);
|
||||
NativeFuncs.godotsharp_str_to_var(godotStr, out godot_variant ret);
|
||||
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a <see cref="Variant"/> value to a byte array, without encoding objects.
|
||||
/// Deserialization can be done with <see cref="BytesToVar"/>.
|
||||
/// Note: If you need object serialization, see <see cref="VarToBytesWithObjects"/>.
|
||||
/// </summary>
|
||||
/// <param name="var"><see cref="Variant"/> that will be encoded.</param>
|
||||
/// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns>
|
||||
public static byte[] VarToBytes(Variant var)
|
||||
{
|
||||
NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.False, out var varBytes);
|
||||
using (varBytes)
|
||||
return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a <see cref="Variant"/>. Encoding objects is allowed (and can potentially
|
||||
/// include executable code). Deserialization can be done with <see cref="BytesToVarWithObjects"/>.
|
||||
/// </summary>
|
||||
/// <param name="var"><see cref="Variant"/> that will be encoded.</param>
|
||||
/// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns>
|
||||
public static byte[] VarToBytesWithObjects(Variant var)
|
||||
{
|
||||
NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.True, out var varBytes);
|
||||
using (varBytes)
|
||||
return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Variant"/> <paramref name="var"/> to a formatted string that
|
||||
/// can later be parsed using <see cref="StrToVar(string)"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 };
|
||||
/// GD.Print(GD.VarToStr(a));
|
||||
/// // Prints:
|
||||
/// // {
|
||||
/// // "a": 1,
|
||||
/// // "b": 2
|
||||
/// // }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="var">Variant that will be converted to string.</param>
|
||||
/// <returns>The <see cref="Variant"/> encoded as a string.</returns>
|
||||
public static string VarToStr(Variant var)
|
||||
{
|
||||
NativeFuncs.godotsharp_var_to_str((godot_variant)var.NativeVar, out godot_string ret);
|
||||
using (ret)
|
||||
return Marshaling.ConvertStringToManaged(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="Variant.Type"/> that corresponds for the given <see cref="Type"/>.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="Variant.Type"/> for the given <paramref name="type"/>.</returns>
|
||||
public static Variant.Type TypeToVariantType(Type type)
|
||||
{
|
||||
return Marshaling.ConvertManagedTypeToVariantType(type, out bool _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new byte array with the data compressed.
|
||||
/// </summary>
|
||||
/// <param name="instance">The byte array to compress.</param>
|
||||
/// <param name="compressionMode">The compression mode, one of <see cref="FileAccess.CompressionMode"/></param>
|
||||
/// <returns>The compressed byte array.</returns>
|
||||
public static byte[] Compress(this byte[] instance, FileAccess.CompressionMode compressionMode = 0)
|
||||
{
|
||||
using godot_packed_byte_array src = Marshaling.ConvertSystemArrayToNativePackedByteArray(instance);
|
||||
NativeFuncs.godotsharp_packed_byte_array_compress(src, (int)compressionMode, out var ret);
|
||||
using (ret)
|
||||
return Marshaling.ConvertNativePackedByteArrayToSystemArray(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new byte array with the data decompressed.
|
||||
/// <para>Note: Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate compression mode lacks a checksum or header.</para>
|
||||
/// </summary>
|
||||
/// <param name="instance">The byte array to decompress.</param>
|
||||
/// <param name="bufferSize">The size of the uncompressed data.</param>
|
||||
/// <param name="compressionMode">The compression mode, one of <see cref="FileAccess.CompressionMode"/></param>
|
||||
/// <returns>The decompressed byte array.</returns>
|
||||
public static byte[] Decompress(this byte[] instance, long bufferSize, FileAccess.CompressionMode compressionMode = 0)
|
||||
{
|
||||
using godot_packed_byte_array src = Marshaling.ConvertSystemArrayToNativePackedByteArray(instance);
|
||||
NativeFuncs.godotsharp_packed_byte_array_decompress(src, bufferSize, (int)compressionMode, out var ret);
|
||||
using (ret)
|
||||
return Marshaling.ConvertNativePackedByteArrayToSystemArray(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new byte array with the data decompressed. <b>This method only accepts brotli, gzip, and deflate compression modes</b>.
|
||||
/// <para>This method is potentially slower than <see cref="Decompress"/>, as it may have to re-allocate its output buffer multiple times while decompressing, whereas <see cref="Decompress"/> knows it's output buffer size from the beginning.</para>
|
||||
/// <para>GZIP has a maximal compression ratio of 1032:1, meaning it's very possible for a small compressed payload to decompress to a potentially very large output. To guard against this, you may provide a maximum size this function is allowed to allocate in bytes via [param max_output_size]. Passing -1 will allow for unbounded output. If any positive value is passed, and the decompression exceeds that amount in bytes, then an error will be returned.</para>
|
||||
/// <para>Note: Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate compression mode lacks a checksum or header.</para>
|
||||
/// </summary>
|
||||
/// <param name="instance">The byte array to decompress.</param>
|
||||
/// <param name="maxOutputSize">The maximum size this function is allowed to allocate in bytes.</param>
|
||||
/// <param name="compressionMode">The compression mode, one of <see cref="FileAccess.CompressionMode"/></param>
|
||||
/// <returns>The decompressed byte array.</returns>
|
||||
public static byte[] DecompressDynamic(this byte[] instance, long maxOutputSize, FileAccess.CompressionMode compressionMode = 0)
|
||||
{
|
||||
using godot_packed_byte_array src = Marshaling.ConvertSystemArrayToNativePackedByteArray(instance);
|
||||
NativeFuncs.godotsharp_packed_byte_array_decompress_dynamic(src, maxOutputSize, (int)compressionMode, out var ret);
|
||||
using (ret)
|
||||
return Marshaling.ConvertNativePackedByteArrayToSystemArray(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
353
modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
Normal file
353
modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
Normal file
@@ -0,0 +1,353 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.Bridge;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public partial class GodotObject : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private static readonly Type _cachedType = typeof(GodotObject);
|
||||
|
||||
private static readonly Dictionary<Type, StringName?> _nativeNames = new Dictionary<Type, StringName?>();
|
||||
|
||||
internal IntPtr NativePtr;
|
||||
private bool _memoryOwn;
|
||||
|
||||
private WeakReference<GodotObject>? _weakReferenceToSelf;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="GodotObject"/>.
|
||||
/// </summary>
|
||||
public GodotObject() : this(false)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
ConstructAndInitialize(NativeCtor, NativeName, _cachedType, refCounted: false);
|
||||
}
|
||||
}
|
||||
|
||||
internal GodotObject(IntPtr nativePtr) : this(false)
|
||||
{
|
||||
// NativePtr must be non-zero before calling ConstructAndInitialize to avoid invoking the constructor NativeCtor.
|
||||
// We don't want to invoke the constructor, because we already have a constructed instance in nativePtr.
|
||||
NativePtr = nativePtr;
|
||||
unsafe
|
||||
{
|
||||
ConstructAndInitialize(NativeCtor, NativeName, _cachedType, refCounted: false);
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe void ConstructAndInitialize(
|
||||
delegate* unmanaged<godot_bool, IntPtr> nativeCtor,
|
||||
StringName nativeName,
|
||||
Type cachedType,
|
||||
bool refCounted
|
||||
)
|
||||
{
|
||||
if (NativePtr == IntPtr.Zero)
|
||||
{
|
||||
Debug.Assert(nativeCtor != null);
|
||||
|
||||
// Need postinitialization.
|
||||
NativePtr = nativeCtor(godot_bool.True);
|
||||
|
||||
InteropUtils.TieManagedToUnmanaged(this, NativePtr,
|
||||
nativeName, refCounted, GetType(), cachedType);
|
||||
}
|
||||
else
|
||||
{
|
||||
InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr,
|
||||
GetType(), cachedType);
|
||||
}
|
||||
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
|
||||
}
|
||||
|
||||
internal GodotObject(bool memoryOwn)
|
||||
{
|
||||
_memoryOwn = memoryOwn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The pointer to the native instance of this <see cref="GodotObject"/>.
|
||||
/// </summary>
|
||||
public IntPtr NativeInstance => NativePtr;
|
||||
|
||||
internal static IntPtr GetPtr(GodotObject? instance)
|
||||
{
|
||||
if (instance == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
// We check if NativePtr is null because this may be called by the debugger.
|
||||
// If the debugger puts a breakpoint in one of the base constructors, before
|
||||
// NativePtr is assigned, that would result in UB or crashes when calling
|
||||
// native functions that receive the pointer, which can happen because the
|
||||
// debugger calls ToString() and tries to get the value of properties.
|
||||
ObjectDisposedException.ThrowIf(instance._disposed || instance.NativePtr == IntPtr.Zero, instance);
|
||||
|
||||
return instance.NativePtr;
|
||||
}
|
||||
|
||||
~GodotObject()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this <see cref="GodotObject"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes implementation of this <see cref="GodotObject"/>.
|
||||
/// </summary>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
|
||||
if (NativePtr != IntPtr.Zero)
|
||||
{
|
||||
IntPtr gcHandleToFree = NativeFuncs.godotsharp_internal_object_get_associated_gchandle(NativePtr);
|
||||
|
||||
if (gcHandleToFree != IntPtr.Zero)
|
||||
{
|
||||
object? target = GCHandle.FromIntPtr(gcHandleToFree).Target;
|
||||
// The GC handle may have been replaced in another thread. Release it only if
|
||||
// it's associated to this managed instance, or if the target is no longer alive.
|
||||
if (target != this && target != null)
|
||||
gcHandleToFree = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (_memoryOwn)
|
||||
{
|
||||
NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, gcHandleToFree,
|
||||
(!disposing).ToGodotBool());
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeFuncs.godotsharp_internal_object_disposed(NativePtr, gcHandleToFree);
|
||||
}
|
||||
|
||||
NativePtr = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (_weakReferenceToSelf != null)
|
||||
{
|
||||
DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="GodotObject"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this object.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str);
|
||||
using (str)
|
||||
return Marshaling.ConvertStringToManaged(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="SignalAwaiter"/> awaiter configured to complete when the instance
|
||||
/// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter.
|
||||
/// </summary>
|
||||
/// <param name="source">
|
||||
/// The instance the awaiter will be listening to.
|
||||
/// </param>
|
||||
/// <param name="signal">
|
||||
/// The signal the awaiter will be waiting for.
|
||||
/// </param>
|
||||
/// <example>
|
||||
/// This sample prints a message once every frame up to 100 times.
|
||||
/// <code>
|
||||
/// public override void _Ready()
|
||||
/// {
|
||||
/// for (int i = 0; i < 100; i++)
|
||||
/// {
|
||||
/// await ToSignal(GetTree(), "process_frame");
|
||||
/// GD.Print($"Frame {i}");
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>
|
||||
/// A <see cref="SignalAwaiter"/> that completes when
|
||||
/// <paramref name="source"/> emits the <paramref name="signal"/>.
|
||||
/// </returns>
|
||||
public SignalAwaiter ToSignal(GodotObject source, StringName signal)
|
||||
{
|
||||
return new SignalAwaiter(source, signal, this);
|
||||
}
|
||||
|
||||
internal static bool IsNativeClass(Type t)
|
||||
{
|
||||
if (ReferenceEquals(t.Assembly, typeof(GodotObject).Assembly))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ReflectionUtils.IsEditorHintCached)
|
||||
{
|
||||
return t.Assembly.GetName().Name == "GodotSharpEditor";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static Type InternalGetClassNativeBase(Type t)
|
||||
{
|
||||
while (!IsNativeClass(t))
|
||||
{
|
||||
Debug.Assert(t.BaseType is not null, "Script types must derive from a native Godot type.");
|
||||
|
||||
t = t.BaseType;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
internal static StringName? InternalGetClassNativeBaseName(Type t)
|
||||
{
|
||||
if (_nativeNames.TryGetValue(t, out var name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
var baseType = InternalGetClassNativeBase(t);
|
||||
|
||||
if (_nativeNames.TryGetValue(baseType, out name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
var field = baseType.GetField("NativeName",
|
||||
BindingFlags.DeclaredOnly | BindingFlags.Static |
|
||||
BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
name = field?.GetValue(null) as StringName;
|
||||
|
||||
_nativeNames[baseType] = name;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
// ReSharper disable once VirtualMemberNeverOverridden.Global
|
||||
/// <summary>
|
||||
/// Set the value of a property contained in this class.
|
||||
/// This method is used by Godot to assign property values.
|
||||
/// Do not call or override this method.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the property to set.</param>
|
||||
/// <param name="value">Value to set the property to if it was found.</param>
|
||||
/// <returns><see langword="true"/> if a property with the given name was found.</returns>
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReSharper disable once VirtualMemberNeverOverridden.Global
|
||||
/// <summary>
|
||||
/// Get the value of a property contained in this class.
|
||||
/// This method is used by Godot to retrieve property values.
|
||||
/// Do not call or override this method.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the property to get.</param>
|
||||
/// <param name="value">Value of the property if it was found.</param>
|
||||
/// <returns><see langword="true"/> if a property with the given name was found.</returns>
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReSharper disable once VirtualMemberNeverOverridden.Global
|
||||
/// <summary>
|
||||
/// Raises the signal with the given name, using the given arguments.
|
||||
/// This method is used by Godot to raise signals from the engine side.\n"
|
||||
/// Do not call or override this method.
|
||||
/// </summary>
|
||||
/// <param name="signal">Name of the signal to raise.</param>
|
||||
/// <param name="args">Arguments to use with the raised signal.</param>
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
|
||||
NativeVariantPtrArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
internal static IntPtr ClassDB_get_method(StringName type, StringName method)
|
||||
{
|
||||
var typeSelf = (godot_string_name)type.NativeValue;
|
||||
var methodSelf = (godot_string_name)method.NativeValue;
|
||||
IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
|
||||
|
||||
if (methodBind == IntPtr.Zero)
|
||||
throw new NativeMethodBindNotFoundException(type + "." + method);
|
||||
|
||||
return methodBind;
|
||||
}
|
||||
|
||||
internal static IntPtr ClassDB_get_method_with_compatibility(StringName type, StringName method, ulong hash)
|
||||
{
|
||||
var typeSelf = (godot_string_name)type.NativeValue;
|
||||
var methodSelf = (godot_string_name)method.NativeValue;
|
||||
IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method_with_compatibility(typeSelf, methodSelf, hash);
|
||||
|
||||
if (methodBind == IntPtr.Zero)
|
||||
throw new NativeMethodBindNotFoundException(type + "." + method);
|
||||
|
||||
return methodBind;
|
||||
}
|
||||
|
||||
internal static unsafe delegate* unmanaged<godot_bool, IntPtr> ClassDB_get_constructor(StringName type)
|
||||
{
|
||||
// for some reason the '??' operator doesn't support 'delegate*'
|
||||
var typeSelf = (godot_string_name)type.NativeValue;
|
||||
var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf);
|
||||
|
||||
if (nativeConstructor == null)
|
||||
throw new NativeConstructorNotFoundException(type);
|
||||
|
||||
return nativeConstructor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves this instance's state to be restored when reloading assemblies.
|
||||
/// Do not call or override this method.
|
||||
/// To add data to be saved and restored, implement <see cref="ISerializationListener"/>.
|
||||
/// </summary>
|
||||
/// <param name="info">Object used to save the data.</param>
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: Should this be a constructor overload?
|
||||
/// <summary>
|
||||
/// Restores this instance's state after reloading assemblies.
|
||||
/// Do not call or override this method.
|
||||
/// To add data to be saved and restored, implement <see cref="ISerializationListener"/>.
|
||||
/// </summary>
|
||||
/// <param name="info">Object that contains the previously saved data.</param>
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public partial class GodotObject
|
||||
{
|
||||
public class NativeMemberNotFoundException : Exception
|
||||
{
|
||||
public NativeMemberNotFoundException()
|
||||
{
|
||||
}
|
||||
|
||||
public NativeMemberNotFoundException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeMemberNotFoundException(string? message, Exception? innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class NativeConstructorNotFoundException : NativeMemberNotFoundException
|
||||
{
|
||||
private readonly string? _nativeClassName;
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private const string Arg_NativeConstructorNotFoundException = "Unable to find the native constructor.";
|
||||
|
||||
public NativeConstructorNotFoundException()
|
||||
: base(Arg_NativeConstructorNotFoundException)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeConstructorNotFoundException(string? nativeClassName)
|
||||
: this(Arg_NativeConstructorNotFoundException, nativeClassName)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeConstructorNotFoundException(string? message, Exception? innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeConstructorNotFoundException(string? message, string? nativeClassName)
|
||||
: base(message)
|
||||
{
|
||||
_nativeClassName = nativeClassName;
|
||||
}
|
||||
|
||||
public NativeConstructorNotFoundException(string? message, string? nativeClassName, Exception? innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
_nativeClassName = nativeClassName;
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder sb;
|
||||
if (string.IsNullOrEmpty(base.Message))
|
||||
{
|
||||
sb = new(Arg_NativeConstructorNotFoundException);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb = new(base.Message);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_nativeClassName))
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $" (Method '{_nativeClassName}')");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NativeMethodBindNotFoundException : NativeMemberNotFoundException
|
||||
{
|
||||
private readonly string? _nativeMethodName;
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private const string Arg_NativeMethodBindNotFoundException = "Unable to find the native method bind.";
|
||||
|
||||
public NativeMethodBindNotFoundException()
|
||||
: base(Arg_NativeMethodBindNotFoundException)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeMethodBindNotFoundException(string? nativeMethodName)
|
||||
: this(Arg_NativeMethodBindNotFoundException, nativeMethodName)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeMethodBindNotFoundException(string? message, Exception? innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeMethodBindNotFoundException(string? message, string? nativeMethodName)
|
||||
: base(message)
|
||||
{
|
||||
_nativeMethodName = nativeMethodName;
|
||||
}
|
||||
|
||||
public NativeMethodBindNotFoundException(string? message, string? nativeMethodName, Exception? innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
_nativeMethodName = nativeMethodName;
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder sb;
|
||||
if (string.IsNullOrEmpty(base.Message))
|
||||
{
|
||||
sb = new(Arg_NativeMethodBindNotFoundException);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb = new(base.Message);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_nativeMethodName))
|
||||
{
|
||||
sb.Append(CultureInfo.InvariantCulture, $" (Method '{_nativeMethodName}')");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public sealed class GodotSynchronizationContext : SynchronizationContext, IDisposable
|
||||
{
|
||||
private readonly BlockingCollection<(SendOrPostCallback Callback, object State)> _queue = new();
|
||||
|
||||
public override void Send(SendOrPostCallback d, object state)
|
||||
{
|
||||
// Shortcut if we're already on this context
|
||||
// Also necessary to avoid a deadlock, since Send is blocking
|
||||
if (Current == this)
|
||||
{
|
||||
d(state);
|
||||
return;
|
||||
}
|
||||
|
||||
var source = new TaskCompletionSource();
|
||||
|
||||
_queue.Add((st =>
|
||||
{
|
||||
try
|
||||
{
|
||||
d(st);
|
||||
}
|
||||
finally
|
||||
{
|
||||
source.SetResult();
|
||||
}
|
||||
}, state));
|
||||
|
||||
source.Task.Wait();
|
||||
}
|
||||
|
||||
public override void Post(SendOrPostCallback d, object state)
|
||||
{
|
||||
_queue.Add((d, state));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the Key method on each workItem object in the _queue to activate their callbacks.
|
||||
/// </summary>
|
||||
public void ExecutePendingContinuations()
|
||||
{
|
||||
while (_queue.TryTake(out var workItem))
|
||||
{
|
||||
workItem.Callback(workItem.State);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_queue.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// GodotTaskScheduler contains a linked list of tasks to perform as a queue. Methods
|
||||
/// within the class are used to control the queue and perform the contained tasks.
|
||||
/// </summary>
|
||||
public sealed class GodotTaskScheduler : TaskScheduler, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The current synchronization context.
|
||||
/// </summary>
|
||||
internal GodotSynchronizationContext Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The queue of tasks for the task scheduler.
|
||||
/// </summary>
|
||||
private readonly LinkedList<Task> _tasks = new LinkedList<Task>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new GodotTaskScheduler instance.
|
||||
/// </summary>
|
||||
public GodotTaskScheduler()
|
||||
{
|
||||
Context = new GodotSynchronizationContext();
|
||||
SynchronizationContext.SetSynchronizationContext(Context);
|
||||
}
|
||||
|
||||
protected sealed override void QueueTask(Task task)
|
||||
{
|
||||
lock (_tasks)
|
||||
{
|
||||
_tasks.AddLast(task);
|
||||
}
|
||||
}
|
||||
|
||||
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
|
||||
{
|
||||
if (SynchronizationContext.Current != Context)
|
||||
return false;
|
||||
|
||||
if (taskWasPreviouslyQueued)
|
||||
TryDequeue(task);
|
||||
|
||||
return TryExecuteTask(task);
|
||||
}
|
||||
|
||||
protected sealed override bool TryDequeue(Task task)
|
||||
{
|
||||
lock (_tasks)
|
||||
{
|
||||
return _tasks.Remove(task);
|
||||
}
|
||||
}
|
||||
|
||||
protected sealed override IEnumerable<Task> GetScheduledTasks()
|
||||
{
|
||||
lock (_tasks)
|
||||
{
|
||||
foreach (Task task in _tasks)
|
||||
yield return task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all queued tasks and pending tasks from the current context.
|
||||
/// </summary>
|
||||
public void Activate()
|
||||
{
|
||||
ExecuteQueuedTasks();
|
||||
Context.ExecutePendingContinuations();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops through and attempts to execute each task in _tasks.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
private void ExecuteQueuedTasks()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Task task;
|
||||
|
||||
lock (_tasks)
|
||||
{
|
||||
if (_tasks.Count > 0)
|
||||
{
|
||||
task = _tasks.First.Value;
|
||||
_tasks.RemoveFirst();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (task != null)
|
||||
{
|
||||
if (!TryExecuteTask(task))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Context.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
internal class GodotTraceListener : TraceListener
|
||||
{
|
||||
public override void Write(string message)
|
||||
{
|
||||
GD.PrintRaw(message);
|
||||
}
|
||||
|
||||
public override void WriteLine(string message)
|
||||
{
|
||||
GD.Print(message);
|
||||
}
|
||||
|
||||
public override void Fail(string message, string detailMessage)
|
||||
{
|
||||
GD.PrintErr("Assertion failed: ", message);
|
||||
GD.PrintErr(" Details: ", detailMessage);
|
||||
|
||||
try
|
||||
{
|
||||
string stackTrace = new StackTrace(true).ToString();
|
||||
GD.PrintErr(stackTrace);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public static partial class GD
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void OnCoreApiAssemblyLoaded(godot_bool isDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dispatcher.InitializeDefaultGodotTaskScheduler();
|
||||
|
||||
if (isDebug.ToBool())
|
||||
{
|
||||
DebuggingUtils.InstallTraceListener();
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
|
||||
{
|
||||
// Exception.ToString() includes the inner exception
|
||||
ExceptionUtils.LogUnhandledException((Exception)e.ExceptionObject);
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that requires a GetAwaiter() method to get a reference to the Awaiter.
|
||||
/// </summary>
|
||||
public interface IAwaitable
|
||||
{
|
||||
IAwaiter GetAwaiter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A templated interface that requires a GetAwaiter() method to get a reference to the Awaiter.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
|
||||
public interface IAwaitable<out TResult>
|
||||
{
|
||||
IAwaiter<TResult> GetAwaiter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that requires a boolean for completion status and a method that gets the result of completion.
|
||||
/// </summary>
|
||||
public interface IAwaiter : INotifyCompletion
|
||||
{
|
||||
bool IsCompleted { get; }
|
||||
|
||||
void GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A templated interface that requires a boolean for completion status and a method that gets the result of completion and returns it.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
|
||||
public interface IAwaiter<out TResult> : INotifyCompletion
|
||||
{
|
||||
bool IsCompleted { get; }
|
||||
|
||||
TResult GetResult();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows a GodotObject to react to the serialization/deserialization
|
||||
/// that occurs when Godot reloads assemblies.
|
||||
/// </summary>
|
||||
public interface ISerializationListener
|
||||
{
|
||||
/// <summary>
|
||||
/// Executed before serializing this instance's state when reloading assemblies.
|
||||
/// Clear any data that should not be serialized.
|
||||
/// </summary>
|
||||
void OnBeforeSerialize();
|
||||
|
||||
/// <summary>
|
||||
/// Executed after deserializing this instance's state after reloading assemblies.
|
||||
/// Restore any state that has been lost.
|
||||
/// </summary>
|
||||
void OnAfterDeserialize();
|
||||
}
|
||||
}
|
||||
1867
modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
Normal file
1867
modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
Normal file
File diff suppressed because it is too large
Load Diff
199
modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
Normal file
199
modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// This file contains extra members for the Mathf class that aren't part of Godot's Core API.
|
||||
// Math API that is also part of Core should go into Mathf.cs.
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public static partial class Mathf
|
||||
{
|
||||
// Define constants with Decimal precision and cast down to double or float.
|
||||
|
||||
/// <summary>
|
||||
/// The natural number <c>e</c>.
|
||||
/// </summary>
|
||||
public const real_t E = (real_t)2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045
|
||||
|
||||
/// <summary>
|
||||
/// The square root of 2.
|
||||
/// </summary>
|
||||
public const real_t Sqrt2 = (real_t)1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
|
||||
|
||||
// Epsilon size should depend on the precision used.
|
||||
private const float EpsilonF = 1e-06f;
|
||||
private const double EpsilonD = 1e-14;
|
||||
|
||||
/// <summary>
|
||||
/// A very small number used for float comparison with error tolerance.
|
||||
/// 1e-06 with single-precision floats, but 1e-14 if <c>REAL_T_IS_DOUBLE</c>.
|
||||
/// </summary>
|
||||
#if REAL_T_IS_DOUBLE
|
||||
public const real_t Epsilon = EpsilonD;
|
||||
#else
|
||||
public const real_t Epsilon = EpsilonF;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of digits after the decimal place.
|
||||
/// </summary>
|
||||
/// <param name="s">The input value.</param>
|
||||
/// <returns>The amount of digits.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int DecimalCount(double s)
|
||||
{
|
||||
return DecimalCount((decimal)s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of digits after the decimal place.
|
||||
/// </summary>
|
||||
/// <param name="s">The input <see langword="decimal"/> value.</param>
|
||||
/// <returns>The amount of digits.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int DecimalCount(decimal s)
|
||||
{
|
||||
return BitConverter.GetBytes(decimal.GetBits(s)[3])[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds <paramref name="s"/> upward (towards positive infinity).
|
||||
///
|
||||
/// This is the same as <see cref="Ceil(float)"/>, but returns an <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="s">The number to ceil.</param>
|
||||
/// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CeilToInt(float s)
|
||||
{
|
||||
return (int)MathF.Ceiling(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds <paramref name="s"/> upward (towards positive infinity).
|
||||
///
|
||||
/// This is the same as <see cref="Ceil(double)"/>, but returns an <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="s">The number to ceil.</param>
|
||||
/// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CeilToInt(double s)
|
||||
{
|
||||
return (int)Math.Ceiling(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds <paramref name="s"/> downward (towards negative infinity).
|
||||
///
|
||||
/// This is the same as <see cref="Floor(float)"/>, but returns an <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="s">The number to floor.</param>
|
||||
/// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int FloorToInt(float s)
|
||||
{
|
||||
return (int)MathF.Floor(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds <paramref name="s"/> downward (towards negative infinity).
|
||||
///
|
||||
/// This is the same as <see cref="Floor(double)"/>, but returns an <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="s">The number to floor.</param>
|
||||
/// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int FloorToInt(double s)
|
||||
{
|
||||
return (int)Math.Floor(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds <paramref name="s"/> to the nearest whole number.
|
||||
///
|
||||
/// This is the same as <see cref="Round(float)"/>, but returns an <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="s">The number to round.</param>
|
||||
/// <returns>The rounded number.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RoundToInt(float s)
|
||||
{
|
||||
return (int)MathF.Round(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds <paramref name="s"/> to the nearest whole number.
|
||||
///
|
||||
/// This is the same as <see cref="Round(double)"/>, but returns an <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="s">The number to round.</param>
|
||||
/// <returns>The rounded number.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RoundToInt(double s)
|
||||
{
|
||||
return (int)Math.Round(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sine and cosine of angle <paramref name="s"/> in radians.
|
||||
/// </summary>
|
||||
/// <param name="s">The angle in radians.</param>
|
||||
/// <returns>The sine and cosine of that angle.</returns>
|
||||
public static (float Sin, float Cos) SinCos(float s)
|
||||
{
|
||||
return MathF.SinCos(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sine and cosine of angle <paramref name="s"/> in radians.
|
||||
/// </summary>
|
||||
/// <param name="s">The angle in radians.</param>
|
||||
/// <returns>The sine and cosine of that angle.</returns>
|
||||
public static (double Sin, double Cos) SinCos(double s)
|
||||
{
|
||||
return Math.SinCos(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately
|
||||
/// equal to each other.
|
||||
/// The comparison is done using the provided tolerance value.
|
||||
/// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(float, float)"/>.
|
||||
/// </summary>
|
||||
/// <param name="a">One of the values.</param>
|
||||
/// <param name="b">The other value.</param>
|
||||
/// <param name="tolerance">The pre-calculated tolerance value.</param>
|
||||
/// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns>
|
||||
public static bool IsEqualApprox(float a, float b, float tolerance)
|
||||
{
|
||||
// Check for exact equality first, required to handle "infinity" values.
|
||||
if (a == b)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Then check for approximate equality.
|
||||
return Math.Abs(a - b) < tolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately
|
||||
/// equal to each other.
|
||||
/// The comparison is done using the provided tolerance value.
|
||||
/// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(double, double)"/>.
|
||||
/// </summary>
|
||||
/// <param name="a">One of the values.</param>
|
||||
/// <param name="b">The other value.</param>
|
||||
/// <param name="tolerance">The pre-calculated tolerance value.</param>
|
||||
/// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns>
|
||||
public static bool IsEqualApprox(double a, double b, double tolerance)
|
||||
{
|
||||
// Check for exact equality first, required to handle "infinity" values.
|
||||
if (a == b)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Then check for approximate equality.
|
||||
return Math.Abs(a - b) < tolerance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Godot.NativeInterop;
|
||||
|
||||
// Ref structs are not allowed as generic type parameters, so we can't use Unsafe.AsPointer<T>/AsRef<T>.
|
||||
// As a workaround we create our own overloads for our structs with some tricks under the hood.
|
||||
|
||||
public static class CustomUnsafe
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_ref* AsPointer(ref godot_ref value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_ref* ReadOnlyRefAsPointer(in godot_ref value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_ref AsRef(godot_ref* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_ref AsRef(in godot_ref source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_variant_call_error* AsPointer(ref godot_variant_call_error value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_variant_call_error* ReadOnlyRefAsPointer(in godot_variant_call_error value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_variant_call_error AsRef(godot_variant_call_error* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_variant_call_error AsRef(in godot_variant_call_error source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_variant* AsPointer(ref godot_variant value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_variant* ReadOnlyRefAsPointer(in godot_variant value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_variant AsRef(godot_variant* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_variant AsRef(in godot_variant source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_string* AsPointer(ref godot_string value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_string* ReadOnlyRefAsPointer(in godot_string value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_string AsRef(godot_string* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_string AsRef(in godot_string source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_string_name* AsPointer(ref godot_string_name value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_string_name* ReadOnlyRefAsPointer(in godot_string_name value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_string_name AsRef(godot_string_name* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_string_name AsRef(in godot_string_name source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_node_path* AsPointer(ref godot_node_path value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_node_path* ReadOnlyRefAsPointer(in godot_node_path value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_node_path AsRef(godot_node_path* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_node_path AsRef(in godot_node_path source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_signal* AsPointer(ref godot_signal value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_signal* ReadOnlyRefAsPointer(in godot_signal value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_signal AsRef(godot_signal* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_signal AsRef(in godot_signal source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_callable* AsPointer(ref godot_callable value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_callable* ReadOnlyRefAsPointer(in godot_callable value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_callable AsRef(godot_callable* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_callable AsRef(in godot_callable source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_array* AsPointer(ref godot_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_array* ReadOnlyRefAsPointer(in godot_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_array AsRef(godot_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_array AsRef(in godot_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_dictionary* AsPointer(ref godot_dictionary value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_dictionary* ReadOnlyRefAsPointer(in godot_dictionary value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_dictionary AsRef(godot_dictionary* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_dictionary AsRef(in godot_dictionary source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_byte_array* AsPointer(ref godot_packed_byte_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_byte_array* ReadOnlyRefAsPointer(in godot_packed_byte_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_byte_array AsRef(godot_packed_byte_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_byte_array AsRef(in godot_packed_byte_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_int32_array* AsPointer(ref godot_packed_int32_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_int32_array* ReadOnlyRefAsPointer(in godot_packed_int32_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_int32_array AsRef(godot_packed_int32_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_int32_array AsRef(in godot_packed_int32_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_int64_array* AsPointer(ref godot_packed_int64_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_int64_array* ReadOnlyRefAsPointer(in godot_packed_int64_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_int64_array AsRef(godot_packed_int64_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_int64_array AsRef(in godot_packed_int64_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_float32_array* AsPointer(ref godot_packed_float32_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_float32_array* ReadOnlyRefAsPointer(in godot_packed_float32_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_float32_array AsRef(godot_packed_float32_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_float32_array AsRef(in godot_packed_float32_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_float64_array* AsPointer(ref godot_packed_float64_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_float64_array* ReadOnlyRefAsPointer(in godot_packed_float64_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_float64_array AsRef(godot_packed_float64_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_float64_array AsRef(in godot_packed_float64_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_string_array* AsPointer(ref godot_packed_string_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_string_array* ReadOnlyRefAsPointer(in godot_packed_string_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_string_array AsRef(godot_packed_string_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_string_array AsRef(in godot_packed_string_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_vector2_array* AsPointer(ref godot_packed_vector2_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_vector2_array* ReadOnlyRefAsPointer(in godot_packed_vector2_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_vector2_array AsRef(godot_packed_vector2_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_vector2_array AsRef(in godot_packed_vector2_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_vector3_array* AsPointer(ref godot_packed_vector3_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_vector3_array* ReadOnlyRefAsPointer(in godot_packed_vector3_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_vector3_array AsRef(godot_packed_vector3_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_vector3_array AsRef(in godot_packed_vector3_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_vector4_array* AsPointer(ref godot_packed_vector4_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_vector4_array* ReadOnlyRefAsPointer(in godot_packed_vector4_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_vector4_array AsRef(godot_packed_vector4_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_vector4_array AsRef(in godot_packed_vector4_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_color_array* AsPointer(ref godot_packed_color_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe godot_packed_color_array* ReadOnlyRefAsPointer(in godot_packed_color_array value)
|
||||
=> value.GetUnsafeAddress();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_color_array AsRef(godot_packed_color_array* source)
|
||||
=> ref *source;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref godot_packed_color_array AsRef(in godot_packed_color_array source)
|
||||
=> ref *ReadOnlyRefAsPointer(in source);
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
internal static class ExceptionUtils
|
||||
{
|
||||
public static void PushError(string message)
|
||||
{
|
||||
GD.PushError(message);
|
||||
}
|
||||
|
||||
private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
// This better not throw
|
||||
PushError(string.Concat("Exception thrown while trying to log another exception...",
|
||||
"\n### Exception ###\n", exceptionToLog.ToString(),
|
||||
"\n### Logger exception ###\n", loggerException.ToString()));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Well, too bad...
|
||||
}
|
||||
}
|
||||
|
||||
private record struct StackInfoTuple(string? File, string Func, int Line);
|
||||
|
||||
private static void CollectExceptionInfo(Exception exception, List<StackInfoTuple> globalFrames,
|
||||
StringBuilder excMsg)
|
||||
{
|
||||
if (excMsg.Length > 0)
|
||||
excMsg.Append(" ---> ");
|
||||
excMsg.Append(exception.GetType().FullName);
|
||||
excMsg.Append(": ");
|
||||
excMsg.Append(exception.Message);
|
||||
|
||||
var innerExc = exception.InnerException;
|
||||
|
||||
if (innerExc != null)
|
||||
{
|
||||
CollectExceptionInfo(innerExc, globalFrames, excMsg);
|
||||
globalFrames.Add(new("", "--- End of inner exception stack trace ---", 0));
|
||||
}
|
||||
|
||||
var stackTrace = new StackTrace(exception, fNeedFileInfo: true);
|
||||
|
||||
foreach (StackFrame frame in stackTrace.GetFrames())
|
||||
{
|
||||
DebuggingUtils.GetStackFrameMethodDecl(frame, out string methodDecl);
|
||||
globalFrames.Add(new(frame.GetFileName(), methodDecl, frame.GetFileLineNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendToScriptDebugger(Exception e)
|
||||
{
|
||||
var globalFrames = new List<StackInfoTuple>();
|
||||
|
||||
var excMsg = new StringBuilder();
|
||||
|
||||
CollectExceptionInfo(e, globalFrames, excMsg);
|
||||
|
||||
string file = globalFrames.Count > 0 ? globalFrames[0].File ?? "" : "";
|
||||
string func = globalFrames.Count > 0 ? globalFrames[0].Func : "";
|
||||
int line = globalFrames.Count > 0 ? globalFrames[0].Line : 0;
|
||||
string errorMsg = e.GetType().FullName ?? "";
|
||||
|
||||
using godot_string nFile = Marshaling.ConvertStringToNative(file);
|
||||
using godot_string nFunc = Marshaling.ConvertStringToNative(func);
|
||||
using godot_string nErrorMsg = Marshaling.ConvertStringToNative(errorMsg);
|
||||
using godot_string nExcMsg = Marshaling.ConvertStringToNative(excMsg.ToString());
|
||||
|
||||
using DebuggingUtils.godot_stack_info_vector stackInfoVector = default;
|
||||
|
||||
stackInfoVector.Resize(globalFrames.Count);
|
||||
|
||||
unsafe
|
||||
{
|
||||
for (int i = 0; i < globalFrames.Count; i++)
|
||||
{
|
||||
DebuggingUtils.godot_stack_info* stackInfo = &stackInfoVector.Elements[i];
|
||||
|
||||
var globalFrame = globalFrames[i];
|
||||
|
||||
// Assign directly to element in Vector. This way we don't need to worry
|
||||
// about disposal if an exception is thrown. The Vector takes care of it.
|
||||
stackInfo->File = Marshaling.ConvertStringToNative(globalFrame.File);
|
||||
stackInfo->Func = Marshaling.ConvertStringToNative(globalFrame.Func);
|
||||
stackInfo->Line = globalFrame.Line;
|
||||
}
|
||||
|
||||
NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line,
|
||||
nErrorMsg, nExcMsg, godot_error_handler_type.ERR_HANDLER_ERROR, stackInfoVector);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogException(Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (NativeFuncs.godotsharp_internal_script_debugger_is_active().ToBool())
|
||||
{
|
||||
SendToScriptDebugger(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.PushError(e.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception unexpected)
|
||||
{
|
||||
OnExceptionLoggerException(unexpected, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogUnhandledException(Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (NativeFuncs.godotsharp_internal_script_debugger_is_active().ToBool())
|
||||
{
|
||||
SendToScriptDebugger(e);
|
||||
}
|
||||
|
||||
// In this case, print it as well in addition to sending it to the script debugger
|
||||
GD.PushError("Unhandled exception\n" + e);
|
||||
}
|
||||
catch (Exception unexpected)
|
||||
{
|
||||
OnExceptionLoggerException(unexpected, e);
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public unsafe static void DebugCheckCallError(godot_string_name method, IntPtr instance, godot_variant** args, int argCount, godot_variant_call_error error)
|
||||
{
|
||||
if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK)
|
||||
{
|
||||
using godot_variant instanceVariant = VariantUtils.CreateFromGodotObjectPtr(instance);
|
||||
string where = GetCallErrorWhere(ref error, method, &instanceVariant, args, argCount);
|
||||
string errorText = GetCallErrorMessage(error, where, args);
|
||||
GD.PushError(errorText);
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public unsafe static void DebugCheckCallError(in godot_callable callable, godot_variant** args, int argCount, godot_variant_call_error error)
|
||||
{
|
||||
if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK)
|
||||
{
|
||||
using godot_variant callableVariant = VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(callable);
|
||||
string where = $"callable '{VariantUtils.ConvertToString(callableVariant)}'";
|
||||
string errorText = GetCallErrorMessage(error, where, args);
|
||||
GD.PushError(errorText);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static string GetCallErrorWhere(ref godot_variant_call_error error, godot_string_name method, godot_variant* instance, godot_variant** args, int argCount)
|
||||
{
|
||||
string? methodstr = null;
|
||||
string basestr = GetVariantTypeName(instance);
|
||||
|
||||
if (method == GodotObject.MethodName.Call || (basestr == "Godot.TreeItem" && method == TreeItem.MethodName.CallRecursive))
|
||||
{
|
||||
if (argCount >= 1)
|
||||
{
|
||||
methodstr = VariantUtils.ConvertToString(*args[0]);
|
||||
if (error.Error == godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT)
|
||||
{
|
||||
error.Argument += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(methodstr))
|
||||
{
|
||||
methodstr = StringName.CreateTakingOwnershipOfDisposableValue(method);
|
||||
}
|
||||
|
||||
return $"function '{methodstr}' in base '{basestr}'";
|
||||
}
|
||||
|
||||
private unsafe static string GetCallErrorMessage(godot_variant_call_error error, string where, godot_variant** args)
|
||||
{
|
||||
switch (error.Error)
|
||||
{
|
||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT:
|
||||
{
|
||||
int errorarg = error.Argument;
|
||||
// Handle the Object to Object case separately as we don't have further class details.
|
||||
#if DEBUG
|
||||
if (error.Expected == Variant.Type.Object && args[errorarg]->Type == error.Expected)
|
||||
{
|
||||
return $"Invalid type in {where}. The Object-derived class of argument {errorarg + 1} (" + GetVariantTypeName(args[errorarg]) + ") is not a subclass of the expected argument class.";
|
||||
}
|
||||
else if (error.Expected == Variant.Type.Array && args[errorarg]->Type == error.Expected)
|
||||
{
|
||||
return $"Invalid type in {where}. The array of argument {errorarg + 1} (" + GetVariantTypeName(args[errorarg]) + ") does not have the same element type as the expected typed array argument.";
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
return $"Invalid type in {where}. Cannot convert argument {errorarg + 1} from {args[errorarg]->Type} to {error.Expected}.";
|
||||
}
|
||||
}
|
||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
return $"Invalid call to {where}. Expected {error.Expected} argument(s).";
|
||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD:
|
||||
return $"Invalid call. Nonexistent {where}.";
|
||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL:
|
||||
return $"Attempt to call {where} on a null instance.";
|
||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_METHOD_NOT_CONST:
|
||||
return $"Attempt to call {where} on a const instance.";
|
||||
default:
|
||||
return $"Bug, call error: #{error.Error}";
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static string GetVariantTypeName(godot_variant* variant)
|
||||
{
|
||||
if (variant->Type == Variant.Type.Object)
|
||||
{
|
||||
GodotObject obj = VariantUtils.ConvertToGodotObject(*variant);
|
||||
if (obj == null)
|
||||
{
|
||||
return "null instance";
|
||||
}
|
||||
else if (!GodotObject.IsInstanceValid(obj))
|
||||
{
|
||||
return "previously freed";
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj.GetType().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return variant->Type.ToString();
|
||||
}
|
||||
|
||||
internal static void ThrowIfNullPtr(IntPtr ptr, [CallerArgumentExpression("ptr")] string? paramName = null)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentNullException(paramName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
public class GodotDllImportResolver
|
||||
{
|
||||
private IntPtr _internalHandle;
|
||||
|
||||
public GodotDllImportResolver(IntPtr internalHandle)
|
||||
{
|
||||
_internalHandle = internalHandle;
|
||||
}
|
||||
|
||||
public IntPtr OnResolveDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||||
{
|
||||
if (libraryName == "__Internal")
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return Win32.GetModuleHandle(IntPtr.Zero);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return _internalHandle;
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return MacOS.dlopen(IntPtr.Zero, MacOS.RTLD_LAZY);
|
||||
}
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private static class MacOS
|
||||
{
|
||||
private const string SystemLibrary = "/usr/lib/libSystem.dylib";
|
||||
|
||||
public const int RTLD_LAZY = 1;
|
||||
|
||||
[DllImport(SystemLibrary)]
|
||||
public static extern IntPtr dlopen(IntPtr path, int mode);
|
||||
}
|
||||
|
||||
private static class Win32
|
||||
{
|
||||
private const string SystemLibrary = "Kernel32.dll";
|
||||
|
||||
[DllImport(SystemLibrary)]
|
||||
public static extern IntPtr GetModuleHandle(IntPtr lpModuleName);
|
||||
}
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.Bridge;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
internal static class InteropUtils
|
||||
{
|
||||
public static GodotObject UnmanagedGetManaged(IntPtr unmanaged)
|
||||
{
|
||||
// The native pointer may be null
|
||||
if (unmanaged == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
IntPtr gcHandlePtr;
|
||||
godot_bool hasCsScriptInstance;
|
||||
|
||||
// First try to get the tied managed instance from a CSharpInstance script instance
|
||||
|
||||
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed(
|
||||
unmanaged, out hasCsScriptInstance);
|
||||
|
||||
if (gcHandlePtr != IntPtr.Zero)
|
||||
return (GodotObject)GCHandle.FromIntPtr(gcHandlePtr).Target;
|
||||
|
||||
// Otherwise, if the object has a CSharpInstance script instance, return null
|
||||
|
||||
if (hasCsScriptInstance.ToBool())
|
||||
return null;
|
||||
|
||||
// If it doesn't have a CSharpInstance script instance, try with native instance bindings
|
||||
|
||||
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged);
|
||||
|
||||
object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null;
|
||||
|
||||
if (target != null)
|
||||
return (GodotObject)target;
|
||||
|
||||
// If the native instance binding GC handle target was collected, create a new one
|
||||
|
||||
gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed(
|
||||
unmanaged, gcHandlePtr);
|
||||
|
||||
return gcHandlePtr != IntPtr.Zero ? (GodotObject)GCHandle.FromIntPtr(gcHandlePtr).Target : null;
|
||||
}
|
||||
|
||||
public static void TieManagedToUnmanaged(GodotObject managed, IntPtr unmanaged,
|
||||
StringName nativeName, bool refCounted, Type type, Type nativeType)
|
||||
{
|
||||
var gcHandle = refCounted ?
|
||||
CustomGCHandle.AllocWeak(managed) :
|
||||
CustomGCHandle.AllocStrong(managed, type);
|
||||
|
||||
if (type == nativeType)
|
||||
{
|
||||
var nativeNameSelf = (godot_string_name)nativeName.NativeValue;
|
||||
NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged(
|
||||
GCHandle.ToIntPtr(gcHandle), unmanaged, nativeNameSelf, refCounted.ToGodotBool());
|
||||
}
|
||||
else
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
// We don't dispose `script` ourselves here.
|
||||
// `tie_user_managed_to_unmanaged` does it for us to avoid another P/Invoke call.
|
||||
godot_ref script;
|
||||
ScriptManagerBridge.GetOrLoadOrCreateScriptForType(type, &script);
|
||||
|
||||
// IMPORTANT: This must be called after GetOrCreateScriptBridgeForType
|
||||
NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged(
|
||||
GCHandle.ToIntPtr(gcHandle), unmanaged, &script, refCounted.ToGodotBool());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void TieManagedToUnmanagedWithPreSetup(GodotObject managed, IntPtr unmanaged,
|
||||
Type type, Type nativeType)
|
||||
{
|
||||
if (type == nativeType)
|
||||
return;
|
||||
|
||||
var strongGCHandle = CustomGCHandle.AllocStrong(managed);
|
||||
NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
|
||||
GCHandle.ToIntPtr(strongGCHandle), unmanaged);
|
||||
}
|
||||
|
||||
public static GodotObject EngineGetSingleton(string name)
|
||||
{
|
||||
using godot_string src = Marshaling.ConvertStringToNative(name);
|
||||
return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(src));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,710 @@
|
||||
#pragma warning disable CA1707 // Identifiers should not contain underscores
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.Collections;
|
||||
using Array = System.Array;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
// We want to use full name qualifiers here even if redundant for clarity
|
||||
// ReSharper disable RedundantNameQualifier
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
public static class Marshaling
|
||||
{
|
||||
internal static Variant.Type ConvertManagedTypeToVariantType(Type type, out bool r_nil_is_variant)
|
||||
{
|
||||
r_nil_is_variant = false;
|
||||
|
||||
switch (Type.GetTypeCode(type))
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
return Variant.Type.Bool;
|
||||
case TypeCode.Char:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.SByte:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.Int16:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.Int32:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.Int64:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.Byte:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.UInt16:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.UInt32:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.UInt64:
|
||||
return Variant.Type.Int;
|
||||
case TypeCode.Single:
|
||||
return Variant.Type.Float;
|
||||
case TypeCode.Double:
|
||||
return Variant.Type.Float;
|
||||
case TypeCode.String:
|
||||
return Variant.Type.String;
|
||||
default:
|
||||
{
|
||||
if (type == typeof(Vector2))
|
||||
return Variant.Type.Vector2;
|
||||
|
||||
if (type == typeof(Vector2I))
|
||||
return Variant.Type.Vector2I;
|
||||
|
||||
if (type == typeof(Rect2))
|
||||
return Variant.Type.Rect2;
|
||||
|
||||
if (type == typeof(Rect2I))
|
||||
return Variant.Type.Rect2I;
|
||||
|
||||
if (type == typeof(Transform2D))
|
||||
return Variant.Type.Transform2D;
|
||||
|
||||
if (type == typeof(Vector3))
|
||||
return Variant.Type.Vector3;
|
||||
|
||||
if (type == typeof(Vector3I))
|
||||
return Variant.Type.Vector3I;
|
||||
|
||||
if (type == typeof(Vector4))
|
||||
return Variant.Type.Vector4;
|
||||
|
||||
if (type == typeof(Vector4I))
|
||||
return Variant.Type.Vector4I;
|
||||
|
||||
if (type == typeof(Basis))
|
||||
return Variant.Type.Basis;
|
||||
|
||||
if (type == typeof(Quaternion))
|
||||
return Variant.Type.Quaternion;
|
||||
|
||||
if (type == typeof(Transform3D))
|
||||
return Variant.Type.Transform3D;
|
||||
|
||||
if (type == typeof(Projection))
|
||||
return Variant.Type.Projection;
|
||||
|
||||
if (type == typeof(Aabb))
|
||||
return Variant.Type.Aabb;
|
||||
|
||||
if (type == typeof(Color))
|
||||
return Variant.Type.Color;
|
||||
|
||||
if (type == typeof(Plane))
|
||||
return Variant.Type.Plane;
|
||||
|
||||
if (type == typeof(Callable))
|
||||
return Variant.Type.Callable;
|
||||
|
||||
if (type == typeof(Signal))
|
||||
return Variant.Type.Signal;
|
||||
|
||||
if (type.IsEnum)
|
||||
return Variant.Type.Int;
|
||||
|
||||
if (type.IsArray || type.IsSZArray)
|
||||
{
|
||||
if (type == typeof(byte[]))
|
||||
return Variant.Type.PackedByteArray;
|
||||
|
||||
if (type == typeof(int[]))
|
||||
return Variant.Type.PackedInt32Array;
|
||||
|
||||
if (type == typeof(long[]))
|
||||
return Variant.Type.PackedInt64Array;
|
||||
|
||||
if (type == typeof(float[]))
|
||||
return Variant.Type.PackedFloat32Array;
|
||||
|
||||
if (type == typeof(double[]))
|
||||
return Variant.Type.PackedFloat64Array;
|
||||
|
||||
if (type == typeof(string[]))
|
||||
return Variant.Type.PackedStringArray;
|
||||
|
||||
if (type == typeof(Vector2[]))
|
||||
return Variant.Type.PackedVector2Array;
|
||||
|
||||
if (type == typeof(Vector3[]))
|
||||
return Variant.Type.PackedVector3Array;
|
||||
|
||||
if (type == typeof(Vector4[]))
|
||||
return Variant.Type.PackedVector4Array;
|
||||
|
||||
if (type == typeof(Color[]))
|
||||
return Variant.Type.PackedColorArray;
|
||||
|
||||
if (type == typeof(StringName[]))
|
||||
return Variant.Type.Array;
|
||||
|
||||
if (type == typeof(NodePath[]))
|
||||
return Variant.Type.Array;
|
||||
|
||||
if (type == typeof(Rid[]))
|
||||
return Variant.Type.Array;
|
||||
|
||||
if (typeof(GodotObject[]).IsAssignableFrom(type))
|
||||
return Variant.Type.Array;
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
if (typeof(GodotObject).IsAssignableFrom(type))
|
||||
return Variant.Type.Object;
|
||||
|
||||
// We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections
|
||||
// because `GetGenericTypeDefinition` is not supported in NativeAOT reflection-free mode.
|
||||
|
||||
if (typeof(IGenericGodotDictionary).IsAssignableFrom(type))
|
||||
return Variant.Type.Dictionary;
|
||||
|
||||
if (typeof(IGenericGodotArray).IsAssignableFrom(type))
|
||||
return Variant.Type.Array;
|
||||
}
|
||||
else if (type == typeof(Variant))
|
||||
{
|
||||
r_nil_is_variant = true;
|
||||
return Variant.Type.Nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typeof(GodotObject).IsAssignableFrom(type))
|
||||
return Variant.Type.Object;
|
||||
|
||||
if (typeof(StringName) == type)
|
||||
return Variant.Type.StringName;
|
||||
|
||||
if (typeof(NodePath) == type)
|
||||
return Variant.Type.NodePath;
|
||||
|
||||
if (typeof(Rid) == type)
|
||||
return Variant.Type.Rid;
|
||||
|
||||
if (typeof(Collections.Dictionary) == type)
|
||||
return Variant.Type.Dictionary;
|
||||
|
||||
if (typeof(Collections.Array) == type)
|
||||
return Variant.Type.Array;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown
|
||||
return Variant.Type.Nil;
|
||||
}
|
||||
|
||||
internal static void GetTypedCollectionParameterInfo<T>(
|
||||
out Variant.Type variantType,
|
||||
out godot_string_name className,
|
||||
out godot_ref script)
|
||||
{
|
||||
variantType = ConvertManagedTypeToVariantType(typeof(T), out _);
|
||||
|
||||
if (variantType != Variant.Type.Object)
|
||||
{
|
||||
className = default;
|
||||
script = default;
|
||||
return;
|
||||
}
|
||||
|
||||
godot_ref scriptRef = default;
|
||||
|
||||
if (!GodotObject.IsNativeClass(typeof(T)))
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Godot.Bridge.ScriptManagerBridge.GetOrLoadOrCreateScriptForType(typeof(T), &scriptRef);
|
||||
}
|
||||
|
||||
// Don't call GodotObject.InternalGetClassNativeBaseName here!
|
||||
// godot_dictionary_set_typed and godot_array_set_typed will call CSharpScript::get_instance_base_type
|
||||
// when a script is passed, because this is better for performance than using reflection to find the
|
||||
// native base type.
|
||||
className = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringName? nativeBaseName = GodotObject.InternalGetClassNativeBaseName(typeof(T));
|
||||
className = nativeBaseName != null ? (godot_string_name)nativeBaseName.NativeValue : default;
|
||||
}
|
||||
|
||||
script = scriptRef;
|
||||
}
|
||||
|
||||
// String
|
||||
|
||||
public static unsafe godot_string ConvertStringToNative(string? p_mono_string)
|
||||
{
|
||||
if (p_mono_string == null)
|
||||
return new godot_string();
|
||||
|
||||
fixed (char* methodChars = p_mono_string)
|
||||
{
|
||||
NativeFuncs.godotsharp_string_new_with_utf16_chars(out godot_string dest, methodChars);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe string ConvertStringToManaged(in godot_string p_string)
|
||||
{
|
||||
if (p_string.Buffer == IntPtr.Zero)
|
||||
return string.Empty;
|
||||
|
||||
const int SizeOfChar32 = 4;
|
||||
byte* bytes = (byte*)p_string.Buffer;
|
||||
int size = p_string.Size;
|
||||
if (size == 0)
|
||||
return string.Empty;
|
||||
size -= 1; // zero at the end
|
||||
int sizeInBytes = size * SizeOfChar32;
|
||||
return System.Text.Encoding.UTF32.GetString(bytes, sizeInBytes);
|
||||
}
|
||||
|
||||
// Callable
|
||||
|
||||
public static godot_callable ConvertCallableToNative(scoped in Callable p_managed_callable)
|
||||
{
|
||||
if (p_managed_callable.Delegate != null)
|
||||
{
|
||||
var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
|
||||
|
||||
IntPtr objectPtr = p_managed_callable.Target != null ?
|
||||
GodotObject.GetPtr(p_managed_callable.Target) :
|
||||
IntPtr.Zero;
|
||||
|
||||
unsafe
|
||||
{
|
||||
NativeFuncs.godotsharp_callable_new_with_delegate(
|
||||
GCHandle.ToIntPtr(gcHandle), (IntPtr)p_managed_callable.Trampoline,
|
||||
objectPtr, out godot_callable callable);
|
||||
|
||||
return callable;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
godot_string_name method;
|
||||
|
||||
if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty)
|
||||
{
|
||||
var src = (godot_string_name)p_managed_callable.Method.NativeValue;
|
||||
method = NativeFuncs.godotsharp_string_name_new_copy(src);
|
||||
}
|
||||
else
|
||||
{
|
||||
method = default;
|
||||
}
|
||||
|
||||
return new godot_callable(method /* Takes ownership of disposable */,
|
||||
p_managed_callable.Target?.GetInstanceId() ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static Callable ConvertCallableToManaged(in godot_callable p_callable)
|
||||
{
|
||||
if (NativeFuncs.godotsharp_callable_get_data_for_marshalling(p_callable,
|
||||
out IntPtr delegateGCHandle, out IntPtr trampoline,
|
||||
out IntPtr godotObject, out godot_string_name name).ToBool())
|
||||
{
|
||||
if (delegateGCHandle != IntPtr.Zero)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return Callable.CreateWithUnsafeTrampoline(
|
||||
(Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target,
|
||||
(delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void>)trampoline);
|
||||
}
|
||||
}
|
||||
|
||||
return new Callable(
|
||||
InteropUtils.UnmanagedGetManaged(godotObject),
|
||||
StringName.CreateTakingOwnershipOfDisposableValue(name));
|
||||
}
|
||||
|
||||
// Some other unsupported callable
|
||||
return new Callable();
|
||||
}
|
||||
|
||||
// Signal
|
||||
|
||||
public static godot_signal ConvertSignalToNative(scoped in Signal p_managed_signal)
|
||||
{
|
||||
ulong ownerId = p_managed_signal.Owner.GetInstanceId();
|
||||
godot_string_name name;
|
||||
|
||||
if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty)
|
||||
{
|
||||
var src = (godot_string_name)p_managed_signal.Name.NativeValue;
|
||||
name = NativeFuncs.godotsharp_string_name_new_copy(src);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = default;
|
||||
}
|
||||
|
||||
return new godot_signal(name, ownerId);
|
||||
}
|
||||
|
||||
public static Signal ConvertSignalToManaged(in godot_signal p_signal)
|
||||
{
|
||||
var owner = GodotObject.InstanceFromId(p_signal.ObjectId);
|
||||
var name = StringName.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_string_name_new_copy(p_signal.Name));
|
||||
return new Signal(owner, name);
|
||||
}
|
||||
|
||||
// Array
|
||||
|
||||
internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(in godot_array p_array)
|
||||
where T : GodotObject
|
||||
{
|
||||
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_array_new_copy(p_array));
|
||||
|
||||
int length = array.Count;
|
||||
var ret = new T[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
ret[i] = (T)array[i].AsGodotObject();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal static StringName[] ConvertNativeGodotArrayToSystemArrayOfStringName(in godot_array p_array)
|
||||
{
|
||||
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_array_new_copy(p_array));
|
||||
|
||||
int length = array.Count;
|
||||
var ret = new StringName[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
ret[i] = array[i].AsStringName();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal static NodePath[] ConvertNativeGodotArrayToSystemArrayOfNodePath(in godot_array p_array)
|
||||
{
|
||||
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_array_new_copy(p_array));
|
||||
|
||||
int length = array.Count;
|
||||
var ret = new NodePath[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
ret[i] = array[i].AsNodePath();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal static Rid[] ConvertNativeGodotArrayToSystemArrayOfRid(in godot_array p_array)
|
||||
{
|
||||
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
|
||||
NativeFuncs.godotsharp_array_new_copy(p_array));
|
||||
|
||||
int length = array.Count;
|
||||
var ret = new Rid[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
ret[i] = array[i].AsRid();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// PackedByteArray
|
||||
|
||||
public static unsafe byte[] ConvertNativePackedByteArrayToSystemArray(in godot_packed_byte_array p_array)
|
||||
{
|
||||
byte* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<byte>();
|
||||
var array = new byte[size];
|
||||
fixed (byte* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, size, size);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(scoped Span<byte> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedByteArray((ReadOnlySpan<byte>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(scoped ReadOnlySpan<byte> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_byte_array();
|
||||
fixed (byte* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_byte_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedInt32Array
|
||||
|
||||
public static unsafe int[] ConvertNativePackedInt32ArrayToSystemArray(godot_packed_int32_array p_array)
|
||||
{
|
||||
int* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<int>();
|
||||
int sizeInBytes = size * sizeof(int);
|
||||
var array = new int[size];
|
||||
fixed (int* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(scoped Span<int> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedInt32Array((ReadOnlySpan<int>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(scoped ReadOnlySpan<int> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_int32_array();
|
||||
fixed (int* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_int32_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedInt64Array
|
||||
|
||||
public static unsafe long[] ConvertNativePackedInt64ArrayToSystemArray(godot_packed_int64_array p_array)
|
||||
{
|
||||
long* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<long>();
|
||||
int sizeInBytes = size * sizeof(long);
|
||||
var array = new long[size];
|
||||
fixed (long* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(scoped Span<long> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedInt64Array((ReadOnlySpan<long>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(scoped ReadOnlySpan<long> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_int64_array();
|
||||
fixed (long* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_int64_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedFloat32Array
|
||||
|
||||
public static unsafe float[] ConvertNativePackedFloat32ArrayToSystemArray(godot_packed_float32_array p_array)
|
||||
{
|
||||
float* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<float>();
|
||||
int sizeInBytes = size * sizeof(float);
|
||||
var array = new float[size];
|
||||
fixed (float* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_float32_array ConvertSystemArrayToNativePackedFloat32Array(scoped Span<float> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedFloat32Array((ReadOnlySpan<float>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_float32_array ConvertSystemArrayToNativePackedFloat32Array(scoped ReadOnlySpan<float> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_float32_array();
|
||||
fixed (float* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_float32_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedFloat64Array
|
||||
|
||||
public static unsafe double[] ConvertNativePackedFloat64ArrayToSystemArray(godot_packed_float64_array p_array)
|
||||
{
|
||||
double* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<double>();
|
||||
int sizeInBytes = size * sizeof(double);
|
||||
var array = new double[size];
|
||||
fixed (double* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_float64_array ConvertSystemArrayToNativePackedFloat64Array(scoped Span<double> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedFloat64Array((ReadOnlySpan<double>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_float64_array ConvertSystemArrayToNativePackedFloat64Array(scoped ReadOnlySpan<double> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_float64_array();
|
||||
fixed (double* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_float64_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedStringArray
|
||||
|
||||
public static unsafe string[] ConvertNativePackedStringArrayToSystemArray(godot_packed_string_array p_array)
|
||||
{
|
||||
godot_string* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<string>();
|
||||
var array = new string[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
array[i] = ConvertStringToManaged(buffer[i]);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(scoped Span<string> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedStringArray((ReadOnlySpan<string>)p_array);
|
||||
}
|
||||
|
||||
public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(scoped ReadOnlySpan<string> p_array)
|
||||
{
|
||||
godot_packed_string_array dest = new godot_packed_string_array();
|
||||
|
||||
if (p_array.IsEmpty)
|
||||
return dest;
|
||||
|
||||
/* TODO: Replace godotsharp_packed_string_array_add with a single internal call to
|
||||
get the write address. We can't use `dest._ptr` directly for writing due to COW. */
|
||||
|
||||
for (int i = 0; i < p_array.Length; i++)
|
||||
{
|
||||
using godot_string godotStrElem = ConvertStringToNative(p_array[i]);
|
||||
NativeFuncs.godotsharp_packed_string_array_add(ref dest, godotStrElem);
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// PackedVector2Array
|
||||
|
||||
public static unsafe Vector2[] ConvertNativePackedVector2ArrayToSystemArray(godot_packed_vector2_array p_array)
|
||||
{
|
||||
Vector2* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<Vector2>();
|
||||
int sizeInBytes = size * sizeof(Vector2);
|
||||
var array = new Vector2[size];
|
||||
fixed (Vector2* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_vector2_array ConvertSystemArrayToNativePackedVector2Array(scoped Span<Vector2> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedVector2Array((ReadOnlySpan<Vector2>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_vector2_array ConvertSystemArrayToNativePackedVector2Array(scoped ReadOnlySpan<Vector2> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_vector2_array();
|
||||
fixed (Vector2* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_vector2_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedVector3Array
|
||||
|
||||
public static unsafe Vector3[] ConvertNativePackedVector3ArrayToSystemArray(godot_packed_vector3_array p_array)
|
||||
{
|
||||
Vector3* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<Vector3>();
|
||||
int sizeInBytes = size * sizeof(Vector3);
|
||||
var array = new Vector3[size];
|
||||
fixed (Vector3* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_vector3_array ConvertSystemArrayToNativePackedVector3Array(scoped Span<Vector3> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedVector3Array((ReadOnlySpan<Vector3>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_vector3_array ConvertSystemArrayToNativePackedVector3Array(scoped ReadOnlySpan<Vector3> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_vector3_array();
|
||||
fixed (Vector3* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedVector4Array
|
||||
|
||||
public static unsafe Vector4[] ConvertNativePackedVector4ArrayToSystemArray(godot_packed_vector4_array p_array)
|
||||
{
|
||||
Vector4* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<Vector4>();
|
||||
int sizeInBytes = size * sizeof(Vector4);
|
||||
var array = new Vector4[size];
|
||||
fixed (Vector4* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_vector4_array ConvertSystemArrayToNativePackedVector4Array(scoped Span<Vector4> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedVector4Array((ReadOnlySpan<Vector4>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_vector4_array ConvertSystemArrayToNativePackedVector4Array(scoped ReadOnlySpan<Vector4> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_vector4_array();
|
||||
fixed (Vector4* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_vector4_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
|
||||
// PackedColorArray
|
||||
|
||||
public static unsafe Color[] ConvertNativePackedColorArrayToSystemArray(godot_packed_color_array p_array)
|
||||
{
|
||||
Color* buffer = p_array.Buffer;
|
||||
int size = p_array.Size;
|
||||
if (size == 0)
|
||||
return Array.Empty<Color>();
|
||||
int sizeInBytes = size * sizeof(Color);
|
||||
var array = new Color[size];
|
||||
fixed (Color* dest = array)
|
||||
Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(scoped Span<Color> p_array)
|
||||
{
|
||||
return ConvertSystemArrayToNativePackedColorArray((ReadOnlySpan<Color>)p_array);
|
||||
}
|
||||
|
||||
public static unsafe godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(scoped ReadOnlySpan<Color> p_array)
|
||||
{
|
||||
if (p_array.IsEmpty)
|
||||
return new godot_packed_color_array();
|
||||
fixed (Color* src = p_array)
|
||||
return NativeFuncs.godotsharp_packed_color_array_new_mem_copy(src, p_array.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
#pragma warning disable CA1707 // Identifiers should not contain underscores
|
||||
#pragma warning disable IDE1006 // Naming rule violation
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot.SourceGenerators.Internal;
|
||||
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
/*
|
||||
* IMPORTANT:
|
||||
* The order of the methods defined in NativeFuncs must match the order
|
||||
* in the array defined at the bottom of 'glue/runtime_interop.cpp'.
|
||||
*/
|
||||
|
||||
[GenerateUnmanagedCallbacks(typeof(UnmanagedCallbacks))]
|
||||
public static unsafe partial class NativeFuncs
|
||||
{
|
||||
private static bool initialized;
|
||||
|
||||
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
|
||||
public static void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
|
||||
{
|
||||
if (initialized)
|
||||
throw new InvalidOperationException("Already initialized.");
|
||||
initialized = true;
|
||||
|
||||
if (unmanagedCallbacksSize != sizeof(UnmanagedCallbacks))
|
||||
throw new ArgumentException("Unmanaged callbacks size mismatch.", nameof(unmanagedCallbacksSize));
|
||||
|
||||
_unmanagedCallbacks = Unsafe.AsRef<UnmanagedCallbacks>((void*)unmanagedCallbacks);
|
||||
}
|
||||
|
||||
private partial struct UnmanagedCallbacks
|
||||
{
|
||||
}
|
||||
|
||||
// Custom functions
|
||||
|
||||
internal static partial godot_bool godotsharp_dotnet_module_is_initialized();
|
||||
|
||||
public static partial IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname,
|
||||
in godot_string_name p_methodname);
|
||||
|
||||
public static partial IntPtr godotsharp_method_bind_get_method_with_compatibility(
|
||||
in godot_string_name p_classname, in godot_string_name p_methodname, ulong p_hash);
|
||||
|
||||
public static partial delegate* unmanaged<godot_bool, IntPtr> godotsharp_get_class_constructor(
|
||||
in godot_string_name p_classname);
|
||||
|
||||
public static partial IntPtr godotsharp_engine_get_singleton(in godot_string p_name);
|
||||
|
||||
|
||||
internal static partial Error godotsharp_stack_info_vector_resize(
|
||||
ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector, int p_size);
|
||||
|
||||
internal static partial void godotsharp_stack_info_vector_destroy(
|
||||
ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
|
||||
|
||||
internal static partial void godotsharp_internal_editor_file_system_update_files(in godot_packed_string_array p_script_paths);
|
||||
|
||||
internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
|
||||
in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
|
||||
godot_error_handler_type p_type, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
|
||||
|
||||
internal static partial godot_bool godotsharp_internal_script_debugger_is_active();
|
||||
|
||||
internal static partial IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr);
|
||||
|
||||
internal static partial void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree);
|
||||
|
||||
internal static partial void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree,
|
||||
godot_bool isFinalizer);
|
||||
|
||||
internal static partial Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
|
||||
in godot_string_name signal,
|
||||
IntPtr target, IntPtr awaiterHandlePtr);
|
||||
|
||||
internal static partial void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
|
||||
IntPtr unmanaged, in godot_string_name nativeName, godot_bool refCounted);
|
||||
|
||||
internal static partial void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
|
||||
IntPtr unmanaged, godot_ref* scriptPtr, godot_bool refCounted);
|
||||
|
||||
internal static partial void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
|
||||
IntPtr gcHandleIntPtr, IntPtr unmanaged);
|
||||
|
||||
internal static partial IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
|
||||
out godot_bool r_has_cs_script_instance);
|
||||
|
||||
internal static partial IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
|
||||
|
||||
internal static partial IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
|
||||
IntPtr oldGCHandlePtr);
|
||||
|
||||
internal static partial void godotsharp_internal_new_csharp_script(godot_ref* r_dest);
|
||||
|
||||
internal static partial godot_bool godotsharp_internal_script_load(in godot_string p_path, godot_ref* r_dest);
|
||||
|
||||
internal static partial void godotsharp_internal_reload_registered_script(IntPtr scriptPtr);
|
||||
|
||||
internal static partial void godotsharp_array_filter_godot_objects_by_native(scoped in godot_string_name p_native_name,
|
||||
scoped in godot_array p_input, out godot_array r_output);
|
||||
|
||||
internal static partial void godotsharp_array_filter_godot_objects_by_non_native(scoped in godot_array p_input,
|
||||
out godot_array r_output);
|
||||
|
||||
public static partial void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest,
|
||||
IntPtr p_ref_counted_ptr);
|
||||
|
||||
public static partial void godotsharp_ref_destroy(ref godot_ref p_instance);
|
||||
|
||||
public static partial void godotsharp_string_name_new_from_string(out godot_string_name r_dest,
|
||||
scoped in godot_string p_name);
|
||||
|
||||
public static partial void godotsharp_node_path_new_from_string(out godot_node_path r_dest,
|
||||
scoped in godot_string p_name);
|
||||
|
||||
public static partial void
|
||||
godotsharp_string_name_as_string(out godot_string r_dest, scoped in godot_string_name p_name);
|
||||
|
||||
public static partial void godotsharp_node_path_as_string(out godot_string r_dest, scoped in godot_node_path p_np);
|
||||
|
||||
public static partial godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_vector4_array godotsharp_packed_vector4_array_new_mem_copy(Vector4* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src,
|
||||
int p_length);
|
||||
|
||||
public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
|
||||
in godot_string p_element);
|
||||
|
||||
public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_trampoline,
|
||||
IntPtr p_object, out godot_callable r_callable);
|
||||
|
||||
internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(scoped in godot_callable p_callable,
|
||||
out IntPtr r_delegate_handle, out IntPtr r_trampoline, out IntPtr r_object, out godot_string_name r_name);
|
||||
|
||||
internal static partial godot_variant godotsharp_callable_call(scoped in godot_callable p_callable,
|
||||
godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
|
||||
|
||||
internal static partial void godotsharp_callable_call_deferred(in godot_callable p_callable,
|
||||
godot_variant** p_args, int p_arg_count);
|
||||
|
||||
internal static partial Color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha);
|
||||
|
||||
internal static partial float godotsharp_color_get_ok_hsl_h(in Color p_self);
|
||||
|
||||
internal static partial float godotsharp_color_get_ok_hsl_s(in Color p_self);
|
||||
|
||||
internal static partial float godotsharp_color_get_ok_hsl_l(in Color p_self);
|
||||
|
||||
// GDNative functions
|
||||
|
||||
// gdnative.h
|
||||
|
||||
public static partial void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args,
|
||||
void* p_ret);
|
||||
|
||||
public static partial godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance,
|
||||
godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
|
||||
|
||||
// variant.h
|
||||
|
||||
public static partial void
|
||||
godotsharp_variant_new_string_name(out godot_variant r_dest, scoped in godot_string_name p_s);
|
||||
|
||||
public static partial void godotsharp_variant_new_copy(out godot_variant r_dest, scoped in godot_variant p_src);
|
||||
|
||||
public static partial void godotsharp_variant_new_node_path(out godot_variant r_dest, scoped in godot_node_path p_np);
|
||||
|
||||
public static partial void godotsharp_variant_new_object(out godot_variant r_dest, IntPtr p_obj);
|
||||
|
||||
public static partial void godotsharp_variant_new_transform2d(out godot_variant r_dest, scoped in Transform2D p_t2d);
|
||||
|
||||
public static partial void godotsharp_variant_new_basis(out godot_variant r_dest, scoped in Basis p_basis);
|
||||
|
||||
public static partial void godotsharp_variant_new_transform3d(out godot_variant r_dest, scoped in Transform3D p_trans);
|
||||
|
||||
public static partial void godotsharp_variant_new_projection(out godot_variant r_dest, scoped in Projection p_proj);
|
||||
|
||||
public static partial void godotsharp_variant_new_aabb(out godot_variant r_dest, scoped in Aabb p_aabb);
|
||||
|
||||
public static partial void godotsharp_variant_new_dictionary(out godot_variant r_dest,
|
||||
scoped in godot_dictionary p_dict);
|
||||
|
||||
public static partial void godotsharp_variant_new_array(out godot_variant r_dest, scoped in godot_array p_arr);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_byte_array p_pba);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_int32_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_int32_array p_pia);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_int64_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_int64_array p_pia);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_float32_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_float32_array p_pra);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_float64_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_float64_array p_pra);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_string_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_string_array p_psa);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_vector2_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_vector2_array p_pv2a);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_vector3_array p_pv3a);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_vector4_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_vector4_array p_pv4a);
|
||||
|
||||
public static partial void godotsharp_variant_new_packed_color_array(out godot_variant r_dest,
|
||||
scoped in godot_packed_color_array p_pca);
|
||||
|
||||
public static partial godot_bool godotsharp_variant_as_bool(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Int64 godotsharp_variant_as_int(scoped in godot_variant p_self);
|
||||
|
||||
public static partial double godotsharp_variant_as_float(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_string godotsharp_variant_as_string(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Vector2 godotsharp_variant_as_vector2(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Vector2I godotsharp_variant_as_vector2i(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Rect2 godotsharp_variant_as_rect2(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Rect2I godotsharp_variant_as_rect2i(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Vector3 godotsharp_variant_as_vector3(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Vector3I godotsharp_variant_as_vector3i(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Transform2D godotsharp_variant_as_transform2d(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Vector4 godotsharp_variant_as_vector4(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Vector4I godotsharp_variant_as_vector4i(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Plane godotsharp_variant_as_plane(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Quaternion godotsharp_variant_as_quaternion(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Aabb godotsharp_variant_as_aabb(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Basis godotsharp_variant_as_basis(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Transform3D godotsharp_variant_as_transform3d(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Projection godotsharp_variant_as_projection(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Color godotsharp_variant_as_color(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_string_name godotsharp_variant_as_string_name(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_node_path godotsharp_variant_as_node_path(scoped in godot_variant p_self);
|
||||
|
||||
public static partial Rid godotsharp_variant_as_rid(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_callable godotsharp_variant_as_callable(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_signal godotsharp_variant_as_signal(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_dictionary godotsharp_variant_as_dictionary(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_array godotsharp_variant_as_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_byte_array godotsharp_variant_as_packed_byte_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_int32_array godotsharp_variant_as_packed_int32_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_int64_array godotsharp_variant_as_packed_int64_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_float32_array godotsharp_variant_as_packed_float32_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_float64_array godotsharp_variant_as_packed_float64_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_string_array godotsharp_variant_as_packed_string_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_vector4_array godotsharp_variant_as_packed_vector4_array(
|
||||
in godot_variant p_self);
|
||||
|
||||
public static partial godot_packed_color_array godotsharp_variant_as_packed_color_array(scoped in godot_variant p_self);
|
||||
|
||||
public static partial godot_bool godotsharp_variant_equals(scoped in godot_variant p_a, scoped in godot_variant p_b);
|
||||
|
||||
// string.h
|
||||
|
||||
public static partial void godotsharp_string_new_with_utf16_chars(out godot_string r_dest, char* p_contents);
|
||||
|
||||
// string_name.h
|
||||
|
||||
public static partial void godotsharp_string_name_new_copy(out godot_string_name r_dest,
|
||||
scoped in godot_string_name p_src);
|
||||
|
||||
// node_path.h
|
||||
|
||||
public static partial void godotsharp_node_path_new_copy(out godot_node_path r_dest, scoped in godot_node_path p_src);
|
||||
|
||||
// array.h
|
||||
|
||||
public static partial void godotsharp_array_new(out godot_array r_dest);
|
||||
|
||||
public static partial void godotsharp_array_new_copy(out godot_array r_dest, scoped in godot_array p_src);
|
||||
|
||||
public static partial godot_variant* godotsharp_array_ptrw(ref godot_array p_self);
|
||||
|
||||
// dictionary.h
|
||||
|
||||
public static partial void godotsharp_dictionary_new(out godot_dictionary r_dest);
|
||||
|
||||
public static partial void godotsharp_dictionary_new_copy(out godot_dictionary r_dest,
|
||||
scoped in godot_dictionary p_src);
|
||||
|
||||
// destroy functions
|
||||
|
||||
public static partial void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_vector4_array_destroy(ref godot_packed_vector4_array p_self);
|
||||
|
||||
public static partial void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self);
|
||||
|
||||
public static partial void godotsharp_variant_destroy(ref godot_variant p_self);
|
||||
|
||||
public static partial void godotsharp_string_destroy(ref godot_string p_self);
|
||||
|
||||
public static partial void godotsharp_string_name_destroy(ref godot_string_name p_self);
|
||||
|
||||
public static partial void godotsharp_node_path_destroy(ref godot_node_path p_self);
|
||||
|
||||
public static partial void godotsharp_signal_destroy(ref godot_signal p_self);
|
||||
|
||||
public static partial void godotsharp_callable_destroy(ref godot_callable p_self);
|
||||
|
||||
public static partial void godotsharp_array_destroy(ref godot_array p_self);
|
||||
|
||||
public static partial void godotsharp_dictionary_destroy(ref godot_dictionary p_self);
|
||||
|
||||
// Array
|
||||
|
||||
public static partial int godotsharp_array_add(ref godot_array p_self, in godot_variant p_item);
|
||||
|
||||
public static partial int godotsharp_array_add_range(ref godot_array p_self, in godot_array p_collection);
|
||||
|
||||
public static partial int godotsharp_array_binary_search(ref godot_array p_self, int p_index, int p_count, in godot_variant p_value);
|
||||
|
||||
public static partial void godotsharp_array_duplicate(scoped ref godot_array p_self, godot_bool p_deep, out godot_array r_dest);
|
||||
|
||||
public static partial void godotsharp_array_fill(ref godot_array p_self, in godot_variant p_value);
|
||||
|
||||
public static partial int godotsharp_array_index_of(ref godot_array p_self, in godot_variant p_item, int p_index = 0);
|
||||
|
||||
public static partial void godotsharp_array_insert(ref godot_array p_self, int p_index, in godot_variant p_item);
|
||||
|
||||
public static partial int godotsharp_array_last_index_of(ref godot_array p_self, in godot_variant p_item, int p_index);
|
||||
|
||||
public static partial void godotsharp_array_make_read_only(ref godot_array p_self);
|
||||
|
||||
public static partial void godotsharp_array_set_typed(
|
||||
ref godot_array p_self,
|
||||
uint p_elem_type,
|
||||
in godot_string_name p_elem_class_name,
|
||||
in godot_ref p_elem_script);
|
||||
|
||||
public static partial godot_bool godotsharp_array_is_typed(ref godot_array p_self);
|
||||
|
||||
public static partial void godotsharp_array_max(scoped ref godot_array p_self, out godot_variant r_value);
|
||||
|
||||
public static partial void godotsharp_array_min(scoped ref godot_array p_self, out godot_variant r_value);
|
||||
|
||||
public static partial void godotsharp_array_pick_random(scoped ref godot_array p_self, out godot_variant r_value);
|
||||
|
||||
public static partial godot_bool godotsharp_array_recursive_equal(ref godot_array p_self, in godot_array p_other);
|
||||
|
||||
public static partial void godotsharp_array_remove_at(ref godot_array p_self, int p_index);
|
||||
|
||||
public static partial Error godotsharp_array_resize(ref godot_array p_self, int p_new_size);
|
||||
|
||||
public static partial void godotsharp_array_reverse(ref godot_array p_self);
|
||||
|
||||
public static partial void godotsharp_array_shuffle(ref godot_array p_self);
|
||||
|
||||
public static partial void godotsharp_array_slice(scoped ref godot_array p_self, int p_start, int p_end,
|
||||
int p_step, godot_bool p_deep, out godot_array r_dest);
|
||||
|
||||
public static partial void godotsharp_array_sort(ref godot_array p_self);
|
||||
|
||||
public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str);
|
||||
|
||||
public static partial void godotsharp_packed_byte_array_compress(scoped in godot_packed_byte_array p_src, int p_mode, out godot_packed_byte_array r_dst);
|
||||
|
||||
public static partial void godotsharp_packed_byte_array_decompress(scoped in godot_packed_byte_array p_src, long p_buffer_size, int p_mode, out godot_packed_byte_array r_dst);
|
||||
|
||||
public static partial void godotsharp_packed_byte_array_decompress_dynamic(scoped in godot_packed_byte_array p_src, long p_buffer_size, int p_mode, out godot_packed_byte_array r_dst);
|
||||
|
||||
// Dictionary
|
||||
|
||||
public static partial godot_bool godotsharp_dictionary_try_get_value(scoped ref godot_dictionary p_self,
|
||||
scoped in godot_variant p_key,
|
||||
out godot_variant r_value);
|
||||
|
||||
public static partial void godotsharp_dictionary_set_value(ref godot_dictionary p_self, in godot_variant p_key,
|
||||
in godot_variant p_value);
|
||||
|
||||
public static partial void godotsharp_dictionary_keys(scoped ref godot_dictionary p_self, out godot_array r_dest);
|
||||
|
||||
public static partial void godotsharp_dictionary_values(scoped ref godot_dictionary p_self, out godot_array r_dest);
|
||||
|
||||
public static partial int godotsharp_dictionary_count(ref godot_dictionary p_self);
|
||||
|
||||
public static partial void godotsharp_dictionary_key_value_pair_at(scoped ref godot_dictionary p_self, int p_index,
|
||||
out godot_variant r_key, out godot_variant r_value);
|
||||
|
||||
public static partial void godotsharp_dictionary_add(ref godot_dictionary p_self, in godot_variant p_key,
|
||||
in godot_variant p_value);
|
||||
|
||||
public static partial void godotsharp_dictionary_clear(ref godot_dictionary p_self);
|
||||
|
||||
public static partial godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self,
|
||||
in godot_variant p_key);
|
||||
|
||||
public static partial void godotsharp_dictionary_duplicate(scoped ref godot_dictionary p_self, godot_bool p_deep,
|
||||
out godot_dictionary r_dest);
|
||||
|
||||
public static partial void godotsharp_dictionary_merge(ref godot_dictionary p_self, in godot_dictionary p_dictionary, godot_bool p_overwrite);
|
||||
|
||||
public static partial godot_bool godotsharp_dictionary_recursive_equal(ref godot_dictionary p_self, in godot_dictionary p_other);
|
||||
|
||||
public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self,
|
||||
in godot_variant p_key);
|
||||
|
||||
public static partial void godotsharp_dictionary_make_read_only(ref godot_dictionary p_self);
|
||||
|
||||
public static partial void godotsharp_dictionary_set_typed(
|
||||
ref godot_dictionary p_self,
|
||||
uint p_key_type,
|
||||
in godot_string_name p_key_class_name,
|
||||
in godot_ref p_key_script,
|
||||
uint p_value_type,
|
||||
in godot_string_name p_value_class_name,
|
||||
in godot_ref p_value_script);
|
||||
|
||||
public static partial godot_bool godotsharp_dictionary_is_typed_key(ref godot_dictionary p_self);
|
||||
|
||||
public static partial godot_bool godotsharp_dictionary_is_typed_value(ref godot_dictionary p_self);
|
||||
|
||||
public static partial uint godotsharp_dictionary_get_typed_key_builtin(ref godot_dictionary p_self);
|
||||
|
||||
public static partial uint godotsharp_dictionary_get_typed_value_builtin(ref godot_dictionary p_self);
|
||||
|
||||
public static partial void godotsharp_dictionary_get_typed_key_class_name(ref godot_dictionary p_self, out godot_string_name r_dest);
|
||||
|
||||
public static partial void godotsharp_dictionary_get_typed_value_class_name(ref godot_dictionary p_self, out godot_string_name r_dest);
|
||||
|
||||
public static partial void godotsharp_dictionary_get_typed_key_script(ref godot_dictionary p_self, out godot_variant r_dest);
|
||||
|
||||
public static partial void godotsharp_dictionary_get_typed_value_script(ref godot_dictionary p_self, out godot_variant r_dest);
|
||||
|
||||
public static partial void godotsharp_dictionary_to_string(scoped ref godot_dictionary p_self, out godot_string r_str);
|
||||
|
||||
// StringExtensions
|
||||
|
||||
public static partial void godotsharp_string_simplify_path(scoped in godot_string p_self,
|
||||
out godot_string r_simplified_path);
|
||||
|
||||
public static partial void godotsharp_string_capitalize(scoped in godot_string p_self,
|
||||
out godot_string r_capitalized);
|
||||
|
||||
public static partial void godotsharp_string_to_camel_case(scoped in godot_string p_self,
|
||||
out godot_string r_camel_case);
|
||||
|
||||
public static partial void godotsharp_string_to_pascal_case(scoped in godot_string p_self,
|
||||
out godot_string r_pascal_case);
|
||||
|
||||
public static partial void godotsharp_string_to_snake_case(scoped in godot_string p_self,
|
||||
out godot_string r_snake_case);
|
||||
|
||||
public static partial void godotsharp_string_to_kebab_case(scoped in godot_string p_self,
|
||||
out godot_string r_kebab_case);
|
||||
|
||||
// NodePath
|
||||
|
||||
public static partial void godotsharp_node_path_get_as_property_path(in godot_node_path p_self,
|
||||
ref godot_node_path r_dest);
|
||||
|
||||
public static partial void godotsharp_node_path_get_concatenated_names(scoped in godot_node_path p_self,
|
||||
out godot_string r_names);
|
||||
|
||||
public static partial void godotsharp_node_path_get_concatenated_subnames(scoped in godot_node_path p_self,
|
||||
out godot_string r_subnames);
|
||||
|
||||
public static partial void godotsharp_node_path_get_name(scoped in godot_node_path p_self, int p_idx,
|
||||
out godot_string r_name);
|
||||
|
||||
public static partial int godotsharp_node_path_get_name_count(in godot_node_path p_self);
|
||||
|
||||
public static partial void godotsharp_node_path_get_subname(scoped in godot_node_path p_self, int p_idx,
|
||||
out godot_string r_subname);
|
||||
|
||||
public static partial int godotsharp_node_path_get_subname_count(in godot_node_path p_self);
|
||||
|
||||
public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self);
|
||||
|
||||
public static partial godot_bool godotsharp_node_path_equals(in godot_node_path p_self, in godot_node_path p_other);
|
||||
|
||||
public static partial int godotsharp_node_path_hash(in godot_node_path p_self);
|
||||
|
||||
// GD, etc
|
||||
|
||||
internal static partial void godotsharp_bytes_to_var(scoped in godot_packed_byte_array p_bytes,
|
||||
godot_bool p_allow_objects,
|
||||
out godot_variant r_ret);
|
||||
|
||||
internal static partial void godotsharp_convert(scoped in godot_variant p_what, int p_type,
|
||||
out godot_variant r_ret);
|
||||
|
||||
internal static partial int godotsharp_hash(in godot_variant p_var);
|
||||
|
||||
internal static partial IntPtr godotsharp_instance_from_id(ulong p_instance_id);
|
||||
|
||||
internal static partial void godotsharp_print(in godot_string p_what);
|
||||
|
||||
public static partial void godotsharp_print_rich(in godot_string p_what);
|
||||
|
||||
internal static partial void godotsharp_printerr(in godot_string p_what);
|
||||
|
||||
internal static partial void godotsharp_printraw(in godot_string p_what);
|
||||
|
||||
internal static partial void godotsharp_prints(in godot_string p_what);
|
||||
|
||||
internal static partial void godotsharp_printt(in godot_string p_what);
|
||||
|
||||
internal static partial float godotsharp_randf();
|
||||
|
||||
internal static partial uint godotsharp_randi();
|
||||
|
||||
internal static partial void godotsharp_randomize();
|
||||
|
||||
internal static partial double godotsharp_randf_range(double from, double to);
|
||||
|
||||
internal static partial double godotsharp_randfn(double mean, double deviation);
|
||||
|
||||
internal static partial int godotsharp_randi_range(int from, int to);
|
||||
|
||||
internal static partial uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed);
|
||||
|
||||
internal static partial void godotsharp_seed(ulong seed);
|
||||
|
||||
internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref);
|
||||
|
||||
internal static partial void godotsharp_str_to_var(scoped in godot_string p_str, out godot_variant r_ret);
|
||||
|
||||
internal static partial void godotsharp_var_to_bytes(scoped in godot_variant p_what, godot_bool p_full_objects,
|
||||
out godot_packed_byte_array r_bytes);
|
||||
|
||||
internal static partial void godotsharp_var_to_str(scoped in godot_variant p_var, out godot_string r_ret);
|
||||
|
||||
internal static partial void godotsharp_err_print_error(in godot_string p_function, in godot_string p_file, int p_line, in godot_string p_error, in godot_string p_message = default, godot_bool p_editor_notify = godot_bool.False, godot_error_handler_type p_type = godot_error_handler_type.ERR_HANDLER_ERROR);
|
||||
|
||||
// Object
|
||||
|
||||
public static partial void godotsharp_object_to_string(IntPtr ptr, out godot_string r_str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#pragma warning disable CA1707 // Identifiers should not contain underscores
|
||||
#pragma warning disable IDE1006 // Naming rule violation
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
public static partial class NativeFuncs
|
||||
{
|
||||
public static godot_variant godotsharp_variant_new_copy(scoped in godot_variant src)
|
||||
{
|
||||
switch (src.Type)
|
||||
{
|
||||
case Variant.Type.Nil:
|
||||
return default;
|
||||
case Variant.Type.Bool:
|
||||
return new godot_variant() { Bool = src.Bool, Type = Variant.Type.Bool };
|
||||
case Variant.Type.Int:
|
||||
return new godot_variant() { Int = src.Int, Type = Variant.Type.Int };
|
||||
case Variant.Type.Float:
|
||||
return new godot_variant() { Float = src.Float, Type = Variant.Type.Float };
|
||||
case Variant.Type.Vector2:
|
||||
return new godot_variant() { Vector2 = src.Vector2, Type = Variant.Type.Vector2 };
|
||||
case Variant.Type.Vector2I:
|
||||
return new godot_variant() { Vector2I = src.Vector2I, Type = Variant.Type.Vector2I };
|
||||
case Variant.Type.Rect2:
|
||||
return new godot_variant() { Rect2 = src.Rect2, Type = Variant.Type.Rect2 };
|
||||
case Variant.Type.Rect2I:
|
||||
return new godot_variant() { Rect2I = src.Rect2I, Type = Variant.Type.Rect2I };
|
||||
case Variant.Type.Vector3:
|
||||
return new godot_variant() { Vector3 = src.Vector3, Type = Variant.Type.Vector3 };
|
||||
case Variant.Type.Vector3I:
|
||||
return new godot_variant() { Vector3I = src.Vector3I, Type = Variant.Type.Vector3I };
|
||||
case Variant.Type.Vector4:
|
||||
return new godot_variant() { Vector4 = src.Vector4, Type = Variant.Type.Vector4 };
|
||||
case Variant.Type.Vector4I:
|
||||
return new godot_variant() { Vector4I = src.Vector4I, Type = Variant.Type.Vector4I };
|
||||
case Variant.Type.Plane:
|
||||
return new godot_variant() { Plane = src.Plane, Type = Variant.Type.Plane };
|
||||
case Variant.Type.Quaternion:
|
||||
return new godot_variant() { Quaternion = src.Quaternion, Type = Variant.Type.Quaternion };
|
||||
case Variant.Type.Color:
|
||||
return new godot_variant() { Color = src.Color, Type = Variant.Type.Color };
|
||||
case Variant.Type.Rid:
|
||||
return new godot_variant() { Rid = src.Rid, Type = Variant.Type.Rid };
|
||||
}
|
||||
|
||||
godotsharp_variant_new_copy(out godot_variant ret, src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_string_name godotsharp_string_name_new_copy(scoped in godot_string_name src)
|
||||
{
|
||||
if (src.IsEmpty)
|
||||
return default;
|
||||
godotsharp_string_name_new_copy(out godot_string_name ret, src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_node_path godotsharp_node_path_new_copy(scoped in godot_node_path src)
|
||||
{
|
||||
if (src.IsEmpty)
|
||||
return default;
|
||||
godotsharp_node_path_new_copy(out godot_node_path ret, src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_array godotsharp_array_new()
|
||||
{
|
||||
godotsharp_array_new(out godot_array ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_array godotsharp_array_new_copy(scoped in godot_array src)
|
||||
{
|
||||
godotsharp_array_new_copy(out godot_array ret, src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_dictionary godotsharp_dictionary_new()
|
||||
{
|
||||
godotsharp_dictionary_new(out godot_dictionary ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_dictionary godotsharp_dictionary_new_copy(scoped in godot_dictionary src)
|
||||
{
|
||||
godotsharp_dictionary_new_copy(out godot_dictionary ret, src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_string_name godotsharp_string_name_new_from_string(string name)
|
||||
{
|
||||
using godot_string src = Marshaling.ConvertStringToNative(name);
|
||||
godotsharp_string_name_new_from_string(out godot_string_name ret, src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_node_path godotsharp_node_path_new_from_string(string name)
|
||||
{
|
||||
using godot_string src = Marshaling.ConvertStringToNative(name);
|
||||
godotsharp_node_path_new_from_string(out godot_node_path ret, src);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
// Our source generators will add trampolines methods that access variant arguments.
|
||||
// This struct makes that possible without having to enable `AllowUnsafeBlocks` in game projects.
|
||||
|
||||
public unsafe ref struct NativeVariantPtrArgs
|
||||
{
|
||||
private godot_variant** _args;
|
||||
private int _argc;
|
||||
|
||||
internal NativeVariantPtrArgs(godot_variant** args, int argc)
|
||||
{
|
||||
_args = args;
|
||||
_argc = argc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of arguments.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _argc;
|
||||
}
|
||||
|
||||
public ref godot_variant this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref *_args[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
#pragma warning disable CA1707 // Identifiers should not contain underscores
|
||||
#pragma warning disable IDE1006 // Naming rule violation
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot.Collections;
|
||||
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
public static partial class VariantUtils
|
||||
{
|
||||
public static godot_variant CreateFromRid(Rid from)
|
||||
=> new() { Type = Variant.Type.Rid, Rid = from };
|
||||
|
||||
public static godot_variant CreateFromBool(bool from)
|
||||
=> new() { Type = Variant.Type.Bool, Bool = from.ToGodotBool() };
|
||||
|
||||
public static godot_variant CreateFromInt(long from)
|
||||
=> new() { Type = Variant.Type.Int, Int = from };
|
||||
|
||||
public static godot_variant CreateFromInt(ulong from)
|
||||
=> new() { Type = Variant.Type.Int, Int = (long)from };
|
||||
|
||||
public static godot_variant CreateFromFloat(double from)
|
||||
=> new() { Type = Variant.Type.Float, Float = from };
|
||||
|
||||
public static godot_variant CreateFromVector2(Vector2 from)
|
||||
=> new() { Type = Variant.Type.Vector2, Vector2 = from };
|
||||
|
||||
public static godot_variant CreateFromVector2I(Vector2I from)
|
||||
=> new() { Type = Variant.Type.Vector2I, Vector2I = from };
|
||||
|
||||
public static godot_variant CreateFromVector3(Vector3 from)
|
||||
=> new() { Type = Variant.Type.Vector3, Vector3 = from };
|
||||
|
||||
public static godot_variant CreateFromVector3I(Vector3I from)
|
||||
=> new() { Type = Variant.Type.Vector3I, Vector3I = from };
|
||||
|
||||
public static godot_variant CreateFromVector4(Vector4 from)
|
||||
=> new() { Type = Variant.Type.Vector4, Vector4 = from };
|
||||
|
||||
public static godot_variant CreateFromVector4I(Vector4I from)
|
||||
=> new() { Type = Variant.Type.Vector4I, Vector4I = from };
|
||||
|
||||
public static godot_variant CreateFromRect2(Rect2 from)
|
||||
=> new() { Type = Variant.Type.Rect2, Rect2 = from };
|
||||
|
||||
public static godot_variant CreateFromRect2I(Rect2I from)
|
||||
=> new() { Type = Variant.Type.Rect2I, Rect2I = from };
|
||||
|
||||
public static godot_variant CreateFromQuaternion(Quaternion from)
|
||||
=> new() { Type = Variant.Type.Quaternion, Quaternion = from };
|
||||
|
||||
public static godot_variant CreateFromColor(Color from)
|
||||
=> new() { Type = Variant.Type.Color, Color = from };
|
||||
|
||||
public static godot_variant CreateFromPlane(Plane from)
|
||||
=> new() { Type = Variant.Type.Plane, Plane = from };
|
||||
|
||||
public static godot_variant CreateFromTransform2D(Transform2D from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_transform2d(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromBasis(Basis from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_basis(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromTransform3D(Transform3D from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_transform3d(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromProjection(Projection from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_projection(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromAabb(Aabb from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_aabb(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Explicit name to make it very clear
|
||||
public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from)
|
||||
=> new() { Type = Variant.Type.Callable, Callable = from };
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromCallable(Callable from)
|
||||
=> CreateFromCallableTakingOwnershipOfDisposableValue(
|
||||
Marshaling.ConvertCallableToNative(from));
|
||||
|
||||
// Explicit name to make it very clear
|
||||
public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from)
|
||||
=> new() { Type = Variant.Type.Signal, Signal = from };
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromSignal(Signal from)
|
||||
=> CreateFromSignalTakingOwnershipOfDisposableValue(
|
||||
Marshaling.ConvertSignalToNative(from));
|
||||
|
||||
// Explicit name to make it very clear
|
||||
public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
|
||||
=> new() { Type = Variant.Type.String, String = from };
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromString(string? from)
|
||||
=> CreateFromStringTakingOwnershipOfDisposableValue(Marshaling.ConvertStringToNative(from));
|
||||
|
||||
public static godot_variant CreateFromPackedByteArray(scoped in godot_packed_byte_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_byte_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedInt32Array(scoped in godot_packed_int32_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_int32_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedInt64Array(scoped in godot_packed_int64_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_int64_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedFloat32Array(scoped in godot_packed_float32_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_float32_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedFloat64Array(scoped in godot_packed_float64_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_float64_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedStringArray(scoped in godot_packed_string_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_string_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedVector2Array(scoped in godot_packed_vector2_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_vector2_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedVector3Array(scoped in godot_packed_vector3_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_vector3_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedVector4Array(scoped in godot_packed_vector4_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_vector4_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromPackedColorArray(scoped in godot_packed_color_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_packed_color_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedByteArray(scoped Span<byte> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedByteArray(from);
|
||||
return CreateFromPackedByteArray(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedInt32Array(scoped Span<int> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedInt32Array(from);
|
||||
return CreateFromPackedInt32Array(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedInt64Array(scoped Span<long> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedInt64Array(from);
|
||||
return CreateFromPackedInt64Array(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedFloat32Array(scoped Span<float> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedFloat32Array(from);
|
||||
return CreateFromPackedFloat32Array(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedFloat64Array(scoped Span<double> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedFloat64Array(from);
|
||||
return CreateFromPackedFloat64Array(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedStringArray(scoped Span<string> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedStringArray(from);
|
||||
return CreateFromPackedStringArray(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedVector2Array(scoped Span<Vector2> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedVector2Array(from);
|
||||
return CreateFromPackedVector2Array(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedVector3Array(scoped Span<Vector3> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedVector3Array(from);
|
||||
return CreateFromPackedVector3Array(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedVector4Array(scoped Span<Vector4> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedVector4Array(from);
|
||||
return CreateFromPackedVector4Array(nativePackedArray);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromPackedColorArray(scoped Span<Color> from)
|
||||
{
|
||||
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedColorArray(from);
|
||||
return CreateFromPackedColorArray(nativePackedArray);
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromSystemArrayOfStringName(scoped Span<StringName> from)
|
||||
{
|
||||
if (from == null)
|
||||
return default;
|
||||
using var fromGodot = new Collections.Array(from);
|
||||
return CreateFromArray((godot_array)fromGodot.NativeValue);
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromSystemArrayOfNodePath(scoped Span<NodePath> from)
|
||||
{
|
||||
if (from == null)
|
||||
return default;
|
||||
using var fromGodot = new Collections.Array(from);
|
||||
return CreateFromArray((godot_array)fromGodot.NativeValue);
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromSystemArrayOfRid(scoped Span<Rid> from)
|
||||
{
|
||||
if (from == null)
|
||||
return default;
|
||||
using var fromGodot = new Collections.Array(from);
|
||||
return CreateFromArray((godot_array)fromGodot.NativeValue);
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromSystemArrayOfGodotObject(GodotObject[]? from)
|
||||
{
|
||||
if (from == null)
|
||||
return default; // Nil
|
||||
using var fromGodot = new Collections.Array(from);
|
||||
return CreateFromArray((godot_array)fromGodot.NativeValue);
|
||||
}
|
||||
|
||||
public static godot_variant CreateFromArray(scoped in godot_array from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromArray(Collections.Array? from)
|
||||
=> from != null ? CreateFromArray((godot_array)from.NativeValue) : default;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromArray<[MustBeVariant] T>(Array<T>? from)
|
||||
=> from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default;
|
||||
|
||||
public static godot_variant CreateFromDictionary(scoped in godot_dictionary from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromDictionary(Dictionary? from)
|
||||
=> from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(Dictionary<TKey, TValue>? from)
|
||||
=> from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default;
|
||||
|
||||
public static godot_variant CreateFromStringName(scoped in godot_string_name from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromStringName(StringName? from)
|
||||
=> from != null ? CreateFromStringName((godot_string_name)from.NativeValue) : default;
|
||||
|
||||
public static godot_variant CreateFromNodePath(scoped in godot_node_path from)
|
||||
{
|
||||
NativeFuncs.godotsharp_variant_new_node_path(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromNodePath(NodePath? from)
|
||||
=> from != null ? CreateFromNodePath((godot_node_path)from.NativeValue) : default;
|
||||
|
||||
public static godot_variant CreateFromGodotObjectPtr(IntPtr from)
|
||||
{
|
||||
if (from == IntPtr.Zero)
|
||||
return new godot_variant();
|
||||
NativeFuncs.godotsharp_variant_new_object(out godot_variant ret, from);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_variant CreateFromGodotObject(GodotObject? from)
|
||||
=> from != null ? CreateFromGodotObjectPtr(GodotObject.GetPtr(from)) : default;
|
||||
|
||||
// We avoid the internal call if the stored type is the same we want.
|
||||
|
||||
public static bool ConvertToBool(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Bool ?
|
||||
p_var.Bool.ToBool() :
|
||||
NativeFuncs.godotsharp_variant_as_bool(p_var).ToBool();
|
||||
|
||||
public static char ConvertToChar(in godot_variant p_var)
|
||||
=> (char)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static sbyte ConvertToInt8(in godot_variant p_var)
|
||||
=> (sbyte)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static short ConvertToInt16(in godot_variant p_var)
|
||||
=> (short)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static int ConvertToInt32(in godot_variant p_var)
|
||||
=> (int)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static long ConvertToInt64(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var);
|
||||
|
||||
public static byte ConvertToUInt8(in godot_variant p_var)
|
||||
=> (byte)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static ushort ConvertToUInt16(in godot_variant p_var)
|
||||
=> (ushort)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static uint ConvertToUInt32(in godot_variant p_var)
|
||||
=> (uint)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static ulong ConvertToUInt64(in godot_variant p_var)
|
||||
=> (ulong)(p_var.Type == Variant.Type.Int ?
|
||||
p_var.Int :
|
||||
NativeFuncs.godotsharp_variant_as_int(p_var));
|
||||
|
||||
public static float ConvertToFloat32(in godot_variant p_var)
|
||||
=> (float)(p_var.Type == Variant.Type.Float ?
|
||||
p_var.Float :
|
||||
NativeFuncs.godotsharp_variant_as_float(p_var));
|
||||
|
||||
public static double ConvertToFloat64(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Float ?
|
||||
p_var.Float :
|
||||
NativeFuncs.godotsharp_variant_as_float(p_var);
|
||||
|
||||
public static Vector2 ConvertToVector2(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Vector2 ?
|
||||
p_var.Vector2 :
|
||||
NativeFuncs.godotsharp_variant_as_vector2(p_var);
|
||||
|
||||
public static Vector2I ConvertToVector2I(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Vector2I ?
|
||||
p_var.Vector2I :
|
||||
NativeFuncs.godotsharp_variant_as_vector2i(p_var);
|
||||
|
||||
public static Rect2 ConvertToRect2(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Rect2 ?
|
||||
p_var.Rect2 :
|
||||
NativeFuncs.godotsharp_variant_as_rect2(p_var);
|
||||
|
||||
public static Rect2I ConvertToRect2I(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Rect2I ?
|
||||
p_var.Rect2I :
|
||||
NativeFuncs.godotsharp_variant_as_rect2i(p_var);
|
||||
|
||||
public static unsafe Transform2D ConvertToTransform2D(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Transform2D ?
|
||||
*p_var.Transform2D :
|
||||
NativeFuncs.godotsharp_variant_as_transform2d(p_var);
|
||||
|
||||
public static Vector3 ConvertToVector3(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Vector3 ?
|
||||
p_var.Vector3 :
|
||||
NativeFuncs.godotsharp_variant_as_vector3(p_var);
|
||||
|
||||
public static Vector3I ConvertToVector3I(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Vector3I ?
|
||||
p_var.Vector3I :
|
||||
NativeFuncs.godotsharp_variant_as_vector3i(p_var);
|
||||
|
||||
public static unsafe Vector4 ConvertToVector4(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Vector4 ?
|
||||
p_var.Vector4 :
|
||||
NativeFuncs.godotsharp_variant_as_vector4(p_var);
|
||||
|
||||
public static unsafe Vector4I ConvertToVector4I(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Vector4I ?
|
||||
p_var.Vector4I :
|
||||
NativeFuncs.godotsharp_variant_as_vector4i(p_var);
|
||||
|
||||
public static unsafe Basis ConvertToBasis(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Basis ?
|
||||
*p_var.Basis :
|
||||
NativeFuncs.godotsharp_variant_as_basis(p_var);
|
||||
|
||||
public static Quaternion ConvertToQuaternion(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Quaternion ?
|
||||
p_var.Quaternion :
|
||||
NativeFuncs.godotsharp_variant_as_quaternion(p_var);
|
||||
|
||||
public static unsafe Transform3D ConvertToTransform3D(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Transform3D ?
|
||||
*p_var.Transform3D :
|
||||
NativeFuncs.godotsharp_variant_as_transform3d(p_var);
|
||||
|
||||
public static unsafe Projection ConvertToProjection(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Projection ?
|
||||
*p_var.Projection :
|
||||
NativeFuncs.godotsharp_variant_as_projection(p_var);
|
||||
|
||||
public static unsafe Aabb ConvertToAabb(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Aabb ?
|
||||
*p_var.Aabb :
|
||||
NativeFuncs.godotsharp_variant_as_aabb(p_var);
|
||||
|
||||
public static Color ConvertToColor(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Color ?
|
||||
p_var.Color :
|
||||
NativeFuncs.godotsharp_variant_as_color(p_var);
|
||||
|
||||
public static Plane ConvertToPlane(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Plane ?
|
||||
p_var.Plane :
|
||||
NativeFuncs.godotsharp_variant_as_plane(p_var);
|
||||
|
||||
public static Rid ConvertToRid(in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Rid ?
|
||||
p_var.Rid :
|
||||
NativeFuncs.godotsharp_variant_as_rid(p_var);
|
||||
|
||||
public static IntPtr ConvertToGodotObjectPtr(in godot_variant p_var)
|
||||
{
|
||||
if (p_var.Type != Variant.Type.Object || p_var.ObjectId == 0)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
return NativeFuncs.godotsharp_instance_from_id(p_var.ObjectId);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static GodotObject ConvertToGodotObject(in godot_variant p_var)
|
||||
=> InteropUtils.UnmanagedGetManaged(ConvertToGodotObjectPtr(p_var));
|
||||
|
||||
public static string ConvertToString(in godot_variant p_var)
|
||||
{
|
||||
switch (p_var.Type)
|
||||
{
|
||||
case Variant.Type.Nil:
|
||||
return ""; // Otherwise, Variant -> String would return the string "Null"
|
||||
case Variant.Type.String:
|
||||
{
|
||||
// We avoid the internal call if the stored type is the same we want.
|
||||
return Marshaling.ConvertStringToManaged(p_var.String);
|
||||
}
|
||||
default:
|
||||
{
|
||||
using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
|
||||
return Marshaling.ConvertStringToManaged(godotString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static godot_string_name ConvertToNativeStringName(scoped in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.StringName ?
|
||||
NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName) :
|
||||
NativeFuncs.godotsharp_variant_as_string_name(p_var);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static StringName ConvertToStringName(in godot_variant p_var)
|
||||
=> StringName.CreateTakingOwnershipOfDisposableValue(ConvertToNativeStringName(p_var));
|
||||
|
||||
public static godot_node_path ConvertToNativeNodePath(scoped in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.NodePath ?
|
||||
NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath) :
|
||||
NativeFuncs.godotsharp_variant_as_node_path(p_var);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static NodePath ConvertToNodePath(in godot_variant p_var)
|
||||
=> NodePath.CreateTakingOwnershipOfDisposableValue(ConvertToNativeNodePath(p_var));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_callable ConvertToNativeCallable(scoped in godot_variant p_var)
|
||||
=> NativeFuncs.godotsharp_variant_as_callable(p_var);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Callable ConvertToCallable(in godot_variant p_var)
|
||||
{
|
||||
using var callable = ConvertToNativeCallable(p_var);
|
||||
return Marshaling.ConvertCallableToManaged(callable);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static godot_signal ConvertToNativeSignal(scoped in godot_variant p_var)
|
||||
=> NativeFuncs.godotsharp_variant_as_signal(p_var);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Signal ConvertToSignal(in godot_variant p_var)
|
||||
{
|
||||
using var signal = ConvertToNativeSignal(p_var);
|
||||
return Marshaling.ConvertSignalToManaged(signal);
|
||||
}
|
||||
|
||||
public static godot_array ConvertToNativeArray(scoped in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Array ?
|
||||
NativeFuncs.godotsharp_array_new_copy(p_var.Array) :
|
||||
NativeFuncs.godotsharp_variant_as_array(p_var);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Collections.Array ConvertToArray(in godot_variant p_var)
|
||||
=> Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Array<T> ConvertToArray<[MustBeVariant] T>(in godot_variant p_var)
|
||||
=> Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var));
|
||||
|
||||
public static godot_dictionary ConvertToNativeDictionary(scoped in godot_variant p_var)
|
||||
=> p_var.Type == Variant.Type.Dictionary ?
|
||||
NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary) :
|
||||
NativeFuncs.godotsharp_variant_as_dictionary(p_var);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Dictionary ConvertToDictionary(in godot_variant p_var)
|
||||
=> Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Dictionary<TKey, TValue> ConvertToDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(in godot_variant p_var)
|
||||
=> Dictionary<TKey, TValue>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var));
|
||||
|
||||
public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var);
|
||||
return Marshaling.ConvertNativePackedByteArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static int[] ConvertAsPackedInt32ArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var);
|
||||
return Marshaling.ConvertNativePackedInt32ArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static long[] ConvertAsPackedInt64ArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var);
|
||||
return Marshaling.ConvertNativePackedInt64ArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static float[] ConvertAsPackedFloat32ArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var);
|
||||
return Marshaling.ConvertNativePackedFloat32ArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static double[] ConvertAsPackedFloat64ArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var);
|
||||
return Marshaling.ConvertNativePackedFloat64ArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static string[] ConvertAsPackedStringArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var);
|
||||
return Marshaling.ConvertNativePackedStringArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static Vector2[] ConvertAsPackedVector2ArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var);
|
||||
return Marshaling.ConvertNativePackedVector2ArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static Vector3[] ConvertAsPackedVector3ArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var);
|
||||
return Marshaling.ConvertNativePackedVector3ArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static Vector4[] ConvertAsPackedVector4ArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector4_array(p_var);
|
||||
return Marshaling.ConvertNativePackedVector4ArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static Color[] ConvertAsPackedColorArrayToSystemArray(in godot_variant p_var)
|
||||
{
|
||||
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
|
||||
return Marshaling.ConvertNativePackedColorArrayToSystemArray(packedArray);
|
||||
}
|
||||
|
||||
public static StringName[] ConvertToSystemArrayOfStringName(in godot_variant p_var)
|
||||
{
|
||||
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
|
||||
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfStringName(godotArray);
|
||||
}
|
||||
|
||||
public static NodePath[] ConvertToSystemArrayOfNodePath(in godot_variant p_var)
|
||||
{
|
||||
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
|
||||
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfNodePath(godotArray);
|
||||
}
|
||||
|
||||
public static Rid[] ConvertToSystemArrayOfRid(in godot_variant p_var)
|
||||
{
|
||||
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
|
||||
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRid(godotArray);
|
||||
}
|
||||
|
||||
public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var)
|
||||
where T : GodotObject
|
||||
{
|
||||
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
|
||||
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(godotArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Godot.NativeInterop;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public partial class VariantUtils
|
||||
{
|
||||
private static InvalidOperationException UnsupportedType<T>() => new InvalidOperationException(
|
||||
$"The type is not supported for conversion to/from Variant: '{typeof(T).FullName}'");
|
||||
|
||||
internal static class GenericConversion<T>
|
||||
{
|
||||
internal delegate godot_variant ToVariantConverter(scoped in T from);
|
||||
internal delegate T FromVariantConverter(in godot_variant from);
|
||||
|
||||
public static unsafe godot_variant ToVariant(scoped in T from) =>
|
||||
ToVariantCb != null ? ToVariantCb(from) : throw UnsupportedType<T>();
|
||||
|
||||
public static unsafe T FromVariant(in godot_variant variant) =>
|
||||
FromVariantCb != null ? FromVariantCb(variant) : throw UnsupportedType<T>();
|
||||
|
||||
internal static ToVariantConverter? ToVariantCb;
|
||||
|
||||
internal static FromVariantConverter? FromVariantCb;
|
||||
|
||||
static GenericConversion()
|
||||
{
|
||||
RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static godot_variant CreateFrom<[MustBeVariant] T>(scoped in T from)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static TTo UnsafeAs<TTo>(in T f) => Unsafe.As<T, TTo>(ref Unsafe.AsRef(in f));
|
||||
|
||||
// `typeof(T) == typeof(X)` is optimized away. We cannot cache `typeof(T)` in a local variable, as it's not optimized when done like that.
|
||||
|
||||
if (typeof(T) == typeof(bool))
|
||||
return CreateFromBool(UnsafeAs<bool>(from));
|
||||
|
||||
if (typeof(T) == typeof(char))
|
||||
return CreateFromInt(UnsafeAs<char>(from));
|
||||
|
||||
if (typeof(T) == typeof(sbyte))
|
||||
return CreateFromInt(UnsafeAs<sbyte>(from));
|
||||
|
||||
if (typeof(T) == typeof(short))
|
||||
return CreateFromInt(UnsafeAs<short>(from));
|
||||
|
||||
if (typeof(T) == typeof(int))
|
||||
return CreateFromInt(UnsafeAs<int>(from));
|
||||
|
||||
if (typeof(T) == typeof(long))
|
||||
return CreateFromInt(UnsafeAs<long>(from));
|
||||
|
||||
if (typeof(T) == typeof(byte))
|
||||
return CreateFromInt(UnsafeAs<byte>(from));
|
||||
|
||||
if (typeof(T) == typeof(ushort))
|
||||
return CreateFromInt(UnsafeAs<ushort>(from));
|
||||
|
||||
if (typeof(T) == typeof(uint))
|
||||
return CreateFromInt(UnsafeAs<uint>(from));
|
||||
|
||||
if (typeof(T) == typeof(ulong))
|
||||
return CreateFromInt(UnsafeAs<ulong>(from));
|
||||
|
||||
if (typeof(T) == typeof(float))
|
||||
return CreateFromFloat(UnsafeAs<float>(from));
|
||||
|
||||
if (typeof(T) == typeof(double))
|
||||
return CreateFromFloat(UnsafeAs<double>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector2))
|
||||
return CreateFromVector2(UnsafeAs<Vector2>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector2I))
|
||||
return CreateFromVector2I(UnsafeAs<Vector2I>(from));
|
||||
|
||||
if (typeof(T) == typeof(Rect2))
|
||||
return CreateFromRect2(UnsafeAs<Rect2>(from));
|
||||
|
||||
if (typeof(T) == typeof(Rect2I))
|
||||
return CreateFromRect2I(UnsafeAs<Rect2I>(from));
|
||||
|
||||
if (typeof(T) == typeof(Transform2D))
|
||||
return CreateFromTransform2D(UnsafeAs<Transform2D>(from));
|
||||
|
||||
if (typeof(T) == typeof(Projection))
|
||||
return CreateFromProjection(UnsafeAs<Projection>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector3))
|
||||
return CreateFromVector3(UnsafeAs<Vector3>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector3I))
|
||||
return CreateFromVector3I(UnsafeAs<Vector3I>(from));
|
||||
|
||||
if (typeof(T) == typeof(Basis))
|
||||
return CreateFromBasis(UnsafeAs<Basis>(from));
|
||||
|
||||
if (typeof(T) == typeof(Quaternion))
|
||||
return CreateFromQuaternion(UnsafeAs<Quaternion>(from));
|
||||
|
||||
if (typeof(T) == typeof(Transform3D))
|
||||
return CreateFromTransform3D(UnsafeAs<Transform3D>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector4))
|
||||
return CreateFromVector4(UnsafeAs<Vector4>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector4I))
|
||||
return CreateFromVector4I(UnsafeAs<Vector4I>(from));
|
||||
|
||||
if (typeof(T) == typeof(Aabb))
|
||||
return CreateFromAabb(UnsafeAs<Aabb>(from));
|
||||
|
||||
if (typeof(T) == typeof(Color))
|
||||
return CreateFromColor(UnsafeAs<Color>(from));
|
||||
|
||||
if (typeof(T) == typeof(Plane))
|
||||
return CreateFromPlane(UnsafeAs<Plane>(from));
|
||||
|
||||
if (typeof(T) == typeof(Callable))
|
||||
return CreateFromCallable(UnsafeAs<Callable>(from));
|
||||
|
||||
if (typeof(T) == typeof(Signal))
|
||||
return CreateFromSignal(UnsafeAs<Signal>(from));
|
||||
|
||||
if (typeof(T) == typeof(string))
|
||||
return CreateFromString(UnsafeAs<string>(from));
|
||||
|
||||
if (typeof(T) == typeof(byte[]))
|
||||
return CreateFromPackedByteArray(UnsafeAs<byte[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(int[]))
|
||||
return CreateFromPackedInt32Array(UnsafeAs<int[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(long[]))
|
||||
return CreateFromPackedInt64Array(UnsafeAs<long[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(float[]))
|
||||
return CreateFromPackedFloat32Array(UnsafeAs<float[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(double[]))
|
||||
return CreateFromPackedFloat64Array(UnsafeAs<double[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(string[]))
|
||||
return CreateFromPackedStringArray(UnsafeAs<string[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector2[]))
|
||||
return CreateFromPackedVector2Array(UnsafeAs<Vector2[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector3[]))
|
||||
return CreateFromPackedVector3Array(UnsafeAs<Vector3[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector4[]))
|
||||
return CreateFromPackedVector4Array(UnsafeAs<Vector4[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Color[]))
|
||||
return CreateFromPackedColorArray(UnsafeAs<Color[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(StringName[]))
|
||||
return CreateFromSystemArrayOfStringName(UnsafeAs<StringName[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(NodePath[]))
|
||||
return CreateFromSystemArrayOfNodePath(UnsafeAs<NodePath[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Rid[]))
|
||||
return CreateFromSystemArrayOfRid(UnsafeAs<Rid[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(StringName))
|
||||
return CreateFromStringName(UnsafeAs<StringName>(from));
|
||||
|
||||
if (typeof(T) == typeof(NodePath))
|
||||
return CreateFromNodePath(UnsafeAs<NodePath>(from));
|
||||
|
||||
if (typeof(T) == typeof(Rid))
|
||||
return CreateFromRid(UnsafeAs<Rid>(from));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Dictionary))
|
||||
return CreateFromDictionary(UnsafeAs<Godot.Collections.Dictionary>(from));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Array))
|
||||
return CreateFromArray(UnsafeAs<Godot.Collections.Array>(from));
|
||||
|
||||
if (typeof(T) == typeof(Variant))
|
||||
return NativeFuncs.godotsharp_variant_new_copy((godot_variant)UnsafeAs<Variant>(from).NativeVar);
|
||||
|
||||
// More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
|
||||
|
||||
// `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
|
||||
|
||||
if (typeof(GodotObject).IsAssignableFrom(typeof(T)))
|
||||
return CreateFromGodotObject(UnsafeAs<GodotObject>(from));
|
||||
|
||||
// `typeof(T).IsValueType` is optimized away
|
||||
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
|
||||
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
|
||||
|
||||
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
|
||||
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
|
||||
// We don't need to know whether it's signed or unsigned.
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 1)
|
||||
return CreateFromInt(UnsafeAs<sbyte>(from));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 2)
|
||||
return CreateFromInt(UnsafeAs<short>(from));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 4)
|
||||
return CreateFromInt(UnsafeAs<int>(from));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 8)
|
||||
return CreateFromInt(UnsafeAs<long>(from));
|
||||
|
||||
throw UnsupportedType<T>();
|
||||
}
|
||||
|
||||
return GenericConversion<T>.ToVariant(from);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static T ConvertTo<[MustBeVariant] T>(in godot_variant variant)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static T UnsafeAsT<TFrom>(TFrom f) => Unsafe.As<TFrom, T>(ref Unsafe.AsRef(in f));
|
||||
|
||||
if (typeof(T) == typeof(bool))
|
||||
return UnsafeAsT(ConvertToBool(variant));
|
||||
|
||||
if (typeof(T) == typeof(char))
|
||||
return UnsafeAsT(ConvertToChar(variant));
|
||||
|
||||
if (typeof(T) == typeof(sbyte))
|
||||
return UnsafeAsT(ConvertToInt8(variant));
|
||||
|
||||
if (typeof(T) == typeof(short))
|
||||
return UnsafeAsT(ConvertToInt16(variant));
|
||||
|
||||
if (typeof(T) == typeof(int))
|
||||
return UnsafeAsT(ConvertToInt32(variant));
|
||||
|
||||
if (typeof(T) == typeof(long))
|
||||
return UnsafeAsT(ConvertToInt64(variant));
|
||||
|
||||
if (typeof(T) == typeof(byte))
|
||||
return UnsafeAsT(ConvertToUInt8(variant));
|
||||
|
||||
if (typeof(T) == typeof(ushort))
|
||||
return UnsafeAsT(ConvertToUInt16(variant));
|
||||
|
||||
if (typeof(T) == typeof(uint))
|
||||
return UnsafeAsT(ConvertToUInt32(variant));
|
||||
|
||||
if (typeof(T) == typeof(ulong))
|
||||
return UnsafeAsT(ConvertToUInt64(variant));
|
||||
|
||||
if (typeof(T) == typeof(float))
|
||||
return UnsafeAsT(ConvertToFloat32(variant));
|
||||
|
||||
if (typeof(T) == typeof(double))
|
||||
return UnsafeAsT(ConvertToFloat64(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector2))
|
||||
return UnsafeAsT(ConvertToVector2(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector2I))
|
||||
return UnsafeAsT(ConvertToVector2I(variant));
|
||||
|
||||
if (typeof(T) == typeof(Rect2))
|
||||
return UnsafeAsT(ConvertToRect2(variant));
|
||||
|
||||
if (typeof(T) == typeof(Rect2I))
|
||||
return UnsafeAsT(ConvertToRect2I(variant));
|
||||
|
||||
if (typeof(T) == typeof(Transform2D))
|
||||
return UnsafeAsT(ConvertToTransform2D(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector3))
|
||||
return UnsafeAsT(ConvertToVector3(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector3I))
|
||||
return UnsafeAsT(ConvertToVector3I(variant));
|
||||
|
||||
if (typeof(T) == typeof(Basis))
|
||||
return UnsafeAsT(ConvertToBasis(variant));
|
||||
|
||||
if (typeof(T) == typeof(Quaternion))
|
||||
return UnsafeAsT(ConvertToQuaternion(variant));
|
||||
|
||||
if (typeof(T) == typeof(Transform3D))
|
||||
return UnsafeAsT(ConvertToTransform3D(variant));
|
||||
|
||||
if (typeof(T) == typeof(Projection))
|
||||
return UnsafeAsT(ConvertToProjection(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector4))
|
||||
return UnsafeAsT(ConvertToVector4(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector4I))
|
||||
return UnsafeAsT(ConvertToVector4I(variant));
|
||||
|
||||
if (typeof(T) == typeof(Aabb))
|
||||
return UnsafeAsT(ConvertToAabb(variant));
|
||||
|
||||
if (typeof(T) == typeof(Color))
|
||||
return UnsafeAsT(ConvertToColor(variant));
|
||||
|
||||
if (typeof(T) == typeof(Plane))
|
||||
return UnsafeAsT(ConvertToPlane(variant));
|
||||
|
||||
if (typeof(T) == typeof(Callable))
|
||||
return UnsafeAsT(ConvertToCallable(variant));
|
||||
|
||||
if (typeof(T) == typeof(Signal))
|
||||
return UnsafeAsT(ConvertToSignal(variant));
|
||||
|
||||
if (typeof(T) == typeof(string))
|
||||
return UnsafeAsT(ConvertToString(variant));
|
||||
|
||||
if (typeof(T) == typeof(byte[]))
|
||||
return UnsafeAsT(ConvertAsPackedByteArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(int[]))
|
||||
return UnsafeAsT(ConvertAsPackedInt32ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(long[]))
|
||||
return UnsafeAsT(ConvertAsPackedInt64ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(float[]))
|
||||
return UnsafeAsT(ConvertAsPackedFloat32ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(double[]))
|
||||
return UnsafeAsT(ConvertAsPackedFloat64ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(string[]))
|
||||
return UnsafeAsT(ConvertAsPackedStringArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector2[]))
|
||||
return UnsafeAsT(ConvertAsPackedVector2ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector3[]))
|
||||
return UnsafeAsT(ConvertAsPackedVector3ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector4[]))
|
||||
return UnsafeAsT(ConvertAsPackedVector4ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Color[]))
|
||||
return UnsafeAsT(ConvertAsPackedColorArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(StringName[]))
|
||||
return UnsafeAsT(ConvertToSystemArrayOfStringName(variant));
|
||||
|
||||
if (typeof(T) == typeof(NodePath[]))
|
||||
return UnsafeAsT(ConvertToSystemArrayOfNodePath(variant));
|
||||
|
||||
if (typeof(T) == typeof(Rid[]))
|
||||
return UnsafeAsT(ConvertToSystemArrayOfRid(variant));
|
||||
|
||||
if (typeof(T) == typeof(StringName))
|
||||
return UnsafeAsT(ConvertToStringName(variant));
|
||||
|
||||
if (typeof(T) == typeof(NodePath))
|
||||
return UnsafeAsT(ConvertToNodePath(variant));
|
||||
|
||||
if (typeof(T) == typeof(Rid))
|
||||
return UnsafeAsT(ConvertToRid(variant));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Dictionary))
|
||||
return UnsafeAsT(ConvertToDictionary(variant));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Array))
|
||||
return UnsafeAsT(ConvertToArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Variant))
|
||||
return UnsafeAsT(Variant.CreateCopyingBorrowed(variant));
|
||||
|
||||
// More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
|
||||
|
||||
// `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
|
||||
|
||||
if (typeof(GodotObject).IsAssignableFrom(typeof(T)))
|
||||
return (T)(object)ConvertToGodotObject(variant);
|
||||
|
||||
// `typeof(T).IsValueType` is optimized away
|
||||
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
|
||||
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
|
||||
|
||||
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
|
||||
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
|
||||
// We don't need to know whether it's signed or unsigned.
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 1)
|
||||
return UnsafeAsT(ConvertToInt8(variant));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 2)
|
||||
return UnsafeAsT(ConvertToInt16(variant));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 4)
|
||||
return UnsafeAsT(ConvertToInt32(variant));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 8)
|
||||
return UnsafeAsT(ConvertToInt64(variant));
|
||||
|
||||
throw UnsupportedType<T>();
|
||||
}
|
||||
|
||||
return GenericConversion<T>.FromVariant(variant);
|
||||
}
|
||||
}
|
||||
328
modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
Normal file
328
modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
Normal file
@@ -0,0 +1,328 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// A pre-parsed relative or absolute path in a scene tree,
|
||||
/// for use with <see cref="Node.GetNode(NodePath)"/> and similar functions.
|
||||
/// It can reference a node, a resource within a node, or a property
|
||||
/// of a node or resource.
|
||||
/// For instance, <c>"Path2D/PathFollow2D/Sprite2D:texture:size"</c>
|
||||
/// would refer to the <c>size</c> property of the <c>texture</c>
|
||||
/// resource on the node named <c>"Sprite2D"</c> which is a child of
|
||||
/// the other named nodes in the path.
|
||||
/// You will usually just pass a string to <see cref="Node.GetNode(NodePath)"/>
|
||||
/// and it will be automatically converted, but you may occasionally
|
||||
/// want to parse a path ahead of time with NodePath.
|
||||
/// Exporting a NodePath variable will give you a node selection widget
|
||||
/// in the properties panel of the editor, which can often be useful.
|
||||
/// A NodePath is composed of a list of slash-separated node names
|
||||
/// (like a filesystem path) and an optional colon-separated list of
|
||||
/// "subnames" which can be resources or properties.
|
||||
///
|
||||
/// Note: In the editor, NodePath properties are automatically updated when moving,
|
||||
/// renaming or deleting a node in the scene tree, but they are never updated at runtime.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Some examples of NodePaths include the following:
|
||||
/// <code>
|
||||
/// // No leading slash means it is relative to the current node.
|
||||
/// new NodePath("A"); // Immediate child A.
|
||||
/// new NodePath("A/B"); // A's child B.
|
||||
/// new NodePath("."); // The current node.
|
||||
/// new NodePath(".."); // The parent node.
|
||||
/// new NodePath("../C"); // A sibling node C.
|
||||
/// // A leading slash means it is absolute from the SceneTree.
|
||||
/// new NodePath("/root"); // Equivalent to GetTree().Root
|
||||
/// new NodePath("/root/Main"); // If your main scene's root node were named "Main".
|
||||
/// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene.
|
||||
/// </code>
|
||||
/// </example>
|
||||
public sealed class NodePath : IDisposable, IEquatable<NodePath?>
|
||||
{
|
||||
internal godot_node_path.movable NativeValue;
|
||||
|
||||
private WeakReference<IDisposable>? _weakReferenceToSelf;
|
||||
|
||||
~NodePath()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this <see cref="NodePath"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Dispose(bool disposing)
|
||||
{
|
||||
// Always dispose `NativeValue` even if disposing is true
|
||||
NativeValue.DangerousSelfRef.Dispose();
|
||||
|
||||
if (_weakReferenceToSelf != null)
|
||||
{
|
||||
DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
|
||||
}
|
||||
}
|
||||
|
||||
private NodePath(godot_node_path nativeValueToOwn)
|
||||
{
|
||||
NativeValue = (godot_node_path.movable)nativeValueToOwn;
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
|
||||
}
|
||||
|
||||
// Explicit name to make it very clear
|
||||
internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn)
|
||||
=> new NodePath(nativeValueToOwn);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an empty <see cref="NodePath"/>.
|
||||
/// </summary>
|
||||
public NodePath()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="NodePath"/> from a string <paramref name="path"/>,
|
||||
/// e.g.: <c>"Path2D/PathFollow2D/Sprite2D:texture:size"</c>.
|
||||
/// A path is absolute if it starts with a slash. Absolute paths
|
||||
/// are only valid in the global scene tree, not within individual
|
||||
/// scenes. In a relative path, <c>"."</c> and <c>".."</c> indicate
|
||||
/// the current node and its parent.
|
||||
/// The "subnames" optionally included after the path to the target
|
||||
/// node can point to resources or properties, and can also be nested.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Examples of valid NodePaths (assuming that those nodes exist and
|
||||
/// have the referenced resources or properties):
|
||||
/// <code>
|
||||
/// // Points to the Sprite2D node.
|
||||
/// "Path2D/PathFollow2D/Sprite2D"
|
||||
/// // Points to the Sprite2D node and its "texture" resource.
|
||||
/// // GetNode() would retrieve "Sprite2D", while GetNodeAndResource()
|
||||
/// // would retrieve both the Sprite2D node and the "texture" resource.
|
||||
/// "Path2D/PathFollow2D/Sprite2D:texture"
|
||||
/// // Points to the Sprite2D node and its "position" property.
|
||||
/// "Path2D/PathFollow2D/Sprite2D:position"
|
||||
/// // Points to the Sprite2D node and the "x" component of its "position" property.
|
||||
/// "Path2D/PathFollow2D/Sprite2D:position:x"
|
||||
/// // Absolute path (from "root")
|
||||
/// "/root/Level/Path2D"
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="path">A string that represents a path in a scene tree.</param>
|
||||
public NodePath(string path)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
NativeValue = (godot_node_path.movable)NativeFuncs.godotsharp_node_path_new_from_string(path);
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to a <see cref="NodePath"/>.
|
||||
/// </summary>
|
||||
/// <param name="from">The string to convert.</param>
|
||||
public static implicit operator NodePath(string from) => new NodePath(from);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="NodePath"/> to a string.
|
||||
/// </summary>
|
||||
/// <param name="from">The <see cref="NodePath"/> to convert.</param>
|
||||
[return: NotNullIfNotNull("from")]
|
||||
public static implicit operator string?(NodePath? from) => from?.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="NodePath"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this <see cref="NodePath"/>.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (IsEmpty)
|
||||
return string.Empty;
|
||||
|
||||
var src = (godot_node_path)NativeValue;
|
||||
NativeFuncs.godotsharp_node_path_as_string(out godot_string dest, src);
|
||||
using (dest)
|
||||
return Marshaling.ConvertStringToManaged(dest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a node path with a colon character (<c>:</c>) prepended,
|
||||
/// transforming it to a pure property path with no node name (defaults
|
||||
/// to resolving from the current node).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // This will be parsed as a node path to the "x" property in the "position" node.
|
||||
/// var nodePath = new NodePath("position:x");
|
||||
/// // This will be parsed as a node path to the "x" component of the "position" property in the current node.
|
||||
/// NodePath propertyPath = nodePath.GetAsPropertyPath();
|
||||
/// GD.Print(propertyPath); // :position:x
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>The <see cref="NodePath"/> as a pure property path.</returns>
|
||||
public NodePath GetAsPropertyPath()
|
||||
{
|
||||
godot_node_path propertyPath = default;
|
||||
var self = (godot_node_path)NativeValue;
|
||||
NativeFuncs.godotsharp_node_path_get_as_property_path(self, ref propertyPath);
|
||||
return CreateTakingOwnershipOfDisposableValue(propertyPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all names concatenated with a slash character (<c>/</c>).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path");
|
||||
/// GD.Print(nodepath.GetConcatenatedNames()); // Path2D/PathFollow2D/Sprite2D
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>The names concatenated with <c>/</c>.</returns>
|
||||
public string GetConcatenatedNames()
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
NativeFuncs.godotsharp_node_path_get_concatenated_names(self, out godot_string names);
|
||||
using (names)
|
||||
return Marshaling.ConvertStringToManaged(names);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all subnames concatenated with a colon character (<c>:</c>)
|
||||
/// as separator, i.e. the right side of the first colon in a node path.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path");
|
||||
/// GD.Print(nodepath.GetConcatenatedSubnames()); // texture:load_path
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>The subnames concatenated with <c>:</c>.</returns>
|
||||
public string GetConcatenatedSubNames()
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
NativeFuncs.godotsharp_node_path_get_concatenated_subnames(self, out godot_string subNames);
|
||||
using (subNames)
|
||||
return Marshaling.ConvertStringToManaged(subNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the node name indicated by <paramref name="idx"/> (0 to <see cref="GetNameCount"/>).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var nodePath = new NodePath("Path2D/PathFollow2D/Sprite2D");
|
||||
/// GD.Print(nodePath.GetName(0)); // Path2D
|
||||
/// GD.Print(nodePath.GetName(1)); // PathFollow2D
|
||||
/// GD.Print(nodePath.GetName(2)); // Sprite
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="idx">The name index.</param>
|
||||
/// <returns>The name at the given index <paramref name="idx"/>.</returns>
|
||||
public string GetName(int idx)
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
NativeFuncs.godotsharp_node_path_get_name(self, idx, out godot_string name);
|
||||
using (name)
|
||||
return Marshaling.ConvertStringToManaged(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of node names which make up the path.
|
||||
/// Subnames (see <see cref="GetSubNameCount"/>) are not included.
|
||||
/// For example, <c>"Path2D/PathFollow2D/Sprite2D"</c> has 3 names.
|
||||
/// </summary>
|
||||
/// <returns>The number of node names which make up the path.</returns>
|
||||
public int GetNameCount()
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
return NativeFuncs.godotsharp_node_path_get_name_count(self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubNameCount"/>).
|
||||
/// </summary>
|
||||
/// <param name="idx">The subname index.</param>
|
||||
/// <returns>The subname at the given index <paramref name="idx"/>.</returns>
|
||||
public string GetSubName(int idx)
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
NativeFuncs.godotsharp_node_path_get_subname(self, idx, out godot_string subName);
|
||||
using (subName)
|
||||
return Marshaling.ConvertStringToManaged(subName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of resource or property names ("subnames") in the path.
|
||||
/// Each subname is listed after a colon character (<c>:</c>) in the node path.
|
||||
/// For example, <c>"Path2D/PathFollow2D/Sprite2D:texture:load_path"</c> has 2 subnames.
|
||||
/// </summary>
|
||||
/// <returns>The number of subnames in the path.</returns>
|
||||
public int GetSubNameCount()
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
return NativeFuncs.godotsharp_node_path_get_subname_count(self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the node path is absolute (as opposed to relative),
|
||||
/// which means that it starts with a slash character (<c>/</c>). Absolute node paths can
|
||||
/// be used to access the root node (<c>"/root"</c>) or autoloads (e.g. <c>"/global"</c>
|
||||
/// if a "global" autoload was registered).
|
||||
/// </summary>
|
||||
/// <returns>If the <see cref="NodePath"/> is an absolute path.</returns>
|
||||
public bool IsAbsolute()
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
return NativeFuncs.godotsharp_node_path_is_absolute(self).ToBool();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the node path is empty.
|
||||
/// </summary>
|
||||
/// <returns>If the <see cref="NodePath"/> is empty.</returns>
|
||||
public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
|
||||
|
||||
public static bool operator ==(NodePath? left, NodePath? right)
|
||||
{
|
||||
if (left is null)
|
||||
return right is null;
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(NodePath? left, NodePath? right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public bool Equals([NotNullWhen(true)] NodePath? other)
|
||||
{
|
||||
if (other is null)
|
||||
return false;
|
||||
var self = (godot_node_path)NativeValue;
|
||||
var otherNative = (godot_node_path)other.NativeValue;
|
||||
return NativeFuncs.godotsharp_node_path_equals(self, otherNative).ToBool();
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return ReferenceEquals(this, obj) || (obj is NodePath other && Equals(other));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var self = (godot_node_path)NativeValue;
|
||||
return NativeFuncs.godotsharp_node_path_hash(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
439
modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
Normal file
439
modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
Normal file
@@ -0,0 +1,439 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Plane represents a normalized plane equation.
|
||||
/// "Over" or "Above" the plane is considered the side of
|
||||
/// the plane towards where the normal is pointing.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Plane : IEquatable<Plane>
|
||||
{
|
||||
private Vector3 _normal;
|
||||
private real_t _d;
|
||||
|
||||
/// <summary>
|
||||
/// The normal of the plane, which must be a unit vector.
|
||||
/// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is
|
||||
/// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <see cref="X"/>, <see cref="Y"/>, and <see cref="Z"/>.</value>
|
||||
public Vector3 Normal
|
||||
{
|
||||
readonly get { return _normal; }
|
||||
set { _normal = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The distance from the origin to the plane (in the direction of
|
||||
/// <see cref="Normal"/>). This value is typically non-negative.
|
||||
/// In the scalar equation of the plane <c>ax + by + cz = d</c>,
|
||||
/// this is <c>d</c>, while the <c>(a, b, c)</c> coordinates are represented
|
||||
/// by the <see cref="Normal"/> property.
|
||||
/// </summary>
|
||||
/// <value>The plane's distance from the origin.</value>
|
||||
public real_t D
|
||||
{
|
||||
readonly get { return _d; }
|
||||
set { _d = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The X component of the plane's normal vector.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <see cref="Normal"/>'s X value.</value>
|
||||
public real_t X
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return _normal.X;
|
||||
}
|
||||
set
|
||||
{
|
||||
_normal.X = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Y component of the plane's normal vector.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <see cref="Normal"/>'s Y value.</value>
|
||||
public real_t Y
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return _normal.Y;
|
||||
}
|
||||
set
|
||||
{
|
||||
_normal.Y = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Z component of the plane's normal vector.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <see cref="Normal"/>'s Z value.</value>
|
||||
public real_t Z
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return _normal.Z;
|
||||
}
|
||||
set
|
||||
{
|
||||
_normal.Z = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the shortest distance from this plane to the position <paramref name="point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point">The position to use for the calculation.</param>
|
||||
/// <returns>The shortest distance.</returns>
|
||||
public readonly real_t DistanceTo(Vector3 point)
|
||||
{
|
||||
return _normal.Dot(point) - _d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the center of the plane, the point on the plane closest to the origin.
|
||||
/// The point where the normal line going through the origin intersects the plane.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
|
||||
public readonly Vector3 GetCenter()
|
||||
{
|
||||
return _normal * _d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if point is inside the plane.
|
||||
/// Comparison uses a custom minimum tolerance threshold.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to check.</param>
|
||||
/// <param name="tolerance">The tolerance threshold.</param>
|
||||
/// <returns>A <see langword="bool"/> for whether or not the plane has the point.</returns>
|
||||
public readonly bool HasPoint(Vector3 point, real_t tolerance = Mathf.Epsilon)
|
||||
{
|
||||
real_t dist = _normal.Dot(point) - _d;
|
||||
return Mathf.Abs(dist) <= tolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the intersection point of the three planes: <paramref name="b"/>, <paramref name="c"/>,
|
||||
/// and this plane. If no intersection is found, <see langword="null"/> is returned.
|
||||
/// </summary>
|
||||
/// <param name="b">One of the three planes to use in the calculation.</param>
|
||||
/// <param name="c">One of the three planes to use in the calculation.</param>
|
||||
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
|
||||
public readonly Vector3? Intersect3(Plane b, Plane c)
|
||||
{
|
||||
real_t denom = _normal.Cross(b._normal).Dot(c._normal);
|
||||
|
||||
if (Mathf.IsZeroApprox(denom))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector3 result = (b._normal.Cross(c._normal) * _d) +
|
||||
(c._normal.Cross(_normal) * b._d) +
|
||||
(_normal.Cross(b._normal) * c._d);
|
||||
|
||||
return result / denom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the intersection point of a ray consisting of the position <paramref name="from"/>
|
||||
/// and the direction normal <paramref name="dir"/> with this plane.
|
||||
/// If no intersection is found, <see langword="null"/> is returned.
|
||||
/// </summary>
|
||||
/// <param name="from">The start of the ray.</param>
|
||||
/// <param name="dir">The direction of the ray, normalized.</param>
|
||||
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
|
||||
public readonly Vector3? IntersectsRay(Vector3 from, Vector3 dir)
|
||||
{
|
||||
real_t den = _normal.Dot(dir);
|
||||
|
||||
if (Mathf.IsZeroApprox(den))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
real_t dist = (_normal.Dot(from) - _d) / den;
|
||||
|
||||
// This is a ray, before the emitting pos (from) does not exist
|
||||
if (dist > Mathf.Epsilon)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return from - (dir * dist);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the intersection point of a line segment from
|
||||
/// position <paramref name="begin"/> to position <paramref name="end"/> with this plane.
|
||||
/// If no intersection is found, <see langword="null"/> is returned.
|
||||
/// </summary>
|
||||
/// <param name="begin">The start of the line segment.</param>
|
||||
/// <param name="end">The end of the line segment.</param>
|
||||
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
|
||||
public readonly Vector3? IntersectsSegment(Vector3 begin, Vector3 end)
|
||||
{
|
||||
Vector3 segment = begin - end;
|
||||
real_t den = _normal.Dot(segment);
|
||||
|
||||
if (Mathf.IsZeroApprox(den))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
real_t dist = (_normal.Dot(begin) - _d) / den;
|
||||
|
||||
// Only allow dist to be in the range of 0 to 1, with tolerance.
|
||||
if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return begin - (segment * dist);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this plane is finite, by calling
|
||||
/// <see cref="Mathf.IsFinite(real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>Whether this vector is finite or not.</returns>
|
||||
public readonly bool IsFinite()
|
||||
{
|
||||
return _normal.IsFinite() && Mathf.IsFinite(D);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to check.</param>
|
||||
/// <returns>A <see langword="bool"/> for whether or not the point is above the plane.</returns>
|
||||
public readonly bool IsPointOver(Vector3 point)
|
||||
{
|
||||
return _normal.Dot(point) > _d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the plane scaled to unit length.
|
||||
/// </summary>
|
||||
/// <returns>A normalized version of the plane.</returns>
|
||||
public readonly Plane Normalized()
|
||||
{
|
||||
real_t len = _normal.Length();
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
return new Plane(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
return new Plane(_normal / len, _d / len);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the orthogonal projection of <paramref name="point"/> into the plane.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to project.</param>
|
||||
/// <returns>The projected point.</returns>
|
||||
public readonly Vector3 Project(Vector3 point)
|
||||
{
|
||||
return point - (_normal * DistanceTo(point));
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0);
|
||||
private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0);
|
||||
private static readonly Plane _planeXY = new Plane(0, 0, 1, 0);
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="Plane"/> that extends in the Y and Z axes (normal vector points +X).
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Plane(1, 0, 0, 0)</c>.</value>
|
||||
public static Plane PlaneYZ { get { return _planeYZ; } }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="Plane"/> that extends in the X and Z axes (normal vector points +Y).
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Plane(0, 1, 0, 0)</c>.</value>
|
||||
public static Plane PlaneXZ { get { return _planeXZ; } }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="Plane"/> that extends in the X and Y axes (normal vector points +Z).
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Plane(0, 0, 1, 0)</c>.</value>
|
||||
public static Plane PlaneXY { get { return _planeXY; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Plane"/> from four values.
|
||||
/// <paramref name="a"/>, <paramref name="b"/> and <paramref name="c"/> become the
|
||||
/// components of the resulting plane's <see cref="Normal"/> vector.
|
||||
/// <paramref name="d"/> becomes the plane's distance from the origin.
|
||||
/// </summary>
|
||||
/// <param name="a">The X component of the plane's normal vector.</param>
|
||||
/// <param name="b">The Y component of the plane's normal vector.</param>
|
||||
/// <param name="c">The Z component of the plane's normal vector.</param>
|
||||
/// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
|
||||
public Plane(real_t a, real_t b, real_t c, real_t d)
|
||||
{
|
||||
_normal = new Vector3(a, b, c);
|
||||
_d = d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector.
|
||||
/// The plane will intersect the origin.
|
||||
/// </summary>
|
||||
/// <param name="normal">The normal of the plane, must be a unit vector.</param>
|
||||
public Plane(Vector3 normal)
|
||||
{
|
||||
_normal = normal;
|
||||
_d = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
|
||||
/// the plane's distance to the origin <paramref name="d"/>.
|
||||
/// </summary>
|
||||
/// <param name="normal">The normal of the plane, must be a unit vector.</param>
|
||||
/// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
|
||||
public Plane(Vector3 normal, real_t d)
|
||||
{
|
||||
_normal = normal;
|
||||
_d = d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
|
||||
/// a <paramref name="point"/> on the plane.
|
||||
/// </summary>
|
||||
/// <param name="normal">The normal of the plane, must be a unit vector.</param>
|
||||
/// <param name="point">The point on the plane.</param>
|
||||
public Plane(Vector3 normal, Vector3 point)
|
||||
{
|
||||
_normal = normal;
|
||||
_d = _normal.Dot(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Plane"/> from the three points, given in clockwise order.
|
||||
/// </summary>
|
||||
/// <param name="v1">The first point.</param>
|
||||
/// <param name="v2">The second point.</param>
|
||||
/// <param name="v3">The third point.</param>
|
||||
public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
|
||||
{
|
||||
_normal = (v1 - v3).Cross(v1 - v2);
|
||||
_normal.Normalize();
|
||||
_d = _normal.Dot(v1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the negative value of the <see cref="Plane"/>.
|
||||
/// This is the same as writing <c>new Plane(-p.Normal, -p.D)</c>.
|
||||
/// This operation flips the direction of the normal vector and
|
||||
/// also flips the distance value, resulting in a Plane that is
|
||||
/// in the same place, but facing the opposite direction.
|
||||
/// </summary>
|
||||
/// <param name="plane">The plane to negate/flip.</param>
|
||||
/// <returns>The negated/flipped plane.</returns>
|
||||
public static Plane operator -(Plane plane)
|
||||
{
|
||||
return new Plane(-plane._normal, -plane._d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the
|
||||
/// <see cref="Plane"/>s are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left rect.</param>
|
||||
/// <param name="right">The right rect.</param>
|
||||
/// <returns>Whether or not the planes are exactly equal.</returns>
|
||||
public static bool operator ==(Plane left, Plane right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the
|
||||
/// <see cref="Plane"/>s are not equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left rect.</param>
|
||||
/// <param name="right">The right rect.</param>
|
||||
/// <returns>Whether or not the planes are not equal.</returns>
|
||||
public static bool operator !=(Plane left, Plane right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this plane and <paramref name="obj"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to compare.</param>
|
||||
/// <returns>Whether or not the plane and the other object are exactly equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Plane other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this plane and <paramref name="other"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other plane to compare.</param>
|
||||
/// <returns>Whether or not the planes are exactly equal.</returns>
|
||||
public readonly bool Equals(Plane other)
|
||||
{
|
||||
return _normal == other._normal && _d == other._d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this plane and <paramref name="other"/> are
|
||||
/// approximately equal, by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <param name="other">The other plane to compare.</param>
|
||||
/// <returns>Whether or not the planes are approximately equal.</returns>
|
||||
public readonly bool IsEqualApprox(Plane other)
|
||||
{
|
||||
return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(_d, other._d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Plane"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this plane.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_normal, _d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Plane"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this plane.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Plane"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this plane.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"{_normal.ToString(format)}, {_d.ToString(format, CultureInfo.InvariantCulture)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
1057
modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
Normal file
1057
modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
Normal file
File diff suppressed because it is too large
Load Diff
848
modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
Normal file
848
modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
Normal file
@@ -0,0 +1,848 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// A unit quaternion used for representing 3D rotations.
|
||||
/// Quaternions need to be normalized to be used for rotation.
|
||||
///
|
||||
/// It is similar to <see cref="Basis"/>, which implements matrix
|
||||
/// representation of rotations, and can be parametrized using both
|
||||
/// an axis-angle pair or Euler angles. Basis stores rotation, scale,
|
||||
/// and shearing, while Quaternion only stores rotation.
|
||||
///
|
||||
/// Due to its compactness and the way it is stored in memory, certain
|
||||
/// operations (obtaining axis-angle and performing SLERP, in particular)
|
||||
/// are more efficient and robust against floating-point errors.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Quaternion : IEquatable<Quaternion>
|
||||
{
|
||||
/// <summary>
|
||||
/// X component of the quaternion (imaginary <c>i</c> axis part).
|
||||
/// Quaternion components should usually not be manipulated directly.
|
||||
/// </summary>
|
||||
public real_t X;
|
||||
|
||||
/// <summary>
|
||||
/// Y component of the quaternion (imaginary <c>j</c> axis part).
|
||||
/// Quaternion components should usually not be manipulated directly.
|
||||
/// </summary>
|
||||
public real_t Y;
|
||||
|
||||
/// <summary>
|
||||
/// Z component of the quaternion (imaginary <c>k</c> axis part).
|
||||
/// Quaternion components should usually not be manipulated directly.
|
||||
/// </summary>
|
||||
public real_t Z;
|
||||
|
||||
/// <summary>
|
||||
/// W component of the quaternion (real part).
|
||||
/// Quaternion components should usually not be manipulated directly.
|
||||
/// </summary>
|
||||
public real_t W;
|
||||
|
||||
/// <summary>
|
||||
/// Access quaternion components using their index.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="index"/> is not 0, 1, 2 or 3.
|
||||
/// </exception>
|
||||
/// <value>
|
||||
/// <c>[0]</c> is equivalent to <see cref="X"/>,
|
||||
/// <c>[1]</c> is equivalent to <see cref="Y"/>,
|
||||
/// <c>[2]</c> is equivalent to <see cref="Z"/>,
|
||||
/// <c>[3]</c> is equivalent to <see cref="W"/>.
|
||||
/// </value>
|
||||
public real_t this[int index]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return X;
|
||||
case 1:
|
||||
return Y;
|
||||
case 2:
|
||||
return Z;
|
||||
case 3:
|
||||
return W;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
X = value;
|
||||
break;
|
||||
case 1:
|
||||
Y = value;
|
||||
break;
|
||||
case 2:
|
||||
Z = value;
|
||||
break;
|
||||
case 3:
|
||||
W = value;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle between this quaternion and <paramref name="to"/>.
|
||||
/// This is the magnitude of the angle you would need to rotate
|
||||
/// by to get from one to the other.
|
||||
///
|
||||
/// Note: This method has an abnormally high amount
|
||||
/// of floating-point error, so methods such as
|
||||
/// <see cref="Mathf.IsZeroApprox(real_t)"/> will not work reliably.
|
||||
/// </summary>
|
||||
/// <param name="to">The other quaternion.</param>
|
||||
/// <returns>The angle between the quaternions.</returns>
|
||||
public readonly real_t AngleTo(Quaternion to)
|
||||
{
|
||||
real_t dot = Dot(to);
|
||||
return Mathf.Acos(Mathf.Clamp(dot * dot * 2 - 1, -1, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a spherical cubic interpolation between quaternions <paramref name="preA"/>, this quaternion,
|
||||
/// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
|
||||
/// </summary>
|
||||
/// <param name="b">The destination quaternion.</param>
|
||||
/// <param name="preA">A quaternion before this quaternion.</param>
|
||||
/// <param name="postB">A quaternion after <paramref name="b"/>.</param>
|
||||
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
|
||||
/// <returns>The interpolated quaternion.</returns>
|
||||
public readonly Quaternion SphericalCubicInterpolate(Quaternion b, Quaternion preA, Quaternion postB, real_t weight)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!IsNormalized())
|
||||
{
|
||||
throw new InvalidOperationException("Quaternion is not normalized");
|
||||
}
|
||||
if (!b.IsNormalized())
|
||||
{
|
||||
throw new ArgumentException("Argument is not normalized", nameof(b));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Align flip phases.
|
||||
Quaternion fromQ = new Basis(this).GetRotationQuaternion();
|
||||
Quaternion preQ = new Basis(preA).GetRotationQuaternion();
|
||||
Quaternion toQ = new Basis(b).GetRotationQuaternion();
|
||||
Quaternion postQ = new Basis(postB).GetRotationQuaternion();
|
||||
|
||||
// Flip quaternions to shortest path if necessary.
|
||||
bool flip1 = Math.Sign(fromQ.Dot(preQ)) < 0;
|
||||
preQ = flip1 ? -preQ : preQ;
|
||||
bool flip2 = Math.Sign(fromQ.Dot(toQ)) < 0;
|
||||
toQ = flip2 ? -toQ : toQ;
|
||||
bool flip3 = flip2 ? toQ.Dot(postQ) <= 0 : Math.Sign(toQ.Dot(postQ)) < 0;
|
||||
postQ = flip3 ? -postQ : postQ;
|
||||
|
||||
// Calc by Expmap in fromQ space.
|
||||
Quaternion lnFrom = new Quaternion(0, 0, 0, 0);
|
||||
Quaternion lnTo = (fromQ.Inverse() * toQ).Log();
|
||||
Quaternion lnPre = (fromQ.Inverse() * preQ).Log();
|
||||
Quaternion lnPost = (fromQ.Inverse() * postQ).Log();
|
||||
Quaternion ln = new Quaternion(
|
||||
Mathf.CubicInterpolate(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight),
|
||||
Mathf.CubicInterpolate(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight),
|
||||
Mathf.CubicInterpolate(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight),
|
||||
0);
|
||||
Quaternion q1 = fromQ * ln.Exp();
|
||||
|
||||
// Calc by Expmap in toQ space.
|
||||
lnFrom = (toQ.Inverse() * fromQ).Log();
|
||||
lnTo = new Quaternion(0, 0, 0, 0);
|
||||
lnPre = (toQ.Inverse() * preQ).Log();
|
||||
lnPost = (toQ.Inverse() * postQ).Log();
|
||||
ln = new Quaternion(
|
||||
Mathf.CubicInterpolate(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight),
|
||||
Mathf.CubicInterpolate(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight),
|
||||
Mathf.CubicInterpolate(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight),
|
||||
0);
|
||||
Quaternion q2 = toQ * ln.Exp();
|
||||
|
||||
// To cancel error made by Expmap ambiguity, do blending.
|
||||
return q1.Slerp(q2, weight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a spherical cubic interpolation between quaternions <paramref name="preA"/>, this quaternion,
|
||||
/// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
|
||||
/// It can perform smoother interpolation than <see cref="SphericalCubicInterpolate"/>
|
||||
/// by the time values.
|
||||
/// </summary>
|
||||
/// <param name="b">The destination quaternion.</param>
|
||||
/// <param name="preA">A quaternion before this quaternion.</param>
|
||||
/// <param name="postB">A quaternion after <paramref name="b"/>.</param>
|
||||
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
|
||||
/// <param name="bT"></param>
|
||||
/// <param name="preAT"></param>
|
||||
/// <param name="postBT"></param>
|
||||
/// <returns>The interpolated quaternion.</returns>
|
||||
public readonly Quaternion SphericalCubicInterpolateInTime(Quaternion b, Quaternion preA, Quaternion postB, real_t weight, real_t bT, real_t preAT, real_t postBT)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!IsNormalized())
|
||||
{
|
||||
throw new InvalidOperationException("Quaternion is not normalized");
|
||||
}
|
||||
if (!b.IsNormalized())
|
||||
{
|
||||
throw new ArgumentException("Argument is not normalized", nameof(b));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Align flip phases.
|
||||
Quaternion fromQ = new Basis(this).GetRotationQuaternion();
|
||||
Quaternion preQ = new Basis(preA).GetRotationQuaternion();
|
||||
Quaternion toQ = new Basis(b).GetRotationQuaternion();
|
||||
Quaternion postQ = new Basis(postB).GetRotationQuaternion();
|
||||
|
||||
// Flip quaternions to shortest path if necessary.
|
||||
bool flip1 = Math.Sign(fromQ.Dot(preQ)) < 0;
|
||||
preQ = flip1 ? -preQ : preQ;
|
||||
bool flip2 = Math.Sign(fromQ.Dot(toQ)) < 0;
|
||||
toQ = flip2 ? -toQ : toQ;
|
||||
bool flip3 = flip2 ? toQ.Dot(postQ) <= 0 : Math.Sign(toQ.Dot(postQ)) < 0;
|
||||
postQ = flip3 ? -postQ : postQ;
|
||||
|
||||
// Calc by Expmap in fromQ space.
|
||||
Quaternion lnFrom = new Quaternion(0, 0, 0, 0);
|
||||
Quaternion lnTo = (fromQ.Inverse() * toQ).Log();
|
||||
Quaternion lnPre = (fromQ.Inverse() * preQ).Log();
|
||||
Quaternion lnPost = (fromQ.Inverse() * postQ).Log();
|
||||
Quaternion ln = new Quaternion(
|
||||
Mathf.CubicInterpolateInTime(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight, bT, preAT, postBT),
|
||||
Mathf.CubicInterpolateInTime(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight, bT, preAT, postBT),
|
||||
Mathf.CubicInterpolateInTime(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight, bT, preAT, postBT),
|
||||
0);
|
||||
Quaternion q1 = fromQ * ln.Exp();
|
||||
|
||||
// Calc by Expmap in toQ space.
|
||||
lnFrom = (toQ.Inverse() * fromQ).Log();
|
||||
lnTo = new Quaternion(0, 0, 0, 0);
|
||||
lnPre = (toQ.Inverse() * preQ).Log();
|
||||
lnPost = (toQ.Inverse() * postQ).Log();
|
||||
ln = new Quaternion(
|
||||
Mathf.CubicInterpolateInTime(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight, bT, preAT, postBT),
|
||||
Mathf.CubicInterpolateInTime(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight, bT, preAT, postBT),
|
||||
Mathf.CubicInterpolateInTime(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight, bT, preAT, postBT),
|
||||
0);
|
||||
Quaternion q2 = toQ * ln.Exp();
|
||||
|
||||
// To cancel error made by Expmap ambiguity, do blending.
|
||||
return q1.Slerp(q2, weight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the dot product of two quaternions.
|
||||
/// </summary>
|
||||
/// <param name="b">The other quaternion.</param>
|
||||
/// <returns>The dot product.</returns>
|
||||
public readonly real_t Dot(Quaternion b)
|
||||
{
|
||||
return (X * b.X) + (Y * b.Y) + (Z * b.Z) + (W * b.W);
|
||||
}
|
||||
|
||||
public readonly Quaternion Exp()
|
||||
{
|
||||
Vector3 v = new Vector3(X, Y, Z);
|
||||
real_t theta = v.Length();
|
||||
v = v.Normalized();
|
||||
if (theta < Mathf.Epsilon || !v.IsNormalized())
|
||||
{
|
||||
return new Quaternion(0, 0, 0, 1);
|
||||
}
|
||||
return new Quaternion(v, theta);
|
||||
}
|
||||
|
||||
public readonly real_t GetAngle()
|
||||
{
|
||||
return 2 * Mathf.Acos(W);
|
||||
}
|
||||
|
||||
public readonly Vector3 GetAxis()
|
||||
{
|
||||
if (Mathf.Abs(W) > 1 - Mathf.Epsilon)
|
||||
{
|
||||
return new Vector3(X, Y, Z);
|
||||
}
|
||||
|
||||
real_t r = 1 / Mathf.Sqrt(1 - W * W);
|
||||
return new Vector3(X * r, Y * r, Z * r);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Euler angles (in the YXZ convention: when decomposing,
|
||||
/// first Z, then X, and Y last) corresponding to the rotation
|
||||
/// represented by the unit quaternion. Returned vector contains
|
||||
/// the rotation angles in the format (X angle, Y angle, Z angle).
|
||||
/// </summary>
|
||||
/// <returns>The Euler angle representation of this quaternion.</returns>
|
||||
public readonly Vector3 GetEuler(EulerOrder order = EulerOrder.Yxz)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!IsNormalized())
|
||||
{
|
||||
throw new InvalidOperationException("Quaternion is not normalized.");
|
||||
}
|
||||
#endif
|
||||
var basis = new Basis(this);
|
||||
return basis.GetEuler(order);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inverse of the quaternion.
|
||||
/// </summary>
|
||||
/// <returns>The inverse quaternion.</returns>
|
||||
public readonly Quaternion Inverse()
|
||||
{
|
||||
#if DEBUG
|
||||
if (!IsNormalized())
|
||||
{
|
||||
throw new InvalidOperationException("Quaternion is not normalized.");
|
||||
}
|
||||
#endif
|
||||
return new Quaternion(-X, -Y, -Z, W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this quaternion is finite, by calling
|
||||
/// <see cref="Mathf.IsFinite(real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>Whether this vector is finite or not.</returns>
|
||||
public readonly bool IsFinite()
|
||||
{
|
||||
return Mathf.IsFinite(X) && Mathf.IsFinite(Y) && Mathf.IsFinite(Z) && Mathf.IsFinite(W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the quaternion is normalized or not.
|
||||
/// </summary>
|
||||
/// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns>
|
||||
public readonly bool IsNormalized()
|
||||
{
|
||||
return Mathf.Abs(LengthSquared() - 1) <= Mathf.Epsilon;
|
||||
}
|
||||
|
||||
public readonly Quaternion Log()
|
||||
{
|
||||
Vector3 v = GetAxis() * GetAngle();
|
||||
return new Quaternion(v.X, v.Y, v.Z, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length (magnitude) of the quaternion.
|
||||
/// </summary>
|
||||
/// <seealso cref="LengthSquared"/>
|
||||
/// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
|
||||
public readonly real_t Length()
|
||||
{
|
||||
return Mathf.Sqrt(LengthSquared());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length (squared magnitude) of the quaternion.
|
||||
/// This method runs faster than <see cref="Length"/>, so prefer it if
|
||||
/// you need to compare quaternions or need the squared length for some formula.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>Dot(this)</c>.</value>
|
||||
public readonly real_t LengthSquared()
|
||||
{
|
||||
return Dot(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the quaternion, normalized to unit length.
|
||||
/// </summary>
|
||||
/// <returns>The normalized quaternion.</returns>
|
||||
public readonly Quaternion Normalized()
|
||||
{
|
||||
return this / Length();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the spherical linear interpolation between
|
||||
/// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>.
|
||||
///
|
||||
/// Note: Both quaternions must be normalized.
|
||||
/// </summary>
|
||||
/// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
|
||||
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
|
||||
/// <returns>The resulting quaternion of the interpolation.</returns>
|
||||
public readonly Quaternion Slerp(Quaternion to, real_t weight)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!IsNormalized())
|
||||
{
|
||||
throw new InvalidOperationException("Quaternion is not normalized.");
|
||||
}
|
||||
if (!to.IsNormalized())
|
||||
{
|
||||
throw new ArgumentException("Argument is not normalized.", nameof(to));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Calculate cosine.
|
||||
real_t cosom = Dot(to);
|
||||
|
||||
var to1 = new Quaternion();
|
||||
|
||||
// Adjust signs if necessary.
|
||||
if (cosom < 0.0)
|
||||
{
|
||||
cosom = -cosom;
|
||||
to1 = -to;
|
||||
}
|
||||
else
|
||||
{
|
||||
to1 = to;
|
||||
}
|
||||
|
||||
real_t sinom, scale0, scale1;
|
||||
|
||||
// Calculate coefficients.
|
||||
if (1.0 - cosom > Mathf.Epsilon)
|
||||
{
|
||||
// Standard case (Slerp).
|
||||
real_t omega = Mathf.Acos(cosom);
|
||||
sinom = Mathf.Sin(omega);
|
||||
scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom;
|
||||
scale1 = Mathf.Sin(weight * omega) / sinom;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Quaternions are very close so we can do a linear interpolation.
|
||||
scale0 = 1.0f - weight;
|
||||
scale1 = weight;
|
||||
}
|
||||
|
||||
// Calculate final values.
|
||||
return new Quaternion
|
||||
(
|
||||
(scale0 * X) + (scale1 * to1.X),
|
||||
(scale0 * Y) + (scale1 * to1.Y),
|
||||
(scale0 * Z) + (scale1 * to1.Z),
|
||||
(scale0 * W) + (scale1 * to1.W)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the spherical linear interpolation between
|
||||
/// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>, but without
|
||||
/// checking if the rotation path is not bigger than 90 degrees.
|
||||
/// </summary>
|
||||
/// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
|
||||
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
|
||||
/// <returns>The resulting quaternion of the interpolation.</returns>
|
||||
public readonly Quaternion Slerpni(Quaternion to, real_t weight)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!IsNormalized())
|
||||
{
|
||||
throw new InvalidOperationException("Quaternion is not normalized");
|
||||
}
|
||||
if (!to.IsNormalized())
|
||||
{
|
||||
throw new ArgumentException("Argument is not normalized", nameof(to));
|
||||
}
|
||||
#endif
|
||||
|
||||
real_t dot = Dot(to);
|
||||
|
||||
if (Mathf.Abs(dot) > 0.9999f)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
real_t theta = Mathf.Acos(dot);
|
||||
real_t sinT = 1.0f / Mathf.Sin(theta);
|
||||
real_t newFactor = Mathf.Sin(weight * theta) * sinT;
|
||||
real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
|
||||
|
||||
return new Quaternion
|
||||
(
|
||||
(invFactor * X) + (newFactor * to.X),
|
||||
(invFactor * Y) + (newFactor * to.Y),
|
||||
(invFactor * Z) + (newFactor * to.Z),
|
||||
(invFactor * W) + (newFactor * to.W)
|
||||
);
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static readonly Quaternion _identity = new Quaternion(0, 0, 0, 1);
|
||||
|
||||
/// <summary>
|
||||
/// The identity quaternion, representing no rotation.
|
||||
/// Equivalent to an identity <see cref="Basis"/> matrix. If a vector is transformed by
|
||||
/// an identity quaternion, it will not change.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Quaternion(0, 0, 0, 1)</c>.</value>
|
||||
public static Quaternion Identity { get { return _identity; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Quaternion"/> defined by the given values.
|
||||
/// </summary>
|
||||
/// <param name="x">X component of the quaternion (imaginary <c>i</c> axis part).</param>
|
||||
/// <param name="y">Y component of the quaternion (imaginary <c>j</c> axis part).</param>
|
||||
/// <param name="z">Z component of the quaternion (imaginary <c>k</c> axis part).</param>
|
||||
/// <param name="w">W component of the quaternion (real part).</param>
|
||||
public Quaternion(real_t x, real_t y, real_t z, real_t w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Quaternion"/> from the given <see cref="Basis"/>.
|
||||
/// </summary>
|
||||
/// <param name="basis">The <see cref="Basis"/> to construct from.</param>
|
||||
public Quaternion(Basis basis)
|
||||
{
|
||||
this = basis.GetQuaternion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Quaternion"/> that will rotate around the given axis
|
||||
/// by the specified angle. The axis must be a normalized vector.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
|
||||
/// <param name="angle">The angle to rotate, in radians.</param>
|
||||
public Quaternion(Vector3 axis, real_t angle)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!axis.IsNormalized())
|
||||
{
|
||||
throw new ArgumentException("Argument is not normalized.", nameof(axis));
|
||||
}
|
||||
#endif
|
||||
|
||||
real_t d = axis.Length();
|
||||
|
||||
if (d == 0f)
|
||||
{
|
||||
X = 0f;
|
||||
Y = 0f;
|
||||
Z = 0f;
|
||||
W = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
(real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f);
|
||||
real_t s = sin / d;
|
||||
|
||||
X = axis.X * s;
|
||||
Y = axis.Y * s;
|
||||
Z = axis.Z * s;
|
||||
W = cos;
|
||||
}
|
||||
}
|
||||
|
||||
public Quaternion(Vector3 arcFrom, Vector3 arcTo)
|
||||
{
|
||||
#if DEBUG
|
||||
if (arcFrom.IsZeroApprox() || arcTo.IsZeroApprox())
|
||||
{
|
||||
throw new ArgumentException("The vectors must not be zero.");
|
||||
}
|
||||
#endif
|
||||
#if REAL_T_IS_DOUBLE
|
||||
const real_t AlmostOne = 0.999999999999999;
|
||||
#else
|
||||
const real_t AlmostOne = 0.99999975f;
|
||||
#endif
|
||||
Vector3 n0 = arcFrom.Normalized();
|
||||
Vector3 n1 = arcTo.Normalized();
|
||||
real_t d = n0.Dot(n1);
|
||||
if (Mathf.Abs(d) > AlmostOne)
|
||||
{
|
||||
if (d >= 0.0f)
|
||||
{
|
||||
X = 0.0f;
|
||||
Y = 0.0f;
|
||||
Z = 0.0f;
|
||||
W = 1.0f;
|
||||
return; // Vectors are same.
|
||||
}
|
||||
Vector3 axis = n0.GetAnyPerpendicular();
|
||||
X = axis.X;
|
||||
Y = axis.Y;
|
||||
Z = axis.Z;
|
||||
W = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 c = n0.Cross(n1);
|
||||
real_t s = Mathf.Sqrt((1.0f + d) * 2.0f);
|
||||
real_t rs = 1.0f / s;
|
||||
|
||||
X = c.X * rs;
|
||||
Y = c.Y * rs;
|
||||
Z = c.Z * rs;
|
||||
W = s * 0.5f;
|
||||
}
|
||||
this = Normalized();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by
|
||||
/// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last),
|
||||
/// given in the vector format as (X angle, Y angle, Z angle).
|
||||
/// </summary>
|
||||
/// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param>
|
||||
public static Quaternion FromEuler(Vector3 eulerYXZ)
|
||||
{
|
||||
real_t halfA1 = eulerYXZ.Y * 0.5f;
|
||||
real_t halfA2 = eulerYXZ.X * 0.5f;
|
||||
real_t halfA3 = eulerYXZ.Z * 0.5f;
|
||||
|
||||
// R = Y(a1).X(a2).Z(a3) convention for Euler angles.
|
||||
// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
|
||||
// a3 is the angle of the first rotation, following the notation in this reference.
|
||||
|
||||
(real_t sinA1, real_t cosA1) = Mathf.SinCos(halfA1);
|
||||
(real_t sinA2, real_t cosA2) = Mathf.SinCos(halfA2);
|
||||
(real_t sinA3, real_t cosA3) = Mathf.SinCos(halfA3);
|
||||
|
||||
return new Quaternion(
|
||||
(sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3),
|
||||
(sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3),
|
||||
(cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3),
|
||||
(sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Composes these two quaternions by multiplying them together.
|
||||
/// This has the effect of rotating the second quaternion
|
||||
/// (the child) by the first quaternion (the parent).
|
||||
/// </summary>
|
||||
/// <param name="left">The parent quaternion.</param>
|
||||
/// <param name="right">The child quaternion.</param>
|
||||
/// <returns>The composed quaternion.</returns>
|
||||
public static Quaternion operator *(Quaternion left, Quaternion right)
|
||||
{
|
||||
return new Quaternion
|
||||
(
|
||||
(left.W * right.X) + (left.X * right.W) + (left.Y * right.Z) - (left.Z * right.Y),
|
||||
(left.W * right.Y) + (left.Y * right.W) + (left.Z * right.X) - (left.X * right.Z),
|
||||
(left.W * right.Z) + (left.Z * right.W) + (left.X * right.Y) - (left.Y * right.X),
|
||||
(left.W * right.W) - (left.X * right.X) - (left.Y * right.Y) - (left.Z * right.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector3 rotated (multiplied) by the quaternion.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to rotate by.</param>
|
||||
/// <param name="vector">A Vector3 to transform.</param>
|
||||
/// <returns>The rotated Vector3.</returns>
|
||||
public static Vector3 operator *(Quaternion quaternion, Vector3 vector)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!quaternion.IsNormalized())
|
||||
{
|
||||
throw new InvalidOperationException("Quaternion is not normalized.");
|
||||
}
|
||||
#endif
|
||||
var u = new Vector3(quaternion.X, quaternion.Y, quaternion.Z);
|
||||
Vector3 uv = u.Cross(vector);
|
||||
return vector + (((uv * quaternion.W) + u.Cross(uv)) * 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector3 rotated (multiplied) by the inverse quaternion.
|
||||
/// <c>vector * quaternion</c> is equivalent to <c>quaternion.Inverse() * vector</c>. See <see cref="Inverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">A Vector3 to inversely rotate.</param>
|
||||
/// <param name="quaternion">The quaternion to rotate by.</param>
|
||||
/// <returns>The inversely rotated Vector3.</returns>
|
||||
public static Vector3 operator *(Vector3 vector, Quaternion quaternion)
|
||||
{
|
||||
return quaternion.Inverse() * vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds each component of the left <see cref="Quaternion"/>
|
||||
/// to the right <see cref="Quaternion"/>. This operation is not
|
||||
/// meaningful on its own, but it can be used as a part of a
|
||||
/// larger expression, such as approximating an intermediate
|
||||
/// rotation between two nearby rotations.
|
||||
/// </summary>
|
||||
/// <param name="left">The left quaternion to add.</param>
|
||||
/// <param name="right">The right quaternion to add.</param>
|
||||
/// <returns>The added quaternion.</returns>
|
||||
public static Quaternion operator +(Quaternion left, Quaternion right)
|
||||
{
|
||||
return new Quaternion(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts each component of the left <see cref="Quaternion"/>
|
||||
/// by the right <see cref="Quaternion"/>. This operation is not
|
||||
/// meaningful on its own, but it can be used as a part of a
|
||||
/// larger expression.
|
||||
/// </summary>
|
||||
/// <param name="left">The left quaternion to subtract.</param>
|
||||
/// <param name="right">The right quaternion to subtract.</param>
|
||||
/// <returns>The subtracted quaternion.</returns>
|
||||
public static Quaternion operator -(Quaternion left, Quaternion right)
|
||||
{
|
||||
return new Quaternion(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the negative value of the <see cref="Quaternion"/>.
|
||||
/// This is the same as writing
|
||||
/// <c>new Quaternion(-q.X, -q.Y, -q.Z, -q.W)</c>. This operation
|
||||
/// results in a quaternion that represents the same rotation.
|
||||
/// </summary>
|
||||
/// <param name="quat">The quaternion to negate.</param>
|
||||
/// <returns>The negated quaternion.</returns>
|
||||
public static Quaternion operator -(Quaternion quat)
|
||||
{
|
||||
return new Quaternion(-quat.X, -quat.Y, -quat.Z, -quat.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Quaternion"/>
|
||||
/// by the given <see cref="real_t"/>. This operation is not
|
||||
/// meaningful on its own, but it can be used as a part of a
|
||||
/// larger expression.
|
||||
/// </summary>
|
||||
/// <param name="left">The quaternion to multiply.</param>
|
||||
/// <param name="right">The value to multiply by.</param>
|
||||
/// <returns>The multiplied quaternion.</returns>
|
||||
public static Quaternion operator *(Quaternion left, real_t right)
|
||||
{
|
||||
return new Quaternion(left.X * right, left.Y * right, left.Z * right, left.W * right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Quaternion"/>
|
||||
/// by the given <see cref="real_t"/>. This operation is not
|
||||
/// meaningful on its own, but it can be used as a part of a
|
||||
/// larger expression.
|
||||
/// </summary>
|
||||
/// <param name="left">The value to multiply by.</param>
|
||||
/// <param name="right">The quaternion to multiply.</param>
|
||||
/// <returns>The multiplied quaternion.</returns>
|
||||
public static Quaternion operator *(real_t left, Quaternion right)
|
||||
{
|
||||
return new Quaternion(right.X * left, right.Y * left, right.Z * left, right.W * left);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides each component of the <see cref="Quaternion"/>
|
||||
/// by the given <see cref="real_t"/>. This operation is not
|
||||
/// meaningful on its own, but it can be used as a part of a
|
||||
/// larger expression.
|
||||
/// </summary>
|
||||
/// <param name="left">The quaternion to divide.</param>
|
||||
/// <param name="right">The value to divide by.</param>
|
||||
/// <returns>The divided quaternion.</returns>
|
||||
public static Quaternion operator /(Quaternion left, real_t right)
|
||||
{
|
||||
return left * (1.0f / right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the quaternions are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left quaternion.</param>
|
||||
/// <param name="right">The right quaternion.</param>
|
||||
/// <returns>Whether or not the quaternions are exactly equal.</returns>
|
||||
public static bool operator ==(Quaternion left, Quaternion right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the quaternions are not equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left quaternion.</param>
|
||||
/// <param name="right">The right quaternion.</param>
|
||||
/// <returns>Whether or not the quaternions are not equal.</returns>
|
||||
public static bool operator !=(Quaternion left, Quaternion right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this quaternion and <paramref name="obj"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to compare.</param>
|
||||
/// <returns>Whether or not the quaternion and the other object are exactly equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Quaternion other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other quaternion to compare.</param>
|
||||
/// <returns>Whether or not the quaternions are exactly equal.</returns>
|
||||
public readonly bool Equals(Quaternion other)
|
||||
{
|
||||
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are approximately equal,
|
||||
/// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <param name="other">The other quaternion to compare.</param>
|
||||
/// <returns>Whether or not the quaternions are approximately equal.</returns>
|
||||
public readonly bool IsEqualApprox(Quaternion other)
|
||||
{
|
||||
return Mathf.IsEqualApprox(X, other.X) && Mathf.IsEqualApprox(Y, other.Y) && Mathf.IsEqualApprox(Z, other.Z) && Mathf.IsEqualApprox(W, other.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Quaternion"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this quaternion.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y, Z, W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Quaternion"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this quaternion.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Quaternion"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this quaternion.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"({X.ToString(format, CultureInfo.InvariantCulture)}, {Y.ToString(format, CultureInfo.InvariantCulture)}, {Z.ToString(format, CultureInfo.InvariantCulture)}, {W.ToString(format, CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
}
|
||||
}
|
||||
499
modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
Normal file
499
modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
Normal file
@@ -0,0 +1,499 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// 2D axis-aligned bounding box. Rect2 consists of a position, a size, and
|
||||
/// several utility functions. It is typically used for fast overlap tests.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Rect2 : IEquatable<Rect2>
|
||||
{
|
||||
private Vector2 _position;
|
||||
private Vector2 _size;
|
||||
|
||||
/// <summary>
|
||||
/// Beginning corner. Typically has values lower than <see cref="End"/>.
|
||||
/// </summary>
|
||||
/// <value>Directly uses a private field.</value>
|
||||
public Vector2 Position
|
||||
{
|
||||
readonly get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive.
|
||||
/// If the size is negative, you can use <see cref="Abs"/> to fix it.
|
||||
/// </summary>
|
||||
/// <value>Directly uses a private field.</value>
|
||||
public Vector2 Size
|
||||
{
|
||||
readonly get { return _size; }
|
||||
set { _size = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ending corner. This is calculated as <see cref="Position"/> plus <see cref="Size"/>.
|
||||
/// Setting this value will change the size.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Getting is equivalent to <paramref name="value"/> = <see cref="Position"/> + <see cref="Size"/>,
|
||||
/// setting is equivalent to <see cref="Size"/> = <paramref name="value"/> - <see cref="Position"/>
|
||||
/// </value>
|
||||
public Vector2 End
|
||||
{
|
||||
readonly get { return _position + _size; }
|
||||
set { _size = value - _position; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The area of this <see cref="Rect2"/>.
|
||||
/// See also <see cref="HasArea"/>.
|
||||
/// </summary>
|
||||
public readonly real_t Area
|
||||
{
|
||||
get { return _size.X * _size.Y; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Rect2"/> with equivalent position and size, modified so that
|
||||
/// the top-left corner is the origin and width and height are positive.
|
||||
/// </summary>
|
||||
/// <returns>The modified <see cref="Rect2"/>.</returns>
|
||||
public readonly Rect2 Abs()
|
||||
{
|
||||
Vector2 end = End;
|
||||
Vector2 topLeft = end.Min(_position);
|
||||
return new Rect2(topLeft, _size.Abs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the intersection of this <see cref="Rect2"/> and <paramref name="b"/>.
|
||||
/// If the rectangles do not intersect, an empty <see cref="Rect2"/> is returned.
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2"/>.</param>
|
||||
/// <returns>
|
||||
/// The intersection of this <see cref="Rect2"/> and <paramref name="b"/>,
|
||||
/// or an empty <see cref="Rect2"/> if they do not intersect.
|
||||
/// </returns>
|
||||
public readonly Rect2 Intersection(Rect2 b)
|
||||
{
|
||||
Rect2 newRect = b;
|
||||
|
||||
if (!Intersects(newRect))
|
||||
{
|
||||
return new Rect2();
|
||||
}
|
||||
|
||||
newRect._position = b._position.Max(_position);
|
||||
|
||||
Vector2 bEnd = b._position + b._size;
|
||||
Vector2 end = _position + _size;
|
||||
|
||||
newRect._size = bEnd.Min(end) - newRect._position;
|
||||
|
||||
return newRect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this <see cref="Rect2"/> is finite, by calling
|
||||
/// <see cref="Mathf.IsFinite(real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>Whether this vector is finite or not.</returns>
|
||||
public bool IsFinite()
|
||||
{
|
||||
return _position.IsFinite() && _size.IsFinite();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one.
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not this <see cref="Rect2"/> encloses <paramref name="b"/>.
|
||||
/// </returns>
|
||||
public readonly bool Encloses(Rect2 b)
|
||||
{
|
||||
return b._position.X >= _position.X && b._position.Y >= _position.Y &&
|
||||
b._position.X + b._size.X <= _position.X + _size.X &&
|
||||
b._position.Y + b._size.Y <= _position.Y + _size.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this <see cref="Rect2"/> expanded to include a given point.
|
||||
/// </summary>
|
||||
/// <param name="to">The point to include.</param>
|
||||
/// <returns>The expanded <see cref="Rect2"/>.</returns>
|
||||
public readonly Rect2 Expand(Vector2 to)
|
||||
{
|
||||
Rect2 expanded = this;
|
||||
|
||||
Vector2 begin = expanded._position;
|
||||
Vector2 end = expanded._position + expanded._size;
|
||||
|
||||
if (to.X < begin.X)
|
||||
{
|
||||
begin.X = to.X;
|
||||
}
|
||||
if (to.Y < begin.Y)
|
||||
{
|
||||
begin.Y = to.Y;
|
||||
}
|
||||
|
||||
if (to.X > end.X)
|
||||
{
|
||||
end.X = to.X;
|
||||
}
|
||||
if (to.Y > end.Y)
|
||||
{
|
||||
end.Y = to.Y;
|
||||
}
|
||||
|
||||
expanded._position = begin;
|
||||
expanded._size = end - begin;
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the center of the <see cref="Rect2"/>, which is equal
|
||||
/// to <see cref="Position"/> + (<see cref="Size"/> / 2).
|
||||
/// </summary>
|
||||
/// <returns>The center.</returns>
|
||||
public readonly Vector2 GetCenter()
|
||||
{
|
||||
return _position + (_size * 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the support point in a given direction.
|
||||
/// This is useful for collision detection algorithms.
|
||||
/// </summary>
|
||||
/// <param name="direction">The direction to find support for.</param>
|
||||
/// <returns>A vector representing the support.</returns>
|
||||
public readonly Vector2 GetSupport(Vector2 direction)
|
||||
{
|
||||
Vector2 support = _position;
|
||||
if (direction.X > 0.0f)
|
||||
{
|
||||
support.X += _size.X;
|
||||
}
|
||||
if (direction.Y > 0.0f)
|
||||
{
|
||||
support.Y += _size.Y;
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
|
||||
/// on all sides.
|
||||
/// </summary>
|
||||
/// <seealso cref="GrowIndividual(real_t, real_t, real_t, real_t)"/>
|
||||
/// <seealso cref="GrowSide(Side, real_t)"/>
|
||||
/// <param name="by">The amount to grow by.</param>
|
||||
/// <returns>The grown <see cref="Rect2"/>.</returns>
|
||||
public readonly Rect2 Grow(real_t by)
|
||||
{
|
||||
Rect2 g = this;
|
||||
|
||||
g._position.X -= by;
|
||||
g._position.Y -= by;
|
||||
g._size.X += by * 2;
|
||||
g._size.Y += by * 2;
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
|
||||
/// on each side individually.
|
||||
/// </summary>
|
||||
/// <seealso cref="Grow(real_t)"/>
|
||||
/// <seealso cref="GrowSide(Side, real_t)"/>
|
||||
/// <param name="left">The amount to grow by on the left side.</param>
|
||||
/// <param name="top">The amount to grow by on the top side.</param>
|
||||
/// <param name="right">The amount to grow by on the right side.</param>
|
||||
/// <param name="bottom">The amount to grow by on the bottom side.</param>
|
||||
/// <returns>The grown <see cref="Rect2"/>.</returns>
|
||||
public readonly Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
|
||||
{
|
||||
Rect2 g = this;
|
||||
|
||||
g._position.X -= left;
|
||||
g._position.Y -= top;
|
||||
g._size.X += left + right;
|
||||
g._size.Y += top + bottom;
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
|
||||
/// on the specified <see cref="Side"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Grow(real_t)"/>
|
||||
/// <seealso cref="GrowIndividual(real_t, real_t, real_t, real_t)"/>
|
||||
/// <param name="side">The side to grow.</param>
|
||||
/// <param name="by">The amount to grow by.</param>
|
||||
/// <returns>The grown <see cref="Rect2"/>.</returns>
|
||||
public readonly Rect2 GrowSide(Side side, real_t by)
|
||||
{
|
||||
Rect2 g = this;
|
||||
|
||||
g = g.GrowIndividual(Side.Left == side ? by : 0,
|
||||
Side.Top == side ? by : 0,
|
||||
Side.Right == side ? by : 0,
|
||||
Side.Bottom == side ? by : 0);
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rect2"/> has
|
||||
/// area, and <see langword="false"/> if the <see cref="Rect2"/>
|
||||
/// is linear, empty, or has a negative <see cref="Size"/>.
|
||||
/// See also <see cref="Area"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> has area.
|
||||
/// </returns>
|
||||
public readonly bool HasArea()
|
||||
{
|
||||
return _size.X > 0.0f && _size.Y > 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rect2"/> contains a point,
|
||||
/// or <see langword="false"/> otherwise.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to check.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> contains <paramref name="point"/>.
|
||||
/// </returns>
|
||||
public readonly bool HasPoint(Vector2 point)
|
||||
{
|
||||
if (point.X < _position.X)
|
||||
return false;
|
||||
if (point.Y < _position.Y)
|
||||
return false;
|
||||
|
||||
if (point.X >= _position.X + _size.X)
|
||||
return false;
|
||||
if (point.Y >= _position.Y + _size.Y)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rect2"/> overlaps with <paramref name="b"/>
|
||||
/// (i.e. they have at least one point in common).
|
||||
///
|
||||
/// If <paramref name="includeBorders"/> is <see langword="true"/>,
|
||||
/// they will also be considered overlapping if their borders touch,
|
||||
/// even without intersection.
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2"/> to check for intersections with.</param>
|
||||
/// <param name="includeBorders">Whether or not to consider borders.</param>
|
||||
/// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
|
||||
public readonly bool Intersects(Rect2 b, bool includeBorders = false)
|
||||
{
|
||||
if (includeBorders)
|
||||
{
|
||||
if (_position.X > b._position.X + b._size.X)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_position.X + _size.X < b._position.X)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_position.Y > b._position.Y + b._size.Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_position.Y + _size.Y < b._position.Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_position.X >= b._position.X + b._size.X)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_position.X + _size.X <= b._position.X)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_position.Y >= b._position.Y + b._size.Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_position.Y + _size.Y <= b._position.Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a larger <see cref="Rect2"/> that contains this <see cref="Rect2"/> and <paramref name="b"/>.
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2"/>.</param>
|
||||
/// <returns>The merged <see cref="Rect2"/>.</returns>
|
||||
public readonly Rect2 Merge(Rect2 b)
|
||||
{
|
||||
Rect2 newRect;
|
||||
|
||||
newRect._position = b._position.Min(_position);
|
||||
|
||||
newRect._size = (b._position + b._size).Max(_position + _size);
|
||||
|
||||
newRect._size -= newRect._position; // Make relative again
|
||||
|
||||
return newRect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2"/> from a position and size.
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="size">The size.</param>
|
||||
public Rect2(Vector2 position, Vector2 size)
|
||||
{
|
||||
_position = position;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2"/> from a position, width, and height.
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public Rect2(Vector2 position, real_t width, real_t height)
|
||||
{
|
||||
_position = position;
|
||||
_size = new Vector2(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2"/> from x, y, and size.
|
||||
/// </summary>
|
||||
/// <param name="x">The position's X coordinate.</param>
|
||||
/// <param name="y">The position's Y coordinate.</param>
|
||||
/// <param name="size">The size.</param>
|
||||
public Rect2(real_t x, real_t y, Vector2 size)
|
||||
{
|
||||
_position = new Vector2(x, y);
|
||||
_size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2"/> from x, y, width, and height.
|
||||
/// </summary>
|
||||
/// <param name="x">The position's X coordinate.</param>
|
||||
/// <param name="y">The position's Y coordinate.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public Rect2(real_t x, real_t y, real_t width, real_t height)
|
||||
{
|
||||
_position = new Vector2(x, y);
|
||||
_size = new Vector2(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the
|
||||
/// <see cref="Rect2"/>s are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left rect.</param>
|
||||
/// <param name="right">The right rect.</param>
|
||||
/// <returns>Whether or not the rects are exactly equal.</returns>
|
||||
public static bool operator ==(Rect2 left, Rect2 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the
|
||||
/// <see cref="Rect2"/>s are not equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left rect.</param>
|
||||
/// <param name="right">The right rect.</param>
|
||||
/// <returns>Whether or not the rects are not equal.</returns>
|
||||
public static bool operator !=(Rect2 left, Rect2 right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this rect and <paramref name="obj"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to compare.</param>
|
||||
/// <returns>Whether or not the rect and the other object are exactly equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Rect2 other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this rect and <paramref name="other"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other rect to compare.</param>
|
||||
/// <returns>Whether or not the rects are exactly equal.</returns>
|
||||
public readonly bool Equals(Rect2 other)
|
||||
{
|
||||
return _position.Equals(other._position) && _size.Equals(other._size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this rect and <paramref name="other"/> are approximately equal,
|
||||
/// by running <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
|
||||
/// </summary>
|
||||
/// <param name="other">The other rect to compare.</param>
|
||||
/// <returns>Whether or not the rects are approximately equal.</returns>
|
||||
public readonly bool IsEqualApprox(Rect2 other)
|
||||
{
|
||||
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Rect2"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this rect.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_position, _size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Rect2"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this rect.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Rect2"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this rect.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"{_position.ToString(format)}, {_size.ToString(format)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
439
modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
Normal file
439
modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
Normal file
@@ -0,0 +1,439 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// 2D axis-aligned bounding box using integers. Rect2I consists of a position, a size, and
|
||||
/// several utility functions. It is typically used for fast overlap tests.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Rect2I : IEquatable<Rect2I>
|
||||
{
|
||||
private Vector2I _position;
|
||||
private Vector2I _size;
|
||||
|
||||
/// <summary>
|
||||
/// Beginning corner. Typically has values lower than <see cref="End"/>.
|
||||
/// </summary>
|
||||
/// <value>Directly uses a private field.</value>
|
||||
public Vector2I Position
|
||||
{
|
||||
readonly get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive.
|
||||
/// If the size is negative, you can use <see cref="Abs"/> to fix it.
|
||||
/// </summary>
|
||||
/// <value>Directly uses a private field.</value>
|
||||
public Vector2I Size
|
||||
{
|
||||
readonly get { return _size; }
|
||||
set { _size = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ending corner. This is calculated as <see cref="Position"/> plus <see cref="Size"/>.
|
||||
/// Setting this value will change the size.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Getting is equivalent to <paramref name="value"/> = <see cref="Position"/> + <see cref="Size"/>,
|
||||
/// setting is equivalent to <see cref="Size"/> = <paramref name="value"/> - <see cref="Position"/>
|
||||
/// </value>
|
||||
public Vector2I End
|
||||
{
|
||||
readonly get { return _position + _size; }
|
||||
set { _size = value - _position; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The area of this <see cref="Rect2I"/>.
|
||||
/// See also <see cref="HasArea"/>.
|
||||
/// </summary>
|
||||
public readonly int Area
|
||||
{
|
||||
get { return _size.X * _size.Y; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Rect2I"/> with equivalent position and size, modified so that
|
||||
/// the top-left corner is the origin and width and height are positive.
|
||||
/// </summary>
|
||||
/// <returns>The modified <see cref="Rect2I"/>.</returns>
|
||||
public readonly Rect2I Abs()
|
||||
{
|
||||
Vector2I end = End;
|
||||
Vector2I topLeft = end.Min(_position);
|
||||
return new Rect2I(topLeft, _size.Abs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the intersection of this <see cref="Rect2I"/> and <paramref name="b"/>.
|
||||
/// If the rectangles do not intersect, an empty <see cref="Rect2I"/> is returned.
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2I"/>.</param>
|
||||
/// <returns>
|
||||
/// The intersection of this <see cref="Rect2I"/> and <paramref name="b"/>,
|
||||
/// or an empty <see cref="Rect2I"/> if they do not intersect.
|
||||
/// </returns>
|
||||
public readonly Rect2I Intersection(Rect2I b)
|
||||
{
|
||||
Rect2I newRect = b;
|
||||
|
||||
if (!Intersects(newRect))
|
||||
{
|
||||
return new Rect2I();
|
||||
}
|
||||
|
||||
newRect._position = b._position.Max(_position);
|
||||
|
||||
Vector2I bEnd = b._position + b._size;
|
||||
Vector2I end = _position + _size;
|
||||
|
||||
newRect._size = bEnd.Min(end) - newRect._position;
|
||||
|
||||
return newRect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this <see cref="Rect2I"/> completely encloses another one.
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2I"/> that may be enclosed.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not this <see cref="Rect2I"/> encloses <paramref name="b"/>.
|
||||
/// </returns>
|
||||
public readonly bool Encloses(Rect2I b)
|
||||
{
|
||||
return b._position.X >= _position.X && b._position.Y >= _position.Y &&
|
||||
b._position.X + b._size.X <= _position.X + _size.X &&
|
||||
b._position.Y + b._size.Y <= _position.Y + _size.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this <see cref="Rect2I"/> expanded to include a given point.
|
||||
/// </summary>
|
||||
/// <param name="to">The point to include.</param>
|
||||
/// <returns>The expanded <see cref="Rect2I"/>.</returns>
|
||||
public readonly Rect2I Expand(Vector2I to)
|
||||
{
|
||||
Rect2I expanded = this;
|
||||
|
||||
Vector2I begin = expanded._position;
|
||||
Vector2I end = expanded._position + expanded._size;
|
||||
|
||||
if (to.X < begin.X)
|
||||
{
|
||||
begin.X = to.X;
|
||||
}
|
||||
if (to.Y < begin.Y)
|
||||
{
|
||||
begin.Y = to.Y;
|
||||
}
|
||||
|
||||
if (to.X > end.X)
|
||||
{
|
||||
end.X = to.X;
|
||||
}
|
||||
if (to.Y > end.Y)
|
||||
{
|
||||
end.Y = to.Y;
|
||||
}
|
||||
|
||||
expanded._position = begin;
|
||||
expanded._size = end - begin;
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the center of the <see cref="Rect2I"/>, which is equal
|
||||
/// to <see cref="Position"/> + (<see cref="Size"/> / 2).
|
||||
/// If <see cref="Size"/> is an odd number, the returned center
|
||||
/// value will be rounded towards <see cref="Position"/>.
|
||||
/// </summary>
|
||||
/// <returns>The center.</returns>
|
||||
public readonly Vector2I GetCenter()
|
||||
{
|
||||
return _position + (_size / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Rect2I"/> grown by the specified amount
|
||||
/// on all sides.
|
||||
/// </summary>
|
||||
/// <seealso cref="GrowIndividual(int, int, int, int)"/>
|
||||
/// <seealso cref="GrowSide(Side, int)"/>
|
||||
/// <param name="by">The amount to grow by.</param>
|
||||
/// <returns>The grown <see cref="Rect2I"/>.</returns>
|
||||
public readonly Rect2I Grow(int by)
|
||||
{
|
||||
Rect2I g = this;
|
||||
|
||||
g._position.X -= by;
|
||||
g._position.Y -= by;
|
||||
g._size.X += by * 2;
|
||||
g._size.Y += by * 2;
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Rect2I"/> grown by the specified amount
|
||||
/// on each side individually.
|
||||
/// </summary>
|
||||
/// <seealso cref="Grow(int)"/>
|
||||
/// <seealso cref="GrowSide(Side, int)"/>
|
||||
/// <param name="left">The amount to grow by on the left side.</param>
|
||||
/// <param name="top">The amount to grow by on the top side.</param>
|
||||
/// <param name="right">The amount to grow by on the right side.</param>
|
||||
/// <param name="bottom">The amount to grow by on the bottom side.</param>
|
||||
/// <returns>The grown <see cref="Rect2I"/>.</returns>
|
||||
public readonly Rect2I GrowIndividual(int left, int top, int right, int bottom)
|
||||
{
|
||||
Rect2I g = this;
|
||||
|
||||
g._position.X -= left;
|
||||
g._position.Y -= top;
|
||||
g._size.X += left + right;
|
||||
g._size.Y += top + bottom;
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the <see cref="Rect2I"/> grown by the specified amount
|
||||
/// on the specified <see cref="Side"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Grow(int)"/>
|
||||
/// <seealso cref="GrowIndividual(int, int, int, int)"/>
|
||||
/// <param name="side">The side to grow.</param>
|
||||
/// <param name="by">The amount to grow by.</param>
|
||||
/// <returns>The grown <see cref="Rect2I"/>.</returns>
|
||||
public readonly Rect2I GrowSide(Side side, int by)
|
||||
{
|
||||
Rect2I g = this;
|
||||
|
||||
g = g.GrowIndividual(Side.Left == side ? by : 0,
|
||||
Side.Top == side ? by : 0,
|
||||
Side.Right == side ? by : 0,
|
||||
Side.Bottom == side ? by : 0);
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rect2I"/> has
|
||||
/// area, and <see langword="false"/> if the <see cref="Rect2I"/>
|
||||
/// is linear, empty, or has a negative <see cref="Size"/>.
|
||||
/// See also <see cref="Area"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Rect2I"/> has area.
|
||||
/// </returns>
|
||||
public readonly bool HasArea()
|
||||
{
|
||||
return _size.X > 0 && _size.Y > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rect2I"/> contains a point,
|
||||
/// or <see langword="false"/> otherwise.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to check.</param>
|
||||
/// <returns>
|
||||
/// A <see langword="bool"/> for whether or not the <see cref="Rect2I"/> contains <paramref name="point"/>.
|
||||
/// </returns>
|
||||
public readonly bool HasPoint(Vector2I point)
|
||||
{
|
||||
if (point.X < _position.X)
|
||||
return false;
|
||||
if (point.Y < _position.Y)
|
||||
return false;
|
||||
|
||||
if (point.X >= _position.X + _size.X)
|
||||
return false;
|
||||
if (point.Y >= _position.Y + _size.Y)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rect2I"/> overlaps with <paramref name="b"/>
|
||||
/// (i.e. they have at least one point in common).
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2I"/> to check for intersections with.</param>
|
||||
/// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
|
||||
public readonly bool Intersects(Rect2I b)
|
||||
{
|
||||
if (_position.X >= b._position.X + b._size.X)
|
||||
return false;
|
||||
if (_position.X + _size.X <= b._position.X)
|
||||
return false;
|
||||
if (_position.Y >= b._position.Y + b._size.Y)
|
||||
return false;
|
||||
if (_position.Y + _size.Y <= b._position.Y)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a larger <see cref="Rect2I"/> that contains this <see cref="Rect2I"/> and <paramref name="b"/>.
|
||||
/// </summary>
|
||||
/// <param name="b">The other <see cref="Rect2I"/>.</param>
|
||||
/// <returns>The merged <see cref="Rect2I"/>.</returns>
|
||||
public readonly Rect2I Merge(Rect2I b)
|
||||
{
|
||||
Rect2I newRect;
|
||||
|
||||
newRect._position = b._position.Min(_position);
|
||||
|
||||
newRect._size = (b._position + b._size).Max(_position + _size);
|
||||
|
||||
newRect._size -= newRect._position; // Make relative again
|
||||
|
||||
return newRect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2I"/> from a position and size.
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="size">The size.</param>
|
||||
public Rect2I(Vector2I position, Vector2I size)
|
||||
{
|
||||
_position = position;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2I"/> from a position, width, and height.
|
||||
/// </summary>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public Rect2I(Vector2I position, int width, int height)
|
||||
{
|
||||
_position = position;
|
||||
_size = new Vector2I(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2I"/> from x, y, and size.
|
||||
/// </summary>
|
||||
/// <param name="x">The position's X coordinate.</param>
|
||||
/// <param name="y">The position's Y coordinate.</param>
|
||||
/// <param name="size">The size.</param>
|
||||
public Rect2I(int x, int y, Vector2I size)
|
||||
{
|
||||
_position = new Vector2I(x, y);
|
||||
_size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Rect2I"/> from x, y, width, and height.
|
||||
/// </summary>
|
||||
/// <param name="x">The position's X coordinate.</param>
|
||||
/// <param name="y">The position's Y coordinate.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public Rect2I(int x, int y, int width, int height)
|
||||
{
|
||||
_position = new Vector2I(x, y);
|
||||
_size = new Vector2I(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the
|
||||
/// <see cref="Rect2I"/>s are exactly equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left rect.</param>
|
||||
/// <param name="right">The right rect.</param>
|
||||
/// <returns>Whether or not the rects are equal.</returns>
|
||||
public static bool operator ==(Rect2I left, Rect2I right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the
|
||||
/// <see cref="Rect2I"/>s are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left rect.</param>
|
||||
/// <param name="right">The right rect.</param>
|
||||
/// <returns>Whether or not the rects are not equal.</returns>
|
||||
public static bool operator !=(Rect2I left, Rect2I right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Rect2I"/> to a <see cref="Rect2"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The rect to convert.</param>
|
||||
public static implicit operator Rect2(Rect2I value)
|
||||
{
|
||||
return new Rect2(value._position, value._size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Rect2"/> to a <see cref="Rect2I"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The rect to convert.</param>
|
||||
public static explicit operator Rect2I(Rect2 value)
|
||||
{
|
||||
return new Rect2I((Vector2I)value.Position, (Vector2I)value.Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this rect and <paramref name="obj"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to compare.</param>
|
||||
/// <returns>Whether or not the rect and the other object are equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Rect2I other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this rect and <paramref name="other"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other rect to compare.</param>
|
||||
/// <returns>Whether or not the rects are equal.</returns>
|
||||
public readonly bool Equals(Rect2I other)
|
||||
{
|
||||
return _position.Equals(other._position) && _size.Equals(other._size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Rect2I"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this rect.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_position, _size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Rect2I"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this rect.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Rect2I"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this rect.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"{_position.ToString(format)}, {_size.ToString(format)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
195
modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
Normal file
195
modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot;
|
||||
|
||||
internal class ReflectionUtils
|
||||
{
|
||||
private static readonly HashSet<Type>? _tupleTypeSet;
|
||||
private static readonly Dictionary<Type, string>? _builtinTypeNameDictionary;
|
||||
internal static readonly bool IsEditorHintCached;
|
||||
|
||||
static ReflectionUtils()
|
||||
{
|
||||
IsEditorHintCached = Engine.IsEditorHint();
|
||||
if (!IsEditorHintCached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tupleTypeSet = new HashSet<Type>
|
||||
{
|
||||
// ValueTuple with only one element should be treated as normal generic type.
|
||||
//typeof(ValueTuple<>),
|
||||
typeof(ValueTuple<,>),
|
||||
typeof(ValueTuple<,,>),
|
||||
typeof(ValueTuple<,,,>),
|
||||
typeof(ValueTuple<,,,,>),
|
||||
typeof(ValueTuple<,,,,,>),
|
||||
typeof(ValueTuple<,,,,,,>),
|
||||
typeof(ValueTuple<,,,,,,,>),
|
||||
};
|
||||
|
||||
_builtinTypeNameDictionary ??= new Dictionary<Type, string>
|
||||
{
|
||||
{ typeof(sbyte), "sbyte" },
|
||||
{ typeof(byte), "byte" },
|
||||
{ typeof(short), "short" },
|
||||
{ typeof(ushort), "ushort" },
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(uint), "uint" },
|
||||
{ typeof(long), "long" },
|
||||
{ typeof(ulong), "ulong" },
|
||||
{ typeof(nint), "nint" },
|
||||
{ typeof(nuint), "nuint" },
|
||||
{ typeof(float), "float" },
|
||||
{ typeof(double), "double" },
|
||||
{ typeof(decimal), "decimal" },
|
||||
{ typeof(bool), "bool" },
|
||||
{ typeof(char), "char" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(object), "object" },
|
||||
};
|
||||
}
|
||||
|
||||
public static Type? FindTypeInLoadedAssemblies(string assemblyName, string typeFullName)
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies()
|
||||
.FirstOrDefault(a => a.GetName().Name == assemblyName)?
|
||||
.GetType(typeFullName);
|
||||
}
|
||||
|
||||
public static string ConstructTypeName(Type type)
|
||||
{
|
||||
if (!IsEditorHintCached)
|
||||
{
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
if (type is { IsArray: false, IsGenericType: false })
|
||||
{
|
||||
return GetSimpleTypeName(type);
|
||||
}
|
||||
|
||||
var typeNameBuilder = new StringBuilder();
|
||||
AppendType(typeNameBuilder, type);
|
||||
return typeNameBuilder.ToString();
|
||||
|
||||
static void AppendType(StringBuilder sb, Type type)
|
||||
{
|
||||
if (type.IsArray)
|
||||
{
|
||||
AppendArray(sb, type);
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
AppendGeneric(sb, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(GetSimpleTypeName(type));
|
||||
}
|
||||
}
|
||||
|
||||
static void AppendArray(StringBuilder sb, Type type)
|
||||
{
|
||||
// Append inner most non-array element.
|
||||
var elementType = type.GetElementType()!;
|
||||
while (elementType.IsArray)
|
||||
{
|
||||
elementType = elementType.GetElementType()!;
|
||||
}
|
||||
|
||||
AppendType(sb, elementType);
|
||||
// Append brackets.
|
||||
AppendArrayBrackets(sb, type);
|
||||
|
||||
static void AppendArrayBrackets(StringBuilder sb, Type? type)
|
||||
{
|
||||
while (type != null && type.IsArray)
|
||||
{
|
||||
int rank = type.GetArrayRank();
|
||||
sb.Append('[');
|
||||
sb.Append(',', rank - 1);
|
||||
sb.Append(']');
|
||||
type = type.GetElementType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AppendGeneric(StringBuilder sb, Type type)
|
||||
{
|
||||
var genericArgs = type.GenericTypeArguments;
|
||||
var genericDefinition = type.GetGenericTypeDefinition();
|
||||
|
||||
// Nullable<T>
|
||||
if (genericDefinition == typeof(Nullable<>))
|
||||
{
|
||||
AppendType(sb, genericArgs[0]);
|
||||
sb.Append('?');
|
||||
return;
|
||||
}
|
||||
|
||||
// ValueTuple
|
||||
Debug.Assert(_tupleTypeSet != null);
|
||||
if (_tupleTypeSet.Contains(genericDefinition))
|
||||
{
|
||||
sb.Append('(');
|
||||
while (true)
|
||||
{
|
||||
// We assume that ValueTuple has 1~8 elements.
|
||||
// And the 8th element (TRest) is always another ValueTuple.
|
||||
|
||||
// This is a hard coded tuple element length check.
|
||||
if (genericArgs.Length != 8)
|
||||
{
|
||||
AppendParamTypes(sb, genericArgs);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendParamTypes(sb, genericArgs.AsSpan(0, 7));
|
||||
sb.Append(", ");
|
||||
|
||||
// TRest should be a ValueTuple!
|
||||
var nextTuple = genericArgs[7];
|
||||
|
||||
genericArgs = nextTuple.GenericTypeArguments;
|
||||
}
|
||||
}
|
||||
sb.Append(')');
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal generic
|
||||
var typeName = type.Name.AsSpan();
|
||||
sb.Append(typeName[..typeName.LastIndexOf('`')]);
|
||||
sb.Append('<');
|
||||
AppendParamTypes(sb, genericArgs);
|
||||
sb.Append('>');
|
||||
|
||||
static void AppendParamTypes(StringBuilder sb, ReadOnlySpan<Type> genericArgs)
|
||||
{
|
||||
int n = genericArgs.Length - 1;
|
||||
for (int i = 0; i < n; i += 1)
|
||||
{
|
||||
AppendType(sb, genericArgs[i]);
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
AppendType(sb, genericArgs[n]);
|
||||
}
|
||||
}
|
||||
|
||||
static string GetSimpleTypeName(Type type)
|
||||
{
|
||||
Debug.Assert(_builtinTypeNameDictionary != null);
|
||||
return _builtinTypeNameDictionary.TryGetValue(type, out string? name) ? name : type.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs
Normal file
106
modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// The RID type is used to access a low-level resource by its unique ID.
|
||||
/// RIDs are opaque, which means they do not grant access to the resource
|
||||
/// by themselves. They are used by the low-level server classes, such as
|
||||
/// <see cref="DisplayServer"/>, <see cref="RenderingServer"/>,
|
||||
/// <see cref="TextServer"/>, etc.
|
||||
///
|
||||
/// A low-level resource may correspond to a high-level <see cref="Resource"/>,
|
||||
/// such as <see cref="Texture"/> or <see cref="Mesh"/>
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly struct Rid : IEquatable<Rid>
|
||||
{
|
||||
private readonly ulong _id; // Default is 0
|
||||
|
||||
internal Rid(ulong id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Rid"/> for the given <see cref="GodotObject"/> <paramref name="from"/>.
|
||||
/// </summary>
|
||||
public Rid(GodotObject from)
|
||||
=> _id = from is Resource res ? res.GetRid()._id : default;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ID of the referenced low-level resource.
|
||||
/// </summary>
|
||||
/// <returns>The ID of the referenced resource.</returns>
|
||||
public ulong Id => _id;
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rid"/> is not <c>0</c>.
|
||||
/// </summary>
|
||||
/// <returns>Whether or not the ID is valid.</returns>
|
||||
public bool IsValid => _id != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if both <see cref="Rid"/>s are equal,
|
||||
/// which means they both refer to the same low-level resource.
|
||||
/// </summary>
|
||||
/// <param name="left">The left RID.</param>
|
||||
/// <param name="right">The right RID.</param>
|
||||
/// <returns>Whether or not the RIDs are equal.</returns>
|
||||
public static bool operator ==(Rid left, Rid right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the <see cref="Rid"/>s are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left RID.</param>
|
||||
/// <param name="right">The right RID.</param>
|
||||
/// <returns>Whether or not the RIDs are equal.</returns>
|
||||
public static bool operator !=(Rid left, Rid right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this RID and <paramref name="obj"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to compare.</param>
|
||||
/// <returns>Whether or not the color and the other object are equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Rid other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the RIDs are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other RID.</param>
|
||||
/// <returns>Whether or not the RIDs are equal.</returns>
|
||||
public readonly bool Equals(Rid other)
|
||||
{
|
||||
return _id == other.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Rid"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this RID.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Rid"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this Rid.</returns>
|
||||
public override readonly string ToString() => $"RID({Id.ToString(null, CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
}
|
||||
38
modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs
Normal file
38
modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a signal defined in an object.
|
||||
/// </summary>
|
||||
public readonly struct Signal : IAwaitable<Variant[]>
|
||||
{
|
||||
private readonly GodotObject _owner;
|
||||
private readonly StringName _signalName;
|
||||
|
||||
/// <summary>
|
||||
/// Object that contains the signal.
|
||||
/// </summary>
|
||||
public GodotObject Owner => _owner;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the signal.
|
||||
/// </summary>
|
||||
public StringName Name => _signalName;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Signal"/> with the name <paramref name="name"/>
|
||||
/// in the specified <paramref name="owner"/>.
|
||||
/// </summary>
|
||||
/// <param name="owner">Object that contains the signal.</param>
|
||||
/// <param name="name">Name of the signal.</param>
|
||||
public Signal(GodotObject owner, StringName name)
|
||||
{
|
||||
_owner = owner;
|
||||
_signalName = name;
|
||||
}
|
||||
|
||||
public IAwaiter<Variant[]> GetAwaiter()
|
||||
{
|
||||
return new SignalAwaiter(_owner, _signalName, _owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public class SignalAwaiter : IAwaiter<Variant[]>, IAwaitable<Variant[]>
|
||||
{
|
||||
private bool _completed;
|
||||
private Variant[] _result;
|
||||
private Action _continuation;
|
||||
|
||||
public SignalAwaiter(GodotObject source, StringName signal, GodotObject target)
|
||||
{
|
||||
var awaiterGcHandle = CustomGCHandle.AllocStrong(this);
|
||||
using godot_string_name signalSrc = NativeFuncs.godotsharp_string_name_new_copy(
|
||||
(godot_string_name)(signal?.NativeValue ?? default));
|
||||
NativeFuncs.godotsharp_internal_signal_awaiter_connect(GodotObject.GetPtr(source), in signalSrc,
|
||||
GodotObject.GetPtr(target), GCHandle.ToIntPtr(awaiterGcHandle));
|
||||
}
|
||||
|
||||
public bool IsCompleted => _completed;
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
_continuation = continuation;
|
||||
}
|
||||
|
||||
public Variant[] GetResult() => _result;
|
||||
|
||||
public IAwaiter<Variant[]> GetAwaiter() => this;
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount,
|
||||
godot_bool* outAwaiterIsNull)
|
||||
{
|
||||
try
|
||||
{
|
||||
var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target;
|
||||
|
||||
if (awaiter == null)
|
||||
{
|
||||
*outAwaiterIsNull = godot_bool.True;
|
||||
return;
|
||||
}
|
||||
|
||||
*outAwaiterIsNull = godot_bool.False;
|
||||
|
||||
awaiter._completed = true;
|
||||
|
||||
if (argCount > 0)
|
||||
{
|
||||
Variant[] signalArgs = new Variant[argCount];
|
||||
|
||||
for (int i = 0; i < argCount; i++)
|
||||
signalArgs[i] = Variant.CreateCopyingBorrowed(*args[i]);
|
||||
|
||||
awaiter._result = signalArgs;
|
||||
}
|
||||
else
|
||||
{
|
||||
awaiter._result = [];
|
||||
}
|
||||
|
||||
awaiter._continuation?.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionUtils.LogException(e);
|
||||
*outAwaiterIsNull = godot_bool.False;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1799
modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
Normal file
1799
modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
Normal file
File diff suppressed because it is too large
Load Diff
167
modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
Normal file
167
modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// StringNames are immutable strings designed for general-purpose representation of unique names.
|
||||
/// StringName ensures that only one instance of a given name exists (so two StringNames with the
|
||||
/// same value are the same object).
|
||||
/// Comparing them is much faster than with regular strings, because only the pointers are compared,
|
||||
/// not the whole strings.
|
||||
/// </summary>
|
||||
public sealed class StringName : IDisposable, IEquatable<StringName?>
|
||||
{
|
||||
internal godot_string_name.movable NativeValue;
|
||||
|
||||
private WeakReference<IDisposable>? _weakReferenceToSelf;
|
||||
|
||||
~StringName()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this <see cref="StringName"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Dispose(bool disposing)
|
||||
{
|
||||
// Always dispose `NativeValue` even if disposing is true
|
||||
NativeValue.DangerousSelfRef.Dispose();
|
||||
|
||||
if (_weakReferenceToSelf != null)
|
||||
{
|
||||
DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
|
||||
}
|
||||
}
|
||||
|
||||
private StringName(godot_string_name nativeValueToOwn)
|
||||
{
|
||||
NativeValue = (godot_string_name.movable)nativeValueToOwn;
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
|
||||
}
|
||||
|
||||
// Explicit name to make it very clear
|
||||
internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn)
|
||||
=> new StringName(nativeValueToOwn);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an empty <see cref="StringName"/>.
|
||||
/// </summary>
|
||||
public StringName()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="StringName"/> from the given <paramref name="name"/> string.
|
||||
/// </summary>
|
||||
/// <param name="name">String to construct the <see cref="StringName"/> from.</param>
|
||||
public StringName(string name)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
NativeValue = (godot_string_name.movable)NativeFuncs.godotsharp_string_name_new_from_string(name);
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to a <see cref="StringName"/>.
|
||||
/// </summary>
|
||||
/// <param name="from">The string to convert.</param>
|
||||
public static implicit operator StringName(string from) => new StringName(from);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="StringName"/> to a string.
|
||||
/// </summary>
|
||||
/// <param name="from">The <see cref="StringName"/> to convert.</param>
|
||||
[return: NotNullIfNotNull("from")]
|
||||
public static implicit operator string?(StringName? from) => from?.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="StringName"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this <see cref="StringName"/>.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (IsEmpty)
|
||||
return string.Empty;
|
||||
|
||||
var src = (godot_string_name)NativeValue;
|
||||
NativeFuncs.godotsharp_string_name_as_string(out godot_string dest, src);
|
||||
using (dest)
|
||||
return Marshaling.ConvertStringToManaged(dest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether this <see cref="StringName"/> is empty.
|
||||
/// </summary>
|
||||
/// <returns>If the <see cref="StringName"/> is empty.</returns>
|
||||
public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
|
||||
|
||||
public static bool operator ==(StringName? left, StringName? right)
|
||||
{
|
||||
if (left is null)
|
||||
return right is null;
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(StringName? left, StringName? right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public bool Equals([NotNullWhen(true)] StringName? other)
|
||||
{
|
||||
if (other is null)
|
||||
return false;
|
||||
return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef;
|
||||
}
|
||||
|
||||
public static bool operator ==(StringName? left, in godot_string_name right)
|
||||
{
|
||||
if (left is null)
|
||||
return right.IsEmpty;
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(StringName? left, in godot_string_name right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static bool operator ==(in godot_string_name left, StringName? right)
|
||||
{
|
||||
return right == left;
|
||||
}
|
||||
|
||||
public static bool operator !=(in godot_string_name left, StringName? right)
|
||||
{
|
||||
return !(right == left);
|
||||
}
|
||||
|
||||
public bool Equals(in godot_string_name other)
|
||||
{
|
||||
return NativeValue.DangerousSelfRef == other;
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return ReferenceEquals(this, obj) || (obj is StringName other && Equals(other));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return NativeValue.DangerousSelfRef.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
665
modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
Normal file
665
modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
Normal file
@@ -0,0 +1,665 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// 2×3 matrix (2 rows, 3 columns) used for 2D linear transformations.
|
||||
/// It can represent transformations such as translation, rotation, or scaling.
|
||||
/// It consists of a three <see cref="Vector2"/> values: x, y, and the origin.
|
||||
///
|
||||
/// For more information, read this documentation article:
|
||||
/// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Transform2D : IEquatable<Transform2D>
|
||||
{
|
||||
/// <summary>
|
||||
/// The basis matrix's X vector (column 0). Equivalent to array index <c>[0]</c>.
|
||||
/// </summary>
|
||||
public Vector2 X;
|
||||
|
||||
/// <summary>
|
||||
/// The basis matrix's Y vector (column 1). Equivalent to array index <c>[1]</c>.
|
||||
/// </summary>
|
||||
public Vector2 Y;
|
||||
|
||||
/// <summary>
|
||||
/// The origin vector (column 2, the third column). Equivalent to array index <c>[2]</c>.
|
||||
/// The origin vector represents translation.
|
||||
/// </summary>
|
||||
public Vector2 Origin;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the transform's rotation (in radians).
|
||||
/// </summary>
|
||||
public readonly real_t Rotation => Mathf.Atan2(X.Y, X.X);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the scale.
|
||||
/// </summary>
|
||||
public readonly Vector2 Scale
|
||||
{
|
||||
get
|
||||
{
|
||||
real_t detSign = Mathf.Sign(Determinant());
|
||||
return new Vector2(X.Length(), detSign * Y.Length());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the transform's skew (in radians).
|
||||
/// </summary>
|
||||
public readonly real_t Skew
|
||||
{
|
||||
get
|
||||
{
|
||||
real_t detSign = Mathf.Sign(Determinant());
|
||||
return Mathf.Acos(X.Normalized().Dot(detSign * Y.Normalized())) - Mathf.Pi * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access whole columns in the form of <see cref="Vector2"/>.
|
||||
/// The third column is the <see cref="Origin"/> vector.
|
||||
/// </summary>
|
||||
/// <param name="column">Which column vector.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="column"/> is not 0, 1 or 2.
|
||||
/// </exception>
|
||||
public Vector2 this[int column]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
return X;
|
||||
case 1:
|
||||
return Y;
|
||||
case 2:
|
||||
return Origin;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(column));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
X = value;
|
||||
return;
|
||||
case 1:
|
||||
Y = value;
|
||||
return;
|
||||
case 2:
|
||||
Origin = value;
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access matrix elements in column-major order.
|
||||
/// The third column is the <see cref="Origin"/> vector.
|
||||
/// </summary>
|
||||
/// <param name="column">Which column, the matrix horizontal position.</param>
|
||||
/// <param name="row">Which row, the matrix vertical position.</param>
|
||||
public real_t this[int column, int row]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
return this[column][row];
|
||||
}
|
||||
set
|
||||
{
|
||||
Vector2 columnVector = this[column];
|
||||
columnVector[row] = value;
|
||||
this[column] = columnVector;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inverse of the transform, under the assumption that
|
||||
/// the basis is invertible (must have non-zero determinant).
|
||||
/// </summary>
|
||||
/// <seealso cref="Inverse"/>
|
||||
/// <returns>The inverse transformation matrix.</returns>
|
||||
public readonly Transform2D AffineInverse()
|
||||
{
|
||||
real_t det = Determinant();
|
||||
|
||||
if (det == 0)
|
||||
throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
|
||||
|
||||
Transform2D inv = this;
|
||||
|
||||
inv[0, 0] = this[1, 1];
|
||||
inv[1, 1] = this[0, 0];
|
||||
|
||||
real_t detInv = 1.0f / det;
|
||||
|
||||
inv[0] *= new Vector2(detInv, -detInv);
|
||||
inv[1] *= new Vector2(-detInv, detInv);
|
||||
|
||||
inv[2] = inv.BasisXform(-inv[2]);
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the determinant of the basis matrix. If the basis is
|
||||
/// uniformly scaled, then its determinant equals the square of the
|
||||
/// scale factor.
|
||||
///
|
||||
/// A negative determinant means the basis was flipped, so one part of
|
||||
/// the scale is negative. A zero determinant means the basis isn't
|
||||
/// invertible, and is usually considered invalid.
|
||||
/// </summary>
|
||||
/// <returns>The determinant of the basis matrix.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly real_t Determinant()
|
||||
{
|
||||
return (X.X * Y.Y) - (X.Y * Y.X);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector transformed (multiplied) by the basis matrix.
|
||||
/// This method does not account for translation (the <see cref="Origin"/> vector).
|
||||
/// </summary>
|
||||
/// <seealso cref="BasisXformInv(Vector2)"/>
|
||||
/// <param name="v">A vector to transform.</param>
|
||||
/// <returns>The transformed vector.</returns>
|
||||
public readonly Vector2 BasisXform(Vector2 v)
|
||||
{
|
||||
return new Vector2(Tdotx(v), Tdoty(v));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector transformed (multiplied) by the inverse basis matrix,
|
||||
/// under the assumption that the basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not).
|
||||
/// This method does not account for translation (the <see cref="Origin"/> vector).
|
||||
/// <c>transform.BasisXformInv(vector)</c> is equivalent to <c>transform.Inverse().BasisXform(vector)</c>. See <see cref="Inverse"/>.
|
||||
/// For non-orthonormal transforms (e.g. with scaling) <c>transform.AffineInverse().BasisXform(vector)</c> can be used instead. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="BasisXform(Vector2)"/>
|
||||
/// <param name="v">A vector to inversely transform.</param>
|
||||
/// <returns>The inversely transformed vector.</returns>
|
||||
public readonly Vector2 BasisXformInv(Vector2 v)
|
||||
{
|
||||
return new Vector2(X.Dot(v), Y.Dot(v));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
|
||||
/// </summary>
|
||||
/// <param name="transform">The other transform.</param>
|
||||
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
|
||||
/// <returns>The interpolated transform.</returns>
|
||||
public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight)
|
||||
{
|
||||
return new Transform2D
|
||||
(
|
||||
Mathf.LerpAngle(Rotation, transform.Rotation, weight),
|
||||
Scale.Lerp(transform.Scale, weight),
|
||||
Mathf.LerpAngle(Skew, transform.Skew, weight),
|
||||
Origin.Lerp(transform.Origin, weight)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inverse of the transform, under the assumption that
|
||||
/// the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not). Use <see cref="AffineInverse"/> for
|
||||
/// non-orthonormal transforms (e.g. with scaling).
|
||||
/// </summary>
|
||||
/// <returns>The inverse matrix.</returns>
|
||||
public readonly Transform2D Inverse()
|
||||
{
|
||||
Transform2D inv = this;
|
||||
|
||||
// Swap
|
||||
inv.X.Y = Y.X;
|
||||
inv.Y.X = X.Y;
|
||||
|
||||
inv.Origin = inv.BasisXform(-inv.Origin);
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this transform is finite, by calling
|
||||
/// <see cref="Mathf.IsFinite(real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>Whether this vector is finite or not.</returns>
|
||||
public readonly bool IsFinite()
|
||||
{
|
||||
return X.IsFinite() && Y.IsFinite() && Origin.IsFinite();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the transform with the basis orthogonal (90 degrees),
|
||||
/// and normalized axis vectors (scale of 1 or -1).
|
||||
/// </summary>
|
||||
/// <returns>The orthonormalized transform.</returns>
|
||||
public readonly Transform2D Orthonormalized()
|
||||
{
|
||||
Transform2D ortho = this;
|
||||
|
||||
Vector2 orthoX = ortho.X;
|
||||
Vector2 orthoY = ortho.Y;
|
||||
|
||||
orthoX.Normalize();
|
||||
orthoY = orthoY - orthoX * orthoX.Dot(orthoY);
|
||||
orthoY.Normalize();
|
||||
|
||||
ortho.X = orthoX;
|
||||
ortho.Y = orthoY;
|
||||
|
||||
return ortho;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the transform by <paramref name="angle"/> (in radians).
|
||||
/// The operation is done in the parent/global frame, equivalent to
|
||||
/// multiplying the matrix from the left.
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle to rotate, in radians.</param>
|
||||
/// <returns>The rotated transformation matrix.</returns>
|
||||
public readonly Transform2D Rotated(real_t angle)
|
||||
{
|
||||
return new Transform2D(angle, new Vector2()) * this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the transform by <paramref name="angle"/> (in radians).
|
||||
/// The operation is done in the local frame, equivalent to
|
||||
/// multiplying the matrix from the right.
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle to rotate, in radians.</param>
|
||||
/// <returns>The rotated transformation matrix.</returns>
|
||||
public readonly Transform2D RotatedLocal(real_t angle)
|
||||
{
|
||||
return this * new Transform2D(angle, new Vector2());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the transform by the given scaling factor.
|
||||
/// The operation is done in the parent/global frame, equivalent to
|
||||
/// multiplying the matrix from the left.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale to introduce.</param>
|
||||
/// <returns>The scaled transformation matrix.</returns>
|
||||
public readonly Transform2D Scaled(Vector2 scale)
|
||||
{
|
||||
Transform2D copy = this;
|
||||
copy.X *= scale;
|
||||
copy.Y *= scale;
|
||||
copy.Origin *= scale;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the transform by the given scaling factor.
|
||||
/// The operation is done in the local frame, equivalent to
|
||||
/// multiplying the matrix from the right.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale to introduce.</param>
|
||||
/// <returns>The scaled transformation matrix.</returns>
|
||||
public readonly Transform2D ScaledLocal(Vector2 scale)
|
||||
{
|
||||
Transform2D copy = this;
|
||||
copy.X *= scale;
|
||||
copy.Y *= scale;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private readonly real_t Tdotx(Vector2 with)
|
||||
{
|
||||
return (this[0, 0] * with[0]) + (this[1, 0] * with[1]);
|
||||
}
|
||||
|
||||
private readonly real_t Tdoty(Vector2 with)
|
||||
{
|
||||
return (this[0, 1] * with[0]) + (this[1, 1] * with[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the transform by the given <paramref name="offset"/>.
|
||||
/// The operation is done in the parent/global frame, equivalent to
|
||||
/// multiplying the matrix from the left.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset to translate by.</param>
|
||||
/// <returns>The translated matrix.</returns>
|
||||
public readonly Transform2D Translated(Vector2 offset)
|
||||
{
|
||||
Transform2D copy = this;
|
||||
copy.Origin += offset;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the transform by the given <paramref name="offset"/>.
|
||||
/// The operation is done in the local frame, equivalent to
|
||||
/// multiplying the matrix from the right.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset to translate by.</param>
|
||||
/// <returns>The translated matrix.</returns>
|
||||
public readonly Transform2D TranslatedLocal(Vector2 offset)
|
||||
{
|
||||
Transform2D copy = this;
|
||||
copy.Origin += copy.BasisXform(offset);
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static readonly Transform2D _identity = new Transform2D(1, 0, 0, 1, 0, 0);
|
||||
private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0);
|
||||
private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// The identity transform, with no translation, rotation, or scaling applied.
|
||||
/// This is used as a replacement for <c>Transform2D()</c> in GDScript.
|
||||
/// Do not use <c>new Transform2D()</c> with no arguments in C#, because it sets all values to zero.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)</c>.</value>
|
||||
public static Transform2D Identity { get { return _identity; } }
|
||||
/// <summary>
|
||||
/// The transform that will flip something along the X axis.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)</c>.</value>
|
||||
public static Transform2D FlipX { get { return _flipX; } }
|
||||
/// <summary>
|
||||
/// The transform that will flip something along the Y axis.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)</c>.</value>
|
||||
public static Transform2D FlipY { get { return _flipY; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from 3 vectors (matrix columns).
|
||||
/// </summary>
|
||||
/// <param name="xAxis">The X vector, or column index 0.</param>
|
||||
/// <param name="yAxis">The Y vector, or column index 1.</param>
|
||||
/// <param name="originPos">The origin vector, or column index 2.</param>
|
||||
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos)
|
||||
{
|
||||
X = xAxis;
|
||||
Y = yAxis;
|
||||
Origin = originPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from the given components.
|
||||
/// Arguments are named such that xy is equal to calling <c>X.Y</c>.
|
||||
/// </summary>
|
||||
/// <param name="xx">The X component of the X column vector, accessed via <c>t.X.X</c> or <c>[0][0]</c>.</param>
|
||||
/// <param name="xy">The Y component of the X column vector, accessed via <c>t.X.Y</c> or <c>[0][1]</c>.</param>
|
||||
/// <param name="yx">The X component of the Y column vector, accessed via <c>t.Y.X</c> or <c>[1][0]</c>.</param>
|
||||
/// <param name="yy">The Y component of the Y column vector, accessed via <c>t.Y.Y</c> or <c>[1][1]</c>.</param>
|
||||
/// <param name="ox">The X component of the origin vector, accessed via <c>t.Origin.X</c> or <c>[2][0]</c>.</param>
|
||||
/// <param name="oy">The Y component of the origin vector, accessed via <c>t.Origin.Y</c> or <c>[2][1]</c>.</param>
|
||||
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
|
||||
{
|
||||
X = new Vector2(xx, xy);
|
||||
Y = new Vector2(yx, yy);
|
||||
Origin = new Vector2(ox, oy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from a <paramref name="rotation"/> value and
|
||||
/// <paramref name="origin"/> vector.
|
||||
/// </summary>
|
||||
/// <param name="rotation">The rotation of the new transform, in radians.</param>
|
||||
/// <param name="origin">The origin vector, or column index 2.</param>
|
||||
public Transform2D(real_t rotation, Vector2 origin)
|
||||
{
|
||||
(real_t sin, real_t cos) = Mathf.SinCos(rotation);
|
||||
X.X = Y.Y = cos;
|
||||
X.Y = Y.X = sin;
|
||||
Y.X *= -1;
|
||||
Origin = origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from a <paramref name="rotation"/> value,
|
||||
/// <paramref name="scale"/> vector, <paramref name="skew"/> value, and
|
||||
/// <paramref name="origin"/> vector.
|
||||
/// </summary>
|
||||
/// <param name="rotation">The rotation of the new transform, in radians.</param>
|
||||
/// <param name="scale">The scale of the new transform.</param>
|
||||
/// <param name="skew">The skew of the new transform, in radians.</param>
|
||||
/// <param name="origin">The origin vector, or column index 2.</param>
|
||||
public Transform2D(real_t rotation, Vector2 scale, real_t skew, Vector2 origin)
|
||||
{
|
||||
(real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation);
|
||||
(real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew);
|
||||
X.X = rotationCos * scale.X;
|
||||
Y.Y = rotationSkewCos * scale.Y;
|
||||
Y.X = -rotationSkewSin * scale.Y;
|
||||
X.Y = rotationSin * scale.X;
|
||||
Origin = origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Composes these two transformation matrices by multiplying them
|
||||
/// together. This has the effect of transforming the second transform
|
||||
/// (the child) by the first transform (the parent).
|
||||
/// </summary>
|
||||
/// <param name="left">The parent transform.</param>
|
||||
/// <param name="right">The child transform.</param>
|
||||
/// <returns>The composed transform.</returns>
|
||||
public static Transform2D operator *(Transform2D left, Transform2D right)
|
||||
{
|
||||
left.Origin = left * right.Origin;
|
||||
|
||||
real_t x0 = left.Tdotx(right.X);
|
||||
real_t x1 = left.Tdoty(right.X);
|
||||
real_t y0 = left.Tdotx(right.Y);
|
||||
real_t y1 = left.Tdoty(right.Y);
|
||||
|
||||
left.X.X = x0;
|
||||
left.X.Y = x1;
|
||||
left.Y.X = y0;
|
||||
left.Y.Y = y1;
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector2 transformed (multiplied) by the transformation matrix.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <param name="vector">A Vector2 to transform.</param>
|
||||
/// <returns>The transformed Vector2.</returns>
|
||||
public static Vector2 operator *(Transform2D transform, Vector2 vector)
|
||||
{
|
||||
return new Vector2(transform.Tdotx(vector), transform.Tdoty(vector)) + transform.Origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector2 transformed (multiplied) by the inverse transformation matrix,
|
||||
/// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not).
|
||||
/// <c>vector * transform</c> is equivalent to <c>transform.Inverse() * vector</c>. See <see cref="Inverse"/>.
|
||||
/// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * vector</c> can be used instead. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">A Vector2 to inversely transform.</param>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <returns>The inversely transformed Vector2.</returns>
|
||||
public static Vector2 operator *(Vector2 vector, Transform2D transform)
|
||||
{
|
||||
Vector2 vInv = vector - transform.Origin;
|
||||
return new Vector2(transform.X.Dot(vInv), transform.Y.Dot(vInv));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Rect2 transformed (multiplied) by the transformation matrix.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <param name="rect">A Rect2 to transform.</param>
|
||||
/// <returns>The transformed Rect2.</returns>
|
||||
public static Rect2 operator *(Transform2D transform, Rect2 rect)
|
||||
{
|
||||
Vector2 pos = transform * rect.Position;
|
||||
Vector2 toX = transform.X * rect.Size.X;
|
||||
Vector2 toY = transform.Y * rect.Size.Y;
|
||||
|
||||
return new Rect2(pos, new Vector2()).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Rect2 transformed (multiplied) by the inverse transformation matrix,
|
||||
/// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not).
|
||||
/// <c>rect * transform</c> is equivalent to <c>transform.Inverse() * rect</c>. See <see cref="Inverse"/>.
|
||||
/// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * rect</c> can be used instead. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="rect">A Rect2 to inversely transform.</param>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <returns>The inversely transformed Rect2.</returns>
|
||||
public static Rect2 operator *(Rect2 rect, Transform2D transform)
|
||||
{
|
||||
Vector2 pos = rect.Position * transform;
|
||||
Vector2 to1 = new Vector2(rect.Position.X, rect.Position.Y + rect.Size.Y) * transform;
|
||||
Vector2 to2 = new Vector2(rect.Position.X + rect.Size.X, rect.Position.Y + rect.Size.Y) * transform;
|
||||
Vector2 to3 = new Vector2(rect.Position.X + rect.Size.X, rect.Position.Y) * transform;
|
||||
|
||||
return new Rect2(pos, new Vector2()).Expand(to1).Expand(to2).Expand(to3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given Vector2[] transformed (multiplied) by the transformation matrix.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <param name="array">A Vector2[] to transform.</param>
|
||||
/// <returns>The transformed copy of the Vector2[].</returns>
|
||||
public static Vector2[] operator *(Transform2D transform, Vector2[] array)
|
||||
{
|
||||
Vector2[] newArray = new Vector2[array.Length];
|
||||
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
newArray[i] = transform * array[i];
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given Vector2[] transformed (multiplied) by the inverse transformation matrix,
|
||||
/// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not).
|
||||
/// <c>array * transform</c> is equivalent to <c>transform.Inverse() * array</c>. See <see cref="Inverse"/>.
|
||||
/// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * array</c> can be used instead. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">A Vector2[] to inversely transform.</param>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <returns>The inversely transformed copy of the Vector2[].</returns>
|
||||
public static Vector2[] operator *(Vector2[] array, Transform2D transform)
|
||||
{
|
||||
Vector2[] newArray = new Vector2[array.Length];
|
||||
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
newArray[i] = array[i] * transform;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transforms are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left transform.</param>
|
||||
/// <param name="right">The right transform.</param>
|
||||
/// <returns>Whether or not the transforms are exactly equal.</returns>
|
||||
public static bool operator ==(Transform2D left, Transform2D right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transforms are not equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left transform.</param>
|
||||
/// <param name="right">The right transform.</param>
|
||||
/// <returns>Whether or not the transforms are not equal.</returns>
|
||||
public static bool operator !=(Transform2D left, Transform2D right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transform is exactly equal
|
||||
/// to the given object (<paramref name="obj"/>).
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with.</param>
|
||||
/// <returns>Whether or not the transform and the object are exactly equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Transform2D other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transforms are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="other">The other transform to compare.</param>
|
||||
/// <returns>Whether or not the matrices are exactly equal.</returns>
|
||||
public readonly bool Equals(Transform2D other)
|
||||
{
|
||||
return X.Equals(other.X) && Y.Equals(other.Y) && Origin.Equals(other.Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this transform and <paramref name="other"/> are approximately equal,
|
||||
/// by running <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
|
||||
/// </summary>
|
||||
/// <param name="other">The other transform to compare.</param>
|
||||
/// <returns>Whether or not the matrices are approximately equal.</returns>
|
||||
public readonly bool IsEqualApprox(Transform2D other)
|
||||
{
|
||||
return X.IsEqualApprox(other.X) && Y.IsEqualApprox(other.Y) && Origin.IsEqualApprox(other.Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Transform2D"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this transform.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y, Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Transform2D"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this transform.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Transform2D"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this transform.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"[X: {X.ToString(format)}, Y: {Y.ToString(format)}, O: {Origin.ToString(format)}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
688
modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
Normal file
688
modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
Normal file
@@ -0,0 +1,688 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ComponentModel;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// 3×4 matrix (3 rows, 4 columns) used for 3D linear transformations.
|
||||
/// It can represent transformations such as translation, rotation, or scaling.
|
||||
/// It consists of a <see cref="Godot.Basis"/> (first 3 columns) and a
|
||||
/// <see cref="Vector3"/> for the origin (last column).
|
||||
///
|
||||
/// For more information, read this documentation article:
|
||||
/// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Transform3D : IEquatable<Transform3D>
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Godot.Basis"/> of this transform. Contains the X, Y, and Z basis
|
||||
/// vectors (columns 0 to 2) and is responsible for rotation and scale.
|
||||
/// </summary>
|
||||
public Basis Basis;
|
||||
|
||||
/// <summary>
|
||||
/// The origin vector (column 3, the fourth column). Equivalent to array index <c>[3]</c>.
|
||||
/// </summary>
|
||||
public Vector3 Origin;
|
||||
|
||||
/// <summary>
|
||||
/// Access whole columns in the form of <see cref="Vector3"/>.
|
||||
/// The fourth column is the <see cref="Origin"/> vector.
|
||||
/// </summary>
|
||||
/// <param name="column">Which column vector.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="column"/> is not 0, 1, 2 or 3.
|
||||
/// </exception>
|
||||
public Vector3 this[int column]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
return Basis.Column0;
|
||||
case 1:
|
||||
return Basis.Column1;
|
||||
case 2:
|
||||
return Basis.Column2;
|
||||
case 3:
|
||||
return Origin;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(column));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
Basis.Column0 = value;
|
||||
return;
|
||||
case 1:
|
||||
Basis.Column1 = value;
|
||||
return;
|
||||
case 2:
|
||||
Basis.Column2 = value;
|
||||
return;
|
||||
case 3:
|
||||
Origin = value;
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access matrix elements in column-major order.
|
||||
/// The fourth column is the <see cref="Origin"/> vector.
|
||||
/// </summary>
|
||||
/// <param name="column">Which column, the matrix horizontal position.</param>
|
||||
/// <param name="row">Which row, the matrix vertical position.</param>
|
||||
public real_t this[int column, int row]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (column == 3)
|
||||
{
|
||||
return Origin[row];
|
||||
}
|
||||
return Basis[column, row];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (column == 3)
|
||||
{
|
||||
Origin[row] = value;
|
||||
return;
|
||||
}
|
||||
Basis[column, row] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inverse of the transform, under the assumption that
|
||||
/// the basis is invertible (must have non-zero determinant).
|
||||
/// </summary>
|
||||
/// <seealso cref="Inverse"/>
|
||||
/// <returns>The inverse transformation matrix.</returns>
|
||||
public readonly Transform3D AffineInverse()
|
||||
{
|
||||
Basis basisInv = Basis.Inverse();
|
||||
return new Transform3D(basisInv, basisInv * -Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a transform interpolated between this transform and another
|
||||
/// <paramref name="transform"/> by a given <paramref name="weight"/>
|
||||
/// (on the range of 0.0 to 1.0).
|
||||
/// </summary>
|
||||
/// <param name="transform">The other transform.</param>
|
||||
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
|
||||
/// <returns>The interpolated transform.</returns>
|
||||
public readonly Transform3D InterpolateWith(Transform3D transform, real_t weight)
|
||||
{
|
||||
Vector3 sourceScale = Basis.Scale;
|
||||
Quaternion sourceRotation = Basis.GetRotationQuaternion();
|
||||
Vector3 sourceLocation = Origin;
|
||||
|
||||
Vector3 destinationScale = transform.Basis.Scale;
|
||||
Quaternion destinationRotation = transform.Basis.GetRotationQuaternion();
|
||||
Vector3 destinationLocation = transform.Origin;
|
||||
|
||||
var interpolated = new Transform3D();
|
||||
Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized();
|
||||
Vector3 scale = sourceScale.Lerp(destinationScale, weight);
|
||||
interpolated.Basis.SetQuaternionScale(quaternion, scale);
|
||||
interpolated.Origin = sourceLocation.Lerp(destinationLocation, weight);
|
||||
|
||||
return interpolated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inverse of the transform, under the assumption that
|
||||
/// the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not). Use <see cref="AffineInverse"/> for
|
||||
/// non-orthonormal transforms (e.g. with scaling).
|
||||
/// </summary>
|
||||
/// <returns>The inverse matrix.</returns>
|
||||
public readonly Transform3D Inverse()
|
||||
{
|
||||
Basis basisTr = Basis.Transposed();
|
||||
return new Transform3D(basisTr, basisTr * -Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this transform is finite, by calling
|
||||
/// <see cref="Mathf.IsFinite(real_t)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>Whether this vector is finite or not.</returns>
|
||||
public readonly bool IsFinite()
|
||||
{
|
||||
return Basis.IsFinite() && Origin.IsFinite();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the transform rotated such that the forward axis (-Z)
|
||||
/// points towards the <paramref name="target"/> position.
|
||||
/// The up axis (+Y) points as close to the <paramref name="up"/> vector
|
||||
/// as possible while staying perpendicular to the forward axis.
|
||||
/// The resulting transform is orthonormalized.
|
||||
/// The existing rotation, scale, and skew information from the original transform is discarded.
|
||||
/// The <paramref name="target"/> and <paramref name="up"/> vectors cannot be zero,
|
||||
/// cannot be parallel to each other, and are defined in global/parent space.
|
||||
/// </summary>
|
||||
/// <param name="target">The object to look at.</param>
|
||||
/// <param name="up">The relative up direction.</param>
|
||||
/// <param name="useModelFront">
|
||||
/// If true, then the model is oriented in reverse,
|
||||
/// towards the model front axis (+Z, Vector3.ModelFront),
|
||||
/// which is more useful for orienting 3D models.
|
||||
/// </param>
|
||||
/// <returns>The resulting transform.</returns>
|
||||
public readonly Transform3D LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false)
|
||||
{
|
||||
Transform3D t = this;
|
||||
t.SetLookAt(Origin, target, up ?? Vector3.Up, useModelFront);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
|
||||
{
|
||||
return LookingAt(target, up, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the transform with the basis orthogonal (90 degrees),
|
||||
/// and normalized axis vectors (scale of 1 or -1).
|
||||
/// </summary>
|
||||
/// <returns>The orthonormalized transform.</returns>
|
||||
public readonly Transform3D Orthonormalized()
|
||||
{
|
||||
return new Transform3D(Basis.Orthonormalized(), Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians).
|
||||
/// The axis must be a normalized vector.
|
||||
/// The operation is done in the parent/global frame, equivalent to
|
||||
/// multiplying the matrix from the left.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
|
||||
/// <param name="angle">The angle to rotate, in radians.</param>
|
||||
/// <returns>The rotated transformation matrix.</returns>
|
||||
public readonly Transform3D Rotated(Vector3 axis, real_t angle)
|
||||
{
|
||||
return new Transform3D(new Basis(axis, angle), new Vector3()) * this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians).
|
||||
/// The axis must be a normalized vector.
|
||||
/// The operation is done in the local frame, equivalent to
|
||||
/// multiplying the matrix from the right.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
|
||||
/// <param name="angle">The angle to rotate, in radians.</param>
|
||||
/// <returns>The rotated transformation matrix.</returns>
|
||||
public readonly Transform3D RotatedLocal(Vector3 axis, real_t angle)
|
||||
{
|
||||
Basis tmpBasis = new Basis(axis, angle);
|
||||
return new Transform3D(Basis * tmpBasis, Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the transform by the given 3D <paramref name="scale"/> factor.
|
||||
/// The operation is done in the parent/global frame, equivalent to
|
||||
/// multiplying the matrix from the left.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale to introduce.</param>
|
||||
/// <returns>The scaled transformation matrix.</returns>
|
||||
public readonly Transform3D Scaled(Vector3 scale)
|
||||
{
|
||||
return new Transform3D(Basis.Scaled(scale), Origin * scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the transform by the given 3D <paramref name="scale"/> factor.
|
||||
/// The operation is done in the local frame, equivalent to
|
||||
/// multiplying the matrix from the right.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale to introduce.</param>
|
||||
/// <returns>The scaled transformation matrix.</returns>
|
||||
public readonly Transform3D ScaledLocal(Vector3 scale)
|
||||
{
|
||||
Basis tmpBasis = Basis.FromScale(scale);
|
||||
return new Transform3D(Basis * tmpBasis, Origin);
|
||||
}
|
||||
|
||||
private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up, bool useModelFront = false)
|
||||
{
|
||||
Basis = Basis.LookingAt(target - eye, up, useModelFront);
|
||||
Origin = eye;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the transform by the given <paramref name="offset"/>.
|
||||
/// The operation is done in the parent/global frame, equivalent to
|
||||
/// multiplying the matrix from the left.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset to translate by.</param>
|
||||
/// <returns>The translated matrix.</returns>
|
||||
public readonly Transform3D Translated(Vector3 offset)
|
||||
{
|
||||
return new Transform3D(Basis, Origin + offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the transform by the given <paramref name="offset"/>.
|
||||
/// The operation is done in the local frame, equivalent to
|
||||
/// multiplying the matrix from the right.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset to translate by.</param>
|
||||
/// <returns>The translated matrix.</returns>
|
||||
public readonly Transform3D TranslatedLocal(Vector3 offset)
|
||||
{
|
||||
return new Transform3D(Basis, new Vector3
|
||||
(
|
||||
Origin[0] + Basis.Row0.Dot(offset),
|
||||
Origin[1] + Basis.Row1.Dot(offset),
|
||||
Origin[2] + Basis.Row2.Dot(offset)
|
||||
));
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static readonly Transform3D _identity = new Transform3D(Basis.Identity, Vector3.Zero);
|
||||
private static readonly Transform3D _flipX = new Transform3D(new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1), Vector3.Zero);
|
||||
private static readonly Transform3D _flipY = new Transform3D(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero);
|
||||
private static readonly Transform3D _flipZ = new Transform3D(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero);
|
||||
|
||||
/// <summary>
|
||||
/// The identity transform, with no translation, rotation, or scaling applied.
|
||||
/// This is used as a replacement for <c>Transform()</c> in GDScript.
|
||||
/// Do not use <c>new Transform()</c> with no arguments in C#, because it sets all values to zero.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)</c>.</value>
|
||||
public static Transform3D Identity { get { return _identity; } }
|
||||
/// <summary>
|
||||
/// The transform that will flip something along the X axis.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)</c>.</value>
|
||||
public static Transform3D FlipX { get { return _flipX; } }
|
||||
/// <summary>
|
||||
/// The transform that will flip something along the Y axis.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)</c>.</value>
|
||||
public static Transform3D FlipY { get { return _flipY; } }
|
||||
/// <summary>
|
||||
/// The transform that will flip something along the Z axis.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)</c>.</value>
|
||||
public static Transform3D FlipZ { get { return _flipZ; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from 4 vectors (matrix columns).
|
||||
/// </summary>
|
||||
/// <param name="column0">The X vector, or column index 0.</param>
|
||||
/// <param name="column1">The Y vector, or column index 1.</param>
|
||||
/// <param name="column2">The Z vector, or column index 2.</param>
|
||||
/// <param name="origin">The origin vector, or column index 3.</param>
|
||||
public Transform3D(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin)
|
||||
{
|
||||
Basis = new Basis(column0, column1, column2);
|
||||
Origin = origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from the given components.
|
||||
/// Arguments are named such that xy is equal to calling <c>Basis.X.Y</c>.
|
||||
/// </summary>
|
||||
/// <param name="xx">The X component of the X column vector, accessed via <c>t.Basis.X.X</c> or <c>[0][0]</c>.</param>
|
||||
/// <param name="yx">The X component of the Y column vector, accessed via <c>t.Basis.Y.X</c> or <c>[1][0]</c>.</param>
|
||||
/// <param name="zx">The X component of the Z column vector, accessed via <c>t.Basis.Z.X</c> or <c>[2][0]</c>.</param>
|
||||
/// <param name="xy">The Y component of the X column vector, accessed via <c>t.Basis.X.Y</c> or <c>[0][1]</c>.</param>
|
||||
/// <param name="yy">The Y component of the Y column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[1][1]</c>.</param>
|
||||
/// <param name="zy">The Y component of the Z column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[2][1]</c>.</param>
|
||||
/// <param name="xz">The Z component of the X column vector, accessed via <c>t.Basis.X.Y</c> or <c>[0][2]</c>.</param>
|
||||
/// <param name="yz">The Z component of the Y column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[1][2]</c>.</param>
|
||||
/// <param name="zz">The Z component of the Z column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[2][2]</c>.</param>
|
||||
/// <param name="ox">The X component of the origin vector, accessed via <c>t.Origin.X</c> or <c>[2][0]</c>.</param>
|
||||
/// <param name="oy">The Y component of the origin vector, accessed via <c>t.Origin.Y</c> or <c>[2][1]</c>.</param>
|
||||
/// <param name="oz">The Z component of the origin vector, accessed via <c>t.Origin.Z</c> or <c>[2][2]</c>.</param>
|
||||
public Transform3D(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz, real_t ox, real_t oy, real_t oz)
|
||||
{
|
||||
Basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz);
|
||||
Origin = new Vector3(ox, oy, oz);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from the given <paramref name="basis"/> and
|
||||
/// <paramref name="origin"/> vector.
|
||||
/// </summary>
|
||||
/// <param name="basis">The <see cref="Godot.Basis"/> to create the basis from.</param>
|
||||
/// <param name="origin">The origin vector, or column index 3.</param>
|
||||
public Transform3D(Basis basis, Vector3 origin)
|
||||
{
|
||||
Basis = basis;
|
||||
Origin = origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a transformation matrix from the given <paramref name="projection"/>
|
||||
/// by trimming the last row of the projection matrix (<c>projection.X.W</c>,
|
||||
/// <c>projection.Y.W</c>, <c>projection.Z.W</c>, and <c>projection.W.W</c>
|
||||
/// are not copied over).
|
||||
/// </summary>
|
||||
/// <param name="projection">The <see cref="Projection"/> to create the transform from.</param>
|
||||
public Transform3D(Projection projection)
|
||||
{
|
||||
Basis = new Basis
|
||||
(
|
||||
projection.X.X, projection.Y.X, projection.Z.X,
|
||||
projection.X.Y, projection.Y.Y, projection.Z.Y,
|
||||
projection.X.Z, projection.Y.Z, projection.Z.Z
|
||||
);
|
||||
Origin = new Vector3
|
||||
(
|
||||
projection.W.X,
|
||||
projection.W.Y,
|
||||
projection.W.Z
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Composes these two transformation matrices by multiplying them
|
||||
/// together. This has the effect of transforming the second transform
|
||||
/// (the child) by the first transform (the parent).
|
||||
/// </summary>
|
||||
/// <param name="left">The parent transform.</param>
|
||||
/// <param name="right">The child transform.</param>
|
||||
/// <returns>The composed transform.</returns>
|
||||
public static Transform3D operator *(Transform3D left, Transform3D right)
|
||||
{
|
||||
left.Origin = left * right.Origin;
|
||||
left.Basis *= right.Basis;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector3 transformed (multiplied) by the transformation matrix.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <param name="vector">A Vector3 to transform.</param>
|
||||
/// <returns>The transformed Vector3.</returns>
|
||||
public static Vector3 operator *(Transform3D transform, Vector3 vector)
|
||||
{
|
||||
return new Vector3
|
||||
(
|
||||
transform.Basis.Row0.Dot(vector) + transform.Origin.X,
|
||||
transform.Basis.Row1.Dot(vector) + transform.Origin.Y,
|
||||
transform.Basis.Row2.Dot(vector) + transform.Origin.Z
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector3 transformed (multiplied) by the inverse transformation matrix,
|
||||
/// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not).
|
||||
/// <c>vector * transform</c> is equivalent to <c>transform.Inverse() * vector</c>. See <see cref="Inverse"/>.
|
||||
/// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * vector</c> can be used instead. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">A Vector3 to inversely transform.</param>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <returns>The inversely transformed Vector3.</returns>
|
||||
public static Vector3 operator *(Vector3 vector, Transform3D transform)
|
||||
{
|
||||
Vector3 vInv = vector - transform.Origin;
|
||||
|
||||
return new Vector3
|
||||
(
|
||||
(transform.Basis.Row0[0] * vInv.X) + (transform.Basis.Row1[0] * vInv.Y) + (transform.Basis.Row2[0] * vInv.Z),
|
||||
(transform.Basis.Row0[1] * vInv.X) + (transform.Basis.Row1[1] * vInv.Y) + (transform.Basis.Row2[1] * vInv.Z),
|
||||
(transform.Basis.Row0[2] * vInv.X) + (transform.Basis.Row1[2] * vInv.Y) + (transform.Basis.Row2[2] * vInv.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an AABB transformed (multiplied) by the transformation matrix.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <param name="aabb">An AABB to transform.</param>
|
||||
/// <returns>The transformed AABB.</returns>
|
||||
public static Aabb operator *(Transform3D transform, Aabb aabb)
|
||||
{
|
||||
Vector3 min = aabb.Position;
|
||||
Vector3 max = aabb.Position + aabb.Size;
|
||||
|
||||
Vector3 tmin = transform.Origin;
|
||||
Vector3 tmax = transform.Origin;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
real_t e = transform.Basis[j][i] * min[j];
|
||||
real_t f = transform.Basis[j][i] * max[j];
|
||||
if (e < f)
|
||||
{
|
||||
tmin[i] += e;
|
||||
tmax[i] += f;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmin[i] += f;
|
||||
tmax[i] += e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Aabb(tmin, tmax - tmin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an AABB transformed (multiplied) by the inverse transformation matrix,
|
||||
/// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not).
|
||||
/// <c>aabb * transform</c> is equivalent to <c>transform.Inverse() * aabb</c>. See <see cref="Inverse"/>.
|
||||
/// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * aabb</c> can be used instead. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="aabb">An AABB to inversely transform.</param>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <returns>The inversely transformed AABB.</returns>
|
||||
public static Aabb operator *(Aabb aabb, Transform3D transform)
|
||||
{
|
||||
Vector3 pos = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z + aabb.Size.Z) * transform;
|
||||
Vector3 to1 = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z) * transform;
|
||||
Vector3 to2 = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y, aabb.Position.Z + aabb.Size.Z) * transform;
|
||||
Vector3 to3 = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y, aabb.Position.Z) * transform;
|
||||
Vector3 to4 = new Vector3(aabb.Position.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z + aabb.Size.Z) * transform;
|
||||
Vector3 to5 = new Vector3(aabb.Position.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z) * transform;
|
||||
Vector3 to6 = new Vector3(aabb.Position.X, aabb.Position.Y, aabb.Position.Z + aabb.Size.Z) * transform;
|
||||
Vector3 to7 = new Vector3(aabb.Position.X, aabb.Position.Y, aabb.Position.Z) * transform;
|
||||
|
||||
return new Aabb(pos, new Vector3()).Expand(to1).Expand(to2).Expand(to3).Expand(to4).Expand(to5).Expand(to6).Expand(to7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Plane transformed (multiplied) by the transformation matrix.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <param name="plane">A Plane to transform.</param>
|
||||
/// <returns>The transformed Plane.</returns>
|
||||
public static Plane operator *(Transform3D transform, Plane plane)
|
||||
{
|
||||
Basis bInvTrans = transform.Basis.Inverse().Transposed();
|
||||
|
||||
// Transform a single point on the plane.
|
||||
Vector3 point = transform * (plane.Normal * plane.D);
|
||||
|
||||
// Use inverse transpose for correct normals with non-uniform scaling.
|
||||
Vector3 normal = (bInvTrans * plane.Normal).Normalized();
|
||||
|
||||
real_t d = normal.Dot(point);
|
||||
return new Plane(normal, d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Plane transformed (multiplied) by the inverse transformation matrix.
|
||||
/// <c>plane * transform</c> is equivalent to <c>transform.AffineInverse() * plane</c>. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="plane">A Plane to inversely transform.</param>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <returns>The inversely transformed Plane.</returns>
|
||||
public static Plane operator *(Plane plane, Transform3D transform)
|
||||
{
|
||||
Transform3D tInv = transform.AffineInverse();
|
||||
Basis bTrans = transform.Basis.Transposed();
|
||||
|
||||
// Transform a single point on the plane.
|
||||
Vector3 point = tInv * (plane.Normal * plane.D);
|
||||
|
||||
// Note that instead of precalculating the transpose, an alternative
|
||||
// would be to use the transpose for the basis transform.
|
||||
// However that would be less SIMD friendly (requiring a swizzle).
|
||||
// So the cost is one extra precalced value in the calling code.
|
||||
// This is probably worth it, as this could be used in bottleneck areas. And
|
||||
// where it is not a bottleneck, the non-fast method is fine.
|
||||
|
||||
// Use transpose for correct normals with non-uniform scaling.
|
||||
Vector3 normal = (bTrans * plane.Normal).Normalized();
|
||||
|
||||
real_t d = normal.Dot(point);
|
||||
return new Plane(normal, d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given Vector3[] transformed (multiplied) by the transformation matrix.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <param name="array">A Vector3[] to transform.</param>
|
||||
/// <returns>The transformed copy of the Vector3[].</returns>
|
||||
public static Vector3[] operator *(Transform3D transform, Vector3[] array)
|
||||
{
|
||||
Vector3[] newArray = new Vector3[array.Length];
|
||||
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
newArray[i] = transform * array[i];
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given Vector3[] transformed (multiplied) by the inverse transformation matrix,
|
||||
/// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection
|
||||
/// is fine, scaling/skew is not).
|
||||
/// <c>array * transform</c> is equivalent to <c>transform.Inverse() * array</c>. See <see cref="Inverse"/>.
|
||||
/// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * array</c> can be used instead. See <see cref="AffineInverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">A Vector3[] to inversely transform.</param>
|
||||
/// <param name="transform">The transformation to apply.</param>
|
||||
/// <returns>The inversely transformed copy of the Vector3[].</returns>
|
||||
public static Vector3[] operator *(Vector3[] array, Transform3D transform)
|
||||
{
|
||||
Vector3[] newArray = new Vector3[array.Length];
|
||||
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
newArray[i] = array[i] * transform;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transforms are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left transform.</param>
|
||||
/// <param name="right">The right transform.</param>
|
||||
/// <returns>Whether or not the transforms are exactly equal.</returns>
|
||||
public static bool operator ==(Transform3D left, Transform3D right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transforms are not equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="left">The left transform.</param>
|
||||
/// <param name="right">The right transform.</param>
|
||||
/// <returns>Whether or not the transforms are not equal.</returns>
|
||||
public static bool operator !=(Transform3D left, Transform3D right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transform is exactly equal
|
||||
/// to the given object (<paramref name="obj"/>).
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with.</param>
|
||||
/// <returns>Whether or not the transform and the object are exactly equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Transform3D other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the transforms are exactly equal.
|
||||
/// Note: Due to floating-point precision errors, consider using
|
||||
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
|
||||
/// </summary>
|
||||
/// <param name="other">The other transform to compare.</param>
|
||||
/// <returns>Whether or not the matrices are exactly equal.</returns>
|
||||
public readonly bool Equals(Transform3D other)
|
||||
{
|
||||
return Basis.Equals(other.Basis) && Origin.Equals(other.Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if this transform and <paramref name="other"/> are approximately equal,
|
||||
/// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
|
||||
/// </summary>
|
||||
/// <param name="other">The other transform to compare.</param>
|
||||
/// <returns>Whether or not the matrices are approximately equal.</returns>
|
||||
public readonly bool IsEqualApprox(Transform3D other)
|
||||
{
|
||||
return Basis.IsEqualApprox(other.Basis) && Origin.IsEqualApprox(other.Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Transform3D"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this transform.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Basis, Origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Transform3D"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this transform.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Transform3D"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this transform.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"[X: {Basis.X.ToString(format)}, Y: {Basis.Y.ToString(format)}, Z: {Basis.Z.ToString(format)}, O: {Origin.ToString(format)}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
953
modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
Normal file
953
modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
Normal file
@@ -0,0 +1,953 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Godot.NativeInterop;
|
||||
|
||||
namespace Godot;
|
||||
|
||||
#nullable enable
|
||||
|
||||
// TODO: Disabled because it is a false positive, see https://github.com/dotnet/roslyn-analyzers/issues/6151
|
||||
#pragma warning disable CA1001 // Types that own disposable fields should be disposable
|
||||
public partial struct Variant : IDisposable
|
||||
#pragma warning restore CA1001
|
||||
{
|
||||
internal godot_variant.movable NativeVar;
|
||||
private object? _obj;
|
||||
private Disposer? _disposer;
|
||||
|
||||
private sealed class Disposer : IDisposable
|
||||
{
|
||||
private godot_variant.movable _native;
|
||||
|
||||
private WeakReference<IDisposable>? _weakReferenceToSelf;
|
||||
|
||||
public Disposer(in godot_variant.movable nativeVar)
|
||||
{
|
||||
_native = nativeVar;
|
||||
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
|
||||
}
|
||||
|
||||
~Disposer()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
_native.DangerousSelfRef.Dispose();
|
||||
|
||||
if (_weakReferenceToSelf != null)
|
||||
{
|
||||
DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Variant(in godot_variant nativeVar)
|
||||
{
|
||||
NativeVar = (godot_variant.movable)nativeVar;
|
||||
_obj = null;
|
||||
|
||||
switch (nativeVar.Type)
|
||||
{
|
||||
case Type.Nil:
|
||||
case Type.Bool:
|
||||
case Type.Int:
|
||||
case Type.Float:
|
||||
case Type.Vector2:
|
||||
case Type.Vector2I:
|
||||
case Type.Rect2:
|
||||
case Type.Rect2I:
|
||||
case Type.Vector3:
|
||||
case Type.Vector3I:
|
||||
case Type.Vector4:
|
||||
case Type.Vector4I:
|
||||
case Type.Plane:
|
||||
case Type.Quaternion:
|
||||
case Type.Color:
|
||||
case Type.Rid:
|
||||
_disposer = null;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
_disposer = new Disposer(NativeVar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// Explicit name to make it very clear
|
||||
public static Variant CreateTakingOwnershipOfDisposableValue(in godot_variant nativeValueToOwn) =>
|
||||
new(nativeValueToOwn);
|
||||
|
||||
// Explicit name to make it very clear
|
||||
public static Variant CreateCopyingBorrowed(in godot_variant nativeValueToOwn) =>
|
||||
new(NativeFuncs.godotsharp_variant_new_copy(nativeValueToOwn));
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Godot.NativeInterop.godot_variant"/> from this instance.
|
||||
/// The caller is responsible of disposing the new instance to avoid memory leaks.
|
||||
/// </summary>
|
||||
public godot_variant CopyNativeVariant() =>
|
||||
NativeFuncs.godotsharp_variant_new_copy((godot_variant)NativeVar);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposer?.Dispose();
|
||||
NativeVar = default;
|
||||
_obj = null;
|
||||
}
|
||||
|
||||
// TODO: Consider renaming Variant.Type to VariantType and this property to Type. VariantType would also avoid ambiguity with System.Type.
|
||||
public Type VariantType => NativeVar.DangerousSelfRef.Type;
|
||||
|
||||
public override string ToString() => AsString();
|
||||
|
||||
public object? Obj =>
|
||||
_obj ??= NativeVar.DangerousSelfRef.Type switch
|
||||
{
|
||||
Type.Bool => AsBool(),
|
||||
Type.Int => AsInt64(),
|
||||
Type.Float => AsDouble(),
|
||||
Type.String => AsString(),
|
||||
Type.Vector2 => AsVector2(),
|
||||
Type.Vector2I => AsVector2I(),
|
||||
Type.Rect2 => AsRect2(),
|
||||
Type.Rect2I => AsRect2I(),
|
||||
Type.Vector3 => AsVector3(),
|
||||
Type.Vector3I => AsVector3I(),
|
||||
Type.Transform2D => AsTransform2D(),
|
||||
Type.Vector4 => AsVector4(),
|
||||
Type.Vector4I => AsVector4I(),
|
||||
Type.Plane => AsPlane(),
|
||||
Type.Quaternion => AsQuaternion(),
|
||||
Type.Aabb => AsAabb(),
|
||||
Type.Basis => AsBasis(),
|
||||
Type.Transform3D => AsTransform3D(),
|
||||
Type.Projection => AsProjection(),
|
||||
Type.Color => AsColor(),
|
||||
Type.StringName => AsStringName(),
|
||||
Type.NodePath => AsNodePath(),
|
||||
Type.Rid => AsRid(),
|
||||
Type.Object => AsGodotObject(),
|
||||
Type.Callable => AsCallable(),
|
||||
Type.Signal => AsSignal(),
|
||||
Type.Dictionary => AsGodotDictionary(),
|
||||
Type.Array => AsGodotArray(),
|
||||
Type.PackedByteArray => AsByteArray(),
|
||||
Type.PackedInt32Array => AsInt32Array(),
|
||||
Type.PackedInt64Array => AsInt64Array(),
|
||||
Type.PackedFloat32Array => AsFloat32Array(),
|
||||
Type.PackedFloat64Array => AsFloat64Array(),
|
||||
Type.PackedStringArray => AsStringArray(),
|
||||
Type.PackedVector2Array => AsVector2Array(),
|
||||
Type.PackedVector3Array => AsVector3Array(),
|
||||
Type.PackedVector4Array => AsVector4Array(),
|
||||
Type.PackedColorArray => AsColorArray(),
|
||||
Type.Nil => null,
|
||||
Type.Max or _ =>
|
||||
throw new InvalidOperationException($"Invalid Variant type: {NativeVar.DangerousSelfRef.Type}"),
|
||||
};
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant From<[MustBeVariant] T>(in T from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFrom(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T As<[MustBeVariant] T>() =>
|
||||
VariantUtils.ConvertTo<T>(NativeVar.DangerousSelfRef);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool AsBool() =>
|
||||
VariantUtils.ConvertToBool((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public char AsChar() =>
|
||||
(char)VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public sbyte AsSByte() =>
|
||||
VariantUtils.ConvertToInt8((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public short AsInt16() =>
|
||||
VariantUtils.ConvertToInt16((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int AsInt32() =>
|
||||
VariantUtils.ConvertToInt32((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public long AsInt64() =>
|
||||
VariantUtils.ConvertToInt64((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public byte AsByte() =>
|
||||
VariantUtils.ConvertToUInt8((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ushort AsUInt16() =>
|
||||
VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public uint AsUInt32() =>
|
||||
VariantUtils.ConvertToUInt32((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ulong AsUInt64() =>
|
||||
VariantUtils.ConvertToUInt64((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public float AsSingle() =>
|
||||
VariantUtils.ConvertToFloat32((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public double AsDouble() =>
|
||||
VariantUtils.ConvertToFloat64((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string AsString() =>
|
||||
VariantUtils.ConvertToString((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector2 AsVector2() =>
|
||||
VariantUtils.ConvertToVector2((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector2I AsVector2I() =>
|
||||
VariantUtils.ConvertToVector2I((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Rect2 AsRect2() =>
|
||||
VariantUtils.ConvertToRect2((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Rect2I AsRect2I() =>
|
||||
VariantUtils.ConvertToRect2I((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Transform2D AsTransform2D() =>
|
||||
VariantUtils.ConvertToTransform2D((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector3 AsVector3() =>
|
||||
VariantUtils.ConvertToVector3((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector3I AsVector3I() =>
|
||||
VariantUtils.ConvertToVector3I((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Basis AsBasis() =>
|
||||
VariantUtils.ConvertToBasis((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Quaternion AsQuaternion() =>
|
||||
VariantUtils.ConvertToQuaternion((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Transform3D AsTransform3D() =>
|
||||
VariantUtils.ConvertToTransform3D((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector4 AsVector4() =>
|
||||
VariantUtils.ConvertToVector4((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector4I AsVector4I() =>
|
||||
VariantUtils.ConvertToVector4I((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Projection AsProjection() =>
|
||||
VariantUtils.ConvertToProjection((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Aabb AsAabb() =>
|
||||
VariantUtils.ConvertToAabb((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Color AsColor() =>
|
||||
VariantUtils.ConvertToColor((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Plane AsPlane() =>
|
||||
VariantUtils.ConvertToPlane((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Callable AsCallable() =>
|
||||
VariantUtils.ConvertToCallable((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Signal AsSignal() =>
|
||||
VariantUtils.ConvertToSignal((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public byte[] AsByteArray() =>
|
||||
VariantUtils.ConvertAsPackedByteArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int[] AsInt32Array() =>
|
||||
VariantUtils.ConvertAsPackedInt32ArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public long[] AsInt64Array() =>
|
||||
VariantUtils.ConvertAsPackedInt64ArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public float[] AsFloat32Array() =>
|
||||
VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public double[] AsFloat64Array() =>
|
||||
VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string[] AsStringArray() =>
|
||||
VariantUtils.ConvertAsPackedStringArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector2[] AsVector2Array() =>
|
||||
VariantUtils.ConvertAsPackedVector2ArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector3[] AsVector3Array() =>
|
||||
VariantUtils.ConvertAsPackedVector3ArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector4[] AsVector4Array() =>
|
||||
VariantUtils.ConvertAsPackedVector4ArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Color[] AsColorArray() =>
|
||||
VariantUtils.ConvertAsPackedColorArrayToSystemArray((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T[] AsGodotObjectArray<T>()
|
||||
where T : GodotObject =>
|
||||
VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Collections.Dictionary<TKey, TValue> AsGodotDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>() =>
|
||||
VariantUtils.ConvertToDictionary<TKey, TValue>((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Collections.Array<T> AsGodotArray<[MustBeVariant] T>() =>
|
||||
VariantUtils.ConvertToArray<T>((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public StringName[] AsSystemArrayOfStringName() =>
|
||||
VariantUtils.ConvertToSystemArrayOfStringName((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public NodePath[] AsSystemArrayOfNodePath() =>
|
||||
VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Rid[] AsSystemArrayOfRid() =>
|
||||
VariantUtils.ConvertToSystemArrayOfRid((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public GodotObject AsGodotObject() =>
|
||||
VariantUtils.ConvertToGodotObject((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public StringName AsStringName() =>
|
||||
VariantUtils.ConvertToStringName((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public NodePath AsNodePath() =>
|
||||
VariantUtils.ConvertToNodePath((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Rid AsRid() =>
|
||||
VariantUtils.ConvertToRid((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Collections.Dictionary AsGodotDictionary() =>
|
||||
VariantUtils.ConvertToDictionary((godot_variant)NativeVar);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Collections.Array AsGodotArray() =>
|
||||
VariantUtils.ConvertToArray((godot_variant)NativeVar);
|
||||
|
||||
// Explicit conversion operators to supported types
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator bool(Variant from) => from.AsBool();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator char(Variant from) => from.AsChar();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator sbyte(Variant from) => from.AsSByte();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator short(Variant from) => from.AsInt16();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator int(Variant from) => from.AsInt32();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator long(Variant from) => from.AsInt64();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator byte(Variant from) => from.AsByte();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator ushort(Variant from) => from.AsUInt16();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator uint(Variant from) => from.AsUInt32();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator ulong(Variant from) => from.AsUInt64();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator float(Variant from) => from.AsSingle();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator double(Variant from) => from.AsDouble();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator string(Variant from) => from.AsString();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector2(Variant from) => from.AsVector2();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector2I(Variant from) => from.AsVector2I();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Rect2(Variant from) => from.AsRect2();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Rect2I(Variant from) => from.AsRect2I();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Transform2D(Variant from) => from.AsTransform2D();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector3(Variant from) => from.AsVector3();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector3I(Variant from) => from.AsVector3I();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Basis(Variant from) => from.AsBasis();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Quaternion(Variant from) => from.AsQuaternion();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Transform3D(Variant from) => from.AsTransform3D();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector4(Variant from) => from.AsVector4();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector4I(Variant from) => from.AsVector4I();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Projection(Variant from) => from.AsProjection();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Aabb(Variant from) => from.AsAabb();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Color(Variant from) => from.AsColor();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Plane(Variant from) => from.AsPlane();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Callable(Variant from) => from.AsCallable();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Signal(Variant from) => from.AsSignal();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator byte[](Variant from) => from.AsByteArray();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator int[](Variant from) => from.AsInt32Array();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator long[](Variant from) => from.AsInt64Array();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator float[](Variant from) => from.AsFloat32Array();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator double[](Variant from) => from.AsFloat64Array();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator string[](Variant from) => from.AsStringArray();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector2[](Variant from) => from.AsVector2Array();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector3[](Variant from) => from.AsVector3Array();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Vector4[](Variant from) => from.AsVector4Array();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Color[](Variant from) => from.AsColorArray();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator StringName[](Variant from) => from.AsSystemArrayOfStringName();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Rid[](Variant from) => from.AsSystemArrayOfRid();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator GodotObject(Variant from) => from.AsGodotObject();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator StringName(Variant from) => from.AsStringName();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator NodePath(Variant from) => from.AsNodePath();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Rid(Variant from) => from.AsRid();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Collections.Dictionary(Variant from) => from.AsGodotDictionary();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Collections.Array(Variant from) => from.AsGodotArray();
|
||||
|
||||
// While we provide implicit conversion operators, normal methods are still needed for
|
||||
// casts that are not done implicitly (e.g.: raw array to Span, enum to integer, etc).
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(bool from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(char from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(sbyte from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(short from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(int from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(long from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(byte from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(ushort from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(uint from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(ulong from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(float from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(double from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(string from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Vector2 from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Vector2I from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Rect2 from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Rect2I from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Transform2D from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Vector3 from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Vector3I from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Basis from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Quaternion from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Transform3D from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Vector4 from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Vector4I from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Projection from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Aabb from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Color from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Plane from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Callable from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Signal from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<byte> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<int> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<long> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<float> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<double> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<string> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<Vector2> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<Vector3> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<Vector4> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<Color> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(GodotObject[] from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom<[MustBeVariant] TKey, [MustBeVariant] TValue>(Collections.Dictionary<TKey, TValue> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom<[MustBeVariant] T>(Collections.Array<T> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<StringName> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<NodePath> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Span<Rid> from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(GodotObject from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(StringName from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(NodePath from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Rid from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Collections.Dictionary from) => from;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant CreateFrom(Collections.Array from) => from;
|
||||
|
||||
// Implicit conversion operators
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(bool from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBool(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(char from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(sbyte from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(short from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(int from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(long from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(byte from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(ushort from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(uint from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(ulong from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(float from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(double from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(string from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromString(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector2 from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector2I from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2I(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Rect2 from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Rect2I from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2I(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Transform2D from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform2D(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector3 from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector3I from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3I(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Basis from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBasis(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Quaternion from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromQuaternion(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Transform3D from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform3D(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector4 from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector4I from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4I(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Projection from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Aabb from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAabb(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Color from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromColor(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Plane from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPlane(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Callable from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromCallable(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Signal from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSignal(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(byte[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(int[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(long[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(float[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(double[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(string[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector2[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector3[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Vector4[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Color[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(GodotObject[] from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(StringName[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(NodePath[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Rid[] from) =>
|
||||
(Variant)from.AsSpan();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<byte> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedByteArray(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<int> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt32Array(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<long> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt64Array(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<float> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat32Array(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<double> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat64Array(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<string> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedStringArray(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<Vector2> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector2Array(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<Vector3> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector3Array(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<Vector4> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector4Array(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<Color> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<StringName> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfStringName(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<NodePath> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Span<Rid> from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRid(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(GodotObject from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromGodotObject(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(StringName from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromStringName(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(NodePath from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromNodePath(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Rid from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRid(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Collections.Dictionary from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Variant(Collections.Array from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
|
||||
}
|
||||
1123
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
Normal file
1123
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
Normal file
File diff suppressed because it is too large
Load Diff
713
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
Normal file
713
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
Normal file
@@ -0,0 +1,713 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// 2-element structure that can be used to represent 2D grid coordinates or pairs of integers.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector2I : IEquatable<Vector2I>
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerated index values for the axes.
|
||||
/// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
|
||||
/// </summary>
|
||||
public enum Axis
|
||||
{
|
||||
/// <summary>
|
||||
/// The vector's X axis.
|
||||
/// </summary>
|
||||
X = 0,
|
||||
/// <summary>
|
||||
/// The vector's Y axis.
|
||||
/// </summary>
|
||||
Y
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The vector's X component. Also accessible by using the index position <c>[0]</c>.
|
||||
/// </summary>
|
||||
public int X;
|
||||
|
||||
/// <summary>
|
||||
/// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
|
||||
/// </summary>
|
||||
public int Y;
|
||||
|
||||
/// <summary>
|
||||
/// Access vector components using their index.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="index"/> is not 0 or 1.
|
||||
/// </exception>
|
||||
/// <value>
|
||||
/// <c>[0]</c> is equivalent to <see cref="X"/>,
|
||||
/// <c>[1]</c> is equivalent to <see cref="Y"/>.
|
||||
/// </value>
|
||||
public int this[int index]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return X;
|
||||
case 1:
|
||||
return Y;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
X = value;
|
||||
return;
|
||||
case 1:
|
||||
Y = value;
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for deconstruction into a tuple.
|
||||
/// </summary>
|
||||
public readonly void Deconstruct(out int x, out int y)
|
||||
{
|
||||
x = X;
|
||||
y = Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components in absolute values (i.e. positive).
|
||||
/// </summary>
|
||||
/// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
|
||||
public readonly Vector2I Abs()
|
||||
{
|
||||
return new Vector2I(Mathf.Abs(X), Mathf.Abs(Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the aspect ratio of this vector, the ratio of <see cref="X"/> to <see cref="Y"/>.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="X"/> component divided by the <see cref="Y"/> component.</returns>
|
||||
public readonly real_t Aspect()
|
||||
{
|
||||
return X / (real_t)Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components clamped between the
|
||||
/// components of <paramref name="min"/> and <paramref name="max"/> using
|
||||
/// <see cref="Mathf.Clamp(int, int, int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="min">The vector with minimum allowed values.</param>
|
||||
/// <param name="max">The vector with maximum allowed values.</param>
|
||||
/// <returns>The vector with all components clamped.</returns>
|
||||
public readonly Vector2I Clamp(Vector2I min, Vector2I max)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
Mathf.Clamp(X, min.X, max.X),
|
||||
Mathf.Clamp(Y, min.Y, max.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components clamped between the
|
||||
/// <paramref name="min"/> and <paramref name="max"/> using
|
||||
/// <see cref="Mathf.Clamp(int, int, int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum allowed value.</param>
|
||||
/// <param name="max">The maximum allowed value.</param>
|
||||
/// <returns>The vector with all components clamped.</returns>
|
||||
public readonly Vector2I Clamp(int min, int max)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
Mathf.Clamp(X, min, max),
|
||||
Mathf.Clamp(Y, min, max)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared distance between this vector and <paramref name="to"/>.
|
||||
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
|
||||
/// you need to compare vectors or need the squared distance for some formula.
|
||||
/// </summary>
|
||||
/// <param name="to">The other vector to use.</param>
|
||||
/// <returns>The squared distance between the two vectors.</returns>
|
||||
public readonly int DistanceSquaredTo(Vector2I to)
|
||||
{
|
||||
return (to - this).LengthSquared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance between this vector and <paramref name="to"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="DistanceSquaredTo(Vector2I)"/>
|
||||
/// <param name="to">The other vector to use.</param>
|
||||
/// <returns>The distance between the two vectors.</returns>
|
||||
public readonly real_t DistanceTo(Vector2I to)
|
||||
{
|
||||
return (to - this).Length();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length (magnitude) of this vector.
|
||||
/// </summary>
|
||||
/// <seealso cref="LengthSquared"/>
|
||||
/// <returns>The length of this vector.</returns>
|
||||
public readonly real_t Length()
|
||||
{
|
||||
int x2 = X * X;
|
||||
int y2 = Y * Y;
|
||||
|
||||
return Mathf.Sqrt(x2 + y2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length (squared magnitude) of this vector.
|
||||
/// This method runs faster than <see cref="Length"/>, so prefer it if
|
||||
/// you need to compare vectors or need the squared length for some formula.
|
||||
/// </summary>
|
||||
/// <returns>The squared length of this vector.</returns>
|
||||
public readonly int LengthSquared()
|
||||
{
|
||||
int x2 = X * X;
|
||||
int y2 = Y * Y;
|
||||
|
||||
return x2 + y2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise maximum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector2I(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other vector to use.</param>
|
||||
/// <returns>The resulting maximum vector.</returns>
|
||||
public readonly Vector2I Max(Vector2I with)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
Mathf.Max(X, with.X),
|
||||
Mathf.Max(Y, with.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise maximum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector2I(Mathf.Max(X, with), Mathf.Max(Y, with))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other value to use.</param>
|
||||
/// <returns>The resulting maximum vector.</returns>
|
||||
public readonly Vector2I Max(int with)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
Mathf.Max(X, with),
|
||||
Mathf.Max(Y, with)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise minimum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector2I(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other vector to use.</param>
|
||||
/// <returns>The resulting minimum vector.</returns>
|
||||
public readonly Vector2I Min(Vector2I with)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
Mathf.Min(X, with.X),
|
||||
Mathf.Min(Y, with.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise minimum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector2I(Mathf.Min(X, with), Mathf.Min(Y, with))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other value to use.</param>
|
||||
/// <returns>The resulting minimum vector.</returns>
|
||||
public readonly Vector2I Min(int with)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
Mathf.Min(X, with),
|
||||
Mathf.Min(Y, with)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
|
||||
/// If both components are equal, this method returns <see cref="Axis.X"/>.
|
||||
/// </summary>
|
||||
/// <returns>The index of the highest axis.</returns>
|
||||
public readonly Axis MaxAxisIndex()
|
||||
{
|
||||
return X < Y ? Axis.Y : Axis.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
|
||||
/// If both components are equal, this method returns <see cref="Axis.Y"/>.
|
||||
/// </summary>
|
||||
/// <returns>The index of the lowest axis.</returns>
|
||||
public readonly Axis MinAxisIndex()
|
||||
{
|
||||
return X < Y ? Axis.X : Axis.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector with each component set to one or negative one, depending
|
||||
/// on the signs of this vector's components, or zero if the component is zero,
|
||||
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
|
||||
public readonly Vector2I Sign()
|
||||
{
|
||||
Vector2I v = this;
|
||||
v.X = Mathf.Sign(v.X);
|
||||
v.Y = Mathf.Sign(v.Y);
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with each component snapped to the closest multiple of the corresponding component in <paramref name="step"/>.
|
||||
/// </summary>
|
||||
/// <param name="step">A vector value representing the step size to snap to.</param>
|
||||
/// <returns>The snapped vector.</returns>
|
||||
public readonly Vector2I Snapped(Vector2I step)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
(int)Mathf.Snapped((double)X, (double)step.X),
|
||||
(int)Mathf.Snapped((double)Y, (double)step.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with each component snapped to the closest multiple of <paramref name="step"/>.
|
||||
/// </summary>
|
||||
/// <param name="step">The step size to snap to.</param>
|
||||
/// <returns>The snapped vector.</returns>
|
||||
public readonly Vector2I Snapped(int step)
|
||||
{
|
||||
return new Vector2I
|
||||
(
|
||||
(int)Mathf.Snapped((double)X, (double)step),
|
||||
(int)Mathf.Snapped((double)Y, (double)step)
|
||||
);
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static readonly Vector2I _minValue = new Vector2I(int.MinValue, int.MinValue);
|
||||
private static readonly Vector2I _maxValue = new Vector2I(int.MaxValue, int.MaxValue);
|
||||
|
||||
private static readonly Vector2I _zero = new Vector2I(0, 0);
|
||||
private static readonly Vector2I _one = new Vector2I(1, 1);
|
||||
|
||||
private static readonly Vector2I _up = new Vector2I(0, -1);
|
||||
private static readonly Vector2I _down = new Vector2I(0, 1);
|
||||
private static readonly Vector2I _right = new Vector2I(1, 0);
|
||||
private static readonly Vector2I _left = new Vector2I(-1, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector2.Inf"/>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(int.MinValue, int.MinValue)</c>.</value>
|
||||
public static Vector2I MinValue { get { return _minValue; } }
|
||||
/// <summary>
|
||||
/// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector2.Inf"/>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(int.MaxValue, int.MaxValue)</c>.</value>
|
||||
public static Vector2I MaxValue { get { return _maxValue; } }
|
||||
|
||||
/// <summary>
|
||||
/// Zero vector, a vector with all components set to <c>0</c>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(0, 0)</c>.</value>
|
||||
public static Vector2I Zero { get { return _zero; } }
|
||||
/// <summary>
|
||||
/// One vector, a vector with all components set to <c>1</c>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(1, 1)</c>.</value>
|
||||
public static Vector2I One { get { return _one; } }
|
||||
|
||||
/// <summary>
|
||||
/// Up unit vector. Y is down in 2D, so this vector points -Y.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(0, -1)</c>.</value>
|
||||
public static Vector2I Up { get { return _up; } }
|
||||
/// <summary>
|
||||
/// Down unit vector. Y is down in 2D, so this vector points +Y.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(0, 1)</c>.</value>
|
||||
public static Vector2I Down { get { return _down; } }
|
||||
/// <summary>
|
||||
/// Right unit vector. Represents the direction of right.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(1, 0)</c>.</value>
|
||||
public static Vector2I Right { get { return _right; } }
|
||||
/// <summary>
|
||||
/// Left unit vector. Represents the direction of left.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector2I(-1, 0)</c>.</value>
|
||||
public static Vector2I Left { get { return _left; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Vector2I"/> with the given components.
|
||||
/// </summary>
|
||||
/// <param name="x">The vector's X component.</param>
|
||||
/// <param name="y">The vector's Y component.</param>
|
||||
public Vector2I(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds each component of the <see cref="Vector2I"/>
|
||||
/// with the components of the given <see cref="Vector2I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The added vector.</returns>
|
||||
public static Vector2I operator +(Vector2I left, Vector2I right)
|
||||
{
|
||||
left.X += right.X;
|
||||
left.Y += right.Y;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts each component of the <see cref="Vector2I"/>
|
||||
/// by the components of the given <see cref="Vector2I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The subtracted vector.</returns>
|
||||
public static Vector2I operator -(Vector2I left, Vector2I right)
|
||||
{
|
||||
left.X -= right.X;
|
||||
left.Y -= right.Y;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the negative value of the <see cref="Vector2I"/>.
|
||||
/// This is the same as writing <c>new Vector2I(-v.X, -v.Y)</c>.
|
||||
/// This operation flips the direction of the vector while
|
||||
/// keeping the same magnitude.
|
||||
/// </summary>
|
||||
/// <param name="vec">The vector to negate/flip.</param>
|
||||
/// <returns>The negated/flipped vector.</returns>
|
||||
public static Vector2I operator -(Vector2I vec)
|
||||
{
|
||||
vec.X = -vec.X;
|
||||
vec.Y = -vec.Y;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector2I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The vector to multiply.</param>
|
||||
/// <param name="scale">The scale to multiply by.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector2I operator *(Vector2I vec, int scale)
|
||||
{
|
||||
vec.X *= scale;
|
||||
vec.Y *= scale;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector2I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale to multiply by.</param>
|
||||
/// <param name="vec">The vector to multiply.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector2I operator *(int scale, Vector2I vec)
|
||||
{
|
||||
vec.X *= scale;
|
||||
vec.Y *= scale;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector2I"/>
|
||||
/// by the components of the given <see cref="Vector2I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector2I operator *(Vector2I left, Vector2I right)
|
||||
{
|
||||
left.X *= right.X;
|
||||
left.Y *= right.Y;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides each component of the <see cref="Vector2I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisor">The divisor value.</param>
|
||||
/// <returns>The divided vector.</returns>
|
||||
public static Vector2I operator /(Vector2I vec, int divisor)
|
||||
{
|
||||
vec.X /= divisor;
|
||||
vec.Y /= divisor;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides each component of the <see cref="Vector2I"/>
|
||||
/// by the components of the given <see cref="Vector2I"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisorv">The divisor vector.</param>
|
||||
/// <returns>The divided vector.</returns>
|
||||
public static Vector2I operator /(Vector2I vec, Vector2I divisorv)
|
||||
{
|
||||
vec.X /= divisorv.X;
|
||||
vec.Y /= divisorv.Y;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remainder of each component of the <see cref="Vector2I"/>
|
||||
/// with the components of the given <see langword="int"/>.
|
||||
/// This operation uses truncated division, which is often not desired
|
||||
/// as it does not work well with negative numbers.
|
||||
/// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
|
||||
/// if you want to handle negative numbers.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Print(new Vector2I(10, -20) % 7); // Prints "(3, -6)"
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisor">The divisor value.</param>
|
||||
/// <returns>The remainder vector.</returns>
|
||||
public static Vector2I operator %(Vector2I vec, int divisor)
|
||||
{
|
||||
vec.X %= divisor;
|
||||
vec.Y %= divisor;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remainder of each component of the <see cref="Vector2I"/>
|
||||
/// with the components of the given <see cref="Vector2I"/>.
|
||||
/// This operation uses truncated division, which is often not desired
|
||||
/// as it does not work well with negative numbers.
|
||||
/// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
|
||||
/// if you want to handle negative numbers.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Print(new Vector2I(10, -20) % new Vector2I(7, 8)); // Prints "(3, -4)"
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisorv">The divisor vector.</param>
|
||||
/// <returns>The remainder vector.</returns>
|
||||
public static Vector2I operator %(Vector2I vec, Vector2I divisorv)
|
||||
{
|
||||
vec.X %= divisorv.X;
|
||||
vec.Y %= divisorv.Y;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the vectors are equal.</returns>
|
||||
public static bool operator ==(Vector2I left, Vector2I right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the vectors are not equal.</returns>
|
||||
public static bool operator !=(Vector2I left, Vector2I right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector2I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is less than
|
||||
/// the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is less than the right.</returns>
|
||||
public static bool operator <(Vector2I left, Vector2I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
return left.Y < right.Y;
|
||||
}
|
||||
return left.X < right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector2I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is greater than
|
||||
/// the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is greater than the right.</returns>
|
||||
public static bool operator >(Vector2I left, Vector2I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
return left.Y > right.Y;
|
||||
}
|
||||
return left.X > right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector2I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is less than
|
||||
/// or equal to the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is less than or equal to the right.</returns>
|
||||
public static bool operator <=(Vector2I left, Vector2I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
return left.Y <= right.Y;
|
||||
}
|
||||
return left.X < right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector2I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is greater than
|
||||
/// or equal to the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is greater than or equal to the right.</returns>
|
||||
public static bool operator >=(Vector2I left, Vector2I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
return left.Y >= right.Y;
|
||||
}
|
||||
return left.X > right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector2I"/> to a <see cref="Vector2"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to convert.</param>
|
||||
public static implicit operator Vector2(Vector2I value)
|
||||
{
|
||||
return new Vector2(value.X, value.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Vector2"/> to a <see cref="Vector2I"/> by truncating
|
||||
/// components' fractional parts (rounding towards zero). For a different
|
||||
/// behavior consider passing the result of <see cref="Vector2.Ceil"/>,
|
||||
/// <see cref="Vector2.Floor"/> or <see cref="Vector2.Round"/> to this conversion operator instead.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to convert.</param>
|
||||
public static explicit operator Vector2I(Vector2 value)
|
||||
{
|
||||
return new Vector2I((int)value.X, (int)value.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vector is equal
|
||||
/// to the given object (<paramref name="obj"/>).
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with.</param>
|
||||
/// <returns>Whether or not the vector and the object are equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Vector2I other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector.</param>
|
||||
/// <returns>Whether or not the vectors are equal.</returns>
|
||||
public readonly bool Equals(Vector2I other)
|
||||
{
|
||||
return X == other.X && Y == other.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Vector2I"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this vector.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector2I"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this vector.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector2I"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this vector.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"({X.ToString(format, CultureInfo.InvariantCulture)}, {Y.ToString(format, CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
}
|
||||
}
|
||||
1301
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
Normal file
1301
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
Normal file
File diff suppressed because it is too large
Load Diff
775
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
Normal file
775
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
Normal file
@@ -0,0 +1,775 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// 3-element structure that can be used to represent 3D grid coordinates or sets of integers.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector3I : IEquatable<Vector3I>
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerated index values for the axes.
|
||||
/// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
|
||||
/// </summary>
|
||||
public enum Axis
|
||||
{
|
||||
/// <summary>
|
||||
/// The vector's X axis.
|
||||
/// </summary>
|
||||
X = 0,
|
||||
/// <summary>
|
||||
/// The vector's Y axis.
|
||||
/// </summary>
|
||||
Y,
|
||||
/// <summary>
|
||||
/// The vector's Z axis.
|
||||
/// </summary>
|
||||
Z
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The vector's X component. Also accessible by using the index position <c>[0]</c>.
|
||||
/// </summary>
|
||||
public int X;
|
||||
|
||||
/// <summary>
|
||||
/// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
|
||||
/// </summary>
|
||||
public int Y;
|
||||
|
||||
/// <summary>
|
||||
/// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
|
||||
/// </summary>
|
||||
public int Z;
|
||||
|
||||
/// <summary>
|
||||
/// Access vector components using their <paramref name="index"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="index"/> is not 0, 1 or 2.
|
||||
/// </exception>
|
||||
/// <value>
|
||||
/// <c>[0]</c> is equivalent to <see cref="X"/>,
|
||||
/// <c>[1]</c> is equivalent to <see cref="Y"/>,
|
||||
/// <c>[2]</c> is equivalent to <see cref="Z"/>.
|
||||
/// </value>
|
||||
public int this[int index]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return X;
|
||||
case 1:
|
||||
return Y;
|
||||
case 2:
|
||||
return Z;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
X = value;
|
||||
return;
|
||||
case 1:
|
||||
Y = value;
|
||||
return;
|
||||
case 2:
|
||||
Z = value;
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for deconstruction into a tuple.
|
||||
/// </summary>
|
||||
public readonly void Deconstruct(out int x, out int y, out int z)
|
||||
{
|
||||
x = X;
|
||||
y = Y;
|
||||
z = Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components in absolute values (i.e. positive).
|
||||
/// </summary>
|
||||
/// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
|
||||
public readonly Vector3I Abs()
|
||||
{
|
||||
return new Vector3I(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components clamped between the
|
||||
/// components of <paramref name="min"/> and <paramref name="max"/> using
|
||||
/// <see cref="Mathf.Clamp(int, int, int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="min">The vector with minimum allowed values.</param>
|
||||
/// <param name="max">The vector with maximum allowed values.</param>
|
||||
/// <returns>The vector with all components clamped.</returns>
|
||||
public readonly Vector3I Clamp(Vector3I min, Vector3I max)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
Mathf.Clamp(X, min.X, max.X),
|
||||
Mathf.Clamp(Y, min.Y, max.Y),
|
||||
Mathf.Clamp(Z, min.Z, max.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components clamped between the
|
||||
/// <paramref name="min"/> and <paramref name="max"/> using
|
||||
/// <see cref="Mathf.Clamp(int, int, int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum allowed value.</param>
|
||||
/// <param name="max">The maximum allowed value.</param>
|
||||
/// <returns>The vector with all components clamped.</returns>
|
||||
public readonly Vector3I Clamp(int min, int max)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
Mathf.Clamp(X, min, max),
|
||||
Mathf.Clamp(Y, min, max),
|
||||
Mathf.Clamp(Z, min, max)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared distance between this vector and <paramref name="to"/>.
|
||||
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
|
||||
/// you need to compare vectors or need the squared distance for some formula.
|
||||
/// </summary>
|
||||
/// <param name="to">The other vector to use.</param>
|
||||
/// <returns>The squared distance between the two vectors.</returns>
|
||||
public readonly int DistanceSquaredTo(Vector3I to)
|
||||
{
|
||||
return (to - this).LengthSquared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance between this vector and <paramref name="to"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="DistanceSquaredTo(Vector3I)"/>
|
||||
/// <param name="to">The other vector to use.</param>
|
||||
/// <returns>The distance between the two vectors.</returns>
|
||||
public readonly real_t DistanceTo(Vector3I to)
|
||||
{
|
||||
return (to - this).Length();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length (magnitude) of this vector.
|
||||
/// </summary>
|
||||
/// <seealso cref="LengthSquared"/>
|
||||
/// <returns>The length of this vector.</returns>
|
||||
public readonly real_t Length()
|
||||
{
|
||||
int x2 = X * X;
|
||||
int y2 = Y * Y;
|
||||
int z2 = Z * Z;
|
||||
|
||||
return Mathf.Sqrt(x2 + y2 + z2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length (squared magnitude) of this vector.
|
||||
/// This method runs faster than <see cref="Length"/>, so prefer it if
|
||||
/// you need to compare vectors or need the squared length for some formula.
|
||||
/// </summary>
|
||||
/// <returns>The squared length of this vector.</returns>
|
||||
public readonly int LengthSquared()
|
||||
{
|
||||
int x2 = X * X;
|
||||
int y2 = Y * Y;
|
||||
int z2 = Z * Z;
|
||||
|
||||
return x2 + y2 + z2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise maximum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector3I(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y), Mathf.Max(Z, with.Z))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other vector to use.</param>
|
||||
/// <returns>The resulting maximum vector.</returns>
|
||||
public readonly Vector3I Max(Vector3I with)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
Mathf.Max(X, with.X),
|
||||
Mathf.Max(Y, with.Y),
|
||||
Mathf.Max(Z, with.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise maximum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector3I(Mathf.Max(X, with), Mathf.Max(Y, with), Mathf.Max(Z, with))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other value to use.</param>
|
||||
/// <returns>The resulting maximum vector.</returns>
|
||||
public readonly Vector3I Max(int with)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
Mathf.Max(X, with),
|
||||
Mathf.Max(Y, with),
|
||||
Mathf.Max(Z, with)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise minimum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector3I(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y), Mathf.Min(Z, with.Z))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other vector to use.</param>
|
||||
/// <returns>The resulting minimum vector.</returns>
|
||||
public readonly Vector3I Min(Vector3I with)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
Mathf.Min(X, with.X),
|
||||
Mathf.Min(Y, with.Y),
|
||||
Mathf.Min(Z, with.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise minimum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector3I(Mathf.Min(X, with), Mathf.Min(Y, with), Mathf.Min(Z, with))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other value to use.</param>
|
||||
/// <returns>The resulting minimum vector.</returns>
|
||||
public readonly Vector3I Min(int with)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
Mathf.Min(X, with),
|
||||
Mathf.Min(Y, with),
|
||||
Mathf.Min(Z, with)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
|
||||
/// If all components are equal, this method returns <see cref="Axis.X"/>.
|
||||
/// </summary>
|
||||
/// <returns>The index of the highest axis.</returns>
|
||||
public readonly Axis MaxAxisIndex()
|
||||
{
|
||||
return X < Y ? (Y < Z ? Axis.Z : Axis.Y) : (X < Z ? Axis.Z : Axis.X);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
|
||||
/// If all components are equal, this method returns <see cref="Axis.Z"/>.
|
||||
/// </summary>
|
||||
/// <returns>The index of the lowest axis.</returns>
|
||||
public readonly Axis MinAxisIndex()
|
||||
{
|
||||
return X < Y ? (X < Z ? Axis.X : Axis.Z) : (Y < Z ? Axis.Y : Axis.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector with each component set to one or negative one, depending
|
||||
/// on the signs of this vector's components, or zero if the component is zero,
|
||||
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
|
||||
public readonly Vector3I Sign()
|
||||
{
|
||||
Vector3I v = this;
|
||||
v.X = Mathf.Sign(v.X);
|
||||
v.Y = Mathf.Sign(v.Y);
|
||||
v.Z = Mathf.Sign(v.Z);
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with each component snapped to the closest multiple of the corresponding component in <paramref name="step"/>.
|
||||
/// </summary>
|
||||
/// <param name="step">A vector value representing the step size to snap to.</param>
|
||||
/// <returns>The snapped vector.</returns>
|
||||
public readonly Vector3I Snapped(Vector3I step)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
(int)Mathf.Snapped((double)X, (double)step.X),
|
||||
(int)Mathf.Snapped((double)Y, (double)step.Y),
|
||||
(int)Mathf.Snapped((double)Z, (double)step.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with each component snapped to the closest multiple of <paramref name="step"/>.
|
||||
/// </summary>
|
||||
/// <param name="step">The step size to snap to.</param>
|
||||
/// <returns>The snapped vector.</returns>
|
||||
public readonly Vector3I Snapped(int step)
|
||||
{
|
||||
return new Vector3I
|
||||
(
|
||||
(int)Mathf.Snapped((double)X, (double)step),
|
||||
(int)Mathf.Snapped((double)Y, (double)step),
|
||||
(int)Mathf.Snapped((double)Z, (double)step)
|
||||
);
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static readonly Vector3I _minValue = new Vector3I(int.MinValue, int.MinValue, int.MinValue);
|
||||
private static readonly Vector3I _maxValue = new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
|
||||
private static readonly Vector3I _zero = new Vector3I(0, 0, 0);
|
||||
private static readonly Vector3I _one = new Vector3I(1, 1, 1);
|
||||
|
||||
private static readonly Vector3I _up = new Vector3I(0, 1, 0);
|
||||
private static readonly Vector3I _down = new Vector3I(0, -1, 0);
|
||||
private static readonly Vector3I _right = new Vector3I(1, 0, 0);
|
||||
private static readonly Vector3I _left = new Vector3I(-1, 0, 0);
|
||||
private static readonly Vector3I _forward = new Vector3I(0, 0, -1);
|
||||
private static readonly Vector3I _back = new Vector3I(0, 0, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector3.Inf"/>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(int.MinValue, int.MinValue, int.MinValue)</c>.</value>
|
||||
public static Vector3I MinValue { get { return _minValue; } }
|
||||
/// <summary>
|
||||
/// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector3.Inf"/>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue)</c>.</value>
|
||||
public static Vector3I MaxValue { get { return _maxValue; } }
|
||||
|
||||
/// <summary>
|
||||
/// Zero vector, a vector with all components set to <c>0</c>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(0, 0, 0)</c>.</value>
|
||||
public static Vector3I Zero { get { return _zero; } }
|
||||
/// <summary>
|
||||
/// One vector, a vector with all components set to <c>1</c>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(1, 1, 1)</c>.</value>
|
||||
public static Vector3I One { get { return _one; } }
|
||||
|
||||
/// <summary>
|
||||
/// Up unit vector.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(0, 1, 0)</c>.</value>
|
||||
public static Vector3I Up { get { return _up; } }
|
||||
/// <summary>
|
||||
/// Down unit vector.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(0, -1, 0)</c>.</value>
|
||||
public static Vector3I Down { get { return _down; } }
|
||||
/// <summary>
|
||||
/// Right unit vector. Represents the local direction of right,
|
||||
/// and the global direction of east.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(1, 0, 0)</c>.</value>
|
||||
public static Vector3I Right { get { return _right; } }
|
||||
/// <summary>
|
||||
/// Left unit vector. Represents the local direction of left,
|
||||
/// and the global direction of west.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(-1, 0, 0)</c>.</value>
|
||||
public static Vector3I Left { get { return _left; } }
|
||||
/// <summary>
|
||||
/// Forward unit vector. Represents the local direction of forward,
|
||||
/// and the global direction of north.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(0, 0, -1)</c>.</value>
|
||||
public static Vector3I Forward { get { return _forward; } }
|
||||
/// <summary>
|
||||
/// Back unit vector. Represents the local direction of back,
|
||||
/// and the global direction of south.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector3I(0, 0, 1)</c>.</value>
|
||||
public static Vector3I Back { get { return _back; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Vector3I"/> with the given components.
|
||||
/// </summary>
|
||||
/// <param name="x">The vector's X component.</param>
|
||||
/// <param name="y">The vector's Y component.</param>
|
||||
/// <param name="z">The vector's Z component.</param>
|
||||
public Vector3I(int x, int y, int z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds each component of the <see cref="Vector3I"/>
|
||||
/// with the components of the given <see cref="Vector3I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The added vector.</returns>
|
||||
public static Vector3I operator +(Vector3I left, Vector3I right)
|
||||
{
|
||||
left.X += right.X;
|
||||
left.Y += right.Y;
|
||||
left.Z += right.Z;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts each component of the <see cref="Vector3I"/>
|
||||
/// by the components of the given <see cref="Vector3I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The subtracted vector.</returns>
|
||||
public static Vector3I operator -(Vector3I left, Vector3I right)
|
||||
{
|
||||
left.X -= right.X;
|
||||
left.Y -= right.Y;
|
||||
left.Z -= right.Z;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the negative value of the <see cref="Vector3I"/>.
|
||||
/// This is the same as writing <c>new Vector3I(-v.X, -v.Y, -v.Z)</c>.
|
||||
/// This operation flips the direction of the vector while
|
||||
/// keeping the same magnitude.
|
||||
/// </summary>
|
||||
/// <param name="vec">The vector to negate/flip.</param>
|
||||
/// <returns>The negated/flipped vector.</returns>
|
||||
public static Vector3I operator -(Vector3I vec)
|
||||
{
|
||||
vec.X = -vec.X;
|
||||
vec.Y = -vec.Y;
|
||||
vec.Z = -vec.Z;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector3I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The vector to multiply.</param>
|
||||
/// <param name="scale">The scale to multiply by.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector3I operator *(Vector3I vec, int scale)
|
||||
{
|
||||
vec.X *= scale;
|
||||
vec.Y *= scale;
|
||||
vec.Z *= scale;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector3I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale to multiply by.</param>
|
||||
/// <param name="vec">The vector to multiply.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector3I operator *(int scale, Vector3I vec)
|
||||
{
|
||||
vec.X *= scale;
|
||||
vec.Y *= scale;
|
||||
vec.Z *= scale;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector3I"/>
|
||||
/// by the components of the given <see cref="Vector3I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector3I operator *(Vector3I left, Vector3I right)
|
||||
{
|
||||
left.X *= right.X;
|
||||
left.Y *= right.Y;
|
||||
left.Z *= right.Z;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides each component of the <see cref="Vector3I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisor">The divisor value.</param>
|
||||
/// <returns>The divided vector.</returns>
|
||||
public static Vector3I operator /(Vector3I vec, int divisor)
|
||||
{
|
||||
vec.X /= divisor;
|
||||
vec.Y /= divisor;
|
||||
vec.Z /= divisor;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides each component of the <see cref="Vector3I"/>
|
||||
/// by the components of the given <see cref="Vector3I"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisorv">The divisor vector.</param>
|
||||
/// <returns>The divided vector.</returns>
|
||||
public static Vector3I operator /(Vector3I vec, Vector3I divisorv)
|
||||
{
|
||||
vec.X /= divisorv.X;
|
||||
vec.Y /= divisorv.Y;
|
||||
vec.Z /= divisorv.Z;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remainder of each component of the <see cref="Vector3I"/>
|
||||
/// with the components of the given <see langword="int"/>.
|
||||
/// This operation uses truncated division, which is often not desired
|
||||
/// as it does not work well with negative numbers.
|
||||
/// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
|
||||
/// if you want to handle negative numbers.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Print(new Vector3I(10, -20, 30) % 7); // Prints "(3, -6, 2)"
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisor">The divisor value.</param>
|
||||
/// <returns>The remainder vector.</returns>
|
||||
public static Vector3I operator %(Vector3I vec, int divisor)
|
||||
{
|
||||
vec.X %= divisor;
|
||||
vec.Y %= divisor;
|
||||
vec.Z %= divisor;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remainder of each component of the <see cref="Vector3I"/>
|
||||
/// with the components of the given <see cref="Vector3I"/>.
|
||||
/// This operation uses truncated division, which is often not desired
|
||||
/// as it does not work well with negative numbers.
|
||||
/// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
|
||||
/// if you want to handle negative numbers.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Print(new Vector3I(10, -20, 30) % new Vector3I(7, 8, 9)); // Prints "(3, -4, 3)"
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisorv">The divisor vector.</param>
|
||||
/// <returns>The remainder vector.</returns>
|
||||
public static Vector3I operator %(Vector3I vec, Vector3I divisorv)
|
||||
{
|
||||
vec.X %= divisorv.X;
|
||||
vec.Y %= divisorv.Y;
|
||||
vec.Z %= divisorv.Z;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the vectors are equal.</returns>
|
||||
public static bool operator ==(Vector3I left, Vector3I right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the vectors are not equal.</returns>
|
||||
public static bool operator !=(Vector3I left, Vector3I right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector3I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is less than
|
||||
/// the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors, and then with the Z values.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is less than the right.</returns>
|
||||
public static bool operator <(Vector3I left, Vector3I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
return left.Z < right.Z;
|
||||
}
|
||||
return left.Y < right.Y;
|
||||
}
|
||||
return left.X < right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector3I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is greater than
|
||||
/// the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors, and then with the Z values.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is greater than the right.</returns>
|
||||
public static bool operator >(Vector3I left, Vector3I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
return left.Z > right.Z;
|
||||
}
|
||||
return left.Y > right.Y;
|
||||
}
|
||||
return left.X > right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector3I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is less than
|
||||
/// or equal to the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors, and then with the Z values.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is less than or equal to the right.</returns>
|
||||
public static bool operator <=(Vector3I left, Vector3I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
return left.Z <= right.Z;
|
||||
}
|
||||
return left.Y < right.Y;
|
||||
}
|
||||
return left.X < right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector3I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is greater than
|
||||
/// or equal to the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y values of the two vectors, and then with the Z values.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is greater than or equal to the right.</returns>
|
||||
public static bool operator >=(Vector3I left, Vector3I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
return left.Z >= right.Z;
|
||||
}
|
||||
return left.Y > right.Y;
|
||||
}
|
||||
return left.X > right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector3I"/> to a <see cref="Vector3"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to convert.</param>
|
||||
public static implicit operator Vector3(Vector3I value)
|
||||
{
|
||||
return new Vector3(value.X, value.Y, value.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Vector3"/> to a <see cref="Vector3I"/> by truncating
|
||||
/// components' fractional parts (rounding towards zero). For a different
|
||||
/// behavior consider passing the result of <see cref="Vector3.Ceil"/>,
|
||||
/// <see cref="Vector3.Floor"/> or <see cref="Vector3.Round"/> to this conversion operator instead.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to convert.</param>
|
||||
public static explicit operator Vector3I(Vector3 value)
|
||||
{
|
||||
return new Vector3I((int)value.X, (int)value.Y, (int)value.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vector is equal
|
||||
/// to the given object (<paramref name="obj"/>).
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with.</param>
|
||||
/// <returns>Whether or not the vector and the object are equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Vector3I other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector.</param>
|
||||
/// <returns>Whether or not the vectors are equal.</returns>
|
||||
public readonly bool Equals(Vector3I other)
|
||||
{
|
||||
return X == other.X && Y == other.Y && Z == other.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Vector3I"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this vector.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y, Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector3I"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this vector.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector3I"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this vector.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"({X.ToString(format, CultureInfo.InvariantCulture)}, {Y.ToString(format, CultureInfo.InvariantCulture)}, {Z.ToString(format, CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
}
|
||||
}
|
||||
1016
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
Normal file
1016
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
Normal file
File diff suppressed because it is too large
Load Diff
801
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
Normal file
801
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
Normal file
@@ -0,0 +1,801 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-element structure that can be used to represent 4D grid coordinates or sets of integers.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector4I : IEquatable<Vector4I>
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerated index values for the axes.
|
||||
/// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
|
||||
/// </summary>
|
||||
public enum Axis
|
||||
{
|
||||
/// <summary>
|
||||
/// The vector's X axis.
|
||||
/// </summary>
|
||||
X = 0,
|
||||
/// <summary>
|
||||
/// The vector's Y axis.
|
||||
/// </summary>
|
||||
Y,
|
||||
/// <summary>
|
||||
/// The vector's Z axis.
|
||||
/// </summary>
|
||||
Z,
|
||||
/// <summary>
|
||||
/// The vector's W axis.
|
||||
/// </summary>
|
||||
W
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The vector's X component. Also accessible by using the index position <c>[0]</c>.
|
||||
/// </summary>
|
||||
public int X;
|
||||
|
||||
/// <summary>
|
||||
/// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
|
||||
/// </summary>
|
||||
public int Y;
|
||||
|
||||
/// <summary>
|
||||
/// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
|
||||
/// </summary>
|
||||
public int Z;
|
||||
|
||||
/// <summary>
|
||||
/// The vector's W component. Also accessible by using the index position <c>[3]</c>.
|
||||
/// </summary>
|
||||
public int W;
|
||||
|
||||
/// <summary>
|
||||
/// Access vector components using their <paramref name="index"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="index"/> is not 0, 1, 2 or 3.
|
||||
/// </exception>
|
||||
/// <value>
|
||||
/// <c>[0]</c> is equivalent to <see cref="X"/>,
|
||||
/// <c>[1]</c> is equivalent to <see cref="Y"/>,
|
||||
/// <c>[2]</c> is equivalent to <see cref="Z"/>.
|
||||
/// <c>[3]</c> is equivalent to <see cref="W"/>.
|
||||
/// </value>
|
||||
public int this[int index]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return X;
|
||||
case 1:
|
||||
return Y;
|
||||
case 2:
|
||||
return Z;
|
||||
case 3:
|
||||
return W;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
X = value;
|
||||
return;
|
||||
case 1:
|
||||
Y = value;
|
||||
return;
|
||||
case 2:
|
||||
Z = value;
|
||||
return;
|
||||
case 3:
|
||||
W = value;
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for deconstruction into a tuple.
|
||||
/// </summary>
|
||||
public readonly void Deconstruct(out int x, out int y, out int z, out int w)
|
||||
{
|
||||
x = X;
|
||||
y = Y;
|
||||
z = Z;
|
||||
w = W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components in absolute values (i.e. positive).
|
||||
/// </summary>
|
||||
/// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
|
||||
public readonly Vector4I Abs()
|
||||
{
|
||||
return new Vector4I(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z), Mathf.Abs(W));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components clamped between the
|
||||
/// components of <paramref name="min"/> and <paramref name="max"/> using
|
||||
/// <see cref="Mathf.Clamp(int, int, int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="min">The vector with minimum allowed values.</param>
|
||||
/// <param name="max">The vector with maximum allowed values.</param>
|
||||
/// <returns>The vector with all components clamped.</returns>
|
||||
public readonly Vector4I Clamp(Vector4I min, Vector4I max)
|
||||
{
|
||||
return new Vector4I
|
||||
(
|
||||
Mathf.Clamp(X, min.X, max.X),
|
||||
Mathf.Clamp(Y, min.Y, max.Y),
|
||||
Mathf.Clamp(Z, min.Z, max.Z),
|
||||
Mathf.Clamp(W, min.W, max.W)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with all components clamped between
|
||||
/// <paramref name="min"/> and <paramref name="max"/> using
|
||||
/// <see cref="Mathf.Clamp(int, int, int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum allowed value.</param>
|
||||
/// <param name="max">The maximum allowed value.</param>
|
||||
/// <returns>The vector with all components clamped.</returns>
|
||||
public readonly Vector4I Clamp(int min, int max)
|
||||
{
|
||||
return new Vector4I
|
||||
(
|
||||
Mathf.Clamp(X, min, max),
|
||||
Mathf.Clamp(Y, min, max),
|
||||
Mathf.Clamp(Z, min, max),
|
||||
Mathf.Clamp(W, min, max)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared distance between this vector and <paramref name="to"/>.
|
||||
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
|
||||
/// you need to compare vectors or need the squared distance for some formula.
|
||||
/// </summary>
|
||||
/// <param name="to">The other vector to use.</param>
|
||||
/// <returns>The squared distance between the two vectors.</returns>
|
||||
public readonly int DistanceSquaredTo(Vector4I to)
|
||||
{
|
||||
return (to - this).LengthSquared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance between this vector and <paramref name="to"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="DistanceSquaredTo(Vector4I)"/>
|
||||
/// <param name="to">The other vector to use.</param>
|
||||
/// <returns>The distance between the two vectors.</returns>
|
||||
public readonly real_t DistanceTo(Vector4I to)
|
||||
{
|
||||
return (to - this).Length();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length (magnitude) of this vector.
|
||||
/// </summary>
|
||||
/// <seealso cref="LengthSquared"/>
|
||||
/// <returns>The length of this vector.</returns>
|
||||
public readonly real_t Length()
|
||||
{
|
||||
int x2 = X * X;
|
||||
int y2 = Y * Y;
|
||||
int z2 = Z * Z;
|
||||
int w2 = W * W;
|
||||
|
||||
return Mathf.Sqrt(x2 + y2 + z2 + w2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length (squared magnitude) of this vector.
|
||||
/// This method runs faster than <see cref="Length"/>, so prefer it if
|
||||
/// you need to compare vectors or need the squared length for some formula.
|
||||
/// </summary>
|
||||
/// <returns>The squared length of this vector.</returns>
|
||||
public readonly int LengthSquared()
|
||||
{
|
||||
int x2 = X * X;
|
||||
int y2 = Y * Y;
|
||||
int z2 = Z * Z;
|
||||
int w2 = W * W;
|
||||
|
||||
return x2 + y2 + z2 + w2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise maximum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector4I(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y), Mathf.Max(Z, with.Z), Mathf.Max(W, with.W))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other vector to use.</param>
|
||||
/// <returns>The resulting maximum vector.</returns>
|
||||
public readonly Vector4I Max(Vector4I with)
|
||||
{
|
||||
return new Vector4I
|
||||
(
|
||||
Mathf.Max(X, with.X),
|
||||
Mathf.Max(Y, with.Y),
|
||||
Mathf.Max(Z, with.Z),
|
||||
Mathf.Max(W, with.W)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise maximum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector4I(Mathf.Max(X, with), Mathf.Max(Y, with), Mathf.Max(Z, with), Mathf.Max(W, with))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other value to use.</param>
|
||||
/// <returns>The resulting maximum vector.</returns>
|
||||
public readonly Vector4I Max(int with)
|
||||
{
|
||||
return new Vector4I
|
||||
(
|
||||
Mathf.Max(X, with),
|
||||
Mathf.Max(Y, with),
|
||||
Mathf.Max(Z, with),
|
||||
Mathf.Max(W, with)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise minimum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector4I(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y), Mathf.Min(Z, with.Z), Mathf.Min(W, with.W))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other vector to use.</param>
|
||||
/// <returns>The resulting minimum vector.</returns>
|
||||
public readonly Vector4I Min(Vector4I with)
|
||||
{
|
||||
return new Vector4I
|
||||
(
|
||||
Mathf.Min(X, with.X),
|
||||
Mathf.Min(Y, with.Y),
|
||||
Mathf.Min(Z, with.Z),
|
||||
Mathf.Min(W, with.W)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the component-wise minimum between
|
||||
/// this vector and <paramref name="with"/>.
|
||||
/// Equivalent to <c>new Vector4I(Mathf.Min(X, with), Mathf.Min(Y, with), Mathf.Min(Z, with), Mathf.Min(W, with))</c>.
|
||||
/// </summary>
|
||||
/// <param name="with">The other value to use.</param>
|
||||
/// <returns>The resulting minimum vector.</returns>
|
||||
public readonly Vector4I Min(int with)
|
||||
{
|
||||
return new Vector4I
|
||||
(
|
||||
Mathf.Min(X, with),
|
||||
Mathf.Min(Y, with),
|
||||
Mathf.Min(Z, with),
|
||||
Mathf.Min(W, with)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
|
||||
/// If all components are equal, this method returns <see cref="Axis.X"/>.
|
||||
/// </summary>
|
||||
/// <returns>The index of the highest axis.</returns>
|
||||
public readonly Axis MaxAxisIndex()
|
||||
{
|
||||
int max_index = 0;
|
||||
int max_value = X;
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
if (this[i] > max_value)
|
||||
{
|
||||
max_index = i;
|
||||
max_value = this[i];
|
||||
}
|
||||
}
|
||||
return (Axis)max_index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
|
||||
/// If all components are equal, this method returns <see cref="Axis.W"/>.
|
||||
/// </summary>
|
||||
/// <returns>The index of the lowest axis.</returns>
|
||||
public readonly Axis MinAxisIndex()
|
||||
{
|
||||
int min_index = 0;
|
||||
int min_value = X;
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
if (this[i] <= min_value)
|
||||
{
|
||||
min_index = i;
|
||||
min_value = this[i];
|
||||
}
|
||||
}
|
||||
return (Axis)min_index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector with each component set to one or negative one, depending
|
||||
/// on the signs of this vector's components, or zero if the component is zero,
|
||||
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
|
||||
/// </summary>
|
||||
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
|
||||
public readonly Vector4I Sign()
|
||||
{
|
||||
return new Vector4I(Mathf.Sign(X), Mathf.Sign(Y), Mathf.Sign(Z), Mathf.Sign(W));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with each component snapped to the closest multiple of the corresponding component in <paramref name="step"/>.
|
||||
/// </summary>
|
||||
/// <param name="step">A vector value representing the step size to snap to.</param>
|
||||
/// <returns>The snapped vector.</returns>
|
||||
public readonly Vector4I Snapped(Vector4I step)
|
||||
{
|
||||
return new Vector4I(
|
||||
(int)Mathf.Snapped((double)X, (double)step.X),
|
||||
(int)Mathf.Snapped((double)Y, (double)step.Y),
|
||||
(int)Mathf.Snapped((double)Z, (double)step.Z),
|
||||
(int)Mathf.Snapped((double)W, (double)step.W)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new vector with each component snapped to the closest multiple of <paramref name="step"/>.
|
||||
/// </summary>
|
||||
/// <param name="step">The step size to snap to.</param>
|
||||
/// <returns>The snapped vector.</returns>
|
||||
public readonly Vector4I Snapped(int step)
|
||||
{
|
||||
return new Vector4I(
|
||||
(int)Mathf.Snapped((double)X, (double)step),
|
||||
(int)Mathf.Snapped((double)Y, (double)step),
|
||||
(int)Mathf.Snapped((double)Z, (double)step),
|
||||
(int)Mathf.Snapped((double)W, (double)step)
|
||||
);
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static readonly Vector4I _minValue = new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue);
|
||||
private static readonly Vector4I _maxValue = new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
|
||||
private static readonly Vector4I _zero = new Vector4I(0, 0, 0, 0);
|
||||
private static readonly Vector4I _one = new Vector4I(1, 1, 1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector4.Inf"/>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue)</c>.</value>
|
||||
public static Vector4I MinValue { get { return _minValue; } }
|
||||
/// <summary>
|
||||
/// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector4.Inf"/>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)</c>.</value>
|
||||
public static Vector4I MaxValue { get { return _maxValue; } }
|
||||
|
||||
/// <summary>
|
||||
/// Zero vector, a vector with all components set to <c>0</c>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector4I(0, 0, 0, 0)</c>.</value>
|
||||
public static Vector4I Zero { get { return _zero; } }
|
||||
/// <summary>
|
||||
/// One vector, a vector with all components set to <c>1</c>.
|
||||
/// </summary>
|
||||
/// <value>Equivalent to <c>new Vector4I(1, 1, 1, 1)</c>.</value>
|
||||
public static Vector4I One { get { return _one; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Vector4I"/> with the given components.
|
||||
/// </summary>
|
||||
/// <param name="x">The vector's X component.</param>
|
||||
/// <param name="y">The vector's Y component.</param>
|
||||
/// <param name="z">The vector's Z component.</param>
|
||||
/// <param name="w">The vector's W component.</param>
|
||||
public Vector4I(int x, int y, int z, int w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds each component of the <see cref="Vector4I"/>
|
||||
/// with the components of the given <see cref="Vector4I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The added vector.</returns>
|
||||
public static Vector4I operator +(Vector4I left, Vector4I right)
|
||||
{
|
||||
left.X += right.X;
|
||||
left.Y += right.Y;
|
||||
left.Z += right.Z;
|
||||
left.W += right.W;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts each component of the <see cref="Vector4I"/>
|
||||
/// by the components of the given <see cref="Vector4I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The subtracted vector.</returns>
|
||||
public static Vector4I operator -(Vector4I left, Vector4I right)
|
||||
{
|
||||
left.X -= right.X;
|
||||
left.Y -= right.Y;
|
||||
left.Z -= right.Z;
|
||||
left.W -= right.W;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the negative value of the <see cref="Vector4I"/>.
|
||||
/// This is the same as writing <c>new Vector4I(-v.X, -v.Y, -v.Z, -v.W)</c>.
|
||||
/// This operation flips the direction of the vector while
|
||||
/// keeping the same magnitude.
|
||||
/// </summary>
|
||||
/// <param name="vec">The vector to negate/flip.</param>
|
||||
/// <returns>The negated/flipped vector.</returns>
|
||||
public static Vector4I operator -(Vector4I vec)
|
||||
{
|
||||
vec.X = -vec.X;
|
||||
vec.Y = -vec.Y;
|
||||
vec.Z = -vec.Z;
|
||||
vec.W = -vec.W;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector4I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The vector to multiply.</param>
|
||||
/// <param name="scale">The scale to multiply by.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector4I operator *(Vector4I vec, int scale)
|
||||
{
|
||||
vec.X *= scale;
|
||||
vec.Y *= scale;
|
||||
vec.Z *= scale;
|
||||
vec.W *= scale;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector4I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale to multiply by.</param>
|
||||
/// <param name="vec">The vector to multiply.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector4I operator *(int scale, Vector4I vec)
|
||||
{
|
||||
vec.X *= scale;
|
||||
vec.Y *= scale;
|
||||
vec.Z *= scale;
|
||||
vec.W *= scale;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies each component of the <see cref="Vector4I"/>
|
||||
/// by the components of the given <see cref="Vector4I"/>.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static Vector4I operator *(Vector4I left, Vector4I right)
|
||||
{
|
||||
left.X *= right.X;
|
||||
left.Y *= right.Y;
|
||||
left.Z *= right.Z;
|
||||
left.W *= right.W;
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides each component of the <see cref="Vector4I"/>
|
||||
/// by the given <see langword="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisor">The divisor value.</param>
|
||||
/// <returns>The divided vector.</returns>
|
||||
public static Vector4I operator /(Vector4I vec, int divisor)
|
||||
{
|
||||
vec.X /= divisor;
|
||||
vec.Y /= divisor;
|
||||
vec.Z /= divisor;
|
||||
vec.W /= divisor;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides each component of the <see cref="Vector4I"/>
|
||||
/// by the components of the given <see cref="Vector4I"/>.
|
||||
/// </summary>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisorv">The divisor vector.</param>
|
||||
/// <returns>The divided vector.</returns>
|
||||
public static Vector4I operator /(Vector4I vec, Vector4I divisorv)
|
||||
{
|
||||
vec.X /= divisorv.X;
|
||||
vec.Y /= divisorv.Y;
|
||||
vec.Z /= divisorv.Z;
|
||||
vec.W /= divisorv.W;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remainder of each component of the <see cref="Vector4I"/>
|
||||
/// with the components of the given <see langword="int"/>.
|
||||
/// This operation uses truncated division, which is often not desired
|
||||
/// as it does not work well with negative numbers.
|
||||
/// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
|
||||
/// if you want to handle negative numbers.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Print(new Vector4I(10, -20, 30, -40) % 7); // Prints "(3, -6, 2, -5)"
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisor">The divisor value.</param>
|
||||
/// <returns>The remainder vector.</returns>
|
||||
public static Vector4I operator %(Vector4I vec, int divisor)
|
||||
{
|
||||
vec.X %= divisor;
|
||||
vec.Y %= divisor;
|
||||
vec.Z %= divisor;
|
||||
vec.W %= divisor;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remainder of each component of the <see cref="Vector4I"/>
|
||||
/// with the components of the given <see cref="Vector4I"/>.
|
||||
/// This operation uses truncated division, which is often not desired
|
||||
/// as it does not work well with negative numbers.
|
||||
/// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
|
||||
/// if you want to handle negative numbers.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GD.Print(new Vector4I(10, -20, 30, -40) % new Vector4I(6, 7, 8, 9)); // Prints "(4, -6, 6, -4)"
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="vec">The dividend vector.</param>
|
||||
/// <param name="divisorv">The divisor vector.</param>
|
||||
/// <returns>The remainder vector.</returns>
|
||||
public static Vector4I operator %(Vector4I vec, Vector4I divisorv)
|
||||
{
|
||||
vec.X %= divisorv.X;
|
||||
vec.Y %= divisorv.Y;
|
||||
vec.Z %= divisorv.Z;
|
||||
vec.W %= divisorv.W;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the vectors are equal.</returns>
|
||||
public static bool operator ==(Vector4I left, Vector4I right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the vectors are not equal.</returns>
|
||||
public static bool operator !=(Vector4I left, Vector4I right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector4I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is less than
|
||||
/// the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y, Z and finally W values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is less than the right.</returns>
|
||||
public static bool operator <(Vector4I left, Vector4I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
if (left.Z == right.Z)
|
||||
{
|
||||
return left.W < right.W;
|
||||
}
|
||||
return left.Z < right.Z;
|
||||
}
|
||||
return left.Y < right.Y;
|
||||
}
|
||||
return left.X < right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector4I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is greater than
|
||||
/// the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y, Z and finally W values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is greater than the right.</returns>
|
||||
public static bool operator >(Vector4I left, Vector4I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
if (left.Z == right.Z)
|
||||
{
|
||||
return left.W > right.W;
|
||||
}
|
||||
return left.Z > right.Z;
|
||||
}
|
||||
return left.Y > right.Y;
|
||||
}
|
||||
return left.X > right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector4I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is less than
|
||||
/// or equal to the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y, Z and finally W values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is less than or equal to the right.</returns>
|
||||
public static bool operator <=(Vector4I left, Vector4I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
if (left.Z == right.Z)
|
||||
{
|
||||
return left.W <= right.W;
|
||||
}
|
||||
return left.Z < right.Z;
|
||||
}
|
||||
return left.Y < right.Y;
|
||||
}
|
||||
return left.X < right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Vector4I"/> vectors by first checking if
|
||||
/// the X value of the <paramref name="left"/> vector is greater than
|
||||
/// or equal to the X value of the <paramref name="right"/> vector.
|
||||
/// If the X values are exactly equal, then it repeats this check
|
||||
/// with the Y, Z and finally W values of the two vectors.
|
||||
/// This operator is useful for sorting vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The left vector.</param>
|
||||
/// <param name="right">The right vector.</param>
|
||||
/// <returns>Whether or not the left is greater than or equal to the right.</returns>
|
||||
public static bool operator >=(Vector4I left, Vector4I right)
|
||||
{
|
||||
if (left.X == right.X)
|
||||
{
|
||||
if (left.Y == right.Y)
|
||||
{
|
||||
if (left.Z == right.Z)
|
||||
{
|
||||
return left.W >= right.W;
|
||||
}
|
||||
return left.Z > right.Z;
|
||||
}
|
||||
return left.Y > right.Y;
|
||||
}
|
||||
return left.X > right.X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector4I"/> to a <see cref="Vector4"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to convert.</param>
|
||||
public static implicit operator Vector4(Vector4I value)
|
||||
{
|
||||
return new Vector4(value.X, value.Y, value.Z, value.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Vector4"/> to a <see cref="Vector4I"/> by truncating
|
||||
/// components' fractional parts (rounding towards zero). For a different
|
||||
/// behavior consider passing the result of <see cref="Vector4.Ceil"/>,
|
||||
/// <see cref="Vector4.Floor"/> or <see cref="Vector4.Round"/> to this conversion operator instead.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to convert.</param>
|
||||
public static explicit operator Vector4I(Vector4 value)
|
||||
{
|
||||
return new Vector4I((int)value.X, (int)value.Y, (int)value.Z, (int)value.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vector is equal
|
||||
/// to the given object (<paramref name="obj"/>).
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with.</param>
|
||||
/// <returns>Whether or not the vector and the object are equal.</returns>
|
||||
public override readonly bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is Vector4I other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector.</param>
|
||||
/// <returns>Whether or not the vectors are equal.</returns>
|
||||
public readonly bool Equals(Vector4I other)
|
||||
{
|
||||
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the hash function for <see cref="Vector4I"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this vector.</returns>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y, Z, W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector4I"/> to a string.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this vector.</returns>
|
||||
public override readonly string ToString() => ToString(null);
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Vector4I"/> to a string with the given <paramref name="format"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this vector.</returns>
|
||||
public readonly string ToString(string? format)
|
||||
{
|
||||
return $"({X.ToString(format, CultureInfo.InvariantCulture)}, {Y.ToString(format, CultureInfo.InvariantCulture)}, {Z.ToString(format, CultureInfo.InvariantCulture)}, {W.ToString(format, CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user