summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/bind/core_bind.cpp21
-rw-r--r--core/io/logger.cpp2
-rw-r--r--doc/classes/Directory.xml4
-rw-r--r--doc/classes/HTTPRequest.xml10
-rw-r--r--doc/classes/PackedScene.xml7
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/Resource.xml3
-rw-r--r--doc/classes/Timer.xml1
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.cpp6
-rw-r--r--drivers/png/image_loader_png.cpp5
-rw-r--r--drivers/png/png_driver_common.cpp7
-rw-r--r--drivers/png/png_driver_common.h2
-rw-r--r--editor/editor_file_system.cpp1
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp7
-rw-r--r--main/main.cpp9
-rw-r--r--modules/bullet/bullet_physics_server.cpp7
-rw-r--r--modules/bullet/space_bullet.cpp5
-rw-r--r--platform/android/export/export.cpp288
-rw-r--r--platform/android/export/gradle_export_util.h101
-rw-r--r--platform/osx/display_server_osx.mm4
-rw-r--r--scene/3d/path_3d.cpp76
-rw-r--r--scene/3d/path_3d.h2
23 files changed, 364 insertions, 212 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="&quot;user://logs/log.txt&quot;">
+ <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="&quot;user://logs/godot.log&quot;">
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/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index 6eef04b87f..4dd66c06e6 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -2566,9 +2566,7 @@ Vector<uint8_t> RasterizerStorageGLES2::mesh_surface_get_array(RID p_mesh, int p
ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector<uint8_t>());
Surface *surface = mesh->surfaces[p_surface];
-#ifndef TOOLS_ENABLED
- ERR_PRINT("OpenGL ES 2.0 does not allow retrieving mesh array data");
-#endif
+
return surface->data;
}
@@ -2612,7 +2610,7 @@ Vector<Vector<uint8_t>> RasterizerStorageGLES2::mesh_surface_get_blend_shapes(RI
ERR_FAIL_COND_V(!mesh, Vector<Vector<uint8_t>>());
ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector<Vector<uint8_t>>());
#ifndef TOOLS_ENABLED
- ERR_PRINT("OpenGL ES 2.0 does not allow retrieving mesh array data");
+ ERR_PRINT("OpenGL ES 2.0 does not allow retrieving blend shape data");
#endif
return mesh->surfaces[p_surface]->blend_shape_data;
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/export.cpp b/platform/android/export/export.cpp
index 474458b00f..10fafd07e1 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -733,6 +733,39 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return OK;
}
+ void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
+ const char **aperms = android_perms;
+ while (*aperms) {
+ bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
+ if (enabled) {
+ r_permissions.push_back("android.permission." + String(*aperms));
+ }
+ aperms++;
+ }
+ PackedStringArray user_perms = p_preset->get("permissions/custom_permissions");
+ for (int i = 0; i < user_perms.size(); i++) {
+ String user_perm = user_perms[i].strip_edges();
+ if (!user_perm.empty()) {
+ r_permissions.push_back(user_perm);
+ }
+ }
+ if (p_give_internet) {
+ if (r_permissions.find("android.permission.INTERNET") == -1) {
+ r_permissions.push_back("android.permission.INTERNET");
+ }
+ }
+
+ int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ if (xr_mode_index == 1 /* XRMode.OVR */) {
+ int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
+ if (hand_tracking_index > 0) {
+ if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) {
+ r_permissions.push_back("com.oculus.permission.HAND_TRACKING");
+ }
+ }
+ }
+ }
+
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) {
// Leaving the unused types commented because looking these constants up
// again later would be annoying
@@ -777,30 +810,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
Vector<String> perms;
-
- const char **aperms = android_perms;
- while (*aperms) {
- bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
- if (enabled) {
- perms.push_back("android.permission." + String(*aperms));
- }
- aperms++;
- }
-
- PackedStringArray user_perms = p_preset->get("permissions/custom_permissions");
-
- for (int i = 0; i < user_perms.size(); i++) {
- String user_perm = user_perms[i].strip_edges();
- if (!user_perm.empty()) {
- perms.push_back(user_perm);
- }
- }
-
- if (p_give_internet) {
- if (perms.find("android.permission.INTERNET") == -1) {
- perms.push_back("android.permission.INTERNET");
- }
- }
+ // Write permissions into the perms variable.
+ _get_permissions(p_preset, p_give_internet, perms);
while (ofs < (uint32_t)p_manifest.size()) {
uint32_t chunk = decode_uint32(&p_manifest[ofs]);
@@ -971,10 +982,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
feature_names.push_back("oculus.software.handtracking");
feature_required_list.push_back(hand_tracking_index == 2);
feature_versions.push_back(-1); // no version attribute should be added.
-
- if (perms.find("com.oculus.permission.HAND_TRACKING") == -1) {
- perms.push_back("com.oculus.permission.HAND_TRACKING");
- }
}
}
@@ -1310,12 +1317,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return str;
}
}
- void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest) {
+
+ void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) {
const int UTF8_FLAG = 0x00000100;
- uint32_t string_block_len = decode_uint32(&p_manifest[16]);
- uint32_t string_count = decode_uint32(&p_manifest[20]);
- uint32_t string_flags = decode_uint32(&p_manifest[28]);
+ uint32_t string_block_len = decode_uint32(&r_manifest[16]);
+ uint32_t string_count = decode_uint32(&r_manifest[20]);
+ uint32_t string_flags = decode_uint32(&r_manifest[28]);
const uint32_t string_table_begins = 40;
Vector<String> string_table;
@@ -1323,10 +1331,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String package_name = p_preset->get("package/name");
for (uint32_t i = 0; i < string_count; i++) {
- uint32_t offset = decode_uint32(&p_manifest[string_table_begins + i * 4]);
+ uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]);
offset += string_table_begins + string_count * 4;
- String str = _parse_string(&p_manifest[offset], string_flags & UTF8_FLAG);
+ String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG);
if (str.begins_with("godot-project-name")) {
if (str == "godot-project-name") {
@@ -1352,7 +1360,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
ret.resize(string_table_begins + string_table.size() * 4);
for (uint32_t i = 0; i < string_table_begins; i++) {
- ret.write[i] = p_manifest[i];
+ ret.write[i] = r_manifest[i];
}
int ofs = 0;
@@ -1387,15 +1395,15 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
//append the rest...
int rest_from = 12 + string_block_len;
int rest_to = ret.size();
- int rest_len = (p_manifest.size() - rest_from);
- ret.resize(ret.size() + (p_manifest.size() - rest_from));
+ int rest_len = (r_manifest.size() - rest_from);
+ ret.resize(ret.size() + (r_manifest.size() - rest_from));
for (int i = 0; i < rest_len; i++) {
- ret.write[rest_to + i] = p_manifest[rest_from + i];
+ ret.write[rest_to + i] = r_manifest[rest_from + i];
}
//finally update the size
encode_uint32(ret.size(), &ret.write[4]);
- p_manifest = ret;
+ r_manifest = ret;
//printf("end\n");
}
@@ -1915,6 +1923,79 @@ public:
return have_plugins_changed || first_build;
}
+ Error get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) {
+ String cmdline = p_preset->get("command_line/extra_args");
+ Vector<String> command_line_strings = cmdline.strip_edges().split(" ");
+ for (int i = 0; i < command_line_strings.size(); i++) {
+ if (command_line_strings[i].strip_edges().length() == 0) {
+ command_line_strings.remove(i);
+ i--;
+ }
+ }
+
+ gen_export_flags(command_line_strings, p_flags);
+
+ bool apk_expansion = p_preset->get("apk_expansion/enable");
+ if (apk_expansion) {
+ int version_code = p_preset->get("version/code");
+ String package_name = p_preset->get("package/unique_name");
+ String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb";
+ String fullpath = p_path.get_base_dir().plus_file(apk_file_name);
+ String apk_expansion_public_key = p_preset->get("apk_expansion/public_key");
+ Error err = save_pack(p_preset, fullpath);
+
+ if (err != OK) {
+ EditorNode::add_io_error("Could not write expansion package file: " + apk_file_name);
+ return err;
+ }
+
+ command_line_strings.push_back("--use_apk_expansion");
+ command_line_strings.push_back("--apk_expansion_md5");
+ command_line_strings.push_back(FileAccess::get_md5(fullpath));
+ command_line_strings.push_back("--apk_expansion_key");
+ command_line_strings.push_back(apk_expansion_public_key.strip_edges());
+ }
+
+ int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ if (xr_mode_index == 1) {
+ command_line_strings.push_back("--xr_mode_ovr");
+ } else { // XRMode.REGULAR is the default.
+ command_line_strings.push_back("--xr_mode_regular");
+ }
+
+ bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer");
+ if (use_32_bit_framebuffer) {
+ command_line_strings.push_back("--use_depth_32");
+ }
+
+ bool immersive = p_preset->get("screen/immersive_mode");
+ if (immersive) {
+ command_line_strings.push_back("--use_immersive");
+ }
+
+ bool debug_opengl = p_preset->get("screen/opengl_debug");
+ if (debug_opengl) {
+ command_line_strings.push_back("--debug_opengl");
+ }
+
+ if (command_line_strings.size()) {
+ r_command_line_flags.resize(4);
+ encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]);
+ for (int i = 0; i < command_line_strings.size(); i++) {
+ print_line(itos(i) + " param: " + command_line_strings[i]);
+ CharString command_line_argument = command_line_strings[i].utf8();
+ int base = r_command_line_flags.size();
+ int length = command_line_argument.length();
+ if (length == 0)
+ continue;
+ r_command_line_flags.resize(base + 4 + length);
+ encode_uint32(length, &r_command_line_flags.write[base]);
+ copymem(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length);
+ }
+ }
+ return OK;
+ }
+
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
@@ -2051,20 +2132,13 @@ public:
zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
- bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer");
- bool immersive = p_preset->get("screen/immersive_mode");
- bool debug_opengl = p_preset->get("screen/opengl_debug");
-
bool _signed = p_preset->get("package/signed");
-
- bool apk_expansion = p_preset->get("apk_expansion/enable");
-
String cmdline = p_preset->get("command_line/extra_args");
- int version_code = p_preset->get("version/code");
String version_name = p_preset->get("version/name");
String package_name = p_preset->get("package/unique_name");
+ bool apk_expansion = p_preset->get("apk_expansion/enable");
String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
String release_keystore = p_preset->get("keystore/release");
@@ -2198,106 +2272,42 @@ public:
CLEANUP_AND_RETURN(ERR_SKIP);
}
Error err = OK;
- Vector<String> cl = cmdline.strip_edges().split(" ");
- for (int i = 0; i < cl.size(); i++) {
- if (cl[i].strip_edges().length() == 0) {
- cl.remove(i);
- i--;
- }
- }
-
- gen_export_flags(cl, p_flags);
if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;
err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so);
- } else {
- //all files
-
- if (apk_expansion) {
- String apkfname = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb";
- String fullpath = p_path.get_base_dir().plus_file(apkfname);
- err = save_pack(p_preset, fullpath);
-
- if (err != OK) {
- unzClose(pkg);
- EditorNode::add_io_error("Could not write expansion package file: " + apkfname);
-
- CLEANUP_AND_RETURN(ERR_SKIP);
- }
-
- cl.push_back("--use_apk_expansion");
- cl.push_back("--apk_expansion_md5");
- cl.push_back(FileAccess::get_md5(fullpath));
- cl.push_back("--apk_expansion_key");
- cl.push_back(apk_expansion_pkey.strip_edges());
-
- } else {
- APKExportData ed;
- ed.ep = &ep;
- ed.apk = unaligned_apk;
-
- err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
- }
- }
-
- int xr_mode_index = p_preset->get("xr_features/xr_mode");
- if (xr_mode_index == 1 /* XRMode.OVR */) {
- cl.push_back("--xr_mode_ovr");
- } else {
- // XRMode.REGULAR is the default.
- cl.push_back("--xr_mode_regular");
+ } else if (!apk_expansion) {
+ APKExportData ed;
+ ed.ep = &ep;
+ ed.apk = unaligned_apk;
+ err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
}
- if (use_32_fb) {
- cl.push_back("--use_depth_32");
+ if (err != OK) {
+ unzClose(pkg);
+ EditorNode::add_io_error("Could not export project files");
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
- if (immersive) {
- cl.push_back("--use_immersive");
- }
-
- if (debug_opengl) {
- cl.push_back("--debug_opengl");
- }
-
- if (cl.size()) {
- //add comandline
- Vector<uint8_t> clf;
- clf.resize(4);
- encode_uint32(cl.size(), &clf.write[0]);
- for (int i = 0; i < cl.size(); i++) {
- print_line(itos(i) + " param: " + cl[i]);
- CharString txt = cl[i].utf8();
- int base = clf.size();
- int length = txt.length();
- if (!length) {
- continue;
- }
- clf.resize(base + 4 + length);
- encode_uint32(length, &clf.write[base]);
- copymem(&clf.write[base + 4], txt.ptr(), length);
- }
-
- zip_fileinfo zipfi = get_zip_fileinfo();
-
- zipOpenNewFileInZip(unaligned_apk,
- "assets/_cl_",
- &zipfi,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- 0, // No compress (little size gain and potentially slower startup)
- Z_DEFAULT_COMPRESSION);
-
- zipWriteInFileInZip(unaligned_apk, clf.ptr(), clf.size());
- zipCloseFileInZip(unaligned_apk);
- }
+ Vector<uint8_t> command_line_flags;
+ // Write command line flags into the command_line_flags variable.
+ err = get_command_line_flags(p_preset, p_path, p_flags, command_line_flags);
+ zip_fileinfo zipfi = get_zip_fileinfo();
+ zipOpenNewFileInZip(unaligned_apk,
+ "assets/_cl_",
+ &zipfi,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ 0, // No compress (little size gain and potentially slower startup)
+ Z_DEFAULT_COMPRESSION);
+ zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size());
+ zipCloseFileInZip(unaligned_apk);
zipClose(unaligned_apk, nullptr);
unzClose(pkg);
@@ -2442,12 +2452,10 @@ public:
memset(extra + info.size_file_extra, 0, padding);
- // write
- zip_fileinfo zipfi = get_zip_fileinfo();
-
+ zip_fileinfo fileinfo = get_zip_fileinfo();
zipOpenNewFileInZip2(final_apk,
file.utf8().get_data(),
- &zipfi,
+ &fileinfo,
extra,
info.size_file_extra + padding,
nullptr,
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/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index b7b750a975..07ecd5d2c6 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -2465,7 +2465,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent
wd_window.transient_parent = INVALID_WINDOW_ID;
wd_parent.transient_children.erase(p_window);
- [wd_window.window_object setParentWindow:nil];
+ [wd_parent.window_object removeChildWindow:wd_window.window_object];
} else {
ERR_FAIL_COND(!windows.has(p_parent));
ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
@@ -2474,7 +2474,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent
wd_window.transient_parent = p_parent;
wd_parent.transient_children.insert(p_window);
- [wd_window.window_object setParentWindow:wd_parent.window_object];
+ [wd_parent.window_object addChildWindow:wd_window.window_object ordered:NSWindowAbove];
}
}
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;