summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/build_scripts/mono_configure.py3
-rw-r--r--modules/mono/csharp_script.cpp56
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln16
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj35
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec22
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props112
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets17
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs56
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs118
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs171
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs341
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs72
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs9
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildManager.cs49
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs116
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs26
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs101
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs4
-rw-r--r--modules/mono/editor/bindings_generator.cpp1
-rw-r--r--modules/mono/editor/csharp_project.cpp69
-rw-r--r--modules/mono/editor/csharp_project.h42
-rw-r--r--modules/mono/editor/script_class_parser.cpp2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs234
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs292
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs568
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs329
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs49
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs148
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs232
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs183
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs161
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs144
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs180
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs314
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs159
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs343
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs143
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj35
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj48
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs25
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp2
-rw-r--r--modules/mono/utils/osx_utils.cpp23
52 files changed, 3609 insertions, 1537 deletions
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 80e3b59325..3e771e06f0 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -125,7 +125,8 @@ def configure(env, env_mono):
if not mono_prefix and (os.getenv("MONO32_PREFIX") or os.getenv("MONO64_PREFIX")):
print(
- "WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead"
+ "WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the"
+ " 'mono_prefix' SCons parameter instead"
)
# Although we don't support building with tools for any platform where we currently use static AOT,
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 22e4d84e98..bbdec224f0 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -44,7 +44,6 @@
#ifdef TOOLS_ENABLED
#include "editor/bindings_generator.h"
-#include "editor/csharp_project.h"
#include "editor/editor_node.h"
#include "editor/node_dock.h"
#endif
@@ -897,7 +896,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Call OnBeforeSerialize
if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call_multilevel(string_names.on_before_serialize);
+ obj->get_script_instance()->call(string_names.on_before_serialize);
}
// Save instance info
@@ -1133,7 +1132,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Call OnAfterDeserialization
if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
+ obj->get_script_instance()->call(string_names.on_after_deserialize);
}
}
}
@@ -1866,41 +1865,6 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args,
return Variant();
}
-void CSharpInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- if (script.is_valid()) {
- MonoObject *mono_object = get_mono_object();
-
- ERR_FAIL_NULL(mono_object);
-
- _call_multilevel(mono_object, p_method, p_args, p_argcount);
- }
-}
-
-void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- GDMonoClass *top = script->script_class;
-
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(p_method, p_argcount);
-
- if (method) {
- method->invoke(p_mono_object, p_args);
- return;
- }
-
- top = top->get_parent_class();
- }
-}
-
-void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- // Sorry, the method is the one that controls the call order
-
- call_multilevel(p_method, p_args, p_argcount);
-}
-
bool CSharpInstance::_reference_owner_unsafe() {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
@@ -1963,7 +1927,7 @@ MonoObject *CSharpInstance::_internal_new_managed() {
bool die = _unreference_owner_unsafe();
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
- CRASH_COND(die == true);
+ CRASH_COND(die);
owner = nullptr;
@@ -2298,7 +2262,7 @@ CSharpInstance::~CSharpInstance() {
// Unreference the owner here, before the new "instance binding" references it.
// Otherwise, the unsafe reference debug checks will incorrectly detect a bug.
bool die = _unreference_owner_unsafe();
- CRASH_COND(die == true); // `owner_keep_alive` holds a reference, so it can't die
+ CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die
void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
CRASH_COND(data == nullptr);
@@ -3136,7 +3100,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
bool die = instance->_unreference_owner_unsafe();
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
- CRASH_COND(die == true);
+ CRASH_COND(die);
p_owner->set_script_instance(nullptr);
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
@@ -3759,13 +3723,9 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
#ifdef TOOLS_ENABLED
if (!FileAccess::exists(p_path)) {
- // The file does not yet exists, let's assume the user just created this script
-
- if (_create_project_solution_if_needed()) {
- CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
- "Compile",
- ProjectSettings::get_singleton()->globalize_path(p_path));
- } else {
+ // The file does not yet exist, let's assume the user just created this script. In such
+ // cases we need to check whether the solution and csproj were already created or not.
+ if (!_create_project_solution_if_needed()) {
ERR_PRINT("C# project could not be created; cannot add file: '" + p_path + "'.");
}
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index c2370364f9..f0b43a40f9 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -265,8 +265,6 @@ class CSharpInstance : public ScriptInstance {
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle);
- void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
-
void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
@@ -285,8 +283,6 @@ public:
/* TODO */ void get_method_list(List<MethodInfo> *p_list) const override {}
bool has_method(const StringName &p_method) const override;
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
- void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) override;
- void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) override;
void mono_object_disposed(MonoObject *p_obj);
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
new file mode 100644
index 0000000000..56c0cb7703
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
new file mode 100644
index 0000000000..86a0a4393e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -0,0 +1,35 @@
+<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+
+ <Description>MSBuild .NET Sdk for Godot projects.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>Godot.NET.Sdk</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>4.0.0-dev2</PackageVersion>
+ <PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl>
+ <PackageType>MSBuildSdk</PackageType>
+ <PackageTags>MSBuildSdk</PackageTags>
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <NuspecFile>Godot.NET.Sdk.nuspec</NuspecFile>
+ <GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn>
+ </PropertyGroup>
+
+ <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') ">
+ <PropertyGroup>
+ <NuspecProperties>
+ id=$(PackageId);
+ description=$(Description);
+ authors=$(Authors);
+ version=$(PackageVersion);
+ packagetype=$(PackageType);
+ tags=$(PackageTags);
+ projecturl=$(PackageProjectUrl)
+ </NuspecProperties>
+ </PropertyGroup>
+ </Target>
+</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
new file mode 100644
index 0000000000..5b5cefe80e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
+ <metadata>
+ <id>$id$</id>
+ <version>$version$</version>
+ <description>$description$</description>
+ <authors>$authors$</authors>
+ <owners>$authors$</owners>
+ <projectUrl>$projecturl$</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <license type="expression">MIT</license>
+ <licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
+ <tags>$tags$</tags>
+ <packageTypes>
+ <packageType name="$packagetype$" />
+ </packageTypes>
+ <repository url="$projecturl$" />
+ </metadata>
+ <files>
+ <file src="Sdk\**" target="Sdk" />\
+ </files>
+</package>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
new file mode 100644
index 0000000000..dfc59e6ccb
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -0,0 +1,112 @@
+<Project>
+ <PropertyGroup>
+ <!-- Determines if we should import Microsoft.NET.Sdk, if it wasn't already imported. -->
+ <GodotSdkImportsMicrosoftNetSdk Condition=" '$(UsingMicrosoftNETSdk)' != 'true' ">true</GodotSdkImportsMicrosoftNetSdk>
+
+ <GodotProjectTypeGuid>{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}</GodotProjectTypeGuid>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <Configurations>Debug;ExportDebug;ExportRelease</Configurations>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+
+ <GodotProjectDir Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)</GodotProjectDir>
+ <GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
+ <GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
+
+ <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.mono\temp\'. -->
+ <BaseOutputPath>$(GodotProjectDir).mono\temp\bin\</BaseOutputPath>
+ <OutputPath>$(GodotProjectDir).mono\temp\bin\$(Configuration)\</OutputPath>
+ <!--
+ Use custom IntermediateOutputPath and BaseIntermediateOutputPath only if it wasn't already set.
+ Otherwise the old values may have already been changed by MSBuild which can cause problems with NuGet.
+ -->
+ <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\$(Configuration)\</IntermediateOutputPath>
+ <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\</BaseIntermediateOutputPath>
+
+ <!-- Do not append the target framework name to the output path. -->
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+ </PropertyGroup>
+
+ <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
+
+ <PropertyGroup>
+ <EnableDefaultNoneItems>false</EnableDefaultNoneItems>
+ </PropertyGroup>
+
+ <!--
+ The Microsoft.NET.Sdk only understands of the Debug and Release configurations.
+ We need to set the following properties manually for ExportDebug and ExportRelease.
+ -->
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' or '$(Configuration)' == 'ExportDebug' ">
+ <DebugSymbols Condition=" '$(DebugSymbols)' == '' ">true</DebugSymbols>
+ <Optimize Condition=" '$(Optimize)' == '' ">false</Optimize>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'ExportRelease' ">
+ <Optimize Condition=" '$(Optimize)' == '' ">true</Optimize>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <GodotApiConfiguration Condition=" '$(Configuration)' != 'ExportRelease' ">Debug</GodotApiConfiguration>
+ <GodotApiConfiguration Condition=" '$(Configuration)' == 'ExportRelease' ">Release</GodotApiConfiguration>
+ </PropertyGroup>
+
+ <!-- Auto-detect the target Godot platform if it was not specified. -->
+ <PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' ">
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Linux))' ">linuxbsd</GodotTargetPlatform>
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">linuxbsd</GodotTargetPlatform>
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(OSX))' ">osx</GodotTargetPlatform>
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Windows))' ">windows</GodotTargetPlatform>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <GodotRealTIsDouble Condition=" '$(GodotRealTIsDouble)' == '' ">false</GodotRealTIsDouble>
+ </PropertyGroup>
+
+ <!-- Godot DefineConstants. -->
+ <PropertyGroup>
+ <!-- Define constant to identify Godot builds. -->
+ <GodotDefineConstants>GODOT</GodotDefineConstants>
+
+ <!--
+ Define constant to determine the target Godot platform. This includes the
+ recognized platform names and the platform category (PC, MOBILE or WEB).
+ -->
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'windows' ">GODOT_WINDOWS;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'linuxbsd' ">GODOT_LINUXBSD;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'osx' ">GODOT_OSX;GODOT_MACOS;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'server' ">GODOT_SERVER;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'uwp' ">GODOT_UWP;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'haiku' ">GODOT_HAIKU;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'android' ">GODOT_ANDROID;GODOT_MOBILE</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'iphone' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'javascript' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
+
+ <GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!-- ExportDebug also defines DEBUG like Debug does. -->
+ <DefineConstants Condition=" '$(Configuration)' == 'ExportDebug' ">$(DefineConstants);DEBUG</DefineConstants>
+ <!-- Debug defines TOOLS to differenciate between Debug and ExportDebug configurations. -->
+ <DefineConstants Condition=" '$(Configuration)' == 'Debug' ">$(DefineConstants);TOOLS</DefineConstants>
+
+ <DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <!--
+ TODO:
+ We should consider a nuget package for reference assemblies. This is difficult because the
+ Godot scripting API is continuaslly breaking backwards compatibility even in patch releases.
+ -->
+ <Reference Include="GodotSharp">
+ <Private>false</Private>
+ <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath>
+ </Reference>
+ <Reference Include="GodotSharpEditor" Condition=" '$(Configuration)' == 'Debug' ">
+ <Private>false</Private>
+ <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
new file mode 100644
index 0000000000..f5afd75505
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
@@ -0,0 +1,17 @@
+<Project>
+ <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
+
+ <PropertyGroup>
+ <EnableGodotProjectTypeGuid Condition=" '$(EnableGodotProjectTypeGuid)' == '' ">true</EnableGodotProjectTypeGuid>
+ <ProjectTypeGuids Condition=" '$(EnableGodotProjectTypeGuid)' == 'true' ">$(GodotProjectTypeGuid);$(DefaultProjectTypeGuid)</ProjectTypeGuids>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!--
+ Define constant to determine whether the real_t type in Godot is double precision or not.
+ By default this is false, like the official Godot builds. If someone is using a custom
+ Godot build where real_t is double, they can override the GodotRealTIsDouble property.
+ -->
+ <DefineConstants Condition=" '$(GodotRealTIsDouble)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
+ </PropertyGroup>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index c2549b4ad5..5edf72d63e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -70,13 +70,14 @@ namespace GodotTools.BuildLogger
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
- if (e.ProjectFile.Length > 0)
+ if (!string.IsNullOrEmpty(e.ProjectFile))
line += $" [{e.ProjectFile}]";
WriteLine(line);
string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
- $@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}";
+ $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
+ $"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
issuesStreamWriter.WriteLine(errorLine);
}
@@ -89,8 +90,9 @@ namespace GodotTools.BuildLogger
WriteLine(line);
- string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," +
- $@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}";
+ string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
+ $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
+ $"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
issuesStreamWriter.WriteLine(warningLine);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs
index 85760a3705..e1ccf0454a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs
@@ -19,7 +19,10 @@ namespace GodotTools.Core
}
if (attempt > maxAttempts + 1)
- return;
+ {
+ // Overwrite the oldest one
+ backupPath = backupPathBase;
+ }
File.Copy(filePath, backupPath, overwrite: true);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index 012b69032e..e6b0e8f1df 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -43,7 +43,7 @@ namespace GodotTools.Core
path.StartsWith(DriveRoot, StringComparison.Ordinal);
}
- public static string ToSafeDirName(this string dirName, bool allowDirSeparator)
+ public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false)
{
var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" };
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
index 99a55c471b..4db71500da 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
index affb2a47e7..ce2b378623 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index 9cb50014b0..41c94f19c8 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -11,13 +11,21 @@
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
</ItemGroup>
+ <!--
+ The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
+ here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
+ We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
+ searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
+ -->
<ItemGroup>
- <!--
- The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
- here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
- We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
- searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
- -->
<None Include="MSBuild.exe" CopyToOutputDirectory="Always" />
</ItemGroup>
+ <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition="$([MSBuild]::IsOsPlatform(Windows))">
+ <PropertyGroup>
+ <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
+ </PropertyGroup>
+ <!-- Need to copy it here as well on Windows -->
+ <Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" />
+ </Target>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
index f93eb9a1fa..ed77076df3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
@@ -22,6 +22,37 @@ namespace GodotTools.ProjectEditor
return string.Join(".", identifiers);
}
+ /// <summary>
+ /// Skips invalid identifier characters including decimal digit numbers at the start of the identifier.
+ /// </summary>
+ private static void SkipInvalidCharacters(string source, int startIndex, StringBuilder outputBuilder)
+ {
+ for (int i = startIndex; i < source.Length; i++)
+ {
+ char @char = source[i];
+
+ switch (char.GetUnicodeCategory(@char))
+ {
+ case UnicodeCategory.UppercaseLetter:
+ case UnicodeCategory.LowercaseLetter:
+ case UnicodeCategory.TitlecaseLetter:
+ case UnicodeCategory.ModifierLetter:
+ case UnicodeCategory.LetterNumber:
+ case UnicodeCategory.OtherLetter:
+ outputBuilder.Append(@char);
+ break;
+ case UnicodeCategory.NonSpacingMark:
+ case UnicodeCategory.SpacingCombiningMark:
+ case UnicodeCategory.ConnectorPunctuation:
+ case UnicodeCategory.DecimalDigitNumber:
+ // Identifiers may start with underscore
+ if (outputBuilder.Length > startIndex || @char == '_')
+ outputBuilder.Append(@char);
+ break;
+ }
+ }
+ }
+
public static string SanitizeIdentifier(string identifier, bool allowEmpty)
{
if (string.IsNullOrEmpty(identifier))
@@ -44,30 +75,7 @@ namespace GodotTools.ProjectEditor
startIndex += 1;
}
- for (int i = startIndex; i < identifier.Length; i++)
- {
- char @char = identifier[i];
-
- switch (Char.GetUnicodeCategory(@char))
- {
- case UnicodeCategory.UppercaseLetter:
- case UnicodeCategory.LowercaseLetter:
- case UnicodeCategory.TitlecaseLetter:
- case UnicodeCategory.ModifierLetter:
- case UnicodeCategory.LetterNumber:
- case UnicodeCategory.OtherLetter:
- identifierBuilder.Append(@char);
- break;
- case UnicodeCategory.NonSpacingMark:
- case UnicodeCategory.SpacingCombiningMark:
- case UnicodeCategory.ConnectorPunctuation:
- case UnicodeCategory.DecimalDigitNumber:
- // Identifiers may start with underscore
- if (identifierBuilder.Length > startIndex || @char == '_')
- identifierBuilder.Append(@char);
- break;
- }
- }
+ SkipInvalidCharacters(identifier, startIndex, identifierBuilder);
if (identifierBuilder.Length == startIndex)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
deleted file mode 100644
index 704f2ec194..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using GodotTools.Core;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Microsoft.Build.Construction;
-using Microsoft.Build.Globbing;
-
-namespace GodotTools.ProjectEditor
-{
- public static class ProjectExtensions
- {
- public static ProjectItemElement FindItemOrNull(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
- {
- string normalizedInclude = include.NormalizePath();
-
- foreach (var itemGroup in root.ItemGroups)
- {
- if (noCondition && itemGroup.Condition.Length != 0)
- continue;
-
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- //var glob = Glob.Parse(item.Include.NormalizePath(), globOptions);
- var glob = MSBuildGlob.Parse(item.Include.NormalizePath());
-
- if (glob.IsMatch(normalizedInclude))
- return item;
- }
- }
-
- return null;
- }
- public static ProjectItemElement FindItemOrNullAbs(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
- {
- string normalizedInclude = Path.GetFullPath(include).NormalizePath();
-
- foreach (var itemGroup in root.ItemGroups)
- {
- if (noCondition && itemGroup.Condition.Length != 0)
- continue;
-
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- var glob = MSBuildGlob.Parse(Path.GetFullPath(item.Include).NormalizePath());
-
- if (glob.IsMatch(normalizedInclude))
- return item;
- }
- }
-
- return null;
- }
-
- public static IEnumerable<ProjectItemElement> FindAllItemsInFolder(this ProjectRootElement root, string itemType, string folder)
- {
- string absFolderNormalizedWithSep = Path.GetFullPath(folder).NormalizePath() + Path.DirectorySeparatorChar;
-
- foreach (var itemGroup in root.ItemGroups)
- {
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
-
- if (absPathNormalized.StartsWith(absFolderNormalizedWithSep))
- yield return item;
- }
- }
- }
-
- public static bool HasItem(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
- {
- return root.FindItemOrNull(itemType, include, noCondition) != null;
- }
-
- public static bool AddItemChecked(this ProjectRootElement root, string itemType, string include)
- {
- if (!root.HasItem(itemType, include, noCondition: true))
- {
- root.AddItem(itemType, include);
- return true;
- }
-
- return false;
- }
-
- public static bool RemoveItemChecked(this ProjectRootElement root, string itemType, string include)
- {
- var item = root.FindItemOrNullAbs(itemType, include);
- if (item != null)
- {
- item.Parent.RemoveChild(item);
- return true;
- }
-
- return false;
- }
-
- public static Guid GetGuid(this ProjectRootElement root)
- {
- foreach (var property in root.Properties)
- {
- if (property.Name == "ProjectGuid")
- return Guid.Parse(property.Value);
- }
-
- return Guid.Empty;
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 679d5bb444..01d7c99662 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -1,174 +1,51 @@
-using GodotTools.Core;
using System;
-using System.Collections.Generic;
using System.IO;
-using System.Reflection;
+using System.Text;
using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
namespace GodotTools.ProjectEditor
{
public static class ProjectGenerator
{
- private const string CoreApiProjectName = "GodotSharp";
- private const string EditorApiProjectName = "GodotSharpEditor";
+ public const string GodotSdkVersionToUse = "4.0.0-dev2";
- public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
- public const string GodotProjectTypeGuid = "{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}";
+ public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}";
- public static readonly string GodotDefaultProjectTypeGuids = $"{GodotProjectTypeGuid};{CSharpProjectTypeGuid}";
-
- public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
+ public static ProjectRootElement GenGameProject(string name)
{
- string path = Path.Combine(dir, name + ".csproj");
-
- ProjectPropertyGroupElement mainGroup;
- var root = CreateLibraryProject(name, "Debug", out mainGroup);
-
- mainGroup.SetProperty("ProjectTypeGuids", GodotDefaultProjectTypeGuids);
- mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
- mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
- mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
- mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'ExportRelease' ";
- mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'ExportRelease' ";
-
- var debugGroup = root.AddPropertyGroup();
- debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
- debugGroup.AddProperty("DebugSymbols", "true");
- debugGroup.AddProperty("DebugType", "portable");
- debugGroup.AddProperty("Optimize", "false");
- debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
- debugGroup.AddProperty("ErrorReport", "prompt");
- debugGroup.AddProperty("WarningLevel", "4");
- debugGroup.AddProperty("ConsolePause", "false");
-
- var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
- coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll"));
- coreApiRef.AddMetadata("Private", "False");
-
- var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
- editorApiRef.Condition = " '$(Configuration)' == 'Debug' ";
- editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll"));
- editorApiRef.AddMetadata("Private", "False");
-
- GenAssemblyInfoFile(root, dir, name);
-
- foreach (var item in compileItems)
- {
- root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
- }
-
- root.Save(path);
-
- return root.GetGuid().ToString().ToUpper();
- }
-
- private static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null)
- {
- string propertiesDir = Path.Combine(dir, "Properties");
- if (!Directory.Exists(propertiesDir))
- Directory.CreateDirectory(propertiesDir);
-
- string usingDirectivesText = string.Empty;
+ if (name.Length == 0)
+ throw new ArgumentException("Project name is empty", nameof(name));
- if (usingDirectives != null)
- {
- foreach (var usingDirective in usingDirectives)
- usingDirectivesText += "\nusing " + usingDirective + ";";
- }
+ var root = ProjectRootElement.Create(NewProjectFileOptions.None);
- string assemblyLinesText = string.Empty;
+ root.Sdk = GodotSdkAttrValue;
- if (assemblyLines != null)
- assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
+ var mainGroup = root.AddPropertyGroup();
+ mainGroup.AddProperty("TargetFramework", "netstandard2.1");
- string content = string.Format(AssemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
+ string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
- string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs");
+ // If the name is not a valid namespace, manually set RootNamespace to a sanitized one.
+ if (sanitizedName != name)
+ mainGroup.AddProperty("RootNamespace", sanitizedName);
- File.WriteAllText(assemblyInfoFile, content);
-
- root.AddItem("Compile", assemblyInfoFile.RelativeToPath(dir).Replace("/", "\\"));
+ return root;
}
- public static ProjectRootElement CreateLibraryProject(string name, string defaultConfig, out ProjectPropertyGroupElement mainGroup)
+ public static string GenAndSaveGameProject(string dir, string name)
{
- if (string.IsNullOrEmpty(name))
- throw new ArgumentException($"{nameof(name)} cannot be empty", nameof(name));
-
- var root = ProjectRootElement.Create();
- root.DefaultTargets = "Build";
-
- mainGroup = root.AddPropertyGroup();
- mainGroup.AddProperty("Configuration", defaultConfig).Condition = " '$(Configuration)' == '' ";
- mainGroup.AddProperty("Platform", "AnyCPU").Condition = " '$(Platform)' == '' ";
- mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}");
- mainGroup.AddProperty("OutputType", "Library");
- mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)"));
- mainGroup.AddProperty("RootNamespace", IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true));
- mainGroup.AddProperty("AssemblyName", name);
- mainGroup.AddProperty("TargetFrameworkVersion", "v4.7");
- mainGroup.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
+ if (name.Length == 0)
+ throw new ArgumentException("Project name is empty", nameof(name));
- var exportDebugGroup = root.AddPropertyGroup();
- exportDebugGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportDebug|AnyCPU' ";
- exportDebugGroup.AddProperty("DebugSymbols", "true");
- exportDebugGroup.AddProperty("DebugType", "portable");
- exportDebugGroup.AddProperty("Optimize", "false");
- exportDebugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
- exportDebugGroup.AddProperty("ErrorReport", "prompt");
- exportDebugGroup.AddProperty("WarningLevel", "4");
- exportDebugGroup.AddProperty("ConsolePause", "false");
-
- var exportReleaseGroup = root.AddPropertyGroup();
- exportReleaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportRelease|AnyCPU' ";
- exportReleaseGroup.AddProperty("DebugType", "portable");
- exportReleaseGroup.AddProperty("Optimize", "true");
- exportReleaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
- exportReleaseGroup.AddProperty("ErrorReport", "prompt");
- exportReleaseGroup.AddProperty("WarningLevel", "4");
- exportReleaseGroup.AddProperty("ConsolePause", "false");
-
- // References
- var referenceGroup = root.AddItemGroup();
- referenceGroup.AddItem("Reference", "System");
- var frameworkRefAssembliesItem = referenceGroup.AddItem("PackageReference", "Microsoft.NETFramework.ReferenceAssemblies");
+ string path = Path.Combine(dir, name + ".csproj");
- // Use metadata (child nodes) instead of attributes for the PackageReference.
- // This is for compatibility with 3.2, where GodotTools uses an old Microsoft.Build.
- frameworkRefAssembliesItem.AddMetadata("Version", "1.0.0");
- frameworkRefAssembliesItem.AddMetadata("PrivateAssets", "All");
+ var root = GenGameProject(name);
- root.AddImport(Path.Combine("$(MSBuildBinPath)", "Microsoft.CSharp.targets").Replace("/", "\\"));
+ // Save (without BOM)
+ root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
- return root;
+ return Guid.NewGuid().ToString().ToUpper();
}
-
- private const string AssemblyInfoTemplate =
- @"using System.Reflection;{0}
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle(""{1}"")]
-[assembly: AssemblyDescription("""")]
-[assembly: AssemblyConfiguration("""")]
-[assembly: AssemblyCompany("""")]
-[assembly: AssemblyProduct("""")]
-[assembly: AssemblyCopyright("""")]
-[assembly: AssemblyTrademark("""")]
-[assembly: AssemblyCulture("""")]
-
-// The assembly version has the format ""{{Major}}.{{Minor}}.{{Build}}.{{Revision}}"".
-// The form ""{{Major}}.{{Minor}}.*"" will automatically update the build and revision,
-// and ""{{Major}}.{{Minor}}.{{Build}}.*"" will update just the revision.
-
-[assembly: AssemblyVersion(""1.0.*"")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("""")]
-{2}";
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 8774b4ee31..4041c56597 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -1,9 +1,9 @@
+using System;
using GodotTools.Core;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Reflection;
using Microsoft.Build.Construction;
using Microsoft.Build.Globbing;
@@ -11,7 +11,7 @@ namespace GodotTools.ProjectEditor
{
public sealed class MSBuildProject
{
- public ProjectRootElement Root { get; }
+ internal ProjectRootElement Root { get; set; }
public bool HasUnsavedChanges { get; set; }
@@ -31,91 +31,7 @@ namespace GodotTools.ProjectEditor
return root != null ? new MSBuildProject(root) : null;
}
- public static void AddItemToProjectChecked(string projectPath, string itemType, string include)
- {
- var dir = Directory.GetParent(projectPath).FullName;
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\");
-
- if (root.AddItemChecked(itemType, normalizedInclude))
- root.Save();
- }
-
- public static void RenameItemInProjectChecked(string projectPath, string itemType, string oldInclude, string newInclude)
- {
- var dir = Directory.GetParent(projectPath).FullName;
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var normalizedOldInclude = oldInclude.NormalizePath();
- var normalizedNewInclude = newInclude.NormalizePath();
-
- var item = root.FindItemOrNullAbs(itemType, normalizedOldInclude);
-
- if (item == null)
- return;
-
- item.Include = normalizedNewInclude.RelativeToPath(dir).Replace("/", "\\");
- root.Save();
- }
-
- public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include)
- {
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var normalizedInclude = include.NormalizePath();
-
- if (root.RemoveItemChecked(itemType, normalizedInclude))
- root.Save();
- }
-
- public static void RenameItemsToNewFolderInProjectChecked(string projectPath, string itemType, string oldFolder, string newFolder)
- {
- var dir = Directory.GetParent(projectPath).FullName;
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- bool dirty = false;
-
- var oldFolderNormalized = oldFolder.NormalizePath();
- var newFolderNormalized = newFolder.NormalizePath();
- string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath();
- string absNewFolderNormalized = Path.GetFullPath(newFolderNormalized).NormalizePath();
-
- foreach (var item in root.FindAllItemsInFolder(itemType, oldFolderNormalized))
- {
- string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
- string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length);
- item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\");
- dirty = true;
- }
-
- if (dirty)
- root.Save();
- }
-
- public static void RemoveItemsInFolderFromProjectChecked(string projectPath, string itemType, string folder)
- {
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var folderNormalized = folder.NormalizePath();
-
- var itemsToRemove = root.FindAllItemsInFolder(itemType, folderNormalized).ToList();
-
- if (itemsToRemove.Count > 0)
- {
- foreach (var item in itemsToRemove)
- item.Parent.RemoveChild(item);
-
- root.Save();
- }
- }
-
- private static string[] GetAllFilesRecursive(string rootDirectory, string mask)
+ private static List<string> GetAllFilesRecursive(string rootDirectory, string mask)
{
string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories);
@@ -125,262 +41,59 @@ namespace GodotTools.ProjectEditor
files[i] = files[i].RelativeToPath(rootDirectory);
}
- return files;
+ return new List<string>(files);
}
- public static string[] GetIncludeFiles(string projectPath, string itemType)
+ // NOTE: Assumes auto-including items. Only used by the scripts metadata generator, which will be replaced with source generators in the future.
+ public static IEnumerable<string> GetIncludeFiles(string projectPath, string itemType)
{
- var result = new List<string>();
- var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
+ var excluded = new List<string>();
+ var includedFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
var root = ProjectRootElement.Open(projectPath);
Debug.Assert(root != null);
- foreach (var itemGroup in root.ItemGroups)
+ foreach (var item in root.Items)
{
- if (itemGroup.Condition.Length != 0)
+ if (string.IsNullOrEmpty(item.Condition))
continue;
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- string normalizedInclude = item.Include.NormalizePath();
+ if (item.ItemType != itemType)
+ continue;
- var glob = MSBuildGlob.Parse(normalizedInclude);
+ string normalizedExclude = item.Exclude.NormalizePath();
- // TODO Check somehow if path has no blob to avoid the following loop...
+ var glob = MSBuildGlob.Parse(normalizedExclude);
- foreach (var existingFile in existingFiles)
- {
- if (glob.IsMatch(existingFile))
- {
- result.Add(existingFile);
- }
- }
- }
+ excluded.AddRange(includedFiles.Where(includedFile => glob.IsMatch(includedFile)));
}
- return result.ToArray();
+ includedFiles.RemoveAll(f => excluded.Contains(f));
+
+ return includedFiles;
}
- public static void EnsureHasProjectTypeGuids(MSBuildProject project)
+ public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
{
- var root = project.Root;
-
- bool found = root.PropertyGroups.Any(pg =>
- string.IsNullOrEmpty(pg.Condition) && pg.Properties.Any(p => p.Name == "ProjectTypeGuids"));
+ var origRoot = project.Root;
- if (found)
+ if (!string.IsNullOrEmpty(origRoot.Sdk))
return;
- root.AddProperty("ProjectTypeGuids", ProjectGenerator.GodotDefaultProjectTypeGuids);
-
+ project.Root = ProjectGenerator.GenGameProject(projectName);
+ project.Root.FullPath = origRoot.FullPath;
project.HasUnsavedChanges = true;
}
- /// Simple function to make sure the Api assembly references are configured correctly
- public static void FixApiHintPath(MSBuildProject project)
- {
- var root = project.Root;
-
- void AddPropertyIfNotPresent(string name, string condition, string value)
- {
- if (root.PropertyGroups
- .Any(g => (string.IsNullOrEmpty(g.Condition) || g.Condition.Trim() == condition) &&
- g.Properties
- .Any(p => p.Name == name &&
- p.Value == value &&
- (p.Condition.Trim() == condition || g.Condition.Trim() == condition))))
- {
- return;
- }
-
- root.AddProperty(name, value).Condition = " " + condition + " ";
- project.HasUnsavedChanges = true;
- }
-
- AddPropertyIfNotPresent(name: "ApiConfiguration",
- condition: "'$(Configuration)' != 'ExportRelease'",
- value: "Debug");
- AddPropertyIfNotPresent(name: "ApiConfiguration",
- condition: "'$(Configuration)' == 'ExportRelease'",
- value: "Release");
-
- void SetReferenceHintPath(string referenceName, string condition, string hintPath)
- {
- foreach (var itemGroup in root.ItemGroups.Where(g =>
- g.Condition.Trim() == string.Empty || g.Condition.Trim() == condition))
- {
- var references = itemGroup.Items.Where(item =>
- item.ItemType == "Reference" &&
- item.Include == referenceName &&
- (item.Condition.Trim() == condition || itemGroup.Condition.Trim() == condition));
-
- var referencesWithHintPath = references.Where(reference =>
- reference.Metadata.Any(m => m.Name == "HintPath"));
-
- if (referencesWithHintPath.Any(reference => reference.Metadata
- .Any(m => m.Name == "HintPath" && m.Value == hintPath)))
- {
- // Found a Reference item with the right HintPath
- return;
- }
-
- var referenceWithHintPath = referencesWithHintPath.FirstOrDefault();
- if (referenceWithHintPath != null)
- {
- // Found a Reference item with a wrong HintPath
- foreach (var metadata in referenceWithHintPath.Metadata.ToList()
- .Where(m => m.Name == "HintPath"))
- {
- // Safe to remove as we duplicate with ToList() to loop
- referenceWithHintPath.RemoveChild(metadata);
- }
-
- referenceWithHintPath.AddMetadata("HintPath", hintPath);
- project.HasUnsavedChanges = true;
- return;
- }
-
- var referenceWithoutHintPath = references.FirstOrDefault();
- if (referenceWithoutHintPath != null)
- {
- // Found a Reference item without a HintPath
- referenceWithoutHintPath.AddMetadata("HintPath", hintPath);
- project.HasUnsavedChanges = true;
- return;
- }
- }
-
- // Found no Reference item at all. Add it.
- root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
- project.HasUnsavedChanges = true;
- }
-
- const string coreProjectName = "GodotSharp";
- const string editorProjectName = "GodotSharpEditor";
-
- const string coreCondition = "";
- const string editorCondition = "'$(Configuration)' == 'Debug'";
-
- var coreHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{coreProjectName}.dll";
- var editorHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{editorProjectName}.dll";
-
- SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath);
- SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath);
- }
-
- public static void MigrateFromOldConfigNames(MSBuildProject project)
- {
- var root = project.Root;
-
- bool hasGodotProjectGeneratorVersion = false;
- bool foundOldConfiguration = false;
-
- foreach (var propertyGroup in root.PropertyGroups.Where(g => string.IsNullOrEmpty(g.Condition)))
- {
- if (!hasGodotProjectGeneratorVersion && propertyGroup.Properties.Any(p => p.Name == "GodotProjectGeneratorVersion"))
- hasGodotProjectGeneratorVersion = true;
-
- foreach (var configItem in propertyGroup.Properties
- .Where(p => p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Tools"))
- {
- configItem.Value = "Debug";
- foundOldConfiguration = true;
- project.HasUnsavedChanges = true;
- }
- }
-
- if (!hasGodotProjectGeneratorVersion)
- {
- root.PropertyGroups.First(g => string.IsNullOrEmpty(g.Condition))?
- .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
- project.HasUnsavedChanges = true;
- }
-
- if (!foundOldConfiguration)
- {
- var toolsConditions = new[]
- {
- "'$(Configuration)|$(Platform)' == 'Tools|AnyCPU'",
- "'$(Configuration)|$(Platform)' != 'Tools|AnyCPU'",
- "'$(Configuration)' == 'Tools'",
- "'$(Configuration)' != 'Tools'"
- };
-
- foundOldConfiguration = root.PropertyGroups
- .Any(g => toolsConditions.Any(c => c == g.Condition.Trim()));
- }
-
- if (foundOldConfiguration)
- {
- void MigrateConfigurationConditions(string oldConfiguration, string newConfiguration)
- {
- void MigrateConditions(string oldCondition, string newCondition)
- {
- foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
- {
- propertyGroup.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
-
- foreach (var propertyGroup in root.PropertyGroups)
- {
- foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
- {
- prop.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
- }
-
- foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
- {
- itemGroup.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
-
- foreach (var itemGroup in root.ItemGroups)
- {
- foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
- {
- item.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
- }
- }
-
- foreach (var op in new[] {"==", "!="})
- {
- MigrateConditions($"'$(Configuration)|$(Platform)' {op} '{oldConfiguration}|AnyCPU'", $"'$(Configuration)|$(Platform)' {op} '{newConfiguration}|AnyCPU'");
- MigrateConditions($"'$(Configuration)' {op} '{oldConfiguration}'", $"'$(Configuration)' {op} '{newConfiguration}'");
- }
- }
-
- MigrateConfigurationConditions("Debug", "ExportDebug");
- MigrateConfigurationConditions("Release", "ExportRelease");
- MigrateConfigurationConditions("Tools", "Debug"); // Must be last
- }
- }
-
- public static void EnsureHasNugetNetFrameworkRefAssemblies(MSBuildProject project)
+ public static void EnsureGodotSdkIsUpToDate(MSBuildProject project)
{
var root = project.Root;
+ string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
- bool found = root.ItemGroups.Any(g => string.IsNullOrEmpty(g.Condition) && g.Items.Any(
- item => item.ItemType == "PackageReference" && item.Include == "Microsoft.NETFramework.ReferenceAssemblies"));
-
- if (found)
+ if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
return;
- var frameworkRefAssembliesItem = root.AddItem("PackageReference", "Microsoft.NETFramework.ReferenceAssemblies");
-
- // Use metadata (child nodes) instead of attributes for the PackageReference.
- // This is for compatibility with 3.2, where GodotTools uses an old Microsoft.Build.
- frameworkRefAssembliesItem.AddMetadata("Version", "1.0.0");
- frameworkRefAssembliesItem.AddMetadata("PrivateAssets", "All");
-
+ root.Sdk = godotSdkAttrValue;
project.HasUnsavedChanges = true;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
index 3de3d8d318..3ab669a9f3 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
@@ -24,48 +24,50 @@ namespace GodotTools
private Button errorsBtn;
private Button viewLogBtn;
- private void _UpdateBuildTabsList()
+ private void _UpdateBuildTab(int index, int? currentTab)
{
- buildTabsList.Clear();
+ var tab = (BuildTab)buildTabs.GetChild(index);
- int currentTab = buildTabs.CurrentTab;
+ string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
+ itemName += " [" + tab.BuildInfo.Configuration + "]";
- bool noCurrentTab = currentTab < 0 || currentTab >= buildTabs.GetTabCount();
+ buildTabsList.AddItem(itemName, tab.IconTexture);
- for (int i = 0; i < buildTabs.GetChildCount(); i++)
- {
- var tab = (BuildTab)buildTabs.GetChild(i);
+ string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
+ itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
+ itemTooltip += "\nStatus: ";
- if (tab == null)
- continue;
+ if (tab.BuildExited)
+ itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
+ else
+ itemTooltip += "Running";
- string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
- itemName += " [" + tab.BuildInfo.Configuration + "]";
+ if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
+ itemTooltip += $"\nErrors: {tab.ErrorCount}";
- buildTabsList.AddItem(itemName, tab.IconTexture);
+ itemTooltip += $"\nWarnings: {tab.WarningCount}";
- string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
- itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
- itemTooltip += "\nStatus: ";
+ buildTabsList.SetItemTooltip(index, itemTooltip);
- if (tab.BuildExited)
- itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
- else
- itemTooltip += "Running";
+ // If this tab was already selected before the changes or if no tab was selected
+ if (currentTab == null || currentTab == index)
+ {
+ buildTabsList.Select(index);
+ _BuildTabsItemSelected(index);
+ }
+ }
- if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
- itemTooltip += $"\nErrors: {tab.ErrorCount}";
+ private void _UpdateBuildTabsList()
+ {
+ buildTabsList.Clear();
- itemTooltip += $"\nWarnings: {tab.WarningCount}";
+ int? currentTab = buildTabs.CurrentTab;
- buildTabsList.SetItemTooltip(i, itemTooltip);
+ if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
+ currentTab = null;
- if (noCurrentTab || currentTab == i)
- {
- buildTabsList.Select(i);
- _BuildTabsItemSelected(i);
- }
- }
+ for (int i = 0; i < buildTabs.GetChildCount(); i++)
+ _UpdateBuildTab(i, currentTab);
}
public BuildTab GetBuildTabFor(BuildInfo buildInfo)
@@ -160,13 +162,7 @@ namespace GodotTools
}
}
- var godotDefines = new[]
- {
- OS.GetName(),
- Internal.GodotIs32Bits() ? "32" : "64"
- };
-
- bool buildSuccess = BuildManager.BuildProjectBlocking("Debug", godotDefines);
+ bool buildSuccess = BuildManager.BuildProjectBlocking("Debug");
if (!buildSuccess)
return;
@@ -272,7 +268,7 @@ namespace GodotTools
};
panelTabs.AddChild(panelBuildsTab);
- var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
+ var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
panelBuildsTab.AddChild(toolBarHBox);
var buildProjectBtn = new Button
@@ -325,7 +321,7 @@ namespace GodotTools
};
panelBuildsTab.AddChild(hsc);
- buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
+ buildTabsList = new ItemList {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
buildTabsList.ItemSelected += _BuildTabsItemSelected;
buildTabsList.NothingSelected += _BuildTabsNothingSelected;
hsc.AddChild(buildTabsList);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index 34e42489eb..d9862ae361 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -118,9 +118,14 @@ namespace GodotTools.Build
string arguments = string.Empty;
if (buildTool == BuildTool.DotnetCli)
- arguments += "msbuild "; // `dotnet msbuild` command
+ arguments += "msbuild"; // `dotnet msbuild` command
- arguments += $@"""{buildInfo.Solution}"" /t:{string.Join(",", buildInfo.Targets)} " +
+ arguments += $@" ""{buildInfo.Solution}""";
+
+ if (buildInfo.Restore)
+ arguments += " /restore";
+
+ arguments += $@" /t:{string.Join(",", buildInfo.Targets)} " +
$@"""/p:{"Configuration=" + buildInfo.Configuration}"" /v:normal " +
$@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{buildInfo.LogsDirPath}""";
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
index 0974d23176..ff7ce97c47 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
@@ -6,6 +6,7 @@ using GodotTools.Build;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
using GodotTools.Utils;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
@@ -152,7 +153,7 @@ namespace GodotTools
}
}
- public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines)
+ public static bool BuildProjectBlocking(string config, [CanBeNull] string platform = null)
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
@@ -168,29 +169,18 @@ namespace GodotTools
return false;
}
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
-
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
{
pr.Step("Building project solution", 0);
var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Build"}, config, restore: true);
- bool escapeNeedsDoubleBackslash = buildTool == BuildTool.MsBuildMono || buildTool == BuildTool.DotnetCli;
-
- // Add Godot defines
- string constants = !escapeNeedsDoubleBackslash ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
-
- foreach (var godotDefine in godotDefines)
- constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};";
+ // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
+ if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
if (Internal.GodotIsRealTDouble())
- constants += "GODOT_REAL_T_IS_DOUBLE;";
-
- constants += !escapeNeedsDoubleBackslash ? "\"" : "\\\"";
-
- buildInfo.CustomProperties.Add(constants);
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
if (!Build(buildInfo))
{
@@ -215,31 +205,10 @@ namespace GodotTools
if (File.Exists(editorScriptsMetadataPath))
File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
- var currentPlayRequest = GodotSharpEditor.Instance.CurrentPlaySettings;
-
- if (currentPlayRequest != null)
- {
- if (currentPlayRequest.Value.HasDebugger)
- {
- // Set the environment variable that will tell the player to connect to the IDE debugger
- // TODO: We should probably add a better way to do this
- Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT",
- "--debugger-agent=transport=dt_socket" +
- $",address={currentPlayRequest.Value.DebuggerHost}:{currentPlayRequest.Value.DebuggerPort}" +
- ",server=n");
- }
-
- if (!currentPlayRequest.Value.BuildBeforePlaying)
- return true; // Requested play from an external editor/IDE which already built the project
- }
-
- var godotDefines = new[]
- {
- Godot.OS.GetName(),
- Internal.GodotIs32Bits() ? "32" : "64"
- };
+ if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
+ return true; // Requested play from an external editor/IDE which already built the project
- return BuildProjectBlocking("Debug", godotDefines);
+ return BuildProjectBlocking("Debug");
}
public static void Initialize()
diff --git a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
index 421729cc11..a8afb38728 100644
--- a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
@@ -1,9 +1,9 @@
using Godot;
using System;
+using System.Linq;
using Godot.Collections;
using GodotTools.Internals;
using GodotTools.ProjectEditor;
-using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using Directory = GodotTools.Utils.Directory;
@@ -15,7 +15,7 @@ namespace GodotTools
{
try
{
- return ProjectGenerator.GenGameProject(dir, name, compileItems: new string[] { });
+ return ProjectGenerator.GenAndSaveGameProject(dir, name);
}
catch (Exception e)
{
@@ -24,14 +24,6 @@ namespace GodotTools
}
}
- public static void AddItem(string projectPath, string itemType, string include)
- {
- if (!(bool)GlobalDef("mono/project/auto_update_project", true))
- return;
-
- ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);
- }
-
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static ulong ConvertToTimestamp(this DateTime value)
@@ -40,81 +32,77 @@ namespace GodotTools
return (ulong)elapsedTime.TotalSeconds;
}
- public static void GenerateScriptsMetadata(string projectPath, string outputPath)
+ private static bool TryParseFileMetadata(string includeFile, ulong modifiedTime, out Dictionary fileMetadata)
{
- if (File.Exists(outputPath))
- File.Delete(outputPath);
+ fileMetadata = null;
- var oldDict = Internal.GetScriptsMetadataOrNothing();
- var newDict = new Godot.Collections.Dictionary<string, object>();
+ var parseError = ScriptClassParser.ParseFile(includeFile, out var classes, out string errorStr);
- foreach (var includeFile in ProjectUtils.GetIncludeFiles(projectPath, "Compile"))
+ if (parseError != Error.Ok)
{
- string projectIncludeFile = ("res://" + includeFile).SimplifyGodotPath();
+ GD.PushError($"Failed to determine namespace and class for script: {includeFile}. Parse error: {errorStr ?? parseError.ToString()}");
+ return false;
+ }
- ulong modifiedTime = File.GetLastWriteTime(projectIncludeFile).ConvertToTimestamp();
+ string searchName = System.IO.Path.GetFileNameWithoutExtension(includeFile);
- if (oldDict.TryGetValue(projectIncludeFile, out var oldFileVar))
- {
- var oldFileDict = (Dictionary)oldFileVar;
-
- if (ulong.TryParse(oldFileDict["modified_time"] as string, out ulong storedModifiedTime))
- {
- if (storedModifiedTime == modifiedTime)
- {
- // No changes so no need to parse again
- newDict[projectIncludeFile] = oldFileDict;
- continue;
- }
- }
- }
+ var firstMatch = classes.FirstOrDefault(classDecl =>
+ classDecl.BaseCount != 0 && // If it doesn't inherit anything, it can't be a Godot.Object.
+ classDecl.SearchName != searchName // Filter by the name we're looking for
+ );
+
+ if (firstMatch == null)
+ return false; // Not found
- Error parseError = ScriptClassParser.ParseFile(projectIncludeFile, out var classes, out string errorStr);
- if (parseError != Error.Ok)
+ fileMetadata = new Dictionary
+ {
+ ["modified_time"] = $"{modifiedTime}",
+ ["class"] = new Dictionary
{
- GD.PushError($"Failed to determine namespace and class for script: {projectIncludeFile}. Parse error: {errorStr ?? parseError.ToString()}");
- continue;
+ ["namespace"] = firstMatch.Namespace,
+ ["class_name"] = firstMatch.Name,
+ ["nested"] = firstMatch.Nested
}
+ };
- string searchName = System.IO.Path.GetFileNameWithoutExtension(projectIncludeFile);
-
- var classDict = new Dictionary();
+ return true;
+ }
- foreach (var classDecl in classes)
- {
- if (classDecl.BaseCount == 0)
- continue; // Does not inherit nor implement anything, so it can't be a script class
+ public static void GenerateScriptsMetadata(string projectPath, string outputPath)
+ {
+ var metadataDict = Internal.GetScriptsMetadataOrNothing().Duplicate();
- string classCmp = classDecl.Nested ?
- classDecl.Name.Substring(classDecl.Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
- classDecl.Name;
+ bool IsUpToDate(string includeFile, ulong modifiedTime)
+ {
+ return metadataDict.TryGetValue(includeFile, out var oldFileVar) &&
+ ulong.TryParse(((Dictionary)oldFileVar)["modified_time"] as string,
+ out ulong storedModifiedTime) && storedModifiedTime == modifiedTime;
+ }
- if (classCmp != searchName)
- continue;
+ var outdatedFiles = ProjectUtils.GetIncludeFiles(projectPath, "Compile")
+ .Select(path => ("res://" + path).SimplifyGodotPath())
+ .ToDictionary(path => path, path => File.GetLastWriteTime(path).ConvertToTimestamp())
+ .Where(pair => !IsUpToDate(includeFile: pair.Key, modifiedTime: pair.Value))
+ .ToArray();
- classDict["namespace"] = classDecl.Namespace;
- classDict["class_name"] = classDecl.Name;
- classDict["nested"] = classDecl.Nested;
- break;
- }
+ foreach (var pair in outdatedFiles)
+ {
+ metadataDict.Remove(pair.Key);
- if (classDict.Count == 0)
- continue; // Not found
+ string includeFile = pair.Key;
- newDict[projectIncludeFile] = new Dictionary { ["modified_time"] = $"{modifiedTime}", ["class"] = classDict };
+ if (TryParseFileMetadata(includeFile, modifiedTime: pair.Value, out var fileMetadata))
+ metadataDict[includeFile] = fileMetadata;
}
- if (newDict.Count > 0)
- {
- string json = JSON.Print(newDict);
+ string json = metadataDict.Count <= 0 ? "{}" : JSON.Print(metadataDict);
- string baseDir = outputPath.GetBaseDir();
+ string baseDir = outputPath.GetBaseDir();
- if (!Directory.Exists(baseDir))
- Directory.CreateDirectory(baseDir);
+ if (!Directory.Exists(baseDir))
+ Directory.CreateDirectory(baseDir);
- File.WriteAllText(outputPath, json);
- }
+ File.WriteAllText(outputPath, json);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 6bfbc62f3b..e819848212 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using GodotTools.Core;
using GodotTools.Internals;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
@@ -145,9 +146,7 @@ namespace GodotTools.Export
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return;
- string platform = DeterminePlatformFromFeatures(features);
-
- if (platform == null)
+ if (!DeterminePlatformFromFeatures(features, out string platform))
throw new NotSupportedException("Target platform not supported");
string outputDir = new FileInfo(path).Directory?.FullName ??
@@ -160,10 +159,7 @@ namespace GodotTools.Export
AddFile(scriptsMetadataPath, scriptsMetadataPath);
- // Turn export features into defines
- var godotDefines = features;
-
- if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines))
+ if (!BuildManager.BuildProjectBlocking(buildConfig, platform))
throw new Exception("Failed to build project");
// Add dependency assemblies
@@ -289,6 +285,7 @@ namespace GodotTools.Export
}
}
+ [NotNull]
private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
{
string target = isDebug ? "release_debug" : "release";
@@ -343,18 +340,19 @@ namespace GodotTools.Export
private static bool PlatformHasTemplateDir(string platform)
{
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
- return !new[] { OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
+ return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform);
}
- private static string DeterminePlatformFromFeatures(IEnumerable<string> features)
+ private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
{
foreach (var feature in features)
{
- if (OS.PlatformNameMap.TryGetValue(feature, out string platform))
- return platform;
+ if (OS.PlatformNameMap.TryGetValue(feature, out platform))
+ return true;
}
- return null;
+ platform = null;
+ return false;
}
private static string GetBclProfileDir(string profile)
@@ -391,7 +389,7 @@ namespace GodotTools.Export
/// </summary>
private static bool PlatformRequiresCustomBcl(string platform)
{
- if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform))
+ if (new[] {OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform))
return true;
// The 'net_4_x' BCL is not compatible between Windows and the other platforms.
@@ -432,7 +430,7 @@ namespace GodotTools.Export
private static string DetermineDataDirNameForProject()
{
var appName = (string)ProjectSettings.GetSetting("application/config/name");
- string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
+ string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index f330f9ed2c..3148458d7e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -38,13 +38,14 @@ namespace GodotTools
public BottomPanel BottomPanel { get; private set; }
- public PlaySettings? CurrentPlaySettings { get; set; }
+ public bool SkipBuildBeforePlaying { get; set; } = false;
public static string ProjectAssemblyName
{
get
{
var projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ projectAssemblyName = projectAssemblyName.ToSafeDirName();
if (string.IsNullOrEmpty(projectAssemblyName))
projectAssemblyName = "UnnamedProject";
return projectAssemblyName;
@@ -175,36 +176,6 @@ namespace GodotTools
// Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
aboutDialog.Exclusive = false;
}
-
- var fileSystemDock = GetEditorInterface().GetFileSystemDock();
-
- fileSystemDock.FilesMoved += (file, newFile) =>
- {
- if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
- {
- ProjectUtils.RenameItemInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(file), ProjectSettings.GlobalizePath(newFile));
- }
- };
-
- fileSystemDock.FileRemoved += file =>
- {
- if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
- ProjectUtils.RemoveItemFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(file));
- };
-
- fileSystemDock.FolderMoved += (oldFolder, newFolder) =>
- {
- ProjectUtils.RenameItemsToNewFolderInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(oldFolder), ProjectSettings.GlobalizePath(newFolder));
- };
-
- fileSystemDock.FolderRemoved += oldFolder =>
- {
- ProjectUtils.RemoveItemsInFolderFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(oldFolder));
- };
}
}
@@ -389,6 +360,37 @@ namespace GodotTools
return BuildManager.EditorBuildCallback();
}
+ private void ApplyNecessaryChangesToSolution()
+ {
+ try
+ {
+ // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
+ DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
+
+ var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
+ ?? throw new Exception("Cannot open C# project");
+
+ // NOTE: The order in which changes are made to the project is important
+
+ // Migrate to MSBuild project Sdks style if using the old style
+ ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, ProjectAssemblyName);
+
+ ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
+
+ if (msbuildProject.HasUnsavedChanges)
+ {
+ // Save a copy of the project before replacing it
+ FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
+
+ msbuildProject.Save();
+ }
+ }
+ catch (Exception e)
+ {
+ GD.PushError(e.ToString());
+ }
+ }
+
public override void EnablePlugin()
{
base.EnablePlugin();
@@ -468,42 +470,7 @@ namespace GodotTools
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
- try
- {
- // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
- DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
-
- var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
- ?? throw new Exception("Cannot open C# project");
-
- // NOTE: The order in which changes are made to the project is important
-
- // Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
- ProjectUtils.MigrateFromOldConfigNames(msbuildProject);
-
- // Apply the other fixes only after configurations have been migrated
-
- // Make sure the existing project has the ProjectTypeGuids property (for VisualStudio)
- ProjectUtils.EnsureHasProjectTypeGuids(msbuildProject);
-
- // Make sure the existing project has Api assembly references configured correctly
- ProjectUtils.FixApiHintPath(msbuildProject);
-
- // Make sure the existing project references the Microsoft.NETFramework.ReferenceAssemblies nuget package
- ProjectUtils.EnsureHasNugetNetFrameworkRefAssemblies(msbuildProject);
-
- if (msbuildProject.HasUnsavedChanges)
- {
- // Save a copy of the project before replacing it
- FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
-
- msbuildProject.Save();
- }
- }
- catch (Exception e)
- {
- GD.PushError(e.ToString());
- }
+ ApplyNecessaryChangesToSolution();
}
else
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 17f3339560..eb34a2d0f7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -330,9 +330,10 @@ namespace GodotTools.Ides
{
DispatchToMainThread(() =>
{
- GodotSharpEditor.Instance.CurrentPlaySettings = new PlaySettings();
+ // TODO: Add BuildBeforePlaying flag to PlayRequest
+
+ // Run the game
Internal.EditorRunPlay();
- GodotSharpEditor.Instance.CurrentPlaySettings = null;
});
return Task.FromResult<Response>(new PlayResponse());
}
@@ -341,10 +342,22 @@ namespace GodotTools.Ides
{
DispatchToMainThread(() =>
{
- GodotSharpEditor.Instance.CurrentPlaySettings =
- new PlaySettings(request.DebuggerHost, request.DebuggerPort, request.BuildBeforePlaying ?? true);
+ // Tell the build callback whether the editor already built the solution or not
+ GodotSharpEditor.Instance.SkipBuildBeforePlaying = !(request.BuildBeforePlaying ?? true);
+
+ // Pass the debugger agent settings to the player via an environment variables
+ // TODO: It would be better if this was an argument in EditorRunPlay instead
+ Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT",
+ "--debugger-agent=transport=dt_socket" +
+ $",address={request.DebuggerHost}:{request.DebuggerPort}" +
+ ",server=n");
+
+ // Run the game
Internal.EditorRunPlay();
- GodotSharpEditor.Instance.CurrentPlaySettings = null;
+
+ // Restore normal settings
+ Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", "");
+ GodotSharpEditor.Instance.SkipBuildBeforePlaying = false;
});
return Task.FromResult<Response>(new DebugPlayResponse());
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
index 569f27649f..c72a84c513 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
@@ -15,6 +15,10 @@ namespace GodotTools.Internals
public bool Nested { get; }
public long BaseCount { get; }
+ public string SearchName => Nested ?
+ Name.Substring(Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
+ Name;
+
public ClassDecl(string name, string @namespace, bool nested, long baseCount)
{
Name = name;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 79e4b7c794..a17c371117 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -45,7 +45,6 @@
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
-#include "csharp_project.h"
#define CS_INDENT " " // 4 whitespaces
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
deleted file mode 100644
index 6f54eb09a2..0000000000
--- a/modules/mono/editor/csharp_project.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*************************************************************************/
-/* csharp_project.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "csharp_project.h"
-
-#include "core/io/json.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
-#include "core/os/os.h"
-#include "core/project_settings.h"
-
-#include "../csharp_script.h"
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../utils/string_utils.h"
-#include "script_class_parser.h"
-
-namespace CSharpProject {
-
-void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
- if (!GLOBAL_DEF("mono/project/auto_update_project", true)) {
- return;
- }
-
- GDMonoAssembly *tools_project_editor_assembly = GDMono::get_singleton()->get_tools_project_editor_assembly();
-
- GDMonoClass *klass = tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ProjectUtils");
-
- Variant project_path = p_project_path;
- Variant item_type = p_item_type;
- Variant include = p_include;
- const Variant *args[3] = { &project_path, &item_type, &include };
- MonoException *exc = nullptr;
- klass->get_method("AddItemToProjectChecked", 3)->invoke(nullptr, args, &exc);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- ERR_FAIL();
- }
-}
-
-} // namespace CSharpProject
diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h
deleted file mode 100644
index 515b8d3d62..0000000000
--- a/modules/mono/editor/csharp_project.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*************************************************************************/
-/* csharp_project.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef CSHARP_PROJECT_H
-#define CSHARP_PROJECT_H
-
-#include "core/ustring.h"
-
-namespace CSharpProject {
-
-void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
-
-} // namespace CSharpProject
-
-#endif // CSHARP_PROJECT_H
diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
index 012ccd5339..430c82953e 100644
--- a/modules/mono/editor/script_class_parser.cpp
+++ b/modules/mono/editor/script_class_parser.cpp
@@ -235,7 +235,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) {
//a number
const CharType *rptr;
- double number = String::to_double(&code[idx], &rptr);
+ double number = String::to_float(&code[idx], &rptr);
idx += (rptr - &code[idx]);
value = number;
return TK_NUMBER;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index 6a4f785551..3aecce50f5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -14,6 +14,10 @@ using real_t = System.Single;
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>
@@ -21,24 +25,55 @@ namespace Godot
private Vector3 _position;
private Vector3 _size;
+ /// <summary>
+ /// Beginning corner. Typically has values lower than End.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector3 Position
{
get { return _position; }
set { _position = value; }
}
+ /// <summary>
+ /// Size from Position to 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
{
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 `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
public Vector3 End
{
get { return _position + _size; }
set { _size = value - _position; }
}
+ /// <summary>
+ /// Returns an 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 AABB.</returns>
+ public AABB Abs()
+ {
+ Vector3 end = End;
+ Vector3 topLeft = new Vector3(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y), Mathf.Min(_position.z, end.z));
+ return new AABB(topLeft, _size.Abs());
+ }
+
+ /// <summary>
+ /// Returns true if this AABB completely encloses another one.
+ /// </summary>
+ /// <param name="with">The other AABB that may be enclosed.</param>
+ /// <returns>A bool for whether or not this AABB encloses `b`.</returns>
public bool Encloses(AABB with)
{
Vector3 src_min = _position;
@@ -54,33 +89,59 @@ namespace Godot
src_max.z > dst_max.z;
}
+ /// <summary>
+ /// Returns this AABB expanded to include a given point.
+ /// </summary>
+ /// <param name="point">The point to include.</param>
+ /// <returns>The expanded AABB.</returns>
public 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>
+ /// Returns the area of the AABB.
+ /// </summary>
+ /// <returns>The area.</returns>
public real_t GetArea()
{
return _size.x * _size.y * _size.z;
}
+ /// <summary>
+ /// Gets the position of one of the 8 endpoints of the AABB.
+ /// </summary>
+ /// <param name="idx">Which endpoint to get.</param>
+ /// <returns>An endpoint of the AABB.</returns>
public Vector3 GetEndpoint(int idx)
{
switch (idx)
@@ -106,6 +167,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the normalized longest axis of the AABB.
+ /// </summary>
+ /// <returns>A vector representing the normalized longest axis of the AABB.</returns>
public Vector3 GetLongestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
@@ -125,6 +190,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the AABB.
+ /// </summary>
+ /// <returns>A <see cref="Vector3.Axis"/> index for which axis is longest.</returns>
public Vector3.Axis GetLongestAxisIndex()
{
var axis = Vector3.Axis.X;
@@ -144,6 +213,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the scalar length of the longest axis of the AABB.
+ /// </summary>
+ /// <returns>The scalar length of the longest axis of the AABB.</returns>
public real_t GetLongestAxisSize()
{
real_t max_size = _size.x;
@@ -157,6 +230,10 @@ namespace Godot
return max_size;
}
+ /// <summary>
+ /// Returns the normalized shortest axis of the AABB.
+ /// </summary>
+ /// <returns>A vector representing the normalized shortest axis of the AABB.</returns>
public Vector3 GetShortestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
@@ -176,6 +253,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the AABB.
+ /// </summary>
+ /// <returns>A <see cref="Vector3.Axis"/> index for which axis is shortest.</returns>
public Vector3.Axis GetShortestAxisIndex()
{
var axis = Vector3.Axis.X;
@@ -195,6 +276,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the scalar length of the shortest axis of the AABB.
+ /// </summary>
+ /// <returns>The scalar length of the shortest axis of the AABB.</returns>
public real_t GetShortestAxisSize()
{
real_t max_size = _size.x;
@@ -208,6 +293,12 @@ namespace Godot
return max_size;
}
+ /// <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 Vector3 GetSupport(Vector3 dir)
{
Vector3 half_extents = _size * 0.5f;
@@ -219,6 +310,11 @@ namespace Godot
dir.z > 0f ? -half_extents.z : half_extents.z);
}
+ /// <summary>
+ /// Returns a copy of the AABB grown a given amount of units towards all the sides.
+ /// </summary>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown AABB.</returns>
public AABB Grow(real_t by)
{
var res = this;
@@ -233,16 +329,29 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns true if the AABB is flat or empty, or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the AABB has area.</returns>
public bool HasNoArea()
{
return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f;
}
+ /// <summary>
+ /// Returns true if the AABB has no surface (no size), or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the AABB has area.</returns>
public bool HasNoSurface()
{
return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f;
}
+ /// <summary>
+ /// Returns true if the AABB contains a point, or false otherwise.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the AABB contains `point`.</returns>
public bool HasPoint(Vector3 point)
{
if (point.x < _position.x)
@@ -261,6 +370,11 @@ namespace Godot
return true;
}
+ /// <summary>
+ /// Returns the intersection of this AABB and `b`.
+ /// </summary>
+ /// <param name="with">The other AABB.</param>
+ /// <returns>The clipped AABB.</returns>
public AABB Intersection(AABB with)
{
Vector3 src_min = _position;
@@ -297,24 +411,57 @@ namespace Godot
return new AABB(min, max - min);
}
- public bool Intersects(AABB with)
+ /// <summary>
+ /// Returns true if the AABB overlaps with `b`
+ /// (i.e. they have at least one point in common).
+ ///
+ /// If `includeBorders` is true, they will also be considered overlapping
+ /// if their borders touch, even without intersection.
+ /// </summary>
+ /// <param name="with">The other AABB to check for intersections with.</param>
+ /// <param name="includeBorders">Whether or not to consider borders.</param>
+ /// <returns>A bool for whether or not they are intersecting.</returns>
+ public bool Intersects(AABB with, bool includeBorders = false)
{
- 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;
+ if (includeBorders)
+ {
+ 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;
+ }
+ else
+ {
+ 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 true if the AABB is on both sides of `plane`.
+ /// </summary>
+ /// <param name="plane">The plane to check for intersection.</param>
+ /// <returns>A bool for whether or not the AABB intersects the plane.</returns>
public bool IntersectsPlane(Plane plane)
{
Vector3[] points =
@@ -335,14 +482,24 @@ namespace Godot
for (int i = 0; i < 8; i++)
{
if (plane.DistanceTo(points[i]) > 0)
+ {
over = true;
+ }
else
+ {
under = true;
+ }
}
return under && over;
}
+ /// <summary>
+ /// Returns true if the AABB intersects the line segment between `from` and `to`.
+ /// </summary>
+ /// <param name="from">The start of the line segment.</param>
+ /// <param name="to">The end of the line segment.</param>
+ /// <returns>A bool for whether or not the AABB intersects the line segment.</returns>
public bool IntersectsSegment(Vector3 from, Vector3 to)
{
real_t min = 0f;
@@ -359,7 +516,9 @@ namespace Godot
if (segFrom < segTo)
{
if (segFrom > boxEnd || segTo < boxBegin)
+ {
return false;
+ }
real_t length = segTo - segFrom;
cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f;
@@ -368,7 +527,9 @@ namespace Godot
else
{
if (segTo > boxEnd || segFrom < boxBegin)
+ {
return false;
+ }
real_t length = segTo - segFrom;
cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f;
@@ -381,14 +542,23 @@ namespace Godot
}
if (cmax < max)
+ {
max = cmax;
+ }
if (max < min)
+ {
return false;
+ }
}
return true;
}
+ /// <summary>
+ /// Returns a larger AABB that contains this AABB and `b`.
+ /// </summary>
+ /// <param name="with">The other AABB.</param>
+ /// <returns>The merged AABB.</returns>
public AABB Merge(AABB with)
{
Vector3 beg1 = _position;
@@ -411,22 +581,52 @@ namespace Godot
return new AABB(min, max - min);
}
- // Constructors
+ /// <summary>
+ /// Constructs an 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 AABB from a position, width, height, and 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 AABB from x, y, z, and 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 AABB from x, y, z, width, height, and 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);
@@ -458,6 +658,12 @@ namespace Godot
return _position == other._position && _size == other._size;
}
+ /// <summary>
+ /// Returns true if this AABB and `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 are approximately equal.</returns>
public bool IsEqualApprox(AABB other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index baf470a0cc..5aba31c622 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -8,6 +8,20 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// 3×3 matrix used for 3D rotation and scale.
+ /// Almost always used as an orthogonal basis for a Transform.
+ ///
+ /// Contains 3 vector fields X, Y and Z as its columns, which are typically
+ /// interpreted as the local basis vectors of a 3D transformation. For such use,
+ /// it is composed of a scaling and a rotation matrix, in that order (M = R.S).
+ ///
+ /// Can also be accessed as array of 3D vectors. These vectors are normally
+ /// orthogonal to each other, but are not necessarily normalized (due to scaling).
+ ///
+ /// 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 Basis : IEquatable<Basis>
@@ -15,9 +29,9 @@ namespace Godot
// NOTE: x, y and z are public-only. Use Column0, Column1 and Column2 internally.
/// <summary>
- /// Returns the basis matrix’s x vector.
- /// This is equivalent to <see cref="Column0"/>.
+ /// The basis matrix's X vector (column 0).
/// </summary>
+ /// <value>Equivalent to <see cref="Column0"/> and array index `[0]`.</value>
public Vector3 x
{
get => Column0;
@@ -25,9 +39,9 @@ namespace Godot
}
/// <summary>
- /// Returns the basis matrix’s y vector.
- /// This is equivalent to <see cref="Column1"/>.
+ /// The basis matrix's Y vector (column 1).
/// </summary>
+ /// <value>Equivalent to <see cref="Column1"/> and array index `[1]`.</value>
public Vector3 y
{
get => Column1;
@@ -35,19 +49,40 @@ namespace Godot
}
/// <summary>
- /// Returns the basis matrix’s z vector.
- /// This is equivalent to <see cref="Column2"/>.
+ /// The basis matrix's Z vector (column 2).
/// </summary>
+ /// <value>Equivalent to <see cref="Column2"/> and array index `[2]`.</value>
public Vector3 z
{
get => Column2;
set => Column2 = value;
}
+ /// <summary>
+ /// Row 0 of the basis matrix. Shows which vectors contribute
+ /// to the X direction. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
public Vector3 Row0;
+
+ /// <summary>
+ /// Row 1 of the basis matrix. Shows which vectors contribute
+ /// to the Y direction. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
public Vector3 Row1;
+
+ /// <summary>
+ /// Row 2 of the basis matrix. Shows which vectors contribute
+ /// to the Z direction. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
public Vector3 Row2;
+ /// <summary>
+ /// Column 0 of the basis matrix (the X vector).
+ /// </summary>
+ /// <value>Equivalent to <see cref="x"/> and array index `[0]`.</value>
public Vector3 Column0
{
get => new Vector3(Row0.x, Row1.x, Row2.x);
@@ -58,6 +93,11 @@ namespace Godot
this.Row2.x = value.z;
}
}
+
+ /// <summary>
+ /// Column 1 of the basis matrix (the Y vector).
+ /// </summary>
+ /// <value>Equivalent to <see cref="y"/> and array index `[1]`.</value>
public Vector3 Column1
{
get => new Vector3(Row0.y, Row1.y, Row2.y);
@@ -68,6 +108,11 @@ namespace Godot
this.Row2.y = value.z;
}
}
+
+ /// <summary>
+ /// Column 2 of the basis matrix (the Z vector).
+ /// </summary>
+ /// <value>Equivalent to <see cref="z"/> and array index `[2]`.</value>
public Vector3 Column2
{
get => new Vector3(Row0.z, Row1.z, Row2.z);
@@ -79,6 +124,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The scale of this basis.
+ /// </summary>
+ /// <value>Equivalent to the lengths of each column vector, but negative if the determinant is negative.</value>
public Vector3 Scale
{
get
@@ -86,11 +135,18 @@ namespace Godot
real_t detSign = Mathf.Sign(Determinant());
return detSign * new Vector3
(
- new Vector3(this.Row0[0], this.Row1[0], this.Row2[0]).Length(),
- new Vector3(this.Row0[1], this.Row1[1], this.Row2[1]).Length(),
- new Vector3(this.Row0[2], this.Row1[2], this.Row2[2]).Length()
+ Column0.Length(),
+ Column1.Length(),
+ Column2.Length()
);
}
+ set
+ {
+ value /= Scale; // Value becomes what's called "delta_scale" in core.
+ Column0 *= value.x;
+ Column1 *= value.y;
+ Column2 *= value.z;
+ }
}
/// <summary>
@@ -157,8 +213,9 @@ namespace Godot
real_t det = orthonormalizedBasis.Determinant();
if (det < 0)
{
- // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles.
- orthonormalizedBasis = orthonormalizedBasis.Scaled(Vector3.NegOne);
+ // Ensure that the determinant is 1, such that result is a proper
+ // rotation matrix which can be represented by Euler angles.
+ orthonormalizedBasis = orthonormalizedBasis.Scaled(-Vector3.One);
}
return orthonormalizedBasis.Quat();
@@ -182,6 +239,15 @@ namespace Godot
Row2 = new Vector3(0, 0, diagonal.z);
}
+ /// <summary>
+ /// Returns the determinant of the basis matrix. If the basis is
+ /// uniformly scaled, its determinant is the square of the scale.
+ ///
+ /// A negative determinant means the basis has a negative scale.
+ /// A zero determinant means the basis isn't invertible,
+ /// and is usually considered invalid.
+ /// </summary>
+ /// <returns>The determinant of the basis matrix.</returns>
public real_t Determinant()
{
real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
@@ -191,6 +257,16 @@ namespace Godot
return Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20;
}
+ /// <summary>
+ /// Returns the basis's rotation in the form of Euler angles
+ /// (in the YXZ convention: when *decomposing*, first Z, then X, and Y last).
+ /// The returned vector contains the rotation angles in
+ /// the format (X angle, Y angle, Z angle).
+ ///
+ /// Consider using the <see cref="Basis.Quat()"/> method instead, which
+ /// returns a <see cref="Godot.Quat"/> quaternion instead of Euler angles.
+ /// </summary>
+ /// <returns>A Vector3 representing the basis rotation in Euler angles.</returns>
public Vector3 GetEuler()
{
Basis m = Orthonormalized();
@@ -223,6 +299,12 @@ namespace Godot
return euler;
}
+ /// <summary>
+ /// Get rows by index. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
+ /// <param name="index">Which row.</param>
+ /// <returns>One of `Row0`, `Row1`, or `Row2`.</returns>
public Vector3 GetRow(int index)
{
switch (index)
@@ -238,6 +320,12 @@ namespace Godot
}
}
+ /// <summary>
+ /// Sets rows by index. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
+ /// <param name="index">Which row.</param>
+ /// <param name="value">The vector to set the row to.</param>
public void SetRow(int index, Vector3 value)
{
switch (index)
@@ -256,16 +344,16 @@ namespace Godot
}
}
- public Vector3 GetColumn(int index)
- {
- return this[index];
- }
-
- public void SetColumn(int index, Vector3 value)
- {
- this[index] = value;
- }
-
+ /// <summary>
+ /// This function considers a discretization of rotations into
+ /// 24 points on unit sphere, lying along the vectors (x, y, z) with
+ /// each component being either -1, 0, or 1, and returns the index
+ /// of the point best representing the orientation of the object.
+ /// It is mainly used by the <see cref="GridMap"/> editor.
+ ///
+ /// For further details, refer to the Godot source code.
+ /// </summary>
+ /// <returns>The orthogonal index.</returns>
public int GetOrthogonalIndex()
{
var orth = this;
@@ -279,11 +367,17 @@ namespace Godot
real_t v = row[j];
if (v > 0.5f)
+ {
v = 1.0f;
+ }
else if (v < -0.5f)
+ {
v = -1.0f;
+ }
else
+ {
v = 0f;
+ }
row[j] = v;
@@ -294,12 +388,18 @@ namespace Godot
for (int i = 0; i < 24; i++)
{
if (orth == _orthoBases[i])
+ {
return i;
+ }
}
return 0;
}
+ /// <summary>
+ /// Returns the inverse of the matrix.
+ /// </summary>
+ /// <returns>The inverse matrix.</returns>
public Basis Inverse()
{
real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
@@ -309,7 +409,9 @@ namespace Godot
real_t det = Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20;
if (det == 0)
+ {
throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
+ }
real_t detInv = 1.0f / det;
@@ -328,11 +430,17 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the orthonormalized version of the basis matrix (useful to
+ /// call occasionally to avoid rounding errors for orthogonal matrices).
+ /// This performs a Gram-Schmidt orthonormalization on the basis of the matrix.
+ /// </summary>
+ /// <returns>An orthonormalized basis matrix.</returns>
public Basis Orthonormalized()
{
- Vector3 column0 = GetColumn(0);
- Vector3 column1 = GetColumn(1);
- Vector3 column2 = GetColumn(2);
+ Vector3 column0 = this[0];
+ Vector3 column1 = this[1];
+ Vector3 column2 = this[2];
column0.Normalize();
column1 = column1 - column0 * column0.Dot(column1);
@@ -343,48 +451,86 @@ namespace Godot
return new Basis(column0, column1, column2);
}
+ /// <summary>
+ /// Introduce an additional rotation around the given `axis`
+ /// by `phi` (in radians). The axis must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <returns>The rotated basis matrix.</returns>
public Basis Rotated(Vector3 axis, real_t phi)
{
return new Basis(axis, phi) * this;
}
+ /// <summary>
+ /// Introduce an additional scaling specified by the given 3D scaling factor.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled basis matrix.</returns>
public Basis Scaled(Vector3 scale)
{
- var b = this;
+ Basis b = this;
b.Row0 *= scale.x;
b.Row1 *= scale.y;
b.Row2 *= scale.z;
return b;
}
- public Basis Slerp(Basis target, real_t t)
+ /// <summary>
+ /// Assuming that the matrix is a proper rotation matrix, slerp performs
+ /// a spherical-linear interpolation with another rotation matrix.
+ /// </summary>
+ /// <param name="target">The destination basis for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting basis matrix of the interpolation.</returns>
+ public Basis Slerp(Basis target, real_t weight)
{
- var from = new Quat(this);
- var to = new Quat(target);
+ Quat from = new Quat(this);
+ Quat to = new Quat(target);
- var b = new Basis(from.Slerp(to, t));
- b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), t);
- b.Row1 *= Mathf.Lerp(Row1.Length(), target.Row1.Length(), t);
- b.Row2 *= Mathf.Lerp(Row2.Length(), target.Row2.Length(), t);
+ Basis b = new Basis(from.Slerp(to, weight));
+ b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), weight);
+ b.Row1 *= Mathf.Lerp(Row1.Length(), target.Row1.Length(), weight);
+ b.Row2 *= Mathf.Lerp(Row2.Length(), target.Row2.Length(), weight);
return b;
}
+ /// <summary>
+ /// Transposed dot product with the X axis of the matrix.
+ /// </summary>
+ /// <param name="with">A vector to calculate the dot product with.</param>
+ /// <returns>The resulting dot product.</returns>
public real_t Tdotx(Vector3 with)
{
return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2];
}
+ /// <summary>
+ /// Transposed dot product with the Y axis of the matrix.
+ /// </summary>
+ /// <param name="with">A vector to calculate the dot product with.</param>
+ /// <returns>The resulting dot product.</returns>
public real_t Tdoty(Vector3 with)
{
return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2];
}
+ /// <summary>
+ /// Transposed dot product with the Z axis of the matrix.
+ /// </summary>
+ /// <param name="with">A vector to calculate the dot product with.</param>
+ /// <returns>The resulting dot product.</returns>
public real_t Tdotz(Vector3 with)
{
return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2];
}
+ /// <summary>
+ /// Returns the transposed version of the basis matrix.
+ /// </summary>
+ /// <returns>The transposed basis matrix.</returns>
public Basis Transposed()
{
var tr = this;
@@ -404,6 +550,11 @@ namespace Godot
return tr;
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the basis matrix.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector3 Xform(Vector3 v)
{
return new Vector3
@@ -414,6 +565,14 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the transposed basis matrix.
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// basis matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector3 XformInv(Vector3 v)
{
return new Vector3
@@ -424,6 +583,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the basis's rotation in the form of a quaternion.
+ /// See <see cref="GetEuler()"/> if you need Euler angles, but keep in
+ /// mind that quaternions should generally be preferred to Euler angles.
+ /// </summary>
+ /// <returns>A <see cref="Godot.Quat"/> representing the basis's rotation.</returns>
public Quat Quat()
{
real_t trace = Row0[0] + Row1[1] + Row2[2];
@@ -508,11 +673,33 @@ namespace Godot
private static readonly Basis _flipY = new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1);
private static readonly Basis _flipZ = new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1);
+ /// <summary>
+ /// The identity basis, with no rotation or scaling applied.
+ /// This is used as a replacement for `Basis()` in GDScript.
+ /// Do not use `new Basis()` with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Back)`.</value>
public static Basis Identity { get { return _identity; } }
+ /// <summary>
+ /// The basis that will flip something along the X axis when used in a transformation.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Left, Vector3.Up, Vector3.Back)`.</value>
public static Basis FlipX { get { return _flipX; } }
+ /// <summary>
+ /// The basis that will flip something along the Y axis when used in a transformation.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Down, Vector3.Back)`.</value>
public static Basis FlipY { get { return _flipY; } }
+ /// <summary>
+ /// The basis that will flip something along the Z axis when used in a transformation.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Forward)`.</value>
public static Basis FlipZ { get { return _flipZ; } }
+ /// <summary>
+ /// Constructs a pure rotation basis matrix from the given quaternion.
+ /// </summary>
+ /// <param name="quat">The quaternion to create the basis from.</param>
public Basis(Quat quat)
{
real_t s = 2.0f / quat.LengthSquared;
@@ -535,26 +722,41 @@ namespace Godot
Row2 = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
}
- public Basis(Vector3 euler)
+ /// <summary>
+ /// Constructs a pure rotation basis matrix from the given Euler angles
+ /// (in the YXZ convention: when *composing*, first Y, then X, and Z last),
+ /// given in the vector format as (X angle, Y angle, Z angle).
+ ///
+ /// Consider using the <see cref="Basis(Quat)"/> constructor instead, which
+ /// uses a <see cref="Godot.Quat"/> quaternion instead of Euler angles.
+ /// </summary>
+ /// <param name="eulerYXZ">The Euler angles to create the basis from.</param>
+ public Basis(Vector3 eulerYXZ)
{
real_t c;
real_t s;
- c = Mathf.Cos(euler.x);
- s = Mathf.Sin(euler.x);
+ c = Mathf.Cos(eulerYXZ.x);
+ s = Mathf.Sin(eulerYXZ.x);
var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c);
- c = Mathf.Cos(euler.y);
- s = Mathf.Sin(euler.y);
+ c = Mathf.Cos(eulerYXZ.y);
+ s = Mathf.Sin(eulerYXZ.y);
var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c);
- c = Mathf.Cos(euler.z);
- s = Mathf.Sin(euler.z);
+ c = Mathf.Cos(eulerYXZ.z);
+ s = Mathf.Sin(eulerYXZ.z);
var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1);
this = ymat * xmat * zmat;
}
+ /// <summary>
+ /// Constructs a pure rotation basis matrix, rotated around the given `axis`
+ /// by `phi` (in radians). The axis must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate, in radians.</param>
public Basis(Vector3 axis, real_t phi)
{
Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
@@ -582,6 +784,12 @@ namespace Godot
Row2.y = xyzt + zyxs;
}
+ /// <summary>
+ /// Constructs a basis matrix from 3 axis vectors (matrix columns).
+ /// </summary>
+ /// <param name="column0">The X vector, or Column0.</param>
+ /// <param name="column1">The Y vector, or Column1.</param>
+ /// <param name="column2">The Z vector, or Column2.</param>
public Basis(Vector3 column0, Vector3 column1, Vector3 column2)
{
Row0 = new Vector3(column0.x, column1.x, column2.x);
@@ -637,6 +845,12 @@ namespace Godot
return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2);
}
+ /// <summary>
+ /// Returns true if this basis and `other` are approximately equal, by running
+ /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other basis to compare.</param>
+ /// <returns>Whether or not the matrices are approximately equal.</returns>
public bool IsEqualApprox(Basis other)
{
return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index 6030b72a44..d851abc6d3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -3,15 +3,44 @@ using System.Runtime.InteropServices;
namespace Godot
{
+ /// <summary>
+ /// A color represented by red, green, blue, and alpha (RGBA) components.
+ /// The alpha component is often used for transparency.
+ /// Values are in floating-point and usually range from 0 to 1.
+ /// Some properties (such as CanvasItem.modulate) may accept values
+ /// greater than 1 (overbright or HDR colors).
+ ///
+ /// If you want to supply values in a range of 0 to 255, you should use
+ /// <see cref="Color8"/> and the `r8`/`g8`/`b8`/`a8` properties.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Color : IEquatable<Color>
{
+ /// <summary>
+ /// The color's red component, typically on the range of 0 to 1.
+ /// </summary>
public float r;
+
+ /// <summary>
+ /// The color's green component, typically on the range of 0 to 1.
+ /// </summary>
public float g;
+
+ /// <summary>
+ /// The color's blue component, typically on the range of 0 to 1.
+ /// </summary>
public float b;
+
+ /// <summary>
+ /// The color's alpha (transparency) component, typically on the range of 0 to 1.
+ /// </summary>
public float a;
+ /// <summary>
+ /// Wrapper for <see cref="r"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int r8
{
get
@@ -24,6 +53,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Wrapper for <see cref="g"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int g8
{
get
@@ -36,6 +69,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Wrapper for <see cref="b"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int b8
{
get
@@ -48,6 +85,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Wrapper for <see cref="a"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int a8
{
get
@@ -60,6 +101,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The HSV hue of this color, on the range 0 to 1.
+ /// </summary>
+ /// <value>Getting is a long process, refer to the source code for details. Setting uses <see cref="FromHsv"/>.</value>
public float h
{
get
@@ -70,21 +115,31 @@ namespace Godot
float delta = max - min;
if (delta == 0)
+ {
return 0;
+ }
float h;
if (r == max)
+ {
h = (g - b) / delta; // Between yellow & magenta
+ }
else if (g == max)
+ {
h = 2 + (b - r) / delta; // Between cyan & yellow
+ }
else
+ {
h = 4 + (r - g) / delta; // Between magenta & cyan
+ }
h /= 6.0f;
if (h < 0)
+ {
h += 1.0f;
+ }
return h;
}
@@ -94,6 +149,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The HSV saturation of this color, on the range 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to the ratio between the min and max RGB value. Setting uses <see cref="FromHsv"/>.</value>
public float s
{
get
@@ -103,7 +162,7 @@ namespace Godot
float delta = max - min;
- return max != 0 ? delta / max : 0;
+ return max == 0 ? 0 : delta / max;
}
set
{
@@ -111,6 +170,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The HSV value (brightness) of this color, on the range 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to using `Max()` on the RGB components. Setting uses <see cref="FromHsv"/>.</value>
public float v
{
get
@@ -123,25 +186,10 @@ namespace Godot
}
}
- public static Color ColorN(string name, float alpha = 1f)
- {
- name = name.Replace(" ", String.Empty);
- name = name.Replace("-", String.Empty);
- name = name.Replace("_", String.Empty);
- name = name.Replace("'", String.Empty);
- name = name.Replace(".", String.Empty);
- name = name.ToLower();
-
- if (!Colors.namedColors.ContainsKey(name))
- {
- throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
- }
-
- Color color = Colors.namedColors[name];
- color.a = alpha;
- return color;
- }
-
+ /// <summary>
+ /// Access color components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.r`, `[1]` is equivalent to `.g`, `[2]` is equivalent to `.b`, `[3]` is equivalent to `.a`.</value>
public float this[int index]
{
get
@@ -182,73 +230,13 @@ namespace Godot
}
}
- public void ToHsv(out float hue, out float saturation, out float value)
- {
- float max = (float)Mathf.Max(r, Mathf.Max(g, b));
- float min = (float)Mathf.Min(r, Mathf.Min(g, b));
-
- float delta = max - min;
-
- if (delta == 0)
- {
- hue = 0;
- }
- else
- {
- if (r == max)
- hue = (g - b) / delta; // Between yellow & magenta
- else if (g == max)
- hue = 2 + (b - r) / delta; // Between cyan & yellow
- else
- hue = 4 + (r - g) / delta; // Between magenta & cyan
-
- hue /= 6.0f;
-
- if (hue < 0)
- hue += 1.0f;
- }
-
- saturation = max == 0 ? 0 : 1f - 1f * min / max;
- value = max;
- }
-
- public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f)
- {
- if (saturation == 0)
- {
- // acp_hromatic (grey)
- return new Color(value, value, value, alpha);
- }
-
- int i;
- float f, p, q, t;
-
- hue *= 6.0f;
- hue %= 6f;
- i = (int)hue;
-
- f = hue - i;
- p = value * (1 - saturation);
- q = value * (1 - saturation * f);
- t = value * (1 - saturation * (1 - f));
-
- switch (i)
- {
- case 0: // Red is the dominant color
- return new Color(value, t, p, alpha);
- case 1: // Green is the dominant color
- return new Color(q, value, p, alpha);
- case 2:
- return new Color(p, value, t, alpha);
- case 3: // Blue is the dominant color
- return new Color(p, q, value, alpha);
- case 4:
- return new Color(t, p, value, alpha);
- default: // (5) Red is the dominant color
- return new Color(value, p, q, alpha);
- }
- }
-
+ /// <summary>
+ /// Returns a new color resulting from blending this color over another.
+ /// If the color is opaque, the result is also opaque.
+ /// The second color may have a range of alpha values.
+ /// </summary>
+ /// <param name="over">The color to blend over.</param>
+ /// <returns>This color blended over `over`.</returns>
public Color Blend(Color over)
{
Color res;
@@ -268,6 +256,10 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns the most contrasting color.
+ /// </summary>
+ /// <returns>The most contrasting color</returns>
public Color Contrasted()
{
return new Color(
@@ -278,6 +270,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a new color resulting from making this color darker
+ /// by the specified ratio (on the range of 0 to 1).
+ /// </summary>
+ /// <param name="amount">The ratio to darken by.</param>
+ /// <returns>The darkened color.</returns>
public Color Darkened(float amount)
{
Color res = this;
@@ -287,6 +285,10 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns the inverted color: `(1 - r, 1 - g, 1 - b, a)`.
+ /// </summary>
+ /// <returns>The inverted color.</returns>
public Color Inverted()
{
return new Color(
@@ -297,6 +299,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a new color resulting from making this color lighter
+ /// by the specified ratio (on the range of 0 to 1).
+ /// </summary>
+ /// <param name="amount">The ratio to lighten by.</param>
+ /// <returns>The darkened color.</returns>
public Color Lightened(float amount)
{
Color res = this;
@@ -306,6 +314,13 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this color and `to` by amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination color for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting color of the interpolation.</returns>
public Color Lerp(Color to, float weight)
{
return new Color
@@ -317,6 +332,13 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this color and `to` by color amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination color for interpolation.</param>
+ /// <param name="weight">A color with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting color of the interpolation.</returns>
public Color Lerp(Color to, Color weight)
{
return new Color
@@ -328,6 +350,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the color's 32-bit integer in ABGR format
+ /// (each byte represents a component of the ABGR profile).
+ /// ABGR is the reversed version of the default format.
+ /// </summary>
+ /// <returns>A uint representing this color in ABGR32 format.</returns>
public uint ToAbgr32()
{
uint c = (byte)Math.Round(a * 255);
@@ -341,6 +369,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 64-bit integer in ABGR format
+ /// (each word represents a component of the ABGR profile).
+ /// ABGR is the reversed version of the default format.
+ /// </summary>
+ /// <returns>A ulong representing this color in ABGR64 format.</returns>
public ulong ToAbgr64()
{
ulong c = (ushort)Math.Round(a * 65535);
@@ -354,6 +388,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 32-bit integer in ARGB format
+ /// (each byte represents a component of the ARGB profile).
+ /// ARGB is more compatible with DirectX, but not used much in Godot.
+ /// </summary>
+ /// <returns>A uint representing this color in ARGB32 format.</returns>
public uint ToArgb32()
{
uint c = (byte)Math.Round(a * 255);
@@ -367,6 +407,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 64-bit integer in ARGB format
+ /// (each word represents a component of the ARGB profile).
+ /// ARGB is more compatible with DirectX, but not used much in Godot.
+ /// </summary>
+ /// <returns>A ulong representing this color in ARGB64 format.</returns>
public ulong ToArgb64()
{
ulong c = (ushort)Math.Round(a * 65535);
@@ -380,6 +426,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 32-bit integer in RGBA format
+ /// (each byte represents a component of the RGBA profile).
+ /// RGBA is Godot's default and recommended format.
+ /// </summary>
+ /// <returns>A uint representing this color in RGBA32 format.</returns>
public uint ToRgba32()
{
uint c = (byte)Math.Round(r * 255);
@@ -393,6 +445,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 64-bit integer in RGBA format
+ /// (each word represents a component of the RGBA profile).
+ /// RGBA is Godot's default and recommended format.
+ /// </summary>
+ /// <returns>A ulong representing this color in RGBA64 format.</returns>
public ulong ToRgba64()
{
ulong c = (ushort)Math.Round(r * 65535);
@@ -406,6 +464,11 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's HTML hexadecimal color string in RGBA format.
+ /// </summary>
+ /// <param name="includeAlpha">Whether or not to include alpha. If false, the color is RGB instead of RGBA.</param>
+ /// <returns>A string for the HTML hexadecimal representation of this color.</returns>
public string ToHtml(bool includeAlpha = true)
{
var txt = string.Empty;
@@ -415,12 +478,20 @@ namespace Godot
txt += ToHex32(b);
if (includeAlpha)
- txt = ToHex32(a) + txt;
+ {
+ txt += ToHex32(a);
+ }
return txt;
}
- // Constructors
+ /// <summary>
+ /// Constructs a color from RGBA values on the range of 0 to 1.
+ /// </summary>
+ /// <param name="r">The color's red component, typically on the range of 0 to 1.</param>
+ /// <param name="g">The color's green component, typically on the range of 0 to 1.</param>
+ /// <param name="b">The color's blue component, typically on the range of 0 to 1.</param>
+ /// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param>
public Color(float r, float g, float b, float a = 1.0f)
{
this.r = r;
@@ -429,6 +500,11 @@ namespace Godot
this.a = a;
}
+ /// <summary>
+ /// Constructs a color from an existing color and an alpha value.
+ /// </summary>
+ /// <param name="c">The color to construct from. Only its RGB values are used.</param>
+ /// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param>
public Color(Color c, float a = 1.0f)
{
r = c.r;
@@ -437,6 +513,11 @@ namespace Godot
this.a = a;
}
+ /// <summary>
+ /// Constructs a color from a 32-bit integer
+ /// (each byte represents a component of the RGBA profile).
+ /// </summary>
+ /// <param name="rgba">The uint representing the color.</param>
public Color(uint rgba)
{
a = (rgba & 0xFF) / 255.0f;
@@ -448,6 +529,11 @@ namespace Godot
r = (rgba & 0xFF) / 255.0f;
}
+ /// <summary>
+ /// Constructs a color from a 64-bit integer
+ /// (each word represents a component of the RGBA profile).
+ /// </summary>
+ /// <param name="rgba">The ulong representing the color.</param>
public Color(ulong rgba)
{
a = (rgba & 0xFFFF) / 65535.0f;
@@ -459,6 +545,212 @@ namespace Godot
r = (rgba & 0xFFFF) / 65535.0f;
}
+ /// <summary>
+ /// Constructs a color from the HTML hexadecimal color string in RGBA format.
+ /// </summary>
+ /// <param name="rgba">A string for the HTML hexadecimal representation of this color.</param>
+ public Color(string rgba)
+ {
+ if (rgba.Length == 0)
+ {
+ r = 0f;
+ g = 0f;
+ b = 0f;
+ a = 1.0f;
+ return;
+ }
+
+ if (rgba[0] == '#')
+ {
+ rgba = rgba.Substring(1);
+ }
+
+ bool alpha;
+
+ if (rgba.Length == 8)
+ {
+ alpha = true;
+ }
+ else if (rgba.Length == 6)
+ {
+ alpha = false;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
+ }
+
+ if (alpha)
+ {
+ a = ParseCol8(rgba, 6) / 255f;
+
+ if (a < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
+ }
+ }
+ else
+ {
+ a = 1.0f;
+ }
+
+ int from = alpha ? 2 : 0;
+
+ r = ParseCol8(rgba, 0) / 255f;
+
+ if (r < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
+ }
+
+ g = ParseCol8(rgba, 2) / 255f;
+
+ if (g < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
+ }
+
+ b = ParseCol8(rgba, 4) / 255f;
+
+ if (b < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
+ }
+ }
+
+ /// <summary>
+ /// Returns a color constructed from integer red, green, blue, and alpha channels.
+ /// Each channel should have 8 bits of information ranging from 0 to 255.
+ /// </summary>
+ /// <param name="r8">The red component represented on the range of 0 to 255.</param>
+ /// <param name="g8">The green component represented on the range of 0 to 255.</param>
+ /// <param name="b8">The blue component represented on the range of 0 to 255.</param>
+ /// <param name="a8">The alpha (transparency) component represented on the range of 0 to 255.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color Color8(byte r8, byte g8, byte b8, byte a8 = 255)
+ {
+ return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f);
+ }
+
+ /// <summary>
+ /// Returns a color according to the standardized name, with the
+ /// specified alpha value. Supported color names are the same as
+ /// the constants defined in <see cref="Colors"/>.
+ /// </summary>
+ /// <param name="name">The name of the color.</param>
+ /// <param name="alpha">The alpha (transparency) component represented on the range of 0 to 1. Default: 1.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color ColorN(string name, float alpha = 1f)
+ {
+ name = name.Replace(" ", String.Empty);
+ name = name.Replace("-", String.Empty);
+ name = name.Replace("_", String.Empty);
+ name = name.Replace("'", String.Empty);
+ name = name.Replace(".", String.Empty);
+ name = name.ToLower();
+
+ if (!Colors.namedColors.ContainsKey(name))
+ {
+ throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
+ }
+
+ Color color = Colors.namedColors[name];
+ color.a = alpha;
+ return color;
+ }
+
+ /// <summary>
+ /// Constructs a color from an HSV profile, with values on the
+ /// range of 0 to 1. This is equivalent to using each of
+ /// the `h`/`s`/`v` properties, but much more efficient.
+ /// </summary>
+ /// <param name="hue">The HSV hue, typically on the range of 0 to 1.</param>
+ /// <param name="saturation">The HSV saturation, typically on the range of 0 to 1.</param>
+ /// <param name="value">The HSV value (brightness), typically on the range of 0 to 1.</param>
+ /// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f)
+ {
+ if (saturation == 0)
+ {
+ // acp_hromatic (grey)
+ return new Color(value, value, value, alpha);
+ }
+
+ int i;
+ float f, p, q, t;
+
+ hue *= 6.0f;
+ hue %= 6f;
+ i = (int)hue;
+
+ f = hue - i;
+ p = value * (1 - saturation);
+ q = value * (1 - saturation * f);
+ t = value * (1 - saturation * (1 - f));
+
+ switch (i)
+ {
+ case 0: // Red is the dominant color
+ return new Color(value, t, p, alpha);
+ case 1: // Green is the dominant color
+ return new Color(q, value, p, alpha);
+ case 2:
+ return new Color(p, value, t, alpha);
+ case 3: // Blue is the dominant color
+ return new Color(p, q, value, alpha);
+ case 4:
+ return new Color(t, p, value, alpha);
+ default: // (5) Red is the dominant color
+ return new Color(value, p, q, alpha);
+ }
+ }
+
+ /// <summary>
+ /// Converts a color to HSV values. This is equivalent to using each of
+ /// the `h`/`s`/`v` properties, but much more efficient.
+ /// </summary>
+ /// <param name="hue">Output parameter for the HSV hue.</param>
+ /// <param name="saturation">Output parameter for the HSV saturation.</param>
+ /// <param name="value">Output parameter for the HSV value.</param>
+ public void ToHsv(out float hue, out float saturation, out float value)
+ {
+ float max = (float)Mathf.Max(r, Mathf.Max(g, b));
+ float min = (float)Mathf.Min(r, Mathf.Min(g, b));
+
+ float delta = max - min;
+
+ if (delta == 0)
+ {
+ hue = 0;
+ }
+ else
+ {
+ if (r == max)
+ {
+ hue = (g - b) / delta; // Between yellow & magenta
+ }
+ else if (g == max)
+ {
+ hue = 2 + (b - r) / delta; // Between cyan & yellow
+ }
+ else
+ {
+ hue = 4 + (r - g) / delta; // Between magenta & cyan
+ }
+
+ hue /= 6.0f;
+
+ if (hue < 0)
+ {
+ hue += 1.0f;
+ }
+ }
+
+ saturation = max == 0 ? 0 : 1f - 1f * min / max;
+ value = max;
+ }
+
private static int ParseCol8(string str, int ofs)
{
int ig = 0;
@@ -488,9 +780,13 @@ namespace Godot
}
if (i == 0)
+ {
ig += v * 16;
+ }
else
+ {
ig += v;
+ }
}
return ig;
@@ -508,9 +804,13 @@ namespace Godot
int lv = v & 0xF;
if (lv < 10)
+ {
c = (char)('0' + lv);
+ }
else
+ {
c = (char)('a' + lv - 10);
+ }
v >>= 4;
ret = c + ret;
@@ -522,10 +822,14 @@ namespace Godot
internal static bool HtmlIsValid(string color)
{
if (color.Length == 0)
+ {
return false;
+ }
if (color[0] == '#')
+ {
color = color.Substring(1, color.Length - 1);
+ }
bool alpha;
@@ -544,83 +848,27 @@ namespace Godot
if (alpha)
{
if (ParseCol8(color, 0) < 0)
+ {
return false;
+ }
}
int from = alpha ? 2 : 0;
if (ParseCol8(color, from + 0) < 0)
- return false;
- if (ParseCol8(color, from + 2) < 0)
- return false;
- if (ParseCol8(color, from + 4) < 0)
- return false;
-
- return true;
- }
-
- public static Color Color8(byte r8, byte g8, byte b8, byte a8 = 255)
- {
- return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f);
- }
-
- public Color(string rgba)
- {
- if (rgba.Length == 0)
{
- r = 0f;
- g = 0f;
- b = 0f;
- a = 1.0f;
- return;
- }
-
- if (rgba[0] == '#')
- rgba = rgba.Substring(1);
-
- bool alpha;
-
- if (rgba.Length == 8)
- {
- alpha = true;
- }
- else if (rgba.Length == 6)
- {
- alpha = false;
- }
- else
- {
- throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
+ return false;
}
-
- if (alpha)
+ if (ParseCol8(color, from + 2) < 0)
{
- a = ParseCol8(rgba, 0) / 255f;
-
- if (a < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
+ return false;
}
- else
+ if (ParseCol8(color, from + 4) < 0)
{
- a = 1.0f;
+ return false;
}
- int from = alpha ? 2 : 0;
-
- r = ParseCol8(rgba, from + 0) / 255f;
-
- if (r < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
-
- g = ParseCol8(rgba, from + 2) / 255f;
-
- if (g < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
-
- b = ParseCol8(rgba, from + 4) / 255f;
-
- if (b < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
+ return true;
}
public static Color operator +(Color left, Color right)
@@ -708,13 +956,13 @@ namespace Godot
if (Mathf.IsEqualApprox(left.g, right.g))
{
if (Mathf.IsEqualApprox(left.b, right.b))
+ {
return left.a < right.a;
+ }
return left.b < right.b;
}
-
return left.g < right.g;
}
-
return left.r < right.r;
}
@@ -725,13 +973,13 @@ namespace Godot
if (Mathf.IsEqualApprox(left.g, right.g))
{
if (Mathf.IsEqualApprox(left.b, right.b))
+ {
return left.a > right.a;
+ }
return left.b > right.b;
}
-
return left.g > right.g;
}
-
return left.r > right.r;
}
@@ -750,6 +998,12 @@ namespace Godot
return r == other.r && g == other.g && b == other.b && a == other.a;
}
+ /// <summary>
+ /// Returns true if this color and `other` are approximately equal, by running
+ /// <see cref="Godot.Mathf.IsEqualApprox(float, float)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other color to compare.</param>
+ /// <returns>Whether or not the colors are approximately equal.</returns>
public bool IsEqualApprox(Color other)
{
return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index f41f5e9fc8..d05a0414aa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -3,6 +3,10 @@ using System.Collections.Generic;
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/color_names.inc
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 4f7aa99df8..6eecc262d6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -11,79 +11,185 @@ namespace Godot
{
// Define constants with Decimal precision and cast down to double or float.
+ /// <summary>
+ /// The circle constant, the circumference of the unit circle in radians.
+ /// </summary>
public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959
+
+ /// <summary>
+ /// Constant that represents how many times the diameter of a circle
+ /// fits around its perimeter. This is equivalent to `Mathf.Tau / 2`.
+ /// </summary>
public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+
+ /// <summary>
+ /// Positive infinity. For negative infinity, use `-Mathf.Inf`.
+ /// </summary>
public const real_t Inf = real_t.PositiveInfinity;
+
+ /// <summary>
+ /// "Not a Number", an invalid value. `NaN` has special properties, including
+ /// that it is not equal to itself. It is output by some invalid operations,
+ /// such as dividing zero by zero.
+ /// </summary>
public const real_t NaN = real_t.NaN;
private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433
private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823
+ /// <summary>
+ /// Returns the absolute value of `s` (i.e. positive value).
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>The absolute value of `s`.</returns>
public static int Abs(int s)
{
return Math.Abs(s);
}
+ /// <summary>
+ /// Returns the absolute value of `s` (i.e. positive value).
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>The absolute value of `s`.</returns>
public static real_t Abs(real_t s)
{
return Math.Abs(s);
}
+ /// <summary>
+ /// Returns the arc cosine of `s` in radians. Use to get the angle of cosine s.
+ /// </summary>
+ /// <param name="s">The input cosine value. Must be on the range of -1.0 to 1.0.</param>
+ /// <returns>An angle that would result in the given cosine value. On the range `0` to `Tau/2`.</returns>
public static real_t Acos(real_t s)
{
return (real_t)Math.Acos(s);
}
+ /// <summary>
+ /// Returns the arc sine of `s` in radians. Use to get the angle of sine s.
+ /// </summary>
+ /// <param name="s">The input sine value. Must be on the range of -1.0 to 1.0.</param>
+ /// <returns>An angle that would result in the given sine value. On the range `-Tau/4` to `Tau/4`.</returns>
public static real_t Asin(real_t s)
{
return (real_t)Math.Asin(s);
}
+ /// <summary>
+ /// Returns the arc tangent of `s` in radians. Use to get the angle of tangent s.
+ ///
+ /// The method cannot know in which quadrant the angle should fall.
+ /// See <see cref="Atan2(real_t, real_t)"/> if you have both `y` and `x`.
+ /// </summary>
+ /// <param name="s">The input tangent value.</param>
+ /// <returns>An angle that would result in the given tangent value. On the range `-Tau/4` to `Tau/4`.</returns>
public static real_t Atan(real_t s)
{
return (real_t)Math.Atan(s);
}
+ /// <summary>
+ /// Returns the arc tangent of `y` and `x` in radians. Use to get the angle
+ /// of the tangent of `y/x`. To compute the value, the method takes into
+ /// account the sign of both arguments in order to determine the quadrant.
+ ///
+ /// Important note: The Y coordinate comes first, by convention.
+ /// </summary>
+ /// <param name="y">The Y coordinate of the point to find the angle to.</param>
+ /// <param name="x">The X coordinate of the point to find the angle to.</param>
+ /// <returns>An angle that would result in the given tangent value. On the range `-Tau/2` to `Tau/2`.</returns>
public static real_t Atan2(real_t y, real_t x)
{
return (real_t)Math.Atan2(y, x);
}
+ /// <summary>
+ /// Converts a 2D point expressed in the cartesian coordinate
+ /// system (X and Y axis) to the polar coordinate system
+ /// (a distance from the origin and an angle).
+ /// </summary>
+ /// <param name="x">The input X coordinate.</param>
+ /// <param name="y">The input Y coordinate.</param>
+ /// <returns>A <see cref="Vector2"/> with X representing the distance and Y representing the angle.</returns>
public static Vector2 Cartesian2Polar(real_t x, real_t y)
{
return new Vector2(Sqrt(x * x + y * y), Atan2(y, x));
}
+ /// <summary>
+ /// Rounds `s` upward (towards positive infinity).
+ /// </summary>
+ /// <param name="s">The number to ceil.</param>
+ /// <returns>The smallest whole number that is not less than `s`.</returns>
public static real_t Ceil(real_t s)
{
return (real_t)Math.Ceiling(s);
}
+ /// <summary>
+ /// Clamps a `value` so that it is not less than `min` and not more than `max`.
+ /// </summary>
+ /// <param name="value">The value to clamp.</param>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The clamped value.</returns>
public static int Clamp(int value, int min, int max)
{
return value < min ? min : value > max ? max : value;
}
+ /// <summary>
+ /// Clamps a `value` so that it is not less than `min` and not more than `max`.
+ /// </summary>
+ /// <param name="value">The value to clamp.</param>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The clamped value.</returns>
public static real_t Clamp(real_t value, real_t min, real_t max)
{
return value < min ? min : value > max ? max : value;
}
+ /// <summary>
+ /// Returns the cosine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The cosine of that angle.</returns>
public static real_t Cos(real_t s)
{
return (real_t)Math.Cos(s);
}
+ /// <summary>
+ /// Returns the hyperbolic cosine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic cosine of that angle.</returns>
public static real_t Cosh(real_t s)
{
return (real_t)Math.Cosh(s);
}
+ /// <summary>
+ /// Converts an angle expressed in degrees to radians.
+ /// </summary>
+ /// <param name="deg">An angle expressed in degrees.</param>
+ /// <returns>The same angle expressed in radians.</returns>
public static real_t Deg2Rad(real_t deg)
{
return deg * Deg2RadConst;
}
+ /// <summary>
+ /// Easing function, based on exponent. The curve values are:
+ /// `0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out.
+ /// Negative values are in-out/out-in.
+ /// </summary>
+ /// <param name="s">The value to ease.</param>
+ /// <param name="curve">`0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out.</param>
+ /// <returns>The eased value.</returns>
public static real_t Ease(real_t s, real_t curve)
{
if (s < 0f)
@@ -118,21 +224,47 @@ namespace Godot
return 0f;
}
+ /// <summary>
+ /// The natural exponential function. It raises the mathematical
+ /// constant `e` to the power of `s` and returns it.
+ /// </summary>
+ /// <param name="s">The exponent to raise `e` to.</param>
+ /// <returns>`e` raised to the power of `s`.</returns>
public static real_t Exp(real_t s)
{
return (real_t)Math.Exp(s);
}
+ /// <summary>
+ /// Rounds `s` downward (towards negative infinity).
+ /// </summary>
+ /// <param name="s">The number to floor.</param>
+ /// <returns>The largest whole number that is not more than `s`.</returns>
public static real_t Floor(real_t s)
{
return (real_t)Math.Floor(s);
}
+ /// <summary>
+ /// Returns a normalized value considering the given range.
+ /// This is the opposite of <see cref="Lerp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="from">The interpolated value.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the inverse interpolation.</returns>
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
return (weight - from) / (to - from);
}
+ /// <summary>
+ /// Returns true if `a` and `b` are approximately equal to each other.
+ /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>A bool for whether or not the two values are approximately equal.</returns>
public static bool IsEqualApprox(real_t a, real_t b)
{
// Check for exact equality first, required to handle "infinity" values.
@@ -149,26 +281,62 @@ namespace Godot
return Abs(a - b) < tolerance;
}
+ /// <summary>
+ /// Returns whether `s` is an infinity value (either positive infinity or negative infinity).
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A bool for whether or not the value is an infinity value.</returns>
public static bool IsInf(real_t s)
{
return real_t.IsInfinity(s);
}
+ /// <summary>
+ /// Returns whether `s` is a `NaN` ("Not a Number" or invalid) value.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A bool for whether or not the value is a `NaN` value.</returns>
public static bool IsNaN(real_t s)
{
return real_t.IsNaN(s);
}
+ /// <summary>
+ /// Returns true if `s` is approximately zero.
+ /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
+ ///
+ /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A bool for whether or not the value is nearly zero.</returns>
public static bool IsZeroApprox(real_t s)
{
return Abs(s) < Epsilon;
}
+ /// <summary>
+ /// Linearly interpolates between two values by a normalized value.
+ /// This is the opposite <see cref="InverseLerp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
return from + (to - from) * weight;
}
+ /// <summary>
+ /// Linearly interpolates between two angles (in radians) by a normalized value.
+ ///
+ /// Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
+ /// but interpolates correctly when the angles wrap around <see cref="Tau"/>.
+ /// </summary>
+ /// <param name="from">The start angle for interpolation.</param>
+ /// <param name="to">The destination angle for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting angle of the interpolation.</returns>
public static real_t LerpAngle(real_t from, real_t to, real_t weight)
{
real_t difference = (to - from) % Mathf.Tau;
@@ -176,36 +344,81 @@ namespace Godot
return from + distance * weight;
}
+ /// <summary>
+ /// Natural logarithm. The amount of time needed to reach a certain level of continuous growth.
+ ///
+ /// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
+ /// </summary>
+ /// <param name="s">The input value.</param>
+ /// <returns>The natural log of `s`.</returns>
public static real_t Log(real_t s)
{
return (real_t)Math.Log(s);
}
+ /// <summary>
+ /// Returns the maximum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is higher.</returns>
public static int Max(int a, int b)
{
return a > b ? a : b;
}
+ /// <summary>
+ /// Returns the maximum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is higher.</returns>
public static real_t Max(real_t a, real_t b)
{
return a > b ? a : b;
}
+ /// <summary>
+ /// Returns the minimum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is lower.</returns>
public static int Min(int a, int b)
{
return a < b ? a : b;
}
+ /// <summary>
+ /// Returns the minimum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is lower.</returns>
public static real_t Min(real_t a, real_t b)
{
return a < b ? a : b;
}
+ /// <summary>
+ /// Moves `from` toward `to` by the `delta` value.
+ ///
+ /// Use a negative delta value to move away.
+ /// </summary>
+ /// <param name="from">The start value.</param>
+ /// <param name="to">The value to move towards.</param>
+ /// <param name="delta">The amount to move by.</param>
+ /// <returns>The value after moving.</returns>
public static real_t MoveToward(real_t from, real_t to, real_t delta)
{
return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta;
}
+ /// <summary>
+ /// Returns the nearest larger power of 2 for the integer `value`.
+ /// </summary>
+ /// <param name="value">The input value.</param>
+ /// <returns>The nearest larger power of 2.</returns>
public static int NearestPo2(int value)
{
value--;
@@ -218,14 +431,25 @@ namespace Godot
return value;
}
+ /// <summary>
+ /// Converts a 2D point expressed in the polar coordinate
+ /// system (a distance from the origin `r` and an angle `th`)
+ /// to the cartesian coordinate system (X and Y axis).
+ /// </summary>
+ /// <param name="r">The distance from the origin.</param>
+ /// <param name="th">The angle of the point.</param>
+ /// <returns>A <see cref="Vector2"/> representing the cartesian coordinate.</returns>
public static Vector2 Polar2Cartesian(real_t r, real_t th)
{
return new Vector2(r * Cos(th), r * Sin(th));
}
/// <summary>
- /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`.
/// </summary>
+ /// <param name="a">The dividend, the primary input.</param>
+ /// <param name="b">The divisor. The output is on the range `[0, b)`.</param>
+ /// <returns>The resulting output.</returns>
public static int PosMod(int a, int b)
{
int c = a % b;
@@ -237,8 +461,11 @@ namespace Godot
}
/// <summary>
- /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`.
/// </summary>
+ /// <param name="a">The dividend, the primary input.</param>
+ /// <param name="b">The divisor. The output is on the range `[0, b)`.</param>
+ /// <returns>The resulting output.</returns>
public static real_t PosMod(real_t a, real_t b)
{
real_t c = a % b;
@@ -249,43 +476,89 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the result of `x` raised to the power of `y`.
+ /// </summary>
+ /// <param name="x">The base.</param>
+ /// <param name="y">The exponent.</param>
+ /// <returns>`x` raised to the power of `y`.</returns>
public static real_t Pow(real_t x, real_t y)
{
return (real_t)Math.Pow(x, y);
}
+ /// <summary>
+ /// Converts an angle expressed in radians to degrees.
+ /// </summary>
+ /// <param name="rad">An angle expressed in radians.</param>
+ /// <returns>The same angle expressed in degrees.</returns>
public static real_t Rad2Deg(real_t rad)
{
return rad * Rad2DegConst;
}
+ /// <summary>
+ /// Rounds `s` to the nearest whole number,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <param name="s">The number to round.</param>
+ /// <returns>The rounded number.</returns>
public static real_t Round(real_t s)
{
return (real_t)Math.Round(s);
}
+ /// <summary>
+ /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`.
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>One of three possible values: `1`, `-1`, or `0`.</returns>
public static int Sign(int s)
{
if (s == 0) return 0;
return s < 0 ? -1 : 1;
}
+ /// <summary>
+ /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`.
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>One of three possible values: `1`, `-1`, or `0`.</returns>
public static int Sign(real_t s)
{
if (s == 0) return 0;
return s < 0 ? -1 : 1;
}
+ /// <summary>
+ /// Returns the sine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The sine of that angle.</returns>
public static real_t Sin(real_t s)
{
return (real_t)Math.Sin(s);
}
+ /// <summary>
+ /// Returns the hyperbolic sine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic sine of that angle.</returns>
public static real_t Sinh(real_t s)
{
return (real_t)Math.Sinh(s);
}
+ /// <summary>
+ /// Returns a number smoothly interpolated between `from` and `to`,
+ /// based on the `weight`. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
+ /// but interpolates faster at the beginning and slower at the end.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
public static real_t SmoothStep(real_t from, real_t to, real_t weight)
{
if (IsEqualApprox(from, to))
@@ -296,11 +569,25 @@ namespace Godot
return x * x * (3 - 2 * x);
}
+ /// <summary>
+ /// Returns the square root of `s`, where `s` is a non-negative number.
+ ///
+ /// If you need negative inputs, use `System.Numerics.Complex`.
+ /// </summary>
+ /// <param name="s">The input number. Must not be negative.</param>
+ /// <returns>The square root of `s`.</returns>
public static real_t Sqrt(real_t s)
{
return (real_t)Math.Sqrt(s);
}
+ /// <summary>
+ /// Returns the position of the first non-zero digit, after the
+ /// decimal point. Note that the maximum return value is 10,
+ /// which is a design decision in the implementation.
+ /// </summary>
+ /// <param name="step">The input value.</param>
+ /// <returns>The position of the first non-zero digit.</returns>
public static int StepDecimals(real_t step)
{
double[] sd = new double[] {
@@ -326,32 +613,68 @@ namespace Godot
return 0;
}
+ /// <summary>
+ /// Snaps float value `s` to a given `step`.
+ /// This can also be used to round a floating point
+ /// number to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="s">The value to stepify.</param>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns></returns>
public static real_t Stepify(real_t s, real_t step)
{
if (step != 0f)
{
- s = Floor(s / step + 0.5f) * step;
+ return Floor(s / step + 0.5f) * step;
}
return s;
}
+ /// <summary>
+ /// Returns the tangent of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The tangent of that angle.</returns>
public static real_t Tan(real_t s)
{
return (real_t)Math.Tan(s);
}
+ /// <summary>
+ /// Returns the hyperbolic tangent of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic tangent of that angle.</returns>
public static real_t Tanh(real_t s)
{
return (real_t)Math.Tanh(s);
}
+ /// <summary>
+ /// Wraps `value` between `min` and `max`. Usable for creating loop-alike
+ /// behavior or infinite surfaces. If `min` is `0`, this is equivalent
+ /// to <see cref="PosMod(int, int)"/>, so prefer using that instead.
+ /// </summary>
+ /// <param name="value">The value to wrap.</param>
+ /// <param name="min">The minimum allowed value and lower bound of the range.</param>
+ /// <param name="max">The maximum allowed value and upper bound of the range.</param>
+ /// <returns>The wrapped value.</returns>
public static int Wrap(int value, int min, int max)
{
int range = max - min;
return range == 0 ? min : min + ((value - min) % range + range) % range;
}
+ /// <summary>
+ /// Wraps `value` between `min` and `max`. Usable for creating loop-alike
+ /// behavior or infinite surfaces. If `min` is `0`, this is equivalent
+ /// to <see cref="PosMod(real_t, real_t)"/>, so prefer using that instead.
+ /// </summary>
+ /// <param name="value">The value to wrap.</param>
+ /// <param name="min">The minimum allowed value and lower bound of the range.</param>
+ /// <param name="max">The maximum allowed value and upper bound of the range.</param>
+ /// <returns>The wrapped value.</returns>
public static real_t Wrap(real_t value, real_t min, real_t max)
{
real_t range = max - min;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index 1b7fd4906f..c2f4701b5f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -12,40 +12,89 @@ namespace Godot
{
// Define constants with Decimal precision and cast down to double or float.
+ /// <summary>
+ /// The natural number `e`.
+ /// </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
+ /// <summary>
+ /// A very small number used for float comparison with error tolerance.
+ /// 1e-06 with single-precision floats, but 1e-14 if `REAL_T_IS_DOUBLE`.
+ /// </summary>
#if REAL_T_IS_DOUBLE
public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
#else
public const real_t Epsilon = 1e-06f;
#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>
public static int DecimalCount(real_t s)
{
return DecimalCount((decimal)s);
}
+ /// <summary>
+ /// Returns the amount of digits after the decimal place.
+ /// </summary>
+ /// <param name="s">The input <see cref="System.Decimal"/> value.</param>
+ /// <returns>The amount of digits.</returns>
public static int DecimalCount(decimal s)
{
return BitConverter.GetBytes(decimal.GetBits(s)[3])[2];
}
+ /// <summary>
+ /// Rounds `s` upward (towards positive infinity).
+ ///
+ /// This is the same as <see cref="Ceil(real_t)"/>, but returns an `int`.
+ /// </summary>
+ /// <param name="s">The number to ceil.</param>
+ /// <returns>The smallest whole number that is not less than `s`.</returns>
public static int CeilToInt(real_t s)
{
return (int)Math.Ceiling(s);
}
+ /// <summary>
+ /// Rounds `s` downward (towards negative infinity).
+ ///
+ /// This is the same as <see cref="Floor(real_t)"/>, but returns an `int`.
+ /// </summary>
+ /// <param name="s">The number to floor.</param>
+ /// <returns>The largest whole number that is not more than `s`.</returns>
public static int FloorToInt(real_t s)
{
return (int)Math.Floor(s);
}
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="s"></param>
+ /// <returns></returns>
public static int RoundToInt(real_t s)
{
return (int)Math.Round(s);
}
+ /// <summary>
+ /// Returns true if `a` and `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(real_t, real_t)"/>.
+ /// </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 bool for whether or not the two values are equal.</returns>
public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance)
{
// Check for exact equality first, required to handle "infinity" values.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 885845e3a4..2f8b5f297c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -8,18 +8,33 @@ using real_t = System.Single;
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;
+ /// <summary>
+ /// The normal of the plane, which must be normalized.
+ /// In the scalar equation of the plane `ax + by + cz = d`, this is
+ /// the vector `(a, b, c)`, where `d` is the <see cref="D"/> property.
+ /// </summary>
+ /// <value>Equivalent to `x`, `y`, and `z`.</value>
public Vector3 Normal
{
get { return _normal; }
set { _normal = 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
{
get
@@ -32,6 +47,10 @@ namespace Godot
}
}
+ /// <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
{
get
@@ -44,6 +63,10 @@ namespace Godot
}
}
+ /// <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
{
get
@@ -56,38 +79,71 @@ namespace Godot
}
}
+ /// <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 `ax + by + cz = d`,
+ /// this is `d`, while the `(a, b, c)` coordinates are represented
+ /// by the <see cref="Normal"/> property.
+ /// </summary>
+ /// <value>The plane's distance from the origin.</value>
public real_t D { get; set; }
+ /// <summary>
+ /// The center of the plane, the point where the normal line intersects the plane.
+ /// </summary>
+ /// <value>Equivalent to <see cref="Normal"/> multiplied by `D`.</value>
public Vector3 Center
{
get
{
return _normal * D;
}
+ set
+ {
+ _normal = value.Normalized();
+ D = value.Length();
+ }
}
+ /// <summary>
+ /// Returns the shortest distance from this plane to the position `point`.
+ /// </summary>
+ /// <param name="point">The position to use for the calculation.</param>
+ /// <returns>The shortest distance.</returns>
public real_t DistanceTo(Vector3 point)
{
return _normal.Dot(point) - D;
}
- public Vector3 GetAnyPoint()
- {
- return _normal * D;
- }
-
+ /// <summary>
+ /// Returns true if point is inside the plane.
+ /// Comparison uses a custom minimum epsilon threshold.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <param name="epsilon">The tolerance threshold.</param>
+ /// <returns>A bool for whether or not the plane has the point.</returns>
public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon)
{
real_t dist = _normal.Dot(point) - D;
return Mathf.Abs(dist) <= epsilon;
}
+ /// <summary>
+ /// Returns the intersection point of the three planes: `b`, `c`,
+ /// and this plane. If no intersection is found, `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 `null` if none is found.</returns>
public 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 +
@@ -96,54 +152,94 @@ namespace Godot
return result / denom;
}
+ /// <summary>
+ /// Returns the intersection point of a ray consisting of the
+ /// position `from` and the direction normal `dir` with this plane.
+ /// If no intersection is found, `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 `null` if none is found.</returns>
public Vector3? IntersectRay(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 `begin` to position `end` with this plane.
+ /// If no intersection is found, `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 `null` if none is found.</returns>
public Vector3? IntersectSegment(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 true if `point` is located above the plane.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the point is above the plane.</returns>
public 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 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 `point` into the plane.
+ /// </summary>
+ /// <param name="point">The point to project.</param>
+ /// <returns>The projected point.</returns>
public Vector3 Project(Vector3 point)
{
return point - _normal * DistanceTo(point);
@@ -154,22 +250,56 @@ namespace Godot
private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0);
private static readonly Plane _planeXY = new Plane(0, 0, 1, 0);
+ /// <summary>
+ /// A plane that extends in the Y and Z axes (normal vector points +X).
+ /// </summary>
+ /// <value>Equivalent to `new Plane(1, 0, 0, 0)`.</value>
public static Plane PlaneYZ { get { return _planeYZ; } }
+
+ /// <summary>
+ /// A plane that extends in the X and Z axes (normal vector points +Y).
+ /// </summary>
+ /// <value>Equivalent to `new Plane(0, 1, 0, 0)`.</value>
public static Plane PlaneXZ { get { return _planeXZ; } }
+
+ /// <summary>
+ /// A plane that extends in the X and Y axes (normal vector points +Z).
+ /// </summary>
+ /// <value>Equivalent to `new Plane(0, 0, 1, 0)`.</value>
public static Plane PlaneXY { get { return _planeXY; } }
- // Constructors
+ /// <summary>
+ /// Constructs a plane from four values. `a`, `b` and `c` become the
+ /// components of the resulting plane's <see cref="Normal"/> vector.
+ /// `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);
this.D = d;
}
+
+ /// <summary>
+ /// Constructs a plane from a normal vector and the plane's distance to the origin.
+ /// </summary>
+ /// <param name="normal">The normal of the plane, must be normalized.</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)
{
this._normal = normal;
this.D = d;
}
+ /// <summary>
+ /// Constructs a 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);
@@ -207,6 +337,12 @@ namespace Godot
return _normal == other._normal && D == other.D;
}
+ /// <summary>
+ /// Returns true if this plane and `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 bool IsEqualApprox(Plane other)
{
return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
index bbc617ea6e..b33490f9cb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
@@ -8,15 +8,51 @@ using real_t = System.Single;
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 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 Quat 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 Quat : IEquatable<Quat>
{
+ /// <summary>
+ /// X component of the quaternion (imaginary `i` axis part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
public real_t x;
+
+ /// <summary>
+ /// Y component of the quaternion (imaginary `j` axis part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
public real_t y;
+
+ /// <summary>
+ /// Z component of the quaternion (imaginary `k` 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>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`, `[3]` is equivalent to `.w`.</value>
public real_t this[int index]
{
get
@@ -57,16 +93,35 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the length (magnitude) of the quaternion.
+ /// </summary>
+ /// <value>Equivalent to `Mathf.Sqrt(LengthSquared)`.</value>
public real_t Length
{
get { 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 `Dot(this)`.</value>
public real_t LengthSquared
{
get { return Dot(this); }
}
+ /// <summary>
+ /// Performs a cubic spherical interpolation between quaternions `preA`,
+ /// this vector, `b`, and `postB`, by the given amount `t`.
+ /// </summary>
+ /// <param name="b">The destination quaternion.</param>
+ /// <param name="preA">A quaternion before this quaternion.</param>
+ /// <param name="postB">A quaternion after `b`.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated quaternion.</returns>
public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t)
{
real_t t2 = (1.0f - t) * t * 2f;
@@ -75,85 +130,131 @@ namespace Godot
return sp.Slerpni(sq, t2);
}
+ /// <summary>
+ /// Returns the dot product of two quaternions.
+ /// </summary>
+ /// <param name="b">The other quaternion.</param>
+ /// <returns>The dot product.</returns>
public real_t Dot(Quat b)
{
return x * b.x + y * b.y + z * b.z + w * b.w;
}
+ /// <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 Vector3 GetEuler()
{
#if DEBUG
if (!IsNormalized())
+ {
throw new InvalidOperationException("Quat is not normalized");
+ }
#endif
var basis = new Basis(this);
return basis.GetEuler();
}
+ /// <summary>
+ /// Returns the inverse of the quaternion.
+ /// </summary>
+ /// <returns>The inverse quaternion.</returns>
public Quat Inverse()
{
#if DEBUG
if (!IsNormalized())
+ {
throw new InvalidOperationException("Quat is not normalized");
+ }
#endif
return new Quat(-x, -y, -z, w);
}
+ /// <summary>
+ /// Returns whether the quaternion is normalized or not.
+ /// </summary>
+ /// <returns>A bool for whether the quaternion is normalized or not.</returns>
+ public bool IsNormalized()
+ {
+ return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
+ }
+
+ /// <summary>
+ /// Returns a copy of the quaternion, normalized to unit length.
+ /// </summary>
+ /// <returns>The normalized quaternion.</returns>
public Quat Normalized()
{
return this / Length;
}
- public Quat Slerp(Quat b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this quaternion and `to` by amount `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 Quat Slerp(Quat to, real_t weight)
{
#if DEBUG
if (!IsNormalized())
+ {
throw new InvalidOperationException("Quat is not normalized");
- if (!b.IsNormalized())
- throw new ArgumentException("Argument is not normalized", nameof(b));
+ }
+ if (!to.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(to));
+ }
#endif
- // Calculate cosine
- real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w;
+ // Calculate cosine.
+ real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
var to1 = new Quat();
- // Adjust signs if necessary
+ // Adjust signs if necessary.
if (cosom < 0.0)
{
cosom = -cosom;
- to1.x = -b.x;
- to1.y = -b.y;
- to1.z = -b.z;
- to1.w = -b.w;
+ to1.x = -to.x;
+ to1.y = -to.y;
+ to1.z = -to.z;
+ to1.w = -to.w;
}
else
{
- to1.x = b.x;
- to1.y = b.y;
- to1.z = b.z;
- to1.w = b.w;
+ to1.x = to.x;
+ to1.y = to.y;
+ to1.z = to.z;
+ to1.w = to.w;
}
real_t sinom, scale0, scale1;
- // Calculate coefficients
+ // Calculate coefficients.
if (1.0 - cosom > Mathf.Epsilon)
{
- // Standard case (Slerp)
+ // Standard case (Slerp).
real_t omega = Mathf.Acos(cosom);
sinom = Mathf.Sin(omega);
- scale0 = Mathf.Sin((1.0f - t) * omega) / sinom;
- scale1 = Mathf.Sin(t * omega) / sinom;
+ 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 - t;
- scale1 = t;
+ // Quaternions are very close so we can do a linear interpolation.
+ scale0 = 1.0f - weight;
+ scale1 = weight;
}
- // Calculate final values
+ // Calculate final values.
return new Quat
(
scale0 * x + scale1 * to1.x,
@@ -163,9 +264,17 @@ namespace Godot
);
}
- public Quat Slerpni(Quat b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this quaternion and `to` by amount `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 Quat Slerpni(Quat to, real_t weight)
{
- real_t dot = Dot(b);
+ real_t dot = Dot(to);
if (Mathf.Abs(dot) > 0.9999f)
{
@@ -174,33 +283,54 @@ namespace Godot
real_t theta = Mathf.Acos(dot);
real_t sinT = 1.0f / Mathf.Sin(theta);
- real_t newFactor = Mathf.Sin(t * theta) * sinT;
- real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT;
+ real_t newFactor = Mathf.Sin(weight * theta) * sinT;
+ real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
return new Quat
(
- invFactor * x + newFactor * b.x,
- invFactor * y + newFactor * b.y,
- invFactor * z + newFactor * b.z,
- invFactor * w + newFactor * b.w
+ invFactor * x + newFactor * to.x,
+ invFactor * y + newFactor * to.y,
+ invFactor * z + newFactor * to.z,
+ invFactor * w + newFactor * to.w
);
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by this quaternion.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector3 Xform(Vector3 v)
{
#if DEBUG
if (!IsNormalized())
+ {
throw new InvalidOperationException("Quat is not normalized");
+ }
#endif
var u = new Vector3(x, y, z);
Vector3 uv = u.Cross(v);
return v + ((uv * w) + u.Cross(uv)) * 2;
}
- // Static Readonly Properties
- public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f);
-
- // Constructors
+ // Constants
+ private static readonly Quat _identity = new Quat(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 `new Quat(0, 0, 0, 1)`.</value>
+ public static Quat Identity { get { return _identity; } }
+
+ /// <summary>
+ /// Constructs a quaternion defined by the given values.
+ /// </summary>
+ /// <param name="x">X component of the quaternion (imaginary `i` axis part).</param>
+ /// <param name="y">Y component of the quaternion (imaginary `j` axis part).</param>
+ /// <param name="z">Z component of the quaternion (imaginary `k` axis part).</param>
+ /// <param name="w">W component of the quaternion (real part).</param>
public Quat(real_t x, real_t y, real_t z, real_t w)
{
this.x = x;
@@ -209,21 +339,31 @@ namespace Godot
this.w = w;
}
- public bool IsNormalized()
- {
- return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
- }
-
+ /// <summary>
+ /// Constructs a quaternion from the given quaternion.
+ /// </summary>
+ /// <param name="q">The existing quaternion.</param>
public Quat(Quat q)
{
this = q;
}
+ /// <summary>
+ /// Constructs a quaternion from the given <see cref="Basis"/>.
+ /// </summary>
+ /// <param name="basis">The basis to construct from.</param>
public Quat(Basis basis)
{
this = basis.Quat();
}
+ /// <summary>
+ /// Constructs a 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"></param>
public Quat(Vector3 eulerYXZ)
{
real_t half_a1 = eulerYXZ.y * 0.5f;
@@ -247,11 +387,19 @@ namespace Godot
w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
}
+ /// <summary>
+ /// Constructs a 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 Quat(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();
@@ -364,6 +512,12 @@ namespace Godot
return x == other.x && y == other.y && z == other.z && w == other.w;
}
+ /// <summary>
+ /// Returns true if this quaternion and `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 bool IsEqualApprox(Quat other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index 1098ffe4e5..f7703c77cc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -8,6 +8,10 @@ using real_t = System.Single;
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>
@@ -15,29 +19,52 @@ namespace Godot
private Vector2 _position;
private Vector2 _size;
+ /// <summary>
+ /// Beginning corner. Typically has values lower than End.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector2 Position
{
get { return _position; }
set { _position = value; }
}
+ /// <summary>
+ /// Size from Position to 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
{
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 `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
public Vector2 End
{
get { return _position + _size; }
set { _size = value - _position; }
}
+ /// <summary>
+ /// The area of this rect.
+ /// </summary>
+ /// <value>Equivalent to <see cref="GetArea()"/>.</value>
public real_t Area
{
get { return GetArea(); }
}
+ /// <summary>
+ /// Returns a 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 rect.</returns>
public Rect2 Abs()
{
Vector2 end = End;
@@ -45,12 +72,19 @@ namespace Godot
return new Rect2(topLeft, _size.Abs());
}
+ /// <summary>
+ /// Returns the intersection of this Rect2 and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The clipped rect.</returns>
public Rect2 Clip(Rect2 b)
{
var newRect = b;
if (!Intersects(newRect))
+ {
return new Rect2();
+ }
newRect._position.x = Mathf.Max(b._position.x, _position.x);
newRect._position.y = Mathf.Max(b._position.y, _position.y);
@@ -64,6 +98,11 @@ namespace Godot
return newRect;
}
+ /// <summary>
+ /// Returns true if this Rect2 completely encloses another one.
+ /// </summary>
+ /// <param name="b">The other rect that may be enclosed.</param>
+ /// <returns>A bool for whether or not this rect encloses `b`.</returns>
public bool Encloses(Rect2 b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
@@ -71,6 +110,11 @@ namespace Godot
b._position.y + b._size.y < _position.y + _size.y;
}
+ /// <summary>
+ /// Returns this Rect2 expanded to include a given point.
+ /// </summary>
+ /// <param name="to">The point to include.</param>
+ /// <returns>The expanded rect.</returns>
public Rect2 Expand(Vector2 to)
{
var expanded = this;
@@ -79,14 +123,22 @@ namespace Godot
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;
@@ -94,11 +146,20 @@ namespace Godot
return expanded;
}
+ /// <summary>
+ /// Returns the area of the Rect2.
+ /// </summary>
+ /// <returns>The area.</returns>
public real_t GetArea()
{
return _size.x * _size.y;
}
+ /// <summary>
+ /// Returns a copy of the Rect2 grown a given amount of units towards all the sides.
+ /// </summary>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2 Grow(real_t by)
{
var g = this;
@@ -111,6 +172,14 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2 grown a given amount of units towards each direction individually.
+ /// </summary>
+ /// <param name="left">The amount to grow by on the left.</param>
+ /// <param name="top">The amount to grow by on the top.</param>
+ /// <param name="right">The amount to grow by on the right.</param>
+ /// <param name="bottom">The amount to grow by on the bottom.</param>
+ /// <returns>The grown rect.</returns>
public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
{
var g = this;
@@ -123,6 +192,12 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2 grown a given amount of units towards the <see cref="Margin"/> direction.
+ /// </summary>
+ /// <param name="margin">The direction to grow in.</param>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2 GrowMargin(Margin margin, real_t by)
{
var g = this;
@@ -135,11 +210,20 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns true if the Rect2 is flat or empty, or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the rect has area.</returns>
public bool HasNoArea()
{
return _size.x <= 0 || _size.y <= 0;
}
+ /// <summary>
+ /// Returns true if the Rect2 contains a point, or false otherwise.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the rect contains `point`.</returns>
public bool HasPoint(Vector2 point)
{
if (point.x < _position.x)
@@ -155,20 +239,65 @@ namespace Godot
return true;
}
- public bool Intersects(Rect2 b)
+ /// <summary>
+ /// Returns true if the Rect2 overlaps with `b`
+ /// (i.e. they have at least one point in common).
+ ///
+ /// If `includeBorders` is true, they will also be considered overlapping
+ /// if their borders touch, even without intersection.
+ /// </summary>
+ /// <param name="b">The other rect to check for intersections with.</param>
+ /// <param name="includeBorders">Whether or not to consider borders.</param>
+ /// <returns>A bool for whether or not they are intersecting.</returns>
+ public bool Intersects(Rect2 b, bool includeBorders = false)
{
- 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;
+ 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 Rect2 that contains this Rect2 and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The merged rect.</returns>
public Rect2 Merge(Rect2 b)
{
Rect2 newRect;
@@ -179,27 +308,53 @@ namespace Godot
newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x);
newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y);
- newRect._size = newRect._size - newRect._position; // Make relative again
+ newRect._size -= newRect._position; // Make relative again
return newRect;
}
- // Constructors
+ /// <summary>
+ /// Constructs a 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 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 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 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);
@@ -231,6 +386,12 @@ namespace Godot
return _position.Equals(other._position) && _size.Equals(other._size);
}
+ /// <summary>
+ /// Returns true if this rect and `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 bool IsEqualApprox(Rect2 other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
index c0b236c524..8f71c00d76 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
@@ -3,6 +3,10 @@ using System.Runtime.InteropServices;
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>
@@ -10,29 +14,52 @@ namespace Godot
private Vector2i _position;
private Vector2i _size;
+ /// <summary>
+ /// Beginning corner. Typically has values lower than End.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector2i Position
{
get { return _position; }
set { _position = value; }
}
+ /// <summary>
+ /// Size from Position to 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
{
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 `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
public Vector2i End
{
get { return _position + _size; }
set { _size = value - _position; }
}
+ /// <summary>
+ /// The area of this rect.
+ /// </summary>
+ /// <value>Equivalent to <see cref="GetArea()"/>.</value>
public int Area
{
get { return GetArea(); }
}
+ /// <summary>
+ /// Returns a 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 rect.</returns>
public Rect2i Abs()
{
Vector2i end = End;
@@ -40,12 +67,19 @@ namespace Godot
return new Rect2i(topLeft, _size.Abs());
}
+ /// <summary>
+ /// Returns the intersection of this Rect2i and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The clipped rect.</returns>
public Rect2i Clip(Rect2i b)
{
var newRect = b;
if (!Intersects(newRect))
+ {
return new Rect2i();
+ }
newRect._position.x = Mathf.Max(b._position.x, _position.x);
newRect._position.y = Mathf.Max(b._position.y, _position.y);
@@ -59,6 +93,11 @@ namespace Godot
return newRect;
}
+ /// <summary>
+ /// Returns true if this Rect2i completely encloses another one.
+ /// </summary>
+ /// <param name="b">The other rect that may be enclosed.</param>
+ /// <returns>A bool for whether or not this rect encloses `b`.</returns>
public bool Encloses(Rect2i b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
@@ -66,6 +105,11 @@ namespace Godot
b._position.y + b._size.y < _position.y + _size.y;
}
+ /// <summary>
+ /// Returns this Rect2i expanded to include a given point.
+ /// </summary>
+ /// <param name="to">The point to include.</param>
+ /// <returns>The expanded rect.</returns>
public Rect2i Expand(Vector2i to)
{
var expanded = this;
@@ -74,14 +118,22 @@ namespace Godot
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;
@@ -89,11 +141,20 @@ namespace Godot
return expanded;
}
+ /// <summary>
+ /// Returns the area of the Rect2.
+ /// </summary>
+ /// <returns>The area.</returns>
public int GetArea()
{
return _size.x * _size.y;
}
+ /// <summary>
+ /// Returns a copy of the Rect2i grown a given amount of units towards all the sides.
+ /// </summary>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2i Grow(int by)
{
var g = this;
@@ -106,6 +167,14 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2i grown a given amount of units towards each direction individually.
+ /// </summary>
+ /// <param name="left">The amount to grow by on the left.</param>
+ /// <param name="top">The amount to grow by on the top.</param>
+ /// <param name="right">The amount to grow by on the right.</param>
+ /// <param name="bottom">The amount to grow by on the bottom.</param>
+ /// <returns>The grown rect.</returns>
public Rect2i GrowIndividual(int left, int top, int right, int bottom)
{
var g = this;
@@ -118,6 +187,12 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2i grown a given amount of units towards the <see cref="Margin"/> direction.
+ /// </summary>
+ /// <param name="margin">The direction to grow in.</param>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2i GrowMargin(Margin margin, int by)
{
var g = this;
@@ -130,11 +205,20 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns true if the Rect2 is flat or empty, or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the rect has area.</returns>
public bool HasNoArea()
{
return _size.x <= 0 || _size.y <= 0;
}
+ /// <summary>
+ /// Returns true if the Rect2 contains a point, or false otherwise.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the rect contains `point`.</returns>
public bool HasPoint(Vector2i point)
{
if (point.x < _position.x)
@@ -150,20 +234,49 @@ namespace Godot
return true;
}
- public bool Intersects(Rect2i b)
+ /// <summary>
+ /// Returns true if the Rect2i overlaps with `b`
+ /// (i.e. they have at least one point in common).
+ ///
+ /// If `includeBorders` is true, they will also be considered overlapping
+ /// if their borders touch, even without intersection.
+ /// </summary>
+ /// <param name="b">The other rect to check for intersections with.</param>
+ /// <param name="includeBorders">Whether or not to consider borders.</param>
+ /// <returns>A bool for whether or not they are intersecting.</returns>
+ public bool Intersects(Rect2i b, bool includeBorders = false)
{
- 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;
+ 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 Rect2i that contains this Rect2 and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The merged rect.</returns>
public Rect2i Merge(Rect2i b)
{
Rect2i newRect;
@@ -174,27 +287,53 @@ namespace Godot
newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x);
newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y);
- newRect._size = newRect._size - newRect._position; // Make relative again
+ newRect._size -= newRect._position; // Make relative again
return newRect;
}
- // Constructors
+ /// <summary>
+ /// Constructs a 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 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 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 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);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
index 6a58b90561..ac47f6029f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
@@ -8,11 +8,28 @@ using real_t = System.Single;
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="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 Transform : IEquatable<Transform>
{
+ /// <summary>
+ /// The <see cref="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 `[3]`.
+ /// </summary>
public Vector3 origin;
/// <summary>
@@ -85,13 +102,24 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation, scaling, and translation.
+ /// </summary>
+ /// <returns>The inverse transformation matrix.</returns>
public Transform AffineInverse()
{
Basis basisInv = basis.Inverse();
return new Transform(basisInv, basisInv.Xform(-origin));
}
- public Transform InterpolateWith(Transform transform, real_t c)
+ /// <summary>
+ /// Interpolates this transform to the other `transform` by `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 Transform InterpolateWith(Transform transform, real_t weight)
{
/* not sure if very "efficient" but good enough? */
@@ -104,18 +132,37 @@ namespace Godot
Vector3 destinationLocation = transform.origin;
var interpolated = new Transform();
- interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, c).Normalized(), sourceScale.Lerp(destinationScale, c));
- interpolated.origin = sourceLocation.Lerp(destinationLocation, c);
+ interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, weight).Normalized(), sourceScale.Lerp(destinationScale, weight));
+ interpolated.origin = sourceLocation.Lerp(destinationLocation, weight);
return interpolated;
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation and translation
+ /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
+ /// </summary>
+ /// <returns>The inverse matrix.</returns>
public Transform Inverse()
{
Basis basisTr = basis.Transposed();
return new Transform(basisTr, basisTr.Xform(-origin));
}
+ /// <summary>
+ /// Returns a copy of the transform rotated such that its
+ /// -Z axis (forward) points towards the target position.
+ ///
+ /// The transform will first be rotated around the given up vector,
+ /// and then fully aligned to the target by a further rotation around
+ /// an axis perpendicular to both the target and up vectors.
+ ///
+ /// Operations take place in global space.
+ /// </summary>
+ /// <param name="target">The object to look at.</param>
+ /// <param name="up">The relative up direction</param>
+ /// <returns>The resulting transform.</returns>
public Transform LookingAt(Vector3 target, Vector3 up)
{
var t = this;
@@ -123,22 +170,39 @@ namespace Godot
return t;
}
+ /// <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 Transform Orthonormalized()
{
return new Transform(basis.Orthonormalized(), origin);
}
+ /// <summary>
+ /// Rotates the transform around the given `axis` by `phi` (in radians),
+ /// using matrix multiplication. The axis must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <returns>The rotated transformation matrix.</returns>
public Transform Rotated(Vector3 axis, real_t phi)
{
return new Transform(new Basis(axis, phi), new Vector3()) * this;
}
+ /// <summary>
+ /// Scales the transform by the given 3D scaling factor, using matrix multiplication.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled transformation matrix.</returns>
public Transform Scaled(Vector3 scale)
{
return new Transform(basis.Scaled(scale), origin * scale);
}
- public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
+ private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
// Z vector
@@ -161,16 +225,30 @@ namespace Godot
origin = eye;
}
- public Transform Translated(Vector3 ofs)
+ /// <summary>
+ /// Translates the transform by the given `offset`,
+ /// relative to the transform's basis vectors.
+ ///
+ /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
+ /// this does not use matrix multiplication.
+ /// </summary>
+ /// <param name="offset">The offset to translate by.</param>
+ /// <returns>The translated matrix.</returns>
+ public Transform Translated(Vector3 offset)
{
return new Transform(basis, new Vector3
(
- origin[0] += basis.Row0.Dot(ofs),
- origin[1] += basis.Row1.Dot(ofs),
- origin[2] += basis.Row2.Dot(ofs)
+ origin[0] += basis.Row0.Dot(offset),
+ origin[1] += basis.Row1.Dot(offset),
+ origin[2] += basis.Row2.Dot(offset)
));
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by this transformation matrix.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector3 Xform(Vector3 v)
{
return new Vector3
@@ -181,6 +259,14 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the transposed transformation matrix.
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// transformation matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector3 XformInv(Vector3 v)
{
Vector3 vInv = v - origin;
@@ -199,24 +285,58 @@ namespace Godot
private static readonly Transform _flipY = new Transform(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero);
private static readonly Transform _flipZ = new Transform(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 `Transform()` in GDScript.
+ /// Do not use `new Transform()` with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value>
public static Transform Identity { get { return _identity; } }
+ /// <summary>
+ /// The transform that will flip something along the X axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value>
public static Transform FlipX { get { return _flipX; } }
+ /// <summary>
+ /// The transform that will flip something along the Y axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)`.</value>
public static Transform FlipY { get { return _flipY; } }
+ /// <summary>
+ /// The transform that will flip something along the Z axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)`.</value>
public static Transform FlipZ { get { return _flipZ; } }
- // Constructors
+ /// <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 Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin)
{
basis = new Basis(column0, column1, column2);
this.origin = origin;
}
+ /// <summary>
+ /// Constructs a transformation matrix from the given quaternion and origin vector.
+ /// </summary>
+ /// <param name="quat">The <see cref="Godot.Quat"/> to create the basis from.</param>
+ /// <param name="origin">The origin vector, or column index 3.</param>
public Transform(Quat quat, Vector3 origin)
{
basis = new Basis(quat);
this.origin = origin;
}
+ /// <summary>
+ /// Constructs a transformation matrix from the given basis and 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 Transform(Basis basis, Vector3 origin)
{
this.basis = basis;
@@ -255,6 +375,12 @@ namespace Godot
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
+ /// <summary>
+ /// Returns true if this transform and `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 bool IsEqualApprox(Transform other)
{
return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 3ae96d4922..06bbe98497 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -8,25 +8,44 @@ using real_t = System.Single;
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 `[0]`.
+ /// </summary>
+ /// <value></value>
public Vector2 x;
+
+ /// <summary>
+ /// The basis matrix's Y vector (column 1). Equivalent to array index `[1]`.
+ /// </summary>
public Vector2 y;
+
+ /// <summary>
+ /// The origin vector (column 2, the third column). Equivalent to array index `[2]`.
+ /// The origin vector represents translation.
+ /// </summary>
public Vector2 origin;
+ /// <summary>
+ /// The rotation of this transformation matrix.
+ /// </summary>
+ /// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value>
public real_t Rotation
{
get
{
- real_t det = BasisDeterminant();
- Transform2D t = Orthonormalized();
- if (det < 0)
- {
- t.ScaleBasis(new Vector2(1, -1));
- }
- return Mathf.Atan2(t.x.y, t.x.x);
+ return Mathf.Atan2(x.y, x.x);
}
set
{
@@ -38,6 +57,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The scale of this transformation matrix.
+ /// </summary>
+ /// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value>
public Vector2 Scale
{
get
@@ -47,8 +70,7 @@ namespace Godot
}
set
{
- x = x.Normalized();
- y = y.Normalized();
+ value /= Scale; // Value becomes what's called "delta_scale" in core.
x *= value.x;
y *= value.y;
}
@@ -112,6 +134,11 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation, scaling, and translation.
+ /// </summary>
+ /// <returns>The inverse transformation matrix.</returns>
public Transform2D AffineInverse()
{
real_t det = BasisDeterminant();
@@ -135,28 +162,58 @@ namespace Godot
return inv;
}
+ /// <summary>
+ /// Returns the determinant of the basis matrix. If the basis is
+ /// uniformly scaled, its determinant is the square of the scale.
+ ///
+ /// A negative determinant means the Y 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>
private real_t BasisDeterminant()
{
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 origin vector).
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector2 BasisXform(Vector2 v)
{
return new Vector2(Tdotx(v), Tdoty(v));
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the inverse basis matrix.
+ /// This method does not account for translation (the origin vector).
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// basis matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector2 BasisXformInv(Vector2 v)
{
return new Vector2(x.Dot(v), y.Dot(v));
}
- public Transform2D InterpolateWith(Transform2D m, real_t c)
+ /// <summary>
+ /// Interpolates this transform to the other `transform` by `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 Transform2D InterpolateWith(Transform2D transform, real_t weight)
{
real_t r1 = Rotation;
- real_t r2 = m.Rotation;
+ real_t r2 = transform.Rotation;
Vector2 s1 = Scale;
- Vector2 s2 = m.Scale;
+ Vector2 s2 = transform.Scale;
// Slerp rotation
var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
@@ -172,28 +229,34 @@ namespace Godot
if (dot > 0.9995f)
{
// Linearly interpolate to avoid numerical precision issues
- v = v1.Lerp(v2, c).Normalized();
+ v = v1.Lerp(v2, weight).Normalized();
}
else
{
- real_t angle = c * Mathf.Acos(dot);
+ real_t angle = weight * Mathf.Acos(dot);
Vector2 v3 = (v2 - v1 * dot).Normalized();
v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle);
}
// Extract parameters
Vector2 p1 = origin;
- Vector2 p2 = m.origin;
+ Vector2 p2 = transform.origin;
// Construct matrix
- var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.Lerp(p2, c));
- Vector2 scale = s1.Lerp(s2, c);
+ var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.Lerp(p2, weight));
+ Vector2 scale = s1.Lerp(s2, weight);
res.x *= scale;
res.y *= scale;
return res;
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation and translation
+ /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
+ /// </summary>
+ /// <returns>The inverse matrix.</returns>
public Transform2D Inverse()
{
var inv = this;
@@ -208,6 +271,11 @@ namespace Godot
return inv;
}
+ /// <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 Transform2D Orthonormalized()
{
var on = this;
@@ -225,11 +293,21 @@ namespace Godot
return on;
}
+ /// <summary>
+ /// Rotates the transform by `phi` (in radians), using matrix multiplication.
+ /// </summary>
+ /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <returns>The rotated transformation matrix.</returns>
public Transform2D Rotated(real_t phi)
{
return this * new Transform2D(phi, new Vector2());
}
+ /// <summary>
+ /// Scales the transform by the given scaling factor, using matrix multiplication.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled transformation matrix.</returns>
public Transform2D Scaled(Vector2 scale)
{
var copy = this;
@@ -257,6 +335,15 @@ namespace Godot
return this[0, 1] * with[0] + this[1, 1] * with[1];
}
+ /// <summary>
+ /// Translates the transform by the given `offset`,
+ /// relative to the transform's basis vectors.
+ ///
+ /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
+ /// this does not use matrix multiplication.
+ /// </summary>
+ /// <param name="offset">The offset to translate by.</param>
+ /// <returns>The translated matrix.</returns>
public Transform2D Translated(Vector2 offset)
{
var copy = this;
@@ -264,11 +351,21 @@ namespace Godot
return copy;
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by this transformation matrix.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector2 Xform(Vector2 v)
{
return new Vector2(Tdotx(v), Tdoty(v)) + origin;
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector2 XformInv(Vector2 v)
{
Vector2 vInv = v - origin;
@@ -280,11 +377,30 @@ namespace Godot
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);
- public static Transform2D Identity => _identity;
- public static Transform2D FlipX => _flipX;
- public static Transform2D FlipY => _flipY;
+ /// <summary>
+ /// The identity transform, with no translation, rotation, or scaling applied.
+ /// This is used as a replacement for `Transform2D()` in GDScript.
+ /// Do not use `new Transform2D()` with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)`.</value>
+ public static Transform2D Identity { get { return _identity; } }
+ /// <summary>
+ /// The transform that will flip something along the X axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)`.</value>
+ public static Transform2D FlipX { get { return _flipX; } }
+ /// <summary>
+ /// The transform that will flip something along the Y axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)`.</value>
+ public static Transform2D FlipY { get { return _flipY; } }
- // Constructors
+ /// <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;
@@ -292,7 +408,16 @@ namespace Godot
origin = originPos;
}
- // Arguments are named such that xy is equal to calling x.y
+ /// <summary>
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling x.y
+ /// </summary>
+ /// <param name="xx">The X component of the X column vector, accessed via `t.x.x` or `[0][0]`</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via `t.x.y` or `[0][1]`</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via `t.y.x` or `[1][0]`</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via `t.y.y` or `[1][1]`</param>
+ /// <param name="ox">The X component of the origin vector, accessed via `t.origin.x` or `[2][0]`</param>
+ /// <param name="oy">The Y component of the origin vector, accessed via `t.origin.y` or `[2][1]`</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);
@@ -300,6 +425,11 @@ namespace Godot
origin = new Vector2(ox, oy);
}
+ /// <summary>
+ /// Constructs a transformation matrix from a rotation value and origin vector.
+ /// </summary>
+ /// <param name="rot">The rotation of the new transform, in radians.</param>
+ /// <param name="pos">The origin vector, or column index 2.</param>
public Transform2D(real_t rot, Vector2 pos)
{
x.x = y.y = Mathf.Cos(rot);
@@ -345,6 +475,12 @@ namespace Godot
return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
}
+ /// <summary>
+ /// Returns true if this transform and `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 bool IsEqualApprox(Transform2D other)
{
return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 7e4804f9fd..26bd828a5b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -21,15 +21,29 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector2 : IEquatable<Vector2>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
Y
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public real_t x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public real_t y;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value>
public real_t this[int index]
{
get
@@ -76,41 +90,80 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
public Vector2 Abs()
{
return new Vector2(Mathf.Abs(x), Mathf.Abs(y));
}
+ /// <summary>
+ /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians.
+ ///
+ /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when
+ /// called with the vector's `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`.
+ /// </summary>
+ /// <returns>The angle of this vector, in radians.</returns>
public real_t Angle()
{
return Mathf.Atan2(y, x);
}
+ /// <summary>
+ /// Returns the angle to the given vector, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleTo(Vector2 to)
{
return Mathf.Atan2(Cross(to), Dot(to));
}
+ /// <summary>
+ /// Returns the angle between the line connecting the two points and the X axis, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleToPoint(Vector2 to)
{
return Mathf.Atan2(y - to.y, x - to.x);
}
+ /// <summary>
+ /// Returns the aspect ratio of this vector, the ratio of `x` to `y`.
+ /// </summary>
+ /// <returns>The `x` component divided by the `y` component.</returns>
public real_t Aspect()
{
return x / y;
}
- public Vector2 Bounce(Vector2 n)
+ /// <summary>
+ /// Returns the vector "bounced off" from a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to bounce off. Must be normalized.</param>
+ /// <returns>The bounced vector.</returns>
+ public Vector2 Bounce(Vector2 normal)
{
- return -Reflect(n);
+ return -Reflect(normal);
}
+ /// <summary>
+ /// Returns a new vector with all components rounded up (towards positive infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
public Vector2 Ceil()
{
return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y));
}
+ /// <summary>
+ /// Returns the vector with a maximum length by limiting its length to `length`.
+ /// </summary>
+ /// <param name="length">The length to limit to.</param>
+ /// <returns>The vector with its length limited.</returns>
public Vector2 Clamped(real_t length)
{
var v = this;
@@ -125,17 +178,30 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns the cross product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>The cross product value.</returns>
public real_t Cross(Vector2 b)
{
return x * b.y - y * b.x;
}
+ /// <summary>
+ /// Performs a cubic interpolation between vectors `preA`, this vector, `b`, and `postB`, by the given amount `t`.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after `b`.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t)
{
- var p0 = preA;
- var p1 = this;
- var p2 = b;
- var p3 = postB;
+ Vector2 p0 = preA;
+ Vector2 p1 = this;
+ Vector2 p2 = b;
+ Vector2 p3 = postB;
real_t t2 = t * t;
real_t t3 = t2 * t;
@@ -146,46 +212,102 @@ namespace Godot
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
}
+ /// <summary>
+ /// Returns the normalized vector pointing from this vector to `b`.
+ /// </summary>
+ /// <param name="b">The other vector to point towards.</param>
+ /// <returns>The direction from this vector to `b`.</returns>
public Vector2 DirectionTo(Vector2 b)
{
return new Vector2(b.x - x, b.y - y).Normalized();
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `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 real_t DistanceSquaredTo(Vector2 to)
{
return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y);
}
+ /// <summary>
+ /// Returns the distance between this vector and `to`.
+ /// </summary>
+ /// <param name="to">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector2 to)
{
return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y));
}
+ /// <summary>
+ /// Returns the dot product of this vector and `with`.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public real_t Dot(Vector2 with)
{
return x * with.x + y * with.y;
}
+ /// <summary>
+ /// Returns a new vector with all components rounded down (towards negative infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
public Vector2 Floor()
{
return new Vector2(Mathf.Floor(x), Mathf.Floor(y));
}
+ /// <summary>
+ /// Returns the inverse of this vector. This is the same as `new Vector2(1 / v.x, 1 / v.y)`.
+ /// </summary>
+ /// <returns>The inverse of this vector.</returns>
+ public Vector2 Inverse()
+ {
+ return new Vector2(1 / x, 1 / y);
+ }
+
+ /// <summary>
+ /// Returns true if the vector is normalized, and false otherwise.
+ /// </summary>
+ /// <returns>A bool indicating whether or not the vector is normalized.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
return Mathf.Sqrt(x * x + y * y);
}
+ /// <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 real_t LengthSquared()
{
return x * x + y * y;
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector2 Lerp(Vector2 to, real_t weight)
{
return new Vector2
@@ -195,6 +317,13 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by the vector amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector2 Lerp(Vector2 to, Vector2 weight)
{
return new Vector2
@@ -204,6 +333,32 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
+ public Axis MaxAxis()
+ {
+ return x < y ? Axis.Y : Axis.X;
+ }
+
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.Y"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
+ public Axis MinAxis()
+ {
+ return x < y ? Axis.X : Axis.Y;
+ }
+
+ /// <summary>
+ /// Moves this vector toward `to` by the fixed `delta` amount.
+ /// </summary>
+ /// <param name="to">The vector to move towards.</param>
+ /// <param name="delta">The amount to move towards by.</param>
+ /// <returns>The resulting vector.</returns>
public Vector2 MoveToward(Vector2 to, real_t delta)
{
var v = this;
@@ -212,6 +367,10 @@ namespace Godot
return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
}
+ /// <summary>
+ /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`.
+ /// </summary>
+ /// <returns>A normalized version of the vector.</returns>
public Vector2 Normalized()
{
var v = this;
@@ -219,6 +378,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns>
public Vector2 PosMod(real_t mod)
{
Vector2 v;
@@ -227,6 +391,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns>
public Vector2 PosMod(Vector2 modv)
{
Vector2 v;
@@ -235,27 +404,59 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns this vector projected onto another vector `b`.
+ /// </summary>
+ /// <param name="onNormal">The vector to project onto.</param>
+ /// <returns>The projected vector.</returns>
public Vector2 Project(Vector2 onNormal)
{
return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
}
- public Vector2 Reflect(Vector2 n)
+ /// <summary>
+ /// Returns this vector reflected from a plane defined by the given `normal`.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
+ /// <returns>The reflected vector.</returns>
+ public Vector2 Reflect(Vector2 normal)
{
- return 2 * Dot(n) * n - this;
+#if DEBUG
+ if (!normal.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(normal));
+ }
+#endif
+ return 2 * Dot(normal) * normal - this;
}
+ /// <summary>
+ /// Rotates this vector by `phi` radians.
+ /// </summary>
+ /// <param name="phi">The angle to rotate by, in radians.</param>
+ /// <returns>The rotated vector.</returns>
public Vector2 Rotated(real_t phi)
{
real_t rads = Angle() + phi;
return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length();
}
+ /// <summary>
+ /// Returns this vector with all components rounded to the nearest integer,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <returns>The rounded vector.</returns>
public Vector2 Round()
{
return new Vector2(Mathf.Round(x), Mathf.Round(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(real_t)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
public Vector2 Sign()
{
Vector2 v;
@@ -264,23 +465,57 @@ namespace Godot
return v;
}
- public Vector2 Slerp(Vector2 b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ ///
+ /// Note: Both vectors must be normalized.
+ /// </summary>
+ /// <param name="to">The destination vector 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 vector of the interpolation.</returns>
+ public Vector2 Slerp(Vector2 to, real_t weight)
{
- real_t theta = AngleTo(b);
- return Rotated(theta * t);
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Vector2.Slerp: From vector is not normalized.");
+ }
+ if (!to.IsNormalized())
+ {
+ throw new InvalidOperationException("Vector2.Slerp: `to` is not normalized.");
+ }
+#endif
+ return Rotated(AngleTo(to) * weight);
}
- public Vector2 Slide(Vector2 n)
+ /// <summary>
+ /// Returns this vector slid along a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to slide on.</param>
+ /// <returns>The slid vector.</returns>
+ public Vector2 Slide(Vector2 normal)
{
- return this - n * Dot(n);
+ return this - normal * Dot(normal);
}
- public Vector2 Snapped(Vector2 by)
+ /// <summary>
+ /// Returns this vector with each component snapped to the nearest multiple of `step`.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public Vector2 Snapped(Vector2 step)
{
- return new Vector2(Mathf.Stepify(x, by.x), Mathf.Stepify(y, by.y));
+ return new Vector2(Mathf.Stepify(x, step.x), Mathf.Stepify(y, step.y));
}
- public Vector2 Tangent()
+ /// <summary>
+ /// Returns a perpendicular vector rotated 90 degrees counter-clockwise
+ /// compared to the original, with the same length.
+ /// </summary>
+ /// <returns>The perpendicular vector.</returns>
+ public Vector2 Perpendicular()
{
return new Vector2(y, -x);
}
@@ -288,7 +523,6 @@ namespace Godot
// Constants
private static readonly Vector2 _zero = new Vector2(0, 0);
private static readonly Vector2 _one = new Vector2(1, 1);
- private static readonly Vector2 _negOne = new Vector2(-1, -1);
private static readonly Vector2 _inf = new Vector2(Mathf.Inf, Mathf.Inf);
private static readonly Vector2 _up = new Vector2(0, -1);
@@ -296,22 +530,58 @@ namespace Godot
private static readonly Vector2 _right = new Vector2(1, 0);
private static readonly Vector2 _left = new Vector2(-1, 0);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(0, 0)`</value>
public static Vector2 Zero { get { return _zero; } }
- public static Vector2 NegOne { get { return _negOne; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(1, 1)`</value>
public static Vector2 One { get { return _one; } }
+ /// <summary>
+ /// Infinity vector, a vector with all components set to `Mathf.Inf`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(Mathf.Inf, Mathf.Inf)`</value>
public static Vector2 Inf { get { return _inf; } }
+ /// <summary>
+ /// Up unit vector. Y is down in 2D, so this vector points -Y.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(0, -1)`</value>
public static Vector2 Up { get { return _up; } }
+ /// <summary>
+ /// Down unit vector. Y is down in 2D, so this vector points +Y.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(0, 1)`</value>
public static Vector2 Down { get { return _down; } }
+ /// <summary>
+ /// Right unit vector. Represents the direction of right.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(1, 0)`</value>
public static Vector2 Right { get { return _right; } }
+ /// <summary>
+ /// Left unit vector. Represents the direction of left.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(-1, 0)`</value>
public static Vector2 Left { get { return _left; } }
- // Constructors
+ /// <summary>
+ /// Constructs a new <see cref="Vector2"/> with the given components.
+ /// </summary>
+ /// <param name="x">The vector's X component.</param>
+ /// <param name="y">The vector's Y component.</param>
public Vector2(real_t x, real_t y)
{
this.x = x;
this.y = y;
}
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector2"/> from an existing <see cref="Vector2"/>.
+ /// </summary>
+ /// <param name="v">The existing <see cref="Vector2"/>.</param>
public Vector2(Vector2 v)
{
x = v.x;
@@ -453,6 +723,12 @@ namespace Godot
return x == other.x && y == other.y;
}
+ /// <summary>
+ /// Returns true if this vector and `other` are approximately equal, by running
+ /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other vector to compare.</param>
+ /// <returns>Whether or not the vectors are approximately equal.</returns>
public bool IsEqualApprox(Vector2 other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index 7dc22d7918..8dd9ab2f0d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -16,15 +16,29 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector2i : IEquatable<Vector2i>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
Y
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public int x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public int y;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value>
public int this[int index]
{
get
@@ -55,56 +69,102 @@ namespace Godot
}
}
+ /// <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 Vector2i Abs()
{
return new Vector2i(Mathf.Abs(x), Mathf.Abs(y));
}
+ /// <summary>
+ /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians.
+ ///
+ /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when
+ /// called with the vector's `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`.
+ /// </summary>
+ /// <returns>The angle of this vector, in radians.</returns>
public real_t Angle()
{
return Mathf.Atan2(y, x);
}
+ /// <summary>
+ /// Returns the angle to the given vector, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleTo(Vector2i to)
{
return Mathf.Atan2(Cross(to), Dot(to));
}
+ /// <summary>
+ /// Returns the angle between the line connecting the two points and the X axis, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleToPoint(Vector2i to)
{
return Mathf.Atan2(y - to.y, x - to.x);
}
+ /// <summary>
+ /// Returns the aspect ratio of this vector, the ratio of `x` to `y`.
+ /// </summary>
+ /// <returns>The `x` component divided by the `y` component.</returns>
public real_t Aspect()
{
return x / (real_t)y;
}
- public Vector2i Bounce(Vector2i n)
- {
- return -Reflect(n);
- }
-
+ /// <summary>
+ /// Returns the cross product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>The cross product vector.</returns>
public int Cross(Vector2i b)
{
return x * b.y - y * b.x;
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `b`.
+ /// 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="b">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
public int DistanceSquaredTo(Vector2i b)
{
return (b - this).LengthSquared();
}
+ /// <summary>
+ /// Returns the distance between this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector2i b)
{
return (b - this).Length();
}
+ /// <summary>
+ /// Returns the dot product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public int Dot(Vector2i b)
{
return x * b.x + y * b.y;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
int x2 = x * x;
@@ -113,6 +173,12 @@ namespace Godot
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 int LengthSquared()
{
int x2 = x * x;
@@ -121,16 +187,31 @@ namespace Godot
return x2 + y2;
}
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
public Axis MaxAxis()
{
return x < y ? Axis.Y : Axis.X;
}
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.Y"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
public Axis MinAxis()
{
- return x > y ? Axis.Y : Axis.X;
+ return x < y ? Axis.X : Axis.Y;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns>
public Vector2i PosMod(int mod)
{
Vector2i v = this;
@@ -139,6 +220,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns>
public Vector2i PosMod(Vector2i modv)
{
Vector2i v = this;
@@ -147,11 +233,12 @@ namespace Godot
return v;
}
- public Vector2i Reflect(Vector2i n)
- {
- return 2 * Dot(n) * n - this;
- }
-
+ /// <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 `1`, `-1`, or `0`.</returns>
public Vector2i Sign()
{
Vector2i v = this;
@@ -160,9 +247,14 @@ namespace Godot
return v;
}
- public Vector2i Tangent()
+ /// <summary>
+ /// Returns a vector rotated 90 degrees counter-clockwise
+ /// compared to the original, with the same length.
+ /// </summary>
+ /// <returns>The perpendicular vector.</returns>
+ public Vector2 Perpendicular()
{
- return new Vector2i(y, -x);
+ return new Vector2(y, -x);
}
// Constants
@@ -174,25 +266,64 @@ namespace Godot
private static readonly Vector2i _right = new Vector2i(1, 0);
private static readonly Vector2i _left = new Vector2i(-1, 0);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(0, 0)`</value>
public static Vector2i Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(1, 1)`</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 `new Vector2i(0, -1)`</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 `new Vector2i(0, 1)`</value>
public static Vector2i Down { get { return _down; } }
+ /// <summary>
+ /// Right unit vector. Represents the direction of right.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(1, 0)`</value>
public static Vector2i Right { get { return _right; } }
+ /// <summary>
+ /// Left unit vector. Represents the direction of left.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(-1, 0)`</value>
public static Vector2i Left { get { return _left; } }
- // Constructors
+ /// <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)
{
this.x = x;
this.y = y;
}
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector2i"/> from an existing <see cref="Vector2i"/>.
+ /// </summary>
+ /// <param name="vi">The existing <see cref="Vector2i"/>.</param>
public Vector2i(Vector2i vi)
{
this.x = vi.x;
this.y = vi.y;
}
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector2i"/> from an existing <see cref="Vector2"/>
+ /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
+ /// </summary>
+ /// <param name="v">The <see cref="Vector2"/> to convert.</param>
public Vector2i(Vector2 v)
{
this.x = Mathf.RoundToInt(v.x);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index b26e17ecba..d9b16a6afd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -21,6 +21,10 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector3 : IEquatable<Vector3>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
@@ -28,10 +32,23 @@ namespace Godot
Z
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public real_t x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public real_t y;
+ /// <summary>
+ /// The vector's Z component. Also accessible by using the index position `[2]`.
+ /// </summary>
public real_t z;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value>
public real_t this[int index]
{
get
@@ -84,26 +101,49 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
public Vector3 Abs()
{
return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z));
}
+ /// <summary>
+ /// Returns the minimum angle to the given vector, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleTo(Vector3 to)
{
return Mathf.Atan2(Cross(to).Length(), Dot(to));
}
- public Vector3 Bounce(Vector3 n)
+ /// <summary>
+ /// Returns this vector "bounced off" from a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to bounce off. Must be normalized.</param>
+ /// <returns>The bounced vector.</returns>
+ public Vector3 Bounce(Vector3 normal)
{
- return -Reflect(n);
+ return -Reflect(normal);
}
+ /// <summary>
+ /// Returns a new vector with all components rounded up (towards positive infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
public Vector3 Ceil()
{
return new Vector3(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z));
}
+ /// <summary>
+ /// Returns the cross product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>The cross product vector.</returns>
public Vector3 Cross(Vector3 b)
{
return new Vector3
@@ -114,12 +154,21 @@ namespace Godot
);
}
+ /// <summary>
+ /// Performs a cubic interpolation between vectors `preA`, this vector,
+ /// `b`, and `postB`, by the given amount `t`.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after `b`.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t)
{
- var p0 = preA;
- var p1 = this;
- var p2 = b;
- var p3 = postB;
+ Vector3 p0 = preA;
+ Vector3 p1 = this;
+ Vector3 p2 = b;
+ Vector3 p3 = postB;
real_t t2 = t * t;
real_t t3 = t2 * t;
@@ -131,41 +180,79 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the normalized vector pointing from this vector to `b`.
+ /// </summary>
+ /// <param name="b">The other vector to point towards.</param>
+ /// <returns>The direction from this vector to `b`.</returns>
public Vector3 DirectionTo(Vector3 b)
{
return new Vector3(b.x - x, b.y - y, b.z - z).Normalized();
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `b`.
+ /// 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="b">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
public real_t DistanceSquaredTo(Vector3 b)
{
return (b - this).LengthSquared();
}
+ /// <summary>
+ /// Returns the distance between this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector3 b)
{
return (b - this).Length();
}
+ /// <summary>
+ /// Returns the dot product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public real_t Dot(Vector3 b)
{
return x * b.x + y * b.y + z * b.z;
}
+ /// <summary>
+ /// Returns a new vector with all components rounded down (towards negative infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
public Vector3 Floor()
{
return new Vector3(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z));
}
+ /// <summary>
+ /// Returns the inverse of this vector. This is the same as `new Vector3(1 / v.x, 1 / v.y, 1 / v.z)`.
+ /// </summary>
+ /// <returns>The inverse of this vector.</returns>
public Vector3 Inverse()
{
- return new Vector3(1.0f / x, 1.0f / y, 1.0f / z);
+ return new Vector3(1 / x, 1 / y, 1 / z);
}
+ /// <summary>
+ /// Returns true if the vector is normalized, and false otherwise.
+ /// </summary>
+ /// <returns>A bool indicating whether or not the vector is normalized.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
real_t x2 = x * x;
@@ -175,6 +262,12 @@ namespace Godot
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 real_t LengthSquared()
{
real_t x2 = x * x;
@@ -184,6 +277,13 @@ namespace Godot
return x2 + y2 + z2;
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector3 Lerp(Vector3 to, real_t weight)
{
return new Vector3
@@ -194,6 +294,13 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by the vector amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector3 Lerp(Vector3 to, Vector3 weight)
{
return new Vector3
@@ -204,24 +311,44 @@ namespace Godot
);
}
- public Vector3 MoveToward(Vector3 to, real_t delta)
- {
- var v = this;
- var vd = to - v;
- var len = vd.Length();
- return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
- }
-
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
public Axis MaxAxis()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.Z"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
public Axis MinAxis()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
+ /// <summary>
+ /// Moves this vector toward `to` by the fixed `delta` amount.
+ /// </summary>
+ /// <param name="to">The vector to move towards.</param>
+ /// <param name="delta">The amount to move towards by.</param>
+ /// <returns>The resulting vector.</returns>
+ public Vector3 MoveToward(Vector3 to, real_t delta)
+ {
+ var v = this;
+ var vd = to - v;
+ var len = vd.Length();
+ return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
+ }
+
+ /// <summary>
+ /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`.
+ /// </summary>
+ /// <returns>A normalized version of the vector.</returns>
public Vector3 Normalized()
{
var v = this;
@@ -229,6 +356,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns the outer product with `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>A <see cref="Basis"/> representing the outer product matrix.</returns>
public Basis Outer(Vector3 b)
{
return new Basis(
@@ -238,6 +370,11 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns>
public Vector3 PosMod(real_t mod)
{
Vector3 v;
@@ -247,6 +384,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns>
public Vector3 PosMod(Vector3 modv)
{
Vector3 v;
@@ -256,30 +398,66 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns this vector projected onto another vector `b`.
+ /// </summary>
+ /// <param name="onNormal">The vector to project onto.</param>
+ /// <returns>The projected vector.</returns>
public Vector3 Project(Vector3 onNormal)
{
return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
}
- public Vector3 Reflect(Vector3 n)
+ /// <summary>
+ /// Returns this vector reflected from a plane defined by the given `normal`.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
+ /// <returns>The reflected vector.</returns>
+ public Vector3 Reflect(Vector3 normal)
{
#if DEBUG
- if (!n.IsNormalized())
- throw new ArgumentException("Argument is not normalized", nameof(n));
+ if (!normal.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(normal));
+ }
#endif
- return 2.0f * n * Dot(n) - this;
+ return 2.0f * Dot(normal) * normal - this;
}
- public Vector3 Round()
+ /// <summary>
+ /// Rotates this vector around a given `axis` vector by `phi` radians.
+ /// The `axis` vector must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The vector to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate by, in radians.</param>
+ /// <returns>The rotated vector.</returns>
+ public Vector3 Rotated(Vector3 axis, real_t phi)
{
- return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z));
+#if DEBUG
+ if (!axis.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(axis));
+ }
+#endif
+ return new Basis(axis, phi).Xform(this);
}
- public Vector3 Rotated(Vector3 axis, real_t phi)
+ /// <summary>
+ /// Returns this vector with all components rounded to the nearest integer,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <returns>The rounded vector.</returns>
+ public Vector3 Round()
{
- return new Basis(axis, phi).Xform(this);
+ return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(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(real_t)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
public Vector3 Sign()
{
Vector3 v;
@@ -289,44 +467,76 @@ namespace Godot
return v;
}
- public Vector3 Slerp(Vector3 b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ ///
+ /// Note: Both vectors must be normalized.
+ /// </summary>
+ /// <param name="to">The destination vector 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 vector of the interpolation.</returns>
+ public Vector3 Slerp(Vector3 to, real_t weight)
{
#if DEBUG
if (!IsNormalized())
- throw new InvalidOperationException("Vector3 is not normalized");
+ {
+ throw new InvalidOperationException("Vector3.Slerp: From vector is not normalized.");
+ }
+ if (!to.IsNormalized())
+ {
+ throw new InvalidOperationException("Vector3.Slerp: `to` is not normalized.");
+ }
#endif
- real_t theta = AngleTo(b);
- return Rotated(Cross(b), theta * t);
+ real_t theta = AngleTo(to);
+ return Rotated(Cross(to), theta * weight);
}
- public Vector3 Slide(Vector3 n)
+ /// <summary>
+ /// Returns this vector slid along a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to slide on.</param>
+ /// <returns>The slid vector.</returns>
+ public Vector3 Slide(Vector3 normal)
{
- return this - n * Dot(n);
+ return this - normal * Dot(normal);
}
- public Vector3 Snapped(Vector3 by)
+ /// <summary>
+ /// Returns this vector with each component snapped to the nearest multiple of `step`.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public Vector3 Snapped(Vector3 step)
{
return new Vector3
(
- Mathf.Stepify(x, by.x),
- Mathf.Stepify(y, by.y),
- Mathf.Stepify(z, by.z)
+ Mathf.Stepify(x, step.x),
+ Mathf.Stepify(y, step.y),
+ Mathf.Stepify(z, step.z)
);
}
+ /// <summary>
+ /// Returns a diagonal matrix with the vector as main diagonal.
+ ///
+ /// This is equivalent to a Basis with no rotation or shearing and
+ /// this vector's components set as the scale.
+ /// </summary>
+ /// <returns>A Basis with the vector as its main diagonal.</returns>
public Basis ToDiagonalMatrix()
{
return new Basis(
- x, 0f, 0f,
- 0f, y, 0f,
- 0f, 0f, z
+ x, 0, 0,
+ 0, y, 0,
+ 0, 0, z
);
}
// Constants
private static readonly Vector3 _zero = new Vector3(0, 0, 0);
private static readonly Vector3 _one = new Vector3(1, 1, 1);
- private static readonly Vector3 _negOne = new Vector3(-1, -1, -1);
private static readonly Vector3 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf);
private static readonly Vector3 _up = new Vector3(0, 1, 0);
@@ -336,25 +546,74 @@ namespace Godot
private static readonly Vector3 _forward = new Vector3(0, 0, -1);
private static readonly Vector3 _back = new Vector3(0, 0, 1);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 0, 0)`</value>
public static Vector3 Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(1, 1, 1)`</value>
public static Vector3 One { get { return _one; } }
- public static Vector3 NegOne { get { return _negOne; } }
+ /// <summary>
+ /// Infinity vector, a vector with all components set to `Mathf.Inf`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf)`</value>
public static Vector3 Inf { get { return _inf; } }
+ /// <summary>
+ /// Up unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 1, 0)`</value>
public static Vector3 Up { get { return _up; } }
+ /// <summary>
+ /// Down unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, -1, 0)`</value>
public static Vector3 Down { get { return _down; } }
+ /// <summary>
+ /// Right unit vector. Represents the local direction of right,
+ /// and the global direction of east.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(1, 0, 0)`</value>
public static Vector3 Right { get { return _right; } }
+ /// <summary>
+ /// Left unit vector. Represents the local direction of left,
+ /// and the global direction of west.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(-1, 0, 0)`</value>
public static Vector3 Left { get { return _left; } }
+ /// <summary>
+ /// Forward unit vector. Represents the local direction of forward,
+ /// and the global direction of north.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 0, -1)`</value>
public static Vector3 Forward { get { return _forward; } }
+ /// <summary>
+ /// Back unit vector. Represents the local direction of back,
+ /// and the global direction of south.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 0, 1)`</value>
public static Vector3 Back { get { return _back; } }
- // Constructors
+ /// <summary>
+ /// Constructs a new <see cref="Vector3"/> 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 Vector3(real_t x, real_t y, real_t z)
{
this.x = x;
this.y = y;
this.z = z;
}
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector3"/> from an existing <see cref="Vector3"/>.
+ /// </summary>
+ /// <param name="v">The existing <see cref="Vector3"/>.</param>
public Vector3(Vector3 v)
{
x = v.x;
@@ -515,6 +774,12 @@ namespace Godot
return x == other.x && y == other.y && z == other.z;
}
+ /// <summary>
+ /// Returns true if this vector and `other` are approximately equal, by running
+ /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other vector to compare.</param>
+ /// <returns>Whether or not the vectors are approximately equal.</returns>
public bool IsEqualApprox(Vector3 other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index c17f900131..bf25ba9cb3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -16,6 +16,10 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector3i : IEquatable<Vector3i>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
@@ -23,10 +27,23 @@ namespace Godot
Z
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public int x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public int y;
+ /// <summary>
+ /// The vector's Z component. Also accessible by using the index position `[2]`.
+ /// </summary>
public int z;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value>
public int this[int index]
{
get
@@ -62,39 +79,51 @@ namespace Godot
}
}
+ /// <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 Vector3i Abs()
{
- Vector3i v = this;
- if (v.x < 0)
- {
- v.x = -v.x;
- }
- if (v.y < 0)
- {
- v.y = -v.y;
- }
- if (v.z < 0)
- {
- v.z = -v.z;
- }
- return v;
+ return new Vector3i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z));
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `b`.
+ /// 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="b">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
public int DistanceSquaredTo(Vector3i b)
{
return (b - this).LengthSquared();
}
+ /// <summary>
+ /// Returns the distance between this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector3i b)
{
return (b - this).Length();
}
+ /// <summary>
+ /// Returns the dot product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public int Dot(Vector3i b)
{
return x * b.x + y * b.y + z * b.z;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
int x2 = x * x;
@@ -104,6 +133,12 @@ namespace Godot
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 int LengthSquared()
{
int x2 = x * x;
@@ -113,16 +148,31 @@ namespace Godot
return x2 + y2 + z2;
}
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
public Axis MaxAxis()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.Z"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
public Axis MinAxis()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns>
public Vector3i PosMod(int mod)
{
Vector3i v = this;
@@ -132,6 +182,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns>
public Vector3i PosMod(Vector3i modv)
{
Vector3i v = this;
@@ -141,6 +196,12 @@ namespace Godot
return v;
}
+ /// <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 `1`, `-1`, or `0`.</returns>
public Vector3i Sign()
{
Vector3i v = this;
@@ -161,29 +222,81 @@ namespace Godot
private static readonly Vector3i _forward = new Vector3i(0, 0, -1);
private static readonly Vector3i _back = new Vector3i(0, 0, 1);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, 0, 0)`</value>
public static Vector3i Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(1, 1, 1)`</value>
public static Vector3i One { get { return _one; } }
+ /// <summary>
+ /// Up unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, 1, 0)`</value>
public static Vector3i Up { get { return _up; } }
+ /// <summary>
+ /// Down unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, -1, 0)`</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 `new Vector3i(1, 0, 0)`</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 `new Vector3i(-1, 0, 0)`</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 `new Vector3i(0, 0, -1)`</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 `new Vector3i(0, 0, 1)`</value>
public static Vector3i Back { get { return _back; } }
- // Constructors
+ /// <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)
{
this.x = x;
this.y = y;
this.z = z;
}
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector3i"/> from an existing <see cref="Vector3i"/>.
+ /// </summary>
+ /// <param name="vi">The existing <see cref="Vector3i"/>.</param>
public Vector3i(Vector3i vi)
{
this.x = vi.x;
this.y = vi.y;
this.z = vi.z;
}
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector3i"/> from an existing <see cref="Vector3"/>
+ /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
+ /// </summary>
+ /// <param name="v">The <see cref="Vector3"/> to convert.</param>
public Vector3i(Vector3 v)
{
this.x = Mathf.RoundToInt(v.x);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 06ec2483c8..86a16c17f1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -1,39 +1,17 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AEBF0036-DA76-4341-B651-A3F2856AB2FA}</ProjectGuid>
- <OutputType>Library</OutputType>
<OutputPath>bin/$(Configuration)</OutputPath>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <AssemblyName>GodotSharp</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFramework>netstandard2.1</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
- <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
+ <EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>portable</DebugType>
- <Optimize>false</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>portable</DebugType>
- <Optimize>true</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
+ <PropertyGroup>
+ <DefineConstants>$(DefineConstants);GODOT</DefineConstants>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
- <Reference Include="System" />
- </ItemGroup>
- <ItemGroup>
<Compile Include="Core\AABB.cs" />
<Compile Include="Core\Array.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
@@ -90,5 +68,4 @@
Fortunately code completion, go to definition and such still work.
-->
<Import Project="Generated\GeneratedIncludes.props" />
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs
index f84e0183f6..da6f293871 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs
@@ -1,27 +1,3 @@
-using System.Reflection;
using System.Runtime.CompilerServices;
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("GodotSharp")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("1.0.*")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
[assembly: InternalsVisibleTo("GodotSharpEditor")]
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
index 8785931312..a8c4ba96b5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
@@ -1,46 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8FBEC238-D944-4074-8548-B3B524305905}</ProjectGuid>
- <OutputType>Library</OutputType>
<OutputPath>bin/$(Configuration)</OutputPath>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <AssemblyName>GodotSharpEditor</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFramework>netstandard2.1</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
- <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
+ <EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>portable</DebugType>
- <Optimize>false</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>portable</DebugType>
- <Optimize>true</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
+ <PropertyGroup>
+ <DefineConstants>$(DefineConstants);GODOT</DefineConstants>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
- <Reference Include="System" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="Properties\AssemblyInfo.cs" />
- </ItemGroup>
- <Import Project="Generated\GeneratedIncludes.props" />
- <ItemGroup>
<ProjectReference Include="..\GodotSharp\GodotSharp.csproj">
- <Private>False</Private>
+ <Private>false</Private>
</ProjectReference>
</ItemGroup>
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <!--
+ We import a props file with auto-generated includes. This works well with Rider.
+ However, Visual Studio and MonoDevelop won't list them in the solution explorer.
+ We can't use wildcards as there may be undesired old files still hanging around.
+ Fortunately code completion, go to definition and such still work.
+ -->
+ <Import Project="Generated\GeneratedIncludes.props" />
</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs
deleted file mode 100644
index 3684b7a3cb..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Reflection;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("GodotSharpEditor")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("1.0.*")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index a170fd36e7..9dbeee57ce 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -425,7 +425,7 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
while (!nested_classes.empty()) {
GDMonoClass *current_nested = nested_classes.front()->get();
- nested_classes.pop_back();
+ nested_classes.pop_front();
void *iter = nullptr;
diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp
index 8e3e51e688..e68466b1cf 100644
--- a/modules/mono/utils/osx_utils.cpp
+++ b/modules/mono/utils/osx_utils.cpp
@@ -38,24 +38,21 @@
#include <CoreServices/CoreServices.h>
bool osx_is_app_bundle_installed(const String &p_bundle_id) {
- CFURLRef app_url = nullptr;
CFStringRef bundle_id = CFStringCreateWithCString(nullptr, p_bundle_id.utf8(), kCFStringEncodingUTF8);
- OSStatus result = LSFindApplicationForInfo(kLSUnknownCreator, bundle_id, nullptr, nullptr, &app_url);
+ CFArrayRef result = LSCopyApplicationURLsForBundleIdentifier(bundle_id, nullptr);
CFRelease(bundle_id);
- if (app_url)
- CFRelease(app_url);
-
- switch (result) {
- case noErr:
+ if (result) {
+ if (CFArrayGetCount(result) > 0) {
+ CFRelease(result);
return true;
- case kLSApplicationNotFoundErr:
- break;
- default:
- break;
+ } else {
+ CFRelease(result);
+ return false;
+ }
+ } else {
+ return false;
}
-
- return false;
}
#endif