summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp10
-rw-r--r--modules/gdscript/gdscript_parser.cpp6
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml4
-rw-r--r--modules/gltf/gltf_document.cpp370
-rw-r--r--modules/gltf/gltf_state.cpp4
-rw-r--r--modules/gltf/gltf_state.h8
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp7
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.h2
-rw-r--r--modules/mono/csharp_script.cpp6
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/Main.cs32
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs23
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs530
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs30
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs32
15 files changed, 684 insertions, 384 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 9b0dc9577b..429f53e59f 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -2563,8 +2563,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
}
- if (is_static && !base_type.is_meta_type && !(callee_type != GDScriptParser::Node::SUBSCRIPT && parser->current_function != nullptr && parser->current_function->is_static)) {
- parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, base_type.to_string());
+ if (is_static && !base_type.is_meta_type && !(is_self && parser->current_function != nullptr && parser->current_function->is_static)) {
+ String caller_type = String(base_type.native_type);
+
+ if (caller_type.is_empty()) {
+ caller_type = base_type.to_string();
+ }
+
+ parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, caller_type);
}
#endif // DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 7f2f49f336..7074520a34 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3740,6 +3740,12 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
+ if (p_annotation->name == SNAME("@export_range")) {
+ if (export_type.builtin_type == Variant::INT) {
+ variable->export_info.type = Variant::INT;
+ }
+ }
+
if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index d0740cf7ca..9a554a0d49 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -66,7 +66,7 @@
</description>
</method>
<method name="get_materials">
- <return type="BaseMaterial3D[]" />
+ <return type="Material[]" />
<description>
</description>
</method>
@@ -169,7 +169,7 @@
</method>
<method name="set_materials">
<return type="void" />
- <param index="0" name="materials" type="BaseMaterial3D[]" />
+ <param index="0" name="materials" type="Material[]" />
<description>
</description>
</method>
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index f27e2385c6..eb8f7e5ebc 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -2484,12 +2484,12 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
if (surface_i < instance_materials.size()) {
v = instance_materials.get(surface_i);
}
- Ref<BaseMaterial3D> mat = v;
+ Ref<Material> mat = v;
if (!mat.is_valid()) {
mat = import_mesh->get_surface_material(surface_i);
}
if (mat.is_valid()) {
- HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Iterator material_cache_i = state->material_cache.find(mat);
+ HashMap<Ref<Material>, GLTFMaterialIndex>::Iterator material_cache_i = state->material_cache.find(mat);
if (material_cache_i && material_cache_i->value != -1) {
primitive["material"] = material_cache_i->value;
} else {
@@ -2937,16 +2937,18 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
}
- Ref<BaseMaterial3D> mat;
+ Ref<Material> mat;
String mat_name;
if (!state->discard_meshes_and_materials) {
if (p.has("material")) {
const int material = p["material"];
ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT);
- Ref<BaseMaterial3D> mat3d = state->materials[material];
+ Ref<Material> mat3d = state->materials[material];
ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT);
- if (has_vertex_color) {
- mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+
+ Ref<BaseMaterial3D> base_material = mat3d;
+ if (has_vertex_color && base_material.is_valid()) {
+ base_material->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
}
mat = mat3d;
@@ -2954,7 +2956,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
Ref<StandardMaterial3D> mat3d;
mat3d.instantiate();
if (has_vertex_color) {
- mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ mat3d->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
}
mat = mat3d;
}
@@ -3382,8 +3384,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
Array materials;
for (int32_t i = 0; i < state->materials.size(); i++) {
Dictionary d;
-
- Ref<BaseMaterial3D> material = state->materials[i];
+ Ref<Material> material = state->materials[i];
if (material.is_null()) {
materials.push_back(d);
continue;
@@ -3391,11 +3392,12 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
if (!material->get_name().is_empty()) {
d["name"] = _gen_unique_name(state, material->get_name());
}
- {
+ Ref<BaseMaterial3D> base_material = material;
+ if (base_material.is_valid()) {
Dictionary mr;
{
Array arr;
- const Color c = material->get_albedo().srgb_to_linear();
+ const Color c = base_material->get_albedo().srgb_to_linear();
arr.push_back(c.r);
arr.push_back(c.g);
arr.push_back(c.b);
@@ -3404,167 +3406,169 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
}
{
Dictionary bct;
- Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
- GLTFTextureIndex gltf_texture_index = -1;
+ if (base_material.is_valid()) {
+ Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
+ GLTFTextureIndex gltf_texture_index = -1;
- if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
- albedo_texture->set_name(material->get_name() + "_albedo");
- gltf_texture_index = _set_texture(state, albedo_texture, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
- }
- if (gltf_texture_index != -1) {
- bct["index"] = gltf_texture_index;
- Dictionary extensions = _serialize_texture_transform_uv1(material);
- if (!extensions.is_empty()) {
- bct["extensions"] = extensions;
- state->use_khr_texture_transform = true;
+ if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
+ albedo_texture->set_name(material->get_name() + "_albedo");
+ gltf_texture_index = _set_texture(state, albedo_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
+ }
+ if (gltf_texture_index != -1) {
+ bct["index"] = gltf_texture_index;
+ Dictionary extensions = _serialize_texture_transform_uv1(material);
+ if (!extensions.is_empty()) {
+ bct["extensions"] = extensions;
+ state->use_khr_texture_transform = true;
+ }
+ mr["baseColorTexture"] = bct;
}
- mr["baseColorTexture"] = bct;
}
}
-
- mr["metallicFactor"] = material->get_metallic();
- mr["roughnessFactor"] = material->get_roughness();
- bool has_roughness = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().is_valid();
- bool has_ao = material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid();
- bool has_metalness = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_image().is_valid();
- if (has_ao || has_roughness || has_metalness) {
- Dictionary mrt;
- Ref<Texture2D> roughness_texture = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS);
- BaseMaterial3D::TextureChannel roughness_channel = material->get_roughness_texture_channel();
- Ref<Texture2D> metallic_texture = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC);
- BaseMaterial3D::TextureChannel metalness_channel = material->get_metallic_texture_channel();
- Ref<Texture2D> ao_texture = material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION);
- BaseMaterial3D::TextureChannel ao_channel = material->get_ao_texture_channel();
- Ref<ImageTexture> orm_texture;
- orm_texture.instantiate();
- Ref<Image> orm_image;
- orm_image.instantiate();
- int32_t height = 0;
- int32_t width = 0;
- Ref<Image> ao_image;
- if (has_ao) {
- height = ao_texture->get_height();
- width = ao_texture->get_width();
- ao_image = ao_texture->get_image();
- Ref<ImageTexture> img_tex = ao_image;
- if (img_tex.is_valid()) {
- ao_image = img_tex->get_image();
+ if (base_material.is_valid()) {
+ mr["metallicFactor"] = base_material->get_metallic();
+ mr["roughnessFactor"] = base_material->get_roughness();
+ bool has_roughness = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().is_valid();
+ bool has_ao = base_material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid();
+ bool has_metalness = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_image().is_valid();
+ if (has_ao || has_roughness || has_metalness) {
+ Dictionary mrt;
+ Ref<Texture2D> roughness_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS);
+ BaseMaterial3D::TextureChannel roughness_channel = base_material->get_roughness_texture_channel();
+ Ref<Texture2D> metallic_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC);
+ BaseMaterial3D::TextureChannel metalness_channel = base_material->get_metallic_texture_channel();
+ Ref<Texture2D> ao_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION);
+ BaseMaterial3D::TextureChannel ao_channel = base_material->get_ao_texture_channel();
+ Ref<ImageTexture> orm_texture;
+ orm_texture.instantiate();
+ Ref<Image> orm_image;
+ orm_image.instantiate();
+ int32_t height = 0;
+ int32_t width = 0;
+ Ref<Image> ao_image;
+ if (has_ao) {
+ height = ao_texture->get_height();
+ width = ao_texture->get_width();
+ ao_image = ao_texture->get_image();
+ Ref<ImageTexture> img_tex = ao_image;
+ if (img_tex.is_valid()) {
+ ao_image = img_tex->get_image();
+ }
+ if (ao_image->is_compressed()) {
+ ao_image->decompress();
+ }
}
- if (ao_image->is_compressed()) {
- ao_image->decompress();
+ Ref<Image> roughness_image;
+ if (has_roughness) {
+ height = roughness_texture->get_height();
+ width = roughness_texture->get_width();
+ roughness_image = roughness_texture->get_image();
+ Ref<ImageTexture> img_tex = roughness_image;
+ if (img_tex.is_valid()) {
+ roughness_image = img_tex->get_image();
+ }
+ if (roughness_image->is_compressed()) {
+ roughness_image->decompress();
+ }
}
- }
- Ref<Image> roughness_image;
- if (has_roughness) {
- height = roughness_texture->get_height();
- width = roughness_texture->get_width();
- roughness_image = roughness_texture->get_image();
- Ref<ImageTexture> img_tex = roughness_image;
- if (img_tex.is_valid()) {
- roughness_image = img_tex->get_image();
+ Ref<Image> metallness_image;
+ if (has_metalness) {
+ height = metallic_texture->get_height();
+ width = metallic_texture->get_width();
+ metallness_image = metallic_texture->get_image();
+ Ref<ImageTexture> img_tex = metallness_image;
+ if (img_tex.is_valid()) {
+ metallness_image = img_tex->get_image();
+ }
+ if (metallness_image->is_compressed()) {
+ metallness_image->decompress();
+ }
}
- if (roughness_image->is_compressed()) {
- roughness_image->decompress();
+ Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
+ if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
+ height = albedo_texture->get_height();
+ width = albedo_texture->get_width();
}
- }
- Ref<Image> metallness_image;
- if (has_metalness) {
- height = metallic_texture->get_height();
- width = metallic_texture->get_width();
- metallness_image = metallic_texture->get_image();
- Ref<ImageTexture> img_tex = metallness_image;
- if (img_tex.is_valid()) {
- metallness_image = img_tex->get_image();
+ orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8);
+ if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) {
+ ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
}
- if (metallness_image->is_compressed()) {
- metallness_image->decompress();
+ if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) {
+ roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
}
- }
- Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
- if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
- height = albedo_texture->get_height();
- width = albedo_texture->get_width();
- }
- orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8);
- if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) {
- ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
- }
- if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) {
- roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
- }
- if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) {
- metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
- }
- for (int32_t h = 0; h < height; h++) {
- for (int32_t w = 0; w < width; w++) {
- Color c = Color(1.0f, 1.0f, 1.0f);
- if (has_ao) {
- if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) {
- c.r = ao_image->get_pixel(w, h).r;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) {
- c.r = ao_image->get_pixel(w, h).g;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) {
- c.r = ao_image->get_pixel(w, h).b;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) {
- c.r = ao_image->get_pixel(w, h).a;
+ if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) {
+ metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ for (int32_t h = 0; h < height; h++) {
+ for (int32_t w = 0; w < width; w++) {
+ Color c = Color(1.0f, 1.0f, 1.0f);
+ if (has_ao) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).a;
+ }
}
- }
- if (has_roughness) {
- if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).r;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).g;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).b;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).a;
+ if (has_roughness) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).a;
+ }
}
- }
- if (has_metalness) {
- if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).r;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).g;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).b;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).a;
+ if (has_metalness) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).a;
+ }
}
+ orm_image->set_pixel(w, h, c);
}
- orm_image->set_pixel(w, h, c);
}
- }
- orm_image->generate_mipmaps();
- orm_texture->set_image(orm_image);
- GLTFTextureIndex orm_texture_index = -1;
- if (has_ao || has_roughness || has_metalness) {
- orm_texture->set_name(material->get_name() + "_orm");
- orm_texture_index = _set_texture(state, orm_texture, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
- }
- if (has_ao) {
- Dictionary occt;
- occt["index"] = orm_texture_index;
- d["occlusionTexture"] = occt;
- }
- if (has_roughness || has_metalness) {
- mrt["index"] = orm_texture_index;
- Dictionary extensions = _serialize_texture_transform_uv1(material);
- if (!extensions.is_empty()) {
- mrt["extensions"] = extensions;
- state->use_khr_texture_transform = true;
+ orm_image->generate_mipmaps();
+ orm_texture->set_image(orm_image);
+ GLTFTextureIndex orm_texture_index = -1;
+ if (has_ao || has_roughness || has_metalness) {
+ orm_texture->set_name(material->get_name() + "_orm");
+ orm_texture_index = _set_texture(state, orm_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
+ }
+ if (has_ao) {
+ Dictionary occt;
+ occt["index"] = orm_texture_index;
+ d["occlusionTexture"] = occt;
+ }
+ if (has_roughness || has_metalness) {
+ mrt["index"] = orm_texture_index;
+ Dictionary extensions = _serialize_texture_transform_uv1(material);
+ if (!extensions.is_empty()) {
+ mrt["extensions"] = extensions;
+ state->use_khr_texture_transform = true;
+ }
+ mr["metallicRoughnessTexture"] = mrt;
}
- mr["metallicRoughnessTexture"] = mrt;
}
}
d["pbrMetallicRoughness"] = mr;
}
-
- if (material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) {
+ if (base_material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) {
Dictionary nt;
Ref<ImageTexture> tex;
tex.instantiate();
{
- Ref<Texture2D> normal_texture = material->get_texture(BaseMaterial3D::TEXTURE_NORMAL);
+ Ref<Texture2D> normal_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_NORMAL);
if (normal_texture.is_valid()) {
// Code for uncompressing RG normal maps
Ref<Image> img = normal_texture->get_image();
@@ -3594,30 +3598,30 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
GLTFTextureIndex gltf_texture_index = -1;
if (tex.is_valid() && tex->get_image().is_valid()) {
tex->set_name(material->get_name() + "_normal");
- gltf_texture_index = _set_texture(state, tex, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
+ gltf_texture_index = _set_texture(state, tex, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
}
- nt["scale"] = material->get_normal_scale();
+ nt["scale"] = base_material->get_normal_scale();
if (gltf_texture_index != -1) {
nt["index"] = gltf_texture_index;
d["normalTexture"] = nt;
}
}
- if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
- const Color c = material->get_emission().linear_to_srgb();
+ if (base_material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
+ const Color c = base_material->get_emission().linear_to_srgb();
Array arr;
arr.push_back(c.r);
arr.push_back(c.g);
arr.push_back(c.b);
d["emissiveFactor"] = arr;
}
- if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
+ if (base_material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
Dictionary et;
- Ref<Texture2D> emission_texture = material->get_texture(BaseMaterial3D::TEXTURE_EMISSION);
+ Ref<Texture2D> emission_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_EMISSION);
GLTFTextureIndex gltf_texture_index = -1;
if (emission_texture.is_valid() && emission_texture->get_image().is_valid()) {
emission_texture->set_name(material->get_name() + "_emission");
- gltf_texture_index = _set_texture(state, emission_texture, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
+ gltf_texture_index = _set_texture(state, emission_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
}
if (gltf_texture_index != -1) {
@@ -3625,14 +3629,14 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
d["emissiveTexture"] = et;
}
}
- const bool ds = material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED;
+ const bool ds = base_material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED;
if (ds) {
d["doubleSided"] = ds;
}
- if (material->get_transparency() == BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR) {
+ if (base_material->get_transparency() == BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR) {
d["alphaMode"] = "MASK";
- d["alphaCutoff"] = material->get_alpha_scissor_threshold();
- } else if (material->get_transparency() != BaseMaterial3D::TRANSPARENCY_DISABLED) {
+ d["alphaCutoff"] = base_material->get_alpha_scissor_threshold();
+ } else if (base_material->get_transparency() != BaseMaterial3D::TRANSPARENCY_DISABLED) {
d["alphaMode"] = "BLEND";
}
materials.push_back(d);
@@ -3838,29 +3842,37 @@ void GLTFDocument::_set_texture_transform_uv1(const Dictionary &d, Ref<BaseMater
if (d.has("extensions")) {
const Dictionary &extensions = d["extensions"];
if (extensions.has("KHR_texture_transform")) {
- const Dictionary &texture_transform = extensions["KHR_texture_transform"];
- const Array &offset_arr = texture_transform["offset"];
- if (offset_arr.size() == 2) {
- const Vector3 offset_vector3 = Vector3(offset_arr[0], offset_arr[1], 0.0f);
- material->set_uv1_offset(offset_vector3);
- }
+ if (material.is_valid()) {
+ const Dictionary &texture_transform = extensions["KHR_texture_transform"];
+ const Array &offset_arr = texture_transform["offset"];
+ if (offset_arr.size() == 2) {
+ const Vector3 offset_vector3 = Vector3(offset_arr[0], offset_arr[1], 0.0f);
+ material->set_uv1_offset(offset_vector3);
+ }
- const Array &scale_arr = texture_transform["scale"];
- if (scale_arr.size() == 2) {
- const Vector3 scale_vector3 = Vector3(scale_arr[0], scale_arr[1], 1.0f);
- material->set_uv1_scale(scale_vector3);
+ const Array &scale_arr = texture_transform["scale"];
+ if (scale_arr.size() == 2) {
+ const Vector3 scale_vector3 = Vector3(scale_arr[0], scale_arr[1], 1.0f);
+ material->set_uv1_scale(scale_vector3);
+ }
}
}
}
}
void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Ref<BaseMaterial3D> p_material) {
+ if (r_spec_gloss.is_null()) {
+ return;
+ }
if (r_spec_gloss->spec_gloss_img.is_null()) {
return;
}
if (r_spec_gloss->diffuse_img.is_null()) {
return;
}
+ if (p_material.is_null()) {
+ return;
+ }
bool has_roughness = false;
bool has_metal = false;
p_material->set_roughness(1.0f);
@@ -6657,21 +6669,17 @@ Dictionary _serialize_texture_transform_uv(Vector2 p_offset, Vector2 p_scale) {
}
Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material) {
- if (p_material.is_valid()) {
- Vector3 offset = p_material->get_uv1_offset();
- Vector3 scale = p_material->get_uv1_scale();
- return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y));
- }
- return Dictionary();
+ ERR_FAIL_NULL_V(p_material, Dictionary());
+ Vector3 offset = p_material->get_uv1_offset();
+ Vector3 scale = p_material->get_uv1_scale();
+ return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y));
}
Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) {
- if (p_material.is_valid()) {
- Vector3 offset = p_material->get_uv2_offset();
- Vector3 scale = p_material->get_uv2_scale();
- return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y));
- }
- return Dictionary();
+ ERR_FAIL_NULL_V(p_material, Dictionary());
+ Vector3 offset = p_material->get_uv2_offset();
+ Vector3 scale = p_material->get_uv2_scale();
+ return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y));
}
Error GLTFDocument::_serialize_version(Ref<GLTFState> state) {
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
index ac5665e396..6654c9e5d2 100644
--- a/modules/gltf/gltf_state.cpp
+++ b/modules/gltf/gltf_state.cpp
@@ -209,11 +209,11 @@ void GLTFState::set_meshes(TypedArray<GLTFMesh> p_meshes) {
GLTFTemplateConvert::set_from_array(meshes, p_meshes);
}
-TypedArray<BaseMaterial3D> GLTFState::get_materials() {
+TypedArray<Material> GLTFState::get_materials() {
return GLTFTemplateConvert::to_array(materials);
}
-void GLTFState::set_materials(TypedArray<BaseMaterial3D> p_materials) {
+void GLTFState::set_materials(TypedArray<Material> p_materials) {
GLTFTemplateConvert::set_from_array(materials, p_materials);
}
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index e24017b0fd..1c20520b22 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -72,8 +72,8 @@ class GLTFState : public Resource {
Vector<Ref<GLTFMesh>> meshes; // meshes are loaded directly, no reason not to.
Vector<AnimationPlayer *> animation_players;
- HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache;
- Vector<Ref<BaseMaterial3D>> materials;
+ HashMap<Ref<Material>, GLTFMaterialIndex> material_cache;
+ Vector<Ref<Material>> materials;
String scene_name;
Vector<int> root_nodes;
@@ -138,8 +138,8 @@ public:
TypedArray<GLTFMesh> get_meshes();
void set_meshes(TypedArray<GLTFMesh> p_meshes);
- TypedArray<BaseMaterial3D> get_materials();
- void set_materials(TypedArray<BaseMaterial3D> p_materials);
+ TypedArray<Material> get_materials();
+ void set_materials(TypedArray<Material> p_materials);
String get_scene_name();
void set_scene_name(String p_scene_name);
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 9c6cbebf0e..c8aedc8b92 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -459,6 +459,7 @@ void GridMapEditor::_delete_selection() {
return;
}
+ Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
undo_redo->create_action(TTR("GridMap Delete Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -479,6 +480,7 @@ void GridMapEditor::_fill_selection() {
return;
}
+ Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
undo_redo->create_action(TTR("GridMap Fill Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -572,6 +574,7 @@ void GridMapEditor::_do_paste() {
rot = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
Vector3 ofs = paste_indicator.current - paste_indicator.click;
+ Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
undo_redo->create_action(TTR("GridMap Paste Selection"));
for (const ClipboardItem &item : clipboard_items) {
@@ -659,6 +662,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
} else {
if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) {
if (set_items.size()) {
+ Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
undo_redo->create_action(TTR("GridMap Paint"));
for (const SetItem &si : set_items) {
undo_redo->add_do_method(node, "set_cell_item", si.position, si.new_value, si.new_orientation);
@@ -680,6 +684,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
if (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_SELECT) {
+ Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
undo_redo->create_action(TTR("GridMap Selection"));
undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end);
undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end);
@@ -1142,8 +1147,6 @@ void GridMapEditor::_bind_methods() {
}
GridMapEditor::GridMapEditor() {
- undo_redo = EditorNode::get_singleton()->get_undo_redo();
-
int mw = EDITOR_DEF("editors/grid_map/palette_min_width", 230);
Control *ec = memnew(Control);
ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE);
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h
index 91f14690ca..1cf2e4cb89 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.h
+++ b/modules/gridmap/editor/grid_map_editor_plugin.h
@@ -41,7 +41,6 @@
#include "scene/gui/spin_box.h"
class ConfirmationDialog;
-class EditorUndoRedoManager;
class MenuButton;
class Node3DEditorPlugin;
@@ -66,7 +65,6 @@ class GridMapEditor : public VBoxContainer {
DISPLAY_LIST
};
- Ref<EditorUndoRedoManager> undo_redo;
InputAction input_action = INPUT_NONE;
Panel *panel = nullptr;
MenuButton *options = nullptr;
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 345d2e4694..a4bffc1e3c 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -710,6 +710,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
return;
}
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ // We disable collectible assemblies in the game player, because the limitations cause
+ // issues with mocking libraries. As such, we can only reload assemblies in the editor.
+ return;
+ }
+
// TODO:
// Currently, this reloads all scripts, including those whose class is not part of the
// assembly load context being unloaded. As such, we unnecessarily reload GodotTools.
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
index 8308bada24..4ce02d221e 100644
--- a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
@@ -28,17 +28,24 @@ namespace GodotPlugins
get => _pluginLoadContext?.AssemblyLoadedPath;
}
+ public bool IsCollectible
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ get => _pluginLoadContext?.IsCollectible ?? false;
+ }
+
[MethodImpl(MethodImplOptions.NoInlining)]
public static (Assembly, PluginLoadContextWrapper) CreateAndLoadFromAssemblyName(
AssemblyName assemblyName,
string pluginPath,
ICollection<string> sharedAssemblies,
- AssemblyLoadContext mainLoadContext
+ AssemblyLoadContext mainLoadContext,
+ bool isCollectible
)
{
var wrapper = new PluginLoadContextWrapper();
wrapper._pluginLoadContext = new PluginLoadContext(
- pluginPath, sharedAssemblies, mainLoadContext);
+ pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName);
return (assembly, wrapper);
}
@@ -61,6 +68,7 @@ namespace GodotPlugins
private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
private static Assembly? _editorApiAssembly;
private static PluginLoadContextWrapper? _projectLoadContext;
+ private static bool _editorHint = false;
private static readonly AssemblyLoadContext MainLoadContext =
AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
@@ -77,15 +85,17 @@ namespace GodotPlugins
{
try
{
+ _editorHint = editorHint.ToBool();
+
_dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
SharedAssemblies.Add(CoreApiAssembly.GetName());
NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver);
- AlcReloadCfg.Configure(alcReloadEnabled: editorHint.ToBool());
+ AlcReloadCfg.Configure(alcReloadEnabled: _editorHint);
NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
- if (editorHint.ToBool())
+ if (_editorHint)
{
_editorApiAssembly = Assembly.Load("GodotSharpEditor");
SharedAssemblies.Add(_editorApiAssembly.GetName());
@@ -128,7 +138,7 @@ namespace GodotPlugins
string assemblyPath = new(nAssemblyPath);
- (var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath);
+ (var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
string loadedAssemblyPath = _projectLoadContext.AssemblyLoadedPath ?? assemblyPath;
*outLoadedAssemblyPath = Marshaling.ConvertStringToNative(loadedAssemblyPath);
@@ -155,7 +165,7 @@ namespace GodotPlugins
if (_editorApiAssembly == null)
throw new InvalidOperationException("The Godot editor API assembly is not loaded.");
- var (assembly, _) = LoadPlugin(assemblyPath);
+ var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
@@ -180,7 +190,7 @@ namespace GodotPlugins
}
}
- private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath)
+ private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath, bool isCollectible)
{
string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
@@ -194,7 +204,7 @@ namespace GodotPlugins
}
return PluginLoadContextWrapper.CreateAndLoadFromAssemblyName(
- new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext);
+ new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext, isCollectible);
}
[UnmanagedCallersOnly]
@@ -218,6 +228,12 @@ namespace GodotPlugins
if (pluginLoadContext == null)
return true;
+ if (!pluginLoadContext.IsCollectible)
+ {
+ Console.Error.WriteLine("Cannot unload a non-collectible assembly load context.");
+ return false;
+ }
+
Console.WriteLine("Unloading assembly load context...");
var alcWeakReference = pluginLoadContext.CreateWeakReference();
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
index dcd572c65e..344b76a202 100644
--- a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
@@ -15,8 +15,8 @@ namespace GodotPlugins
public string? AssemblyLoadedPath { get; private set; }
public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies,
- AssemblyLoadContext mainLoadContext)
- : base(isCollectible: true)
+ AssemblyLoadContext mainLoadContext, bool isCollectible)
+ : base(isCollectible)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
_sharedAssemblies = sharedAssemblies;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index f2667c6807..3f9e986f62 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -282,7 +282,7 @@ namespace Godot
/// <summary>
/// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by
- /// the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
/// </summary>
/// <param name="start">The start value for the interpolation.</param>
/// <param name="control1">Control point that defines the bezier curve.</param>
@@ -303,6 +303,27 @@ namespace Godot
}
/// <summary>
+ /// Returns the derivative at the given <paramref name="t"/> on a one dimensional Bezier curve defined by
+ /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="start">The start value for the interpolation.</param>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination value for the interpolation.</param>
+ /// <param name="t">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 BezierDerivative(real_t start, real_t control1, real_t control2, real_t end, real_t t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ real_t omt = 1 - t;
+ real_t omt2 = omt * omt;
+ real_t t2 = t * t;
+
+ real_t d = (control1 - start) * 3 * omt2 + (control2 - control1) * 6 * omt * t + (end - control2) * 3 * t2;
+ return d;
+ }
+
+ /// <summary>
/// Converts an angle expressed in degrees to radians.
/// </summary>
/// <param name="deg">An angle expressed in degrees.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
index 371729ebec..8b1b73fcc3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -3,6 +3,14 @@ using System.Runtime.InteropServices;
namespace Godot
{
+ /// <summary>
+ /// A 4x4 matrix used for 3D projective transformations. It can represent transformations such as
+ /// translation, rotation, scaling, shearing, and perspective division. It consists of four
+ /// <see cref="Vector4"/> columns.
+ /// For purely linear transformations (translation, rotation, and scale), it is recommended to use
+ /// <see cref="Transform3D"/>, as it is more performant and has a lower memory footprint.
+ /// Used internally as <see cref="Camera3D"/>'s projection matrix.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Projection : IEquatable<Projection>
@@ -59,48 +67,107 @@ namespace Godot
public Vector4 w;
/// <summary>
- /// Constructs a projection from 4 vectors (matrix columns).
+ /// Access whole columns in the form of <see cref="Vector4"/>.
/// </summary>
- /// <param name="x">The X column, or column index 0.</param>
- /// <param name="y">The Y column, or column index 1.</param>
- /// <param name="z">The Z column, or column index 2.</param>
- /// <param name="w">The W column, or column index 3.</param>
- public Projection(Vector4 x, Vector4 y, Vector4 z, Vector4 w)
+ /// <param name="column">Which column vector.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="column"/> is not 0, 1, 2 or 3.
+ /// </exception>
+ public Vector4 this[int column]
{
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
+ readonly get
+ {
+ switch (column)
+ {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return w;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
+ set
+ {
+ switch (column)
+ {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ case 3:
+ w = value;
+ return;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
}
/// <summary>
- /// Constructs a new <see cref="Projection"/> from a <see cref="Transform3D"/>.
+ /// Access single values.
/// </summary>
- /// <param name="transform">The <see cref="Transform3D"/>.</param>
- public Projection(Transform3D transform)
+ /// <param name="column">Which column vector.</param>
+ /// <param name="row">Which row of the column.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="column"/> or <paramref name="row"/> are not 0, 1, 2 or 3.
+ /// </exception>
+ public real_t this[int column, int row]
{
- x = new Vector4(transform.basis.Row0.x, transform.basis.Row1.x, transform.basis.Row2.x, 0);
- y = new Vector4(transform.basis.Row0.y, transform.basis.Row1.y, transform.basis.Row2.y, 0);
- z = new Vector4(transform.basis.Row0.z, transform.basis.Row1.z, transform.basis.Row2.z, 0);
- w = new Vector4(transform.origin.x, transform.origin.y, transform.origin.z, 1);
+ readonly get
+ {
+ switch (column)
+ {
+ case 0:
+ return x[row];
+ case 1:
+ return y[row];
+ case 2:
+ return z[row];
+ case 3:
+ return w[row];
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
+ set
+ {
+ switch (column)
+ {
+ case 0:
+ x[row] = value;
+ return;
+ case 1:
+ y[row] = value;
+ return;
+ case 2:
+ z[row] = value;
+ return;
+ case 3:
+ w[row] = value;
+ return;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
}
/// <summary>
- /// Constructs a new <see cref="Transform3D"/> from the <see cref="Projection"/>.
+ /// Creates a new <see cref="Projection"/> that projects positions from a depth range of
+ /// <c>-1</c> to <c>1</c> to one that ranges from <c>0</c> to <c>1</c>, and flips the projected
+ /// positions vertically, according to <paramref name="flipY"/>.
/// </summary>
- /// <param name="proj">The <see cref="Projection"/>.</param>
- public static explicit operator Transform3D(Projection proj)
- {
- return new Transform3D(
- new Basis(
- new Vector3(proj.x.x, proj.x.y, proj.x.z),
- new Vector3(proj.y.x, proj.y.y, proj.y.z),
- new Vector3(proj.z.x, proj.z.y, proj.z.z)
- ),
- new Vector3(proj.w.x, proj.w.y, proj.w.z)
- );
- }
-
+ /// <param name="flipY">If the projection should be flipped vertically.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateDepthCorrection(bool flipY)
{
return new Projection(
@@ -111,6 +178,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that scales a given projection to fit around
+ /// a given <see cref="AABB"/> in projection space.
+ /// </summary>
+ /// <param name="aabb">The AABB to fit the projection around.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateFitAabb(AABB aabb)
{
Vector3 min = aabb.Position;
@@ -124,6 +197,25 @@ namespace Godot
);
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> for projecting positions onto a head-mounted display with
+ /// the given X:Y aspect ratio, distance between eyes, display width, distance to lens, oversampling factor,
+ /// and depth clipping planes.
+ /// <paramref name="eye"/> creates the projection for the left eye when set to 1,
+ /// or the right eye when set to 2.
+ /// </summary>
+ /// <param name="eye">
+ /// The eye to create the projection for.
+ /// The left eye when set to 1, the right eye when set to 2.
+ /// </param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="intraocularDist">The distance between the eyes.</param>
+ /// <param name="displayWidth">The display width.</param>
+ /// <param name="displayToLens">The distance to the lens.</param>
+ /// <param name="oversample">The oversampling factor.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateForHmd(int eye, real_t aspect, real_t intraocularDist, real_t displayWidth, real_t displayToLens, real_t oversample, real_t zNear, real_t zFar)
{
real_t f1 = (intraocularDist * (real_t)0.5) / displayToLens;
@@ -148,6 +240,17 @@ namespace Godot
}
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions in a frustum with
+ /// the given clipping planes.
+ /// </summary>
+ /// <param name="left">The left clipping distance.</param>
+ /// <param name="right">The right clipping distance.</param>
+ /// <param name="bottom">The bottom clipping distance.</param>
+ /// <param name="top">The top clipping distance.</param>
+ /// <param name="near">The near clipping distance.</param>
+ /// <param name="far">The far clipping distance.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateFrustum(real_t left, real_t right, real_t bottom, real_t top, real_t near, real_t far)
{
if (right <= left)
@@ -179,6 +282,18 @@ namespace Godot
);
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions in a frustum with
+ /// the given size, X:Y aspect ratio, offset, and clipping planes.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="size">The frustum size.</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="offset">The offset to apply.</param>
+ /// <param name="near">The near clipping distance.</param>
+ /// <param name="far">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateFrustumAspect(real_t size, real_t aspect, Vector2 offset, real_t near, real_t far, bool flipFov)
{
if (!flipFov)
@@ -188,6 +303,11 @@ namespace Godot
return CreateFrustum(-size / 2 + offset.x, +size / 2 + offset.x, -size / aspect / 2 + offset.y, +size / aspect / 2 + offset.y, near, far);
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions into the given <see cref="Rect2"/>.
+ /// </summary>
+ /// <param name="rect">The Rect2 to project positions into.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateLightAtlasRect(Rect2 rect)
{
return new Projection(
@@ -198,6 +318,17 @@ namespace Godot
);
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using an orthogonal projection with
+ /// the given clipping planes.
+ /// </summary>
+ /// <param name="left">The left clipping distance.</param>
+ /// <param name="right">The right clipping distance.</param>
+ /// <param name="bottom">The bottom clipping distance.</param>
+ /// <param name="top">The top clipping distance.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateOrthogonal(real_t left, real_t right, real_t bottom, real_t top, real_t zNear, real_t zFar)
{
Projection proj = Projection.Identity;
@@ -211,6 +342,17 @@ namespace Godot
return proj;
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using an orthogonal projection with
+ /// the given size, X:Y aspect ratio, and clipping planes.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="size">The frustum size.</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreateOrthogonalAspect(real_t size, real_t aspect, real_t zNear, real_t zFar, bool flipFov)
{
if (!flipFov)
@@ -220,6 +362,17 @@ namespace Godot
return CreateOrthogonal(-size / 2, +size / 2, -size / aspect / 2, +size / aspect / 2, zNear, zFar);
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using a perspective projection with
+ /// the given Y-axis field of view (in degrees), X:Y aspect ratio, and clipping planes.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="fovyDegrees">The vertical field of view (in degrees).</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreatePerspective(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov)
{
if (flipFov)
@@ -249,6 +402,27 @@ namespace Godot
return proj;
}
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using a perspective projection with
+ /// the given Y-axis field of view (in degrees), X:Y aspect ratio, and clipping distances.
+ /// The projection is adjusted for a head-mounted display with the given distance between eyes and distance
+ /// to a point that can be focused on.
+ /// <paramref name="eye"/> creates the projection for the left eye when set to 1,
+ /// or the right eye when set to 2.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="fovyDegrees">The vertical field of view (in degrees).</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <param name="eye">
+ /// The eye to create the projection for.
+ /// The left eye when set to 1, the right eye when set to 2.
+ /// </param>
+ /// <param name="intraocularDist">The distance between the eyes.</param>
+ /// <param name="convergenceDist">The distance to a point of convergence that can be focused on.</param>
+ /// <returns>The created projection.</returns>
public static Projection CreatePerspectiveHmd(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov, int eye, real_t intraocularDist, real_t convergenceDist)
{
if (flipFov)
@@ -286,6 +460,13 @@ namespace Godot
return proj * cm;
}
+ /// <summary>
+ /// Returns a scalar value that is the signed factor by which areas are scaled by this matrix.
+ /// If the sign is negative, the matrix flips the orientation of the area.
+ /// The determinant can be used to calculate the invertibility of a matrix or solve linear systems
+ /// of equations involving the matrix, among other applications.
+ /// </summary>
+ /// <returns>The determinant calculated from this projection.</returns>
public readonly real_t Determinant()
{
return x.w * y.z * z.y * w.x - x.z * y.w * z.y * w.x -
@@ -302,12 +483,20 @@ namespace Godot
x.y * y.x * z.z * w.w + x.x * y.y * z.z * w.w;
}
+ /// <summary>
+ /// Returns the X:Y aspect ratio of this <see cref="Projection"/>'s viewport.
+ /// </summary>
+ /// <returns>The aspect ratio from this projection's viewport.</returns>
public readonly real_t GetAspect()
{
Vector2 vpHe = GetViewportHalfExtents();
return vpHe.x / vpHe.y;
}
+ /// <summary>
+ /// Returns the horizontal field of view of the projection (in degrees).
+ /// </summary>
+ /// <returns>The horizontal field of view of this projection.</returns>
public readonly real_t GetFov()
{
Plane rightPlane = new Plane(x.w - x.x, y.w - y.x, z.w - z.x, -w.w + w.x).Normalized();
@@ -322,11 +511,22 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the vertical field of view of the projection (in degrees) associated with
+ /// the given horizontal field of view (in degrees) and aspect ratio.
+ /// </summary>
+ /// <param name="fovx">The horizontal field of view (in degrees).</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <returns>The vertical field of view of this projection.</returns>
public static real_t GetFovy(real_t fovx, real_t aspect)
{
return Mathf.RadToDeg(Mathf.Atan(aspect * Mathf.Tan(Mathf.DegToRad(fovx) * (real_t)0.5)) * (real_t)2.0);
}
+ /// <summary>
+ /// Returns the factor by which the visible level of detail is scaled by this <see cref="Projection"/>.
+ /// </summary>
+ /// <returns>The level of detail factor for this projection.</returns>
public readonly real_t GetLodMultiplier()
{
if (IsOrthogonal())
@@ -341,6 +541,12 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the number of pixels with the given pixel width displayed per meter, after
+ /// this <see cref="Projection"/> is applied.
+ /// </summary>
+ /// <param name="forPixelWidth">The width for each pixel (in meters).</param>
+ /// <returns>The number of pixels per meter.</returns>
public readonly int GetPixelsPerMeter(int forPixelWidth)
{
Vector3 result = this * new Vector3(1, 0, -1);
@@ -348,6 +554,15 @@ namespace Godot
return (int)((result.x * (real_t)0.5 + (real_t)0.5) * forPixelWidth);
}
+ /// <summary>
+ /// Returns the clipping plane of this <see cref="Projection"/> whose index is given
+ /// by <paramref name="plane"/>.
+ /// <paramref name="plane"/> should be equal to one of <see cref="Planes.Near"/>,
+ /// <see cref="Planes.Far"/>, <see cref="Planes.Left"/>, <see cref="Planes.Top"/>,
+ /// <see cref="Planes.Right"/>, or <see cref="Planes.Bottom"/>.
+ /// </summary>
+ /// <param name="plane">The kind of clipping plane to get from the projection.</param>
+ /// <returns>The clipping plane of this projection.</returns>
public readonly Plane GetProjectionPlane(Planes plane)
{
Plane newPlane = plane switch
@@ -364,28 +579,49 @@ namespace Godot
return newPlane.Normalized();
}
+ /// <summary>
+ /// Returns the dimensions of the far clipping plane of the projection, divided by two.
+ /// </summary>
+ /// <returns>The half extents for this projection's far plane.</returns>
public readonly Vector2 GetFarPlaneHalfExtents()
{
var res = GetProjectionPlane(Planes.Far).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top));
return new Vector2(res.Value.x, res.Value.y);
}
+ /// <summary>
+ /// Returns the dimensions of the viewport plane that this <see cref="Projection"/>
+ /// projects positions onto, divided by two.
+ /// </summary>
+ /// <returns>The half extents for this projection's viewport plane.</returns>
public readonly Vector2 GetViewportHalfExtents()
{
var res = GetProjectionPlane(Planes.Near).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top));
return new Vector2(res.Value.x, res.Value.y);
}
+ /// <summary>
+ /// Returns the distance for this <see cref="Projection"/> beyond which positions are clipped.
+ /// </summary>
+ /// <returns>The distance beyond which positions are clipped.</returns>
public readonly real_t GetZFar()
{
return GetProjectionPlane(Planes.Far).D;
}
+ /// <summary>
+ /// Returns the distance for this <see cref="Projection"/> before which positions are clipped.
+ /// </summary>
+ /// <returns>The distance before which positions are clipped.</returns>
public readonly real_t GetZNear()
{
return -GetProjectionPlane(Planes.Near).D;
}
+ /// <summary>
+ /// Returns a copy of this <see cref="Projection"/> with the signs of the values of the Y column flipped.
+ /// </summary>
+ /// <returns>The flipped projection.</returns>
public readonly Projection FlippedY()
{
Projection proj = this;
@@ -393,6 +629,13 @@ namespace Godot
return proj;
}
+ /// <summary>
+ /// Returns a <see cref="Projection"/> with the near clipping distance adjusted to be
+ /// <paramref name="newZNear"/>.
+ /// Note: The original <see cref="Projection"/> must be a perspective projection.
+ /// </summary>
+ /// <param name="newZNear">The near clipping distance to adjust the projection to.</param>
+ /// <returns>The adjusted projection.</returns>
public readonly Projection PerspectiveZNearAdjusted(real_t newZNear)
{
Projection proj = this;
@@ -404,6 +647,12 @@ namespace Godot
return proj;
}
+ /// <summary>
+ /// Returns a <see cref="Projection"/> with the X and Y values from the given <see cref="Vector2"/>
+ /// added to the first and second values of the final column respectively.
+ /// </summary>
+ /// <param name="offset">The offset to apply to the projection.</param>
+ /// <returns>The offseted projection.</returns>
public readonly Projection JitterOffseted(Vector2 offset)
{
Projection proj = this;
@@ -412,6 +661,11 @@ namespace Godot
return proj;
}
+ /// <summary>
+ /// Returns a <see cref="Projection"/> that performs the inverse of this <see cref="Projection"/>'s
+ /// projective transformation.
+ /// </summary>
+ /// <returns>The inverted projection.</returns>
public readonly Projection Inverse()
{
Projection proj = this;
@@ -535,11 +789,70 @@ namespace Godot
return proj;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this <see cref="Projection"/> performs an orthogonal projection.
+ /// </summary>
+ /// <returns>If the projection performs an orthogonal projection.</returns>
public readonly bool IsOrthogonal()
{
return w.w == (real_t)1.0;
}
+ // Constants
+ private static readonly Projection _zero = new Projection(
+ new Vector4(0, 0, 0, 0),
+ new Vector4(0, 0, 0, 0),
+ new Vector4(0, 0, 0, 0),
+ new Vector4(0, 0, 0, 0)
+ );
+ private static readonly Projection _identity = new Projection(
+ new Vector4(1, 0, 0, 0),
+ new Vector4(0, 1, 0, 0),
+ new Vector4(0, 0, 1, 0),
+ new Vector4(0, 0, 0, 1)
+ );
+
+ /// <summary>
+ /// Zero projection, a projection with all components set to <c>0</c>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Projection(Vector4.Zero, Vector4.Zero, Vector4.Zero, Vector4.Zero)</c>.</value>
+ public static Projection Zero { get { return _zero; } }
+
+ /// <summary>
+ /// The identity projection, with no distortion applied.
+ /// This is used as a replacement for <c>Projection()</c> in GDScript.
+ /// Do not use <c>new Projection()</c> with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to <c>new Projection(new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1))</c>.</value>
+ public static Projection Identity { get { return _identity; } }
+
+ /// <summary>
+ /// Constructs a projection from 4 vectors (matrix columns).
+ /// </summary>
+ /// <param name="x">The X column, or column index 0.</param>
+ /// <param name="y">The Y column, or column index 1.</param>
+ /// <param name="z">The Z column, or column index 2.</param>
+ /// <param name="w">The W column, or column index 3.</param>
+ public Projection(Vector4 x, Vector4 y, Vector4 z, Vector4 w)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Projection"/> from a <see cref="Transform3D"/>.
+ /// </summary>
+ /// <param name="transform">The <see cref="Transform3D"/>.</param>
+ public Projection(Transform3D transform)
+ {
+ x = new Vector4(transform.basis.Row0.x, transform.basis.Row1.x, transform.basis.Row2.x, 0);
+ y = new Vector4(transform.basis.Row0.y, transform.basis.Row1.y, transform.basis.Row2.y, 0);
+ z = new Vector4(transform.basis.Row0.z, transform.basis.Row1.z, transform.basis.Row2.z, 0);
+ w = new Vector4(transform.origin.x, transform.origin.y, transform.origin.z, 1);
+ }
+
/// <summary>
/// Composes these two projections by multiplying them
/// together. This has the effect of applying the right
@@ -646,127 +959,41 @@ namespace Godot
}
/// <summary>
- /// Access whole columns in the form of <see cref="Vector4"/>.
+ /// Constructs a new <see cref="Transform3D"/> from the <see cref="Projection"/>.
/// </summary>
- /// <param name="column">Which column vector.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <paramref name="column"/> is not 0, 1, 2 or 3.
- /// </exception>
- public Vector4 this[int column]
+ /// <param name="proj">The <see cref="Projection"/>.</param>
+ public static explicit operator Transform3D(Projection proj)
{
- readonly get
- {
- switch (column)
- {
- case 0:
- return x;
- case 1:
- return y;
- case 2:
- return z;
- case 3:
- return w;
- default:
- throw new ArgumentOutOfRangeException(nameof(column));
- }
- }
- set
- {
- switch (column)
- {
- case 0:
- x = value;
- return;
- case 1:
- y = value;
- return;
- case 2:
- z = value;
- return;
- case 3:
- w = value;
- return;
- default:
- throw new ArgumentOutOfRangeException(nameof(column));
- }
- }
+ return new Transform3D(
+ new Basis(
+ new Vector3(proj.x.x, proj.x.y, proj.x.z),
+ new Vector3(proj.y.x, proj.y.y, proj.y.z),
+ new Vector3(proj.z.x, proj.z.y, proj.z.z)
+ ),
+ new Vector3(proj.w.x, proj.w.y, proj.w.z)
+ );
}
/// <summary>
- /// Access single values.
+ /// Returns <see langword="true"/> if the projection is exactly equal
+ /// to the given object (<see paramref="obj"/>).
/// </summary>
- /// <param name="column">Which column vector.</param>
- /// <param name="row">Which row of the column.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <paramref name="column"/> or <paramref name="row"/> are not 0, 1, 2 or 3.
- /// </exception>
- public real_t this[int column, int row]
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
+ public override readonly bool Equals(object obj)
{
- readonly get
- {
- switch (column)
- {
- case 0:
- return x[row];
- case 1:
- return y[row];
- case 2:
- return z[row];
- case 3:
- return w[row];
- default:
- throw new ArgumentOutOfRangeException(nameof(column));
- }
- }
- set
- {
- switch (column)
- {
- case 0:
- x[row] = value;
- return;
- case 1:
- y[row] = value;
- return;
- case 2:
- z[row] = value;
- return;
- case 3:
- w[row] = value;
- return;
- default:
- throw new ArgumentOutOfRangeException(nameof(column));
- }
- }
+ return obj is Projection other && Equals(other);
}
- // Constants
- private static readonly Projection _zero = new Projection(
- new Vector4(0, 0, 0, 0),
- new Vector4(0, 0, 0, 0),
- new Vector4(0, 0, 0, 0),
- new Vector4(0, 0, 0, 0)
- );
- private static readonly Projection _identity = new Projection(
- new Vector4(1, 0, 0, 0),
- new Vector4(0, 1, 0, 0),
- new Vector4(0, 0, 1, 0),
- new Vector4(0, 0, 0, 1)
- );
-
- /// <summary>
- /// Zero projection, a projection with all components set to <c>0</c>.
- /// </summary>
- /// <value>Equivalent to <c>new Projection(Vector4.Zero, Vector4.Zero, Vector4.Zero, Vector4.Zero)</c>.</value>
- public static Projection Zero { get { return _zero; } }
-
/// <summary>
- /// The identity projection, with no distortion applied.
- /// This is used as a replacement for <c>Projection()</c> in GDScript.
- /// Do not use <c>new Projection()</c> with no arguments in C#, because it sets all values to zero.
+ /// Returns <see langword="true"/> if the projections are exactly equal.
/// </summary>
- /// <value>Equivalent to <c>new Projection(new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1))</c>.</value>
- public static Projection Identity { get { return _identity; } }
+ /// <param name="other">The other projection.</param>
+ /// <returns>Whether or not the projections are exactly equal.</returns>
+ public readonly bool Equals(Projection other)
+ {
+ return x == other.x && y == other.y && z == other.z && w == other.w;
+ }
/// <summary>
/// Serves as the hash function for <see cref="Projection"/>.
@@ -797,26 +1024,5 @@ namespace Godot
$"{z.x.ToString(format)}, {z.y.ToString(format)}, {z.z.ToString(format)}, {z.w.ToString(format)}\n" +
$"{w.x.ToString(format)}, {w.y.ToString(format)}, {w.z.ToString(format)}, {w.w.ToString(format)}\n";
}
-
- /// <summary>
- /// Returns <see langword="true"/> if the projection is exactly equal
- /// to the given object (<see paramref="obj"/>).
- /// </summary>
- /// <param name="obj">The object to compare with.</param>
- /// <returns>Whether or not the vector and the object are equal.</returns>
- public override readonly bool Equals(object obj)
- {
- return obj is Projection other && Equals(other);
- }
-
- /// <summary>
- /// Returns <see langword="true"/> if the projections are exactly equal.
- /// </summary>
- /// <param name="other">The other projection.</param>
- /// <returns>Whether or not the projections are exactly equal.</returns>
- public readonly bool Equals(Projection other)
- {
- return x == other.x && y == other.y && z == other.z && w == other.w;
- }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 535391f447..c471eceded 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -240,7 +240,7 @@ namespace Godot
/// <summary>
/// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by this vector
- /// and the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
/// </summary>
/// <param name="control1">Control point that defines the bezier curve.</param>
/// <param name="control2">Control point that defines the bezier curve.</param>
@@ -249,14 +249,28 @@ namespace Godot
/// <returns>The interpolated vector.</returns>
public readonly Vector2 BezierInterpolate(Vector2 control1, Vector2 control2, Vector2 end, real_t t)
{
- // Formula from Wikipedia article on Bezier curves
- real_t omt = 1 - t;
- real_t omt2 = omt * omt;
- real_t omt3 = omt2 * omt;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
+ return new Vector2
+ (
+ Mathf.BezierInterpolate(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierInterpolate(y, control1.y, control2.y, end.y, t)
+ );
+ }
- return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ /// <summary>
+ /// Returns the derivative at the given <paramref name="t"/> on the Bezier curve defined by this vector
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination value for the interpolation.</param>
+ /// <param name="t">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 readonly Vector2 BezierDerivative(Vector2 control1, Vector2 control2, Vector2 end, real_t t)
+ {
+ return new Vector2(
+ Mathf.BezierDerivative(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierDerivative(y, control1.y, control2.y, end.y, t)
+ );
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 53bd0b0908..fefdee33a5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -234,7 +234,7 @@ namespace Godot
/// <summary>
/// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by this vector
- /// and the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
/// </summary>
/// <param name="control1">Control point that defines the bezier curve.</param>
/// <param name="control2">Control point that defines the bezier curve.</param>
@@ -243,14 +243,30 @@ namespace Godot
/// <returns>The interpolated vector.</returns>
public readonly Vector3 BezierInterpolate(Vector3 control1, Vector3 control2, Vector3 end, real_t t)
{
- // Formula from Wikipedia article on Bezier curves
- real_t omt = 1 - t;
- real_t omt2 = omt * omt;
- real_t omt3 = omt2 * omt;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
+ return new Vector3
+ (
+ Mathf.BezierInterpolate(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierInterpolate(y, control1.y, control2.y, end.y, t),
+ Mathf.BezierInterpolate(z, control1.z, control2.z, end.z, t)
+ );
+ }
- return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ /// <summary>
+ /// Returns the derivative at the given <paramref name="t"/> on the Bezier curve defined by this vector
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination value for the interpolation.</param>
+ /// <param name="t">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 readonly Vector3 BezierDerivative(Vector3 control1, Vector3 control2, Vector3 end, real_t t)
+ {
+ return new Vector3(
+ Mathf.BezierDerivative(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierDerivative(y, control1.y, control2.y, end.y, t),
+ Mathf.BezierDerivative(z, control1.z, control2.z, end.y, t)
+ );
}
/// <summary>