diff options
-rw-r--r-- | core/bind/core_bind.cpp | 21 | ||||
-rw-r--r-- | core/io/logger.cpp | 2 | ||||
-rw-r--r-- | doc/classes/Directory.xml | 4 | ||||
-rw-r--r-- | doc/classes/HTTPRequest.xml | 10 | ||||
-rw-r--r-- | doc/classes/PackedScene.xml | 7 | ||||
-rw-r--r-- | doc/classes/ProjectSettings.xml | 6 | ||||
-rw-r--r-- | doc/classes/Resource.xml | 3 | ||||
-rw-r--r-- | doc/classes/Timer.xml | 1 | ||||
-rw-r--r-- | drivers/png/image_loader_png.cpp | 5 | ||||
-rw-r--r-- | drivers/png/png_driver_common.cpp | 7 | ||||
-rw-r--r-- | drivers/png/png_driver_common.h | 2 | ||||
-rw-r--r-- | editor/editor_file_system.cpp | 1 | ||||
-rw-r--r-- | editor/editor_node.cpp | 2 | ||||
-rw-r--r-- | editor/plugins/script_editor_plugin.cpp | 7 | ||||
-rw-r--r-- | main/main.cpp | 9 | ||||
-rw-r--r-- | modules/bullet/bullet_physics_server.cpp | 7 | ||||
-rw-r--r-- | modules/bullet/space_bullet.cpp | 5 | ||||
-rw-r--r-- | platform/android/export/gradle_export_util.h | 101 | ||||
-rw-r--r-- | scene/3d/path_3d.cpp | 76 | ||||
-rw-r--r-- | scene/3d/path_3d.h | 2 |
20 files changed, 212 insertions, 66 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 6df8dd380a..94da74cbda 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1673,8 +1673,15 @@ int _Directory::get_current_drive() { } Error _Directory::change_dir(String p_dir) { - ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - return d->change_dir(p_dir); + ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); + Error err = d->change_dir(p_dir); + + if (err != OK) { + return err; + } + dir_open = true; + + return OK; } String _Directory::get_current_dir() { @@ -1705,8 +1712,7 @@ Error _Directory::make_dir_recursive(String p_dir) { } bool _Directory::file_exists(String p_file) { - ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); - + ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); if (!p_file.is_rel_path()) { return FileAccess::exists(p_file); } @@ -1715,16 +1721,15 @@ bool _Directory::file_exists(String p_file) { } bool _Directory::dir_exists(String p_dir) { - ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); + ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); if (!p_dir.is_rel_path()) { DirAccess *d = DirAccess::create_for_path(p_dir); bool exists = d->dir_exists(p_dir); memdelete(d); return exists; - - } else { - return d->dir_exists(p_dir); } + + return d->dir_exists(p_dir); } int _Directory::get_space_left() { diff --git a/core/io/logger.cpp b/core/io/logger.cpp index ef78b1194e..886e5695b1 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -152,7 +152,7 @@ void RotatedFileLogger::rotate_file() { char timestamp[21]; OS::Date date = OS::get_singleton()->get_date(); OS::Time time = OS::get_singleton()->get_time(); - sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); + sprintf(timestamp, "_%04d-%02d-%02d_%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); String backup_name = base_path.get_basename() + timestamp; if (base_path.get_extension() != String()) { diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml index ed4257a809..a86dbfedde 100644 --- a/doc/classes/Directory.xml +++ b/doc/classes/Directory.xml @@ -5,7 +5,7 @@ </brief_description> <description> Directory type. It is used to manage directories and their content (not restricted to the project folder). - When creating a new [Directory], its default opened directory will be [code]res://[/code]. This may change in the future, so it is advised to always use [method open] to initialize your [Directory] where you want to operate, with explicit error checking. + When creating a new [Directory], it must be explicitly opened using [method open] before most methods can be used. However, [method file_exists] and [method dir_exists] can be used without opening a directory. If so, they use a path relative to [code]res://[/code]. Here is an example on how to iterate through the files of a directory: [codeblock] func dir_contents(path): @@ -63,6 +63,7 @@ </argument> <description> Returns whether the target directory exists. The argument can be relative to the current directory, or an absolute path. + If the [Directory] is not open, the path is relative to [code]res://[/code]. </description> </method> <method name="file_exists"> @@ -72,6 +73,7 @@ </argument> <description> Returns whether the target file exists. The argument can be relative to the current directory, or an absolute path. + If the [Directory] is not open, the path is relative to [code]res://[/code]. </description> </method> <method name="get_current_dir"> diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 53ca1fc6a9..0b0d71fccf 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -14,11 +14,19 @@ add_child(http_request) http_request.connect("request_completed", self, "_http_request_completed") - # Perform the HTTP request. The URL below returns some JSON as of writing. + # Perform a GET request. The URL below returns JSON as of writing. var error = http_request.request("https://httpbin.org/get") if error != OK: push_error("An error occurred in the HTTP request.") + # Perform a POST request. The URL below returns JSON as of writing. + # Note: Don't make simultaneous requests using a single HTTPRequest node. + # The snippet below is provided for reference only. + var body = {"name": "Godette"} + var error = http_request.request("https://httpbin.org/post", [], true, HTTPClient.METHOD_POST, body) + if error != OK: + push_error("An error occurred in the HTTP request.") + # Called when the HTTP request is completed. func _http_request_completed(result, response_code, headers, body): diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index 2d70dea012..bb56330248 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -7,6 +7,13 @@ A simplified interface to a scene file. Provides access to operations and checks that can be performed on the scene resource itself. Can be used to save a node to a file. When saving, the node as well as all the node it owns get saved (see [code]owner[/code] property on [Node]). [b]Note:[/b] The node doesn't need to own itself. + [b]Example of loading a saved scene:[/b] + [codeblock] + # Use `load()` instead of `preload()` if the path isn't known at compile-time. + var scene = preload("res://scene.tscn").instance() + # Add the node as a child of the node the script is attached to. + add_child(scene) + [/codeblock] [b]Example of saving a node with different owners:[/b] The following example creates 3 objects: [code]Node2D[/code] ([code]node[/code]), [code]RigidBody2D[/code] ([code]rigid[/code]) and [code]CollisionObject2D[/code] ([code]collision[/code]). [code]collision[/code] is a child of [code]rigid[/code] which is a child of [code]node[/code]. Only [code]rigid[/code] is owned by [code]node[/code] and [code]pack[/code] will therefore only save those two nodes, but not [code]collision[/code]. [codeblock] # Create the objects. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 6a7a6b84f6..c8427ac61f 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -802,10 +802,12 @@ <member name="logging/file_logging/enable_file_logging" type="bool" setter="" getter="" default="false"> If [code]true[/code], logs all output to files. </member> - <member name="logging/file_logging/log_path" type="String" setter="" getter="" default=""user://logs/log.txt""> + <member name="logging/file_logging/enable_file_logging.pc" type="bool" setter="" getter="" default="true"> + </member> + <member name="logging/file_logging/log_path" type="String" setter="" getter="" default=""user://logs/godot.log""> Path to logs within the project. Using an [code]user://[/code] path is recommended. </member> - <member name="logging/file_logging/max_log_files" type="int" setter="" getter="" default="10"> + <member name="logging/file_logging/max_log_files" type="int" setter="" getter="" default="5"> Specifies the maximum amount of log files allowed (used for rotation). </member> <member name="memory/limits/message_queue/max_size_kb" type="int" setter="" getter="" default="1024"> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index 5bc34772c8..0aa40dffb3 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -23,7 +23,8 @@ <argument index="0" name="subresources" type="bool" default="false"> </argument> <description> - Duplicates the resource, returning a new resource. By default, sub-resources are shared between resource copies for efficiency, this can be changed by passing [code]true[/code] to the [code]subresources[/code] argument. + Duplicates the resource, returning a new resource. By default, sub-resources are shared between resource copies for efficiency. This can be changed by passing [code]true[/code] to the [code]subresources[/code] argument which will copy the subresources. + [b]Note:[/b] If [code]subresources[/code] is [code]true[/code], this method will only perform a shallow copy. Nested resources within subresources will not be duplicated and will still be shared. </description> </method> <method name="get_local_scene" qualifiers="const"> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index c1e5987a06..5d684755fa 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -5,6 +5,7 @@ </brief_description> <description> Counts down a specified interval and emits a signal on reaching 0. Can be set to repeat or "one-shot" mode. + [b]Note:[/b] To create an one-shot timer without instantiating a node, use [method SceneTree.create_timer]. </description> <tutorials> </tutorials> diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index 8af58a7ed7..79924b849c 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -50,7 +50,7 @@ Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force f->close(); } const uint8_t *reader = file_buffer.ptr(); - return PNGDriverCommon::png_to_image(reader, buffer_size, p_image); + return PNGDriverCommon::png_to_image(reader, buffer_size, p_force_linear, p_image); } void ImageLoaderPNG::get_recognized_extensions(List<String> *p_extensions) const { @@ -61,7 +61,8 @@ Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) { Ref<Image> img; img.instance(); - Error err = PNGDriverCommon::png_to_image(p_png, p_size, img); + // the value of p_force_linear does not matter since it only applies to 16 bit + Error err = PNGDriverCommon::png_to_image(p_png, p_size, false, img); ERR_FAIL_COND_V(err, Ref<Image>()); return img; diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp index 77d5e68826..d3e187c501 100644 --- a/drivers/png/png_driver_common.cpp +++ b/drivers/png/png_driver_common.cpp @@ -58,7 +58,7 @@ static bool check_error(const png_image &image) { return false; } -Error png_to_image(const uint8_t *p_source, size_t p_size, Ref<Image> p_image) { +Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Ref<Image> p_image) { png_image png_img; zeromem(&png_img, sizeof(png_img)); png_img.version = PNG_IMAGE_VERSION; @@ -99,6 +99,11 @@ Error png_to_image(const uint8_t *p_source, size_t p_size, Ref<Image> p_image) { return ERR_UNAVAILABLE; } + if (!p_force_linear) { + // assume 16 bit pngs without sRGB or gAMA chunks are in sRGB format + png_img.flags |= PNG_IMAGE_FLAG_16BIT_sRGB; + } + const png_uint_32 stride = PNG_IMAGE_ROW_STRIDE(png_img); Vector<uint8_t> buffer; Error err = buffer.resize(PNG_IMAGE_BUFFER_SIZE(png_img, stride)); diff --git a/drivers/png/png_driver_common.h b/drivers/png/png_driver_common.h index 12129f034e..2099ddc536 100644 --- a/drivers/png/png_driver_common.h +++ b/drivers/png/png_driver_common.h @@ -36,7 +36,7 @@ namespace PNGDriverCommon { // Attempt to load png from buffer (p_source, p_size) into p_image -Error png_to_image(const uint8_t *p_source, size_t p_size, Ref<Image> p_image); +Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Ref<Image> p_image); // Append p_image, as a png, to p_buffer. // Contents of p_buffer is unspecified if error returned. diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index e367ed4989..a5edcf5c22 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -533,6 +533,7 @@ bool EditorFileSystem::_update_scan_actions() { if (_test_for_reimport(full_path, false)) { //must reimport reimports.push_back(full_path); + reimports.append_array(_get_dependencies(full_path)); } else { //must not reimport, all was good //update modified times, to avoid reimport diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 454170647f..fe68797483 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1241,7 +1241,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { // The 3D editor may be disabled as a feature, but scenes can still be opened. // This check prevents the preview from regenerating in case those scenes are then saved. Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile(); - if (!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) { + if (profile.is_valid() && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) { img = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_data(); } } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 66f0155c6a..8386d44e69 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -537,11 +537,14 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); if (current) { + Ref<Script> script = current->get_edited_resource(); if (p_save) { - _menu_option(FILE_SAVE); + // Do not try to save internal scripts + if (!(script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1)) { + _menu_option(FILE_SAVE); + } } - Ref<Script> script = current->get_edited_resource(); if (script != nullptr) { previous_scripts.push_back(script->get_path()); notify_script_close(script); diff --git a/main/main.cpp b/main/main.cpp index 7492d3d6c9..4fbbc21f41 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -955,8 +955,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #endif GLOBAL_DEF("logging/file_logging/enable_file_logging", false); - GLOBAL_DEF("logging/file_logging/log_path", "user://logs/log.txt"); - GLOBAL_DEF("logging/file_logging/max_log_files", 10); + // Only file logging by default on desktop platforms as logs can't be + // accessed easily on mobile/Web platforms (if at all). + // This also prevents logs from being created for the editor instance, as feature tags + // are disabled while in the editor (even if they should logically apply). + GLOBAL_DEF("logging/file_logging/enable_file_logging.pc", true); + GLOBAL_DEF("logging/file_logging/log_path", "user://logs/godot.log"); + GLOBAL_DEF("logging/file_logging/max_log_files", 5); ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers if (FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) { String base_path = GLOBAL_GET("logging/file_logging/log_path"); diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index bb1678a994..8f64c11867 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -1557,6 +1557,13 @@ void BulletPhysicsServer3D::sync() { } void BulletPhysicsServer3D::flush_queries() { + if (!active) { + return; + } + + for (int i = 0; i < active_spaces_count; ++i) { + active_spaces[i]->flush_queries(); + } } void BulletPhysicsServer3D::finish() { diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 8204d7ed20..70e137b16d 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -579,10 +579,6 @@ void SpaceBullet::remove_all_collision_objects() { } } -void onBulletPreTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep) { - static_cast<SpaceBullet *>(p_dynamicsWorld->getWorldUserInfo())->flush_queries(); -} - void onBulletTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep) { const btCollisionObjectArray &colObjArray = p_dynamicsWorld->getCollisionObjectArray(); @@ -650,7 +646,6 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) { dynamicsWorld->setWorldUserInfo(this); - dynamicsWorld->setInternalTickCallback(onBulletPreTickCallback, this, true); dynamicsWorld->setInternalTickCallback(onBulletTickCallback, this, false); dynamicsWorld->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(ghostPairCallback); // Setup ghost check dynamicsWorld->getPairCache()->setOverlapFilterCallback(godotFilterCallback); diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h new file mode 100644 index 0000000000..f988047483 --- /dev/null +++ b/platform/android/export/gradle_export_util.h @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* gradle_export_util.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 GODOT_GRADLE_EXPORT_UTIL_H +#define GODOT_GRADLE_EXPORT_UTIL_H + +#include "core/io/zip_io.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "editor/editor_export.h" + +// Utility method used to create a directory. +Error create_directory(const String &p_dir) { + if (!DirAccess::exists(p_dir)) { + DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); + Error err = filesystem_da->make_dir_recursive(p_dir); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); + memdelete(filesystem_da); + } + return OK; +} + +// Implementation of EditorExportSaveSharedObject. +// This method will only be called as an input to export_project_files. +// This method lets the .so files for all ABIs to be copied +// into the gradle project from the .AAR file +Error ignore_so_file(void *p_userdata, const SharedObject &p_so) { + return OK; +} + +// Writes p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + if (err != OK) { + return err; + } + FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + fa->store_buffer(p_data.ptr(), p_data.size()); + memdelete(fa); + return OK; +} + +// Writes string p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_string_at_path(const String &p_path, const String &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + if (err != OK) { + return err; + } + FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + fa->store_string(p_data); + memdelete(fa); + return OK; +} + +// Implementation of EditorExportSaveFunction. +// This method will only be called as an input to export_project_files. +// It is used by the export_project_files method to save all the asset files into the gradle project. +// It's functionality mirrors that of the method save_apk_file. +// This method will be called ONLY when custom build is enabled. +Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + String dst_path = p_path.replace_first("res://", "res://android/build/assets/"); + Error err = store_file_at_path(dst_path, p_data, Z_NO_COMPRESSION); + return err; +} + +#endif //GODOT_GRADLE_EXPORT_UTIL_H diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 40d988ff9f..bf69a8598d 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -84,7 +84,7 @@ void Path3D::_bind_methods() { ////////////// -void PathFollow3D::_update_transform() { +void PathFollow3D::_update_transform(bool p_update_xyz_rot) { if (!path) { return; } @@ -156,45 +156,47 @@ void PathFollow3D::_update_transform() { t.origin = pos; - Vector3 t_prev = (pos - c->interpolate_baked(offset - delta_offset, cubic)).normalized(); - Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized(); - - Vector3 axis = t_prev.cross(t_cur); - float dot = t_prev.dot(t_cur); - float angle = Math::acos(CLAMP(dot, -1, 1)); - - if (likely(!Math::is_zero_approx(angle))) { - if (rotation_mode == ROTATION_Y) { - // assuming we're referring to global Y-axis. is this correct? - axis.x = 0; - axis.z = 0; - } else if (rotation_mode == ROTATION_XY) { - axis.z = 0; - } else if (rotation_mode == ROTATION_XYZ) { - // all components are allowed - } + if (p_update_xyz_rot) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree + Vector3 t_prev = (pos - c->interpolate_baked(offset - delta_offset, cubic)).normalized(); + Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized(); + + Vector3 axis = t_prev.cross(t_cur); + float dot = t_prev.dot(t_cur); + float angle = Math::acos(CLAMP(dot, -1, 1)); + + if (likely(!Math::is_zero_approx(angle))) { + if (rotation_mode == ROTATION_Y) { + // assuming we're referring to global Y-axis. is this correct? + axis.x = 0; + axis.z = 0; + } else if (rotation_mode == ROTATION_XY) { + axis.z = 0; + } else if (rotation_mode == ROTATION_XYZ) { + // all components are allowed + } - if (likely(!Math::is_zero_approx(axis.length()))) { - t.rotate_basis(axis.normalized(), angle); + if (likely(!Math::is_zero_approx(axis.length()))) { + t.rotate_basis(axis.normalized(), angle); + } } - } - // do the additional tilting - float tilt_angle = c->interpolate_baked_tilt(offset); - Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? - - if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { - if (rotation_mode == ROTATION_Y) { - tilt_axis.x = 0; - tilt_axis.z = 0; - } else if (rotation_mode == ROTATION_XY) { - tilt_axis.z = 0; - } else if (rotation_mode == ROTATION_XYZ) { - // all components are allowed - } + // do the additional tilting + float tilt_angle = c->interpolate_baked_tilt(offset); + Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? + + if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { + if (rotation_mode == ROTATION_Y) { + tilt_axis.x = 0; + tilt_axis.z = 0; + } else if (rotation_mode == ROTATION_XY) { + tilt_axis.z = 0; + } else if (rotation_mode == ROTATION_XYZ) { + // all components are allowed + } - if (likely(!Math::is_zero_approx(tilt_axis.length()))) { - t.rotate_basis(tilt_axis.normalized(), tilt_angle); + if (likely(!Math::is_zero_approx(tilt_axis.length()))) { + t.rotate_basis(tilt_axis.normalized(), tilt_angle); + } } } @@ -213,7 +215,7 @@ void PathFollow3D::_notification(int p_what) { if (parent) { path = Object::cast_to<Path3D>(parent); if (path) { - _update_transform(); + _update_transform(false); } } diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 7f227a8a6f..9c50bd4906 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -75,7 +75,7 @@ private: bool loop; RotationMode rotation_mode; - void _update_transform(); + void _update_transform(bool p_update_xyz_rot = true); protected: virtual void _validate_property(PropertyInfo &property) const; |