Merge pull request #52638 from akien-mga/3.x-cherrypicks
This commit is contained in:
commit
47d56ace79
|
@ -184,7 +184,7 @@ Error ConfigFile::_internal_save(FileAccess *file) {
|
|||
for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) {
|
||||
String vstr;
|
||||
VariantWriter::write_to_string(F.get(), vstr);
|
||||
file->store_string(F.key() + "=" + vstr + "\n");
|
||||
file->store_string(F.key().property_name_encode() + "=" + vstr + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
|
|||
conn_port = p_port;
|
||||
conn_host = p_host;
|
||||
|
||||
ip_candidates.clear();
|
||||
|
||||
ssl = p_ssl;
|
||||
ssl_verify_host = p_verify_host;
|
||||
|
||||
|
@ -306,6 +308,7 @@ void HTTPClient::close() {
|
|||
resolving = IP::RESOLVER_INVALID_ID;
|
||||
}
|
||||
|
||||
ip_candidates.clear();
|
||||
response_headers.clear();
|
||||
response_str.clear();
|
||||
body_size = -1;
|
||||
|
@ -328,10 +331,17 @@ Error HTTPClient::poll() {
|
|||
return OK; // Still resolving
|
||||
|
||||
case IP::RESOLVER_STATUS_DONE: {
|
||||
IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving);
|
||||
Error err = tcp_connection->connect_to_host(host, conn_port);
|
||||
ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving);
|
||||
IP::get_singleton()->erase_resolve_item(resolving);
|
||||
resolving = IP::RESOLVER_INVALID_ID;
|
||||
|
||||
Error err = ERR_BUG; // Should be at least one entry.
|
||||
while (ip_candidates.size() > 0) {
|
||||
err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port);
|
||||
if (err == OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
status = STATUS_CANT_CONNECT;
|
||||
return err;
|
||||
|
@ -385,6 +395,7 @@ Error HTTPClient::poll() {
|
|||
if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
|
||||
// Handshake has been successful
|
||||
handshaking = false;
|
||||
ip_candidates.clear();
|
||||
status = STATUS_CONNECTED;
|
||||
return OK;
|
||||
} else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
|
||||
|
@ -395,15 +406,24 @@ Error HTTPClient::poll() {
|
|||
}
|
||||
// ... we will need to poll more for handshake to finish
|
||||
} else {
|
||||
ip_candidates.clear();
|
||||
status = STATUS_CONNECTED;
|
||||
}
|
||||
return OK;
|
||||
} break;
|
||||
case StreamPeerTCP::STATUS_ERROR:
|
||||
case StreamPeerTCP::STATUS_NONE: {
|
||||
Error err = ERR_CANT_CONNECT;
|
||||
while (ip_candidates.size() > 0) {
|
||||
tcp_connection->disconnect_from_host();
|
||||
err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port);
|
||||
if (err == OK) {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
close();
|
||||
status = STATUS_CANT_CONNECT;
|
||||
return ERR_CANT_CONNECT;
|
||||
return err;
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
|
|
|
@ -159,6 +159,7 @@ private:
|
|||
#ifndef JAVASCRIPT_ENABLED
|
||||
Status status;
|
||||
IP::ResolverID resolving;
|
||||
Array ip_candidates;
|
||||
int conn_port;
|
||||
String conn_host;
|
||||
bool ssl;
|
||||
|
|
|
@ -169,7 +169,7 @@ public:
|
|||
push_back(p_val);
|
||||
} else {
|
||||
resize(count + 1);
|
||||
for (U i = count; i > p_pos; i--) {
|
||||
for (U i = count - 1; i > p_pos; i--) {
|
||||
data[i] = data[i - 1];
|
||||
}
|
||||
data[p_pos] = p_val;
|
||||
|
|
|
@ -285,7 +285,7 @@
|
|||
<theme_item name="more" data_type="icon" type="Texture">
|
||||
The icon for the zoom in button.
|
||||
</theme_item>
|
||||
<theme_item name="port_grab_distance_horizontal" data_type="constant" type="int" default="48">
|
||||
<theme_item name="port_grab_distance_horizontal" data_type="constant" type="int" default="24">
|
||||
The horizontal range within which a port can be grabbed (on both sides).
|
||||
</theme_item>
|
||||
<theme_item name="port_grab_distance_vertical" data_type="constant" type="int" default="6">
|
||||
|
|
|
@ -163,6 +163,7 @@
|
|||
<argument index="0" name="renormalize" type="bool" default="false" />
|
||||
<description>
|
||||
Generates mipmaps for the image. Mipmaps are precalculated lower-resolution copies of the image that are automatically used if the image needs to be scaled down when rendered. They help improve image quality and performance when rendering. This method returns an error if the image is compressed, in a custom format, or if the image's width/height is [code]0[/code].
|
||||
[b]Note:[/b] Mipmap generation is done on the CPU, is single-threaded and is [i]always[/i] done on the main thread. This means generating mipmaps will result in noticeable stuttering during gameplay, even if [method generate_mipmaps] is called from a [Thread].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_data" qualifiers="const">
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
<members>
|
||||
<member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased" default="false">
|
||||
If [code]true[/code], the line's border will be anti-aliased.
|
||||
[b]Note:[/b] Line2D is not accelerated by batching when being anti-aliased.
|
||||
</member>
|
||||
<member name="begin_cap_mode" type="int" setter="set_begin_cap_mode" getter="get_begin_cap_mode" enum="Line2D.LineCapMode" default="0">
|
||||
Controls the style of the line's first point. Use [enum LineCapMode] constants.
|
||||
|
|
|
@ -45,6 +45,13 @@
|
|||
Returns the item's mesh.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_item_mesh_transform" qualifiers="const">
|
||||
<return type="Transform" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
<description>
|
||||
Returns the transform applied to the item's mesh.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_item_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
|
@ -102,6 +109,14 @@
|
|||
Sets the item's mesh.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_item_mesh_transform">
|
||||
<return type="void" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
<argument index="1" name="mesh_transform" type="Transform" />
|
||||
<description>
|
||||
Sets the transform to apply to the item's mesh.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_item_name">
|
||||
<return type="void" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
|
|
|
@ -1727,7 +1727,7 @@ void EditorNode::_dialog_action(String p_file) {
|
|||
ml = Ref<MeshLibrary>(memnew(MeshLibrary));
|
||||
}
|
||||
|
||||
MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true);
|
||||
MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true, file_export_lib_apply_xforms->is_pressed());
|
||||
|
||||
Error err = ResourceSaver::save(p_file, ml);
|
||||
if (err) {
|
||||
|
@ -6751,6 +6751,10 @@ EditorNode::EditorNode() {
|
|||
file_export_lib_merge->set_text(TTR("Merge With Existing"));
|
||||
file_export_lib_merge->set_pressed(true);
|
||||
file_export_lib->get_vbox()->add_child(file_export_lib_merge);
|
||||
file_export_lib_apply_xforms = memnew(CheckBox);
|
||||
file_export_lib_apply_xforms->set_text(TTR("Apply MeshInstance Transforms"));
|
||||
file_export_lib_apply_xforms->set_pressed(false);
|
||||
file_export_lib->get_vbox()->add_child(file_export_lib_apply_xforms);
|
||||
gui_base->add_child(file_export_lib);
|
||||
|
||||
file_script = memnew(EditorFileDialog);
|
||||
|
|
|
@ -335,6 +335,7 @@ private:
|
|||
EditorFileDialog *file_script;
|
||||
EditorFileDialog *file_android_build_source;
|
||||
CheckBox *file_export_lib_merge;
|
||||
CheckBox *file_export_lib_apply_xforms;
|
||||
String current_path;
|
||||
MenuButton *update_spinner;
|
||||
|
||||
|
|
|
@ -47,23 +47,25 @@ void MeshLibraryEditor::edit(const Ref<MeshLibrary> &p_mesh_library) {
|
|||
}
|
||||
}
|
||||
|
||||
void MeshLibraryEditor::_menu_confirm() {
|
||||
void MeshLibraryEditor::_menu_remove_confirm() {
|
||||
switch (option) {
|
||||
case MENU_OPTION_REMOVE_ITEM: {
|
||||
mesh_library->remove_item(to_erase);
|
||||
} break;
|
||||
case MENU_OPTION_UPDATE_FROM_SCENE: {
|
||||
String existing = mesh_library->get_meta("_editor_source_scene");
|
||||
ERR_FAIL_COND(existing == "");
|
||||
_import_scene_cbk(existing);
|
||||
|
||||
} break;
|
||||
default: {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge) {
|
||||
void MeshLibraryEditor::_menu_update_confirm(bool p_apply_xforms) {
|
||||
cd_update->hide();
|
||||
apply_xforms = p_apply_xforms;
|
||||
String existing = mesh_library->get_meta("_editor_source_scene");
|
||||
ERR_FAIL_COND(existing == "");
|
||||
_import_scene_cbk(existing);
|
||||
}
|
||||
|
||||
void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms) {
|
||||
if (!p_merge) {
|
||||
p_library->clear();
|
||||
}
|
||||
|
@ -108,6 +110,13 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
|
|||
}
|
||||
|
||||
p_library->set_item_mesh(id, mesh);
|
||||
|
||||
if (p_apply_xforms) {
|
||||
p_library->set_item_mesh_transform(id, mi->get_transform());
|
||||
} else {
|
||||
p_library->set_item_mesh_transform(id, Transform());
|
||||
}
|
||||
|
||||
mesh_instances[id] = mi;
|
||||
|
||||
Vector<MeshLibrary::ShapeData> collisions;
|
||||
|
@ -197,15 +206,16 @@ void MeshLibraryEditor::_import_scene_cbk(const String &p_str) {
|
|||
|
||||
ERR_FAIL_COND_MSG(!scene, "Cannot create an instance from PackedScene '" + p_str + "'.");
|
||||
|
||||
_import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE);
|
||||
_import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE, apply_xforms);
|
||||
|
||||
memdelete(scene);
|
||||
mesh_library->set_meta("_editor_source_scene", p_str);
|
||||
|
||||
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), false);
|
||||
}
|
||||
|
||||
Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge) {
|
||||
_import_scene(p_base_scene, ml, p_merge);
|
||||
Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge, bool p_apply_xforms) {
|
||||
_import_scene(p_base_scene, ml, p_merge, p_apply_xforms);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -219,23 +229,29 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
|
|||
String p = editor->get_inspector()->get_selected_path();
|
||||
if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
|
||||
to_erase = p.get_slice("/", 3).to_int();
|
||||
cd->set_text(vformat(TTR("Remove item %d?"), to_erase));
|
||||
cd->popup_centered(Size2(300, 60));
|
||||
cd_remove->set_text(vformat(TTR("Remove item %d?"), to_erase));
|
||||
cd_remove->popup_centered(Size2(300, 60));
|
||||
}
|
||||
} break;
|
||||
case MENU_OPTION_IMPORT_FROM_SCENE: {
|
||||
apply_xforms = false;
|
||||
file->popup_centered_ratio();
|
||||
} break;
|
||||
case MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS: {
|
||||
apply_xforms = true;
|
||||
file->popup_centered_ratio();
|
||||
} break;
|
||||
case MENU_OPTION_UPDATE_FROM_SCENE: {
|
||||
cd->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
|
||||
cd->popup_centered(Size2(500, 60));
|
||||
cd_update->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
|
||||
cd_update->popup_centered(Size2(500, 60));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void MeshLibraryEditor::_bind_methods() {
|
||||
ClassDB::bind_method("_menu_cbk", &MeshLibraryEditor::_menu_cbk);
|
||||
ClassDB::bind_method("_menu_confirm", &MeshLibraryEditor::_menu_confirm);
|
||||
ClassDB::bind_method("_menu_remove_confirm", &MeshLibraryEditor::_menu_remove_confirm);
|
||||
ClassDB::bind_method("_menu_update_confirm", &MeshLibraryEditor::_menu_update_confirm);
|
||||
ClassDB::bind_method("_import_scene_cbk", &MeshLibraryEditor::_import_scene_cbk);
|
||||
}
|
||||
|
||||
|
@ -261,16 +277,22 @@ MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) {
|
|||
menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
|
||||
menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM);
|
||||
menu->get_popup()->add_separator();
|
||||
menu->get_popup()->add_item(TTR("Import from Scene"), MENU_OPTION_IMPORT_FROM_SCENE);
|
||||
menu->get_popup()->add_item(TTR("Import from Scene (Ignore Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE);
|
||||
menu->get_popup()->add_item(TTR("Import from Scene (Apply Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS);
|
||||
menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE);
|
||||
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true);
|
||||
menu->get_popup()->connect("id_pressed", this, "_menu_cbk");
|
||||
menu->hide();
|
||||
|
||||
editor = p_editor;
|
||||
cd = memnew(ConfirmationDialog);
|
||||
add_child(cd);
|
||||
cd->get_ok()->connect("pressed", this, "_menu_confirm");
|
||||
cd_remove = memnew(ConfirmationDialog);
|
||||
add_child(cd_remove);
|
||||
cd_remove->get_ok()->connect("pressed", this, "_menu_remove_confirm");
|
||||
cd_update = memnew(ConfirmationDialog);
|
||||
add_child(cd_update);
|
||||
cd_update->get_ok()->set_text("Apply without Transforms");
|
||||
cd_update->get_ok()->connect("pressed", this, "_menu_update_confirm", varray(false));
|
||||
cd_update->add_button("Apply with Transforms")->connect("pressed", this, "_menu_update_confirm", varray(true));
|
||||
}
|
||||
|
||||
void MeshLibraryEditorPlugin::edit(Object *p_node) {
|
||||
|
|
|
@ -41,8 +41,10 @@ class MeshLibraryEditor : public Control {
|
|||
|
||||
EditorNode *editor;
|
||||
MenuButton *menu;
|
||||
ConfirmationDialog *cd;
|
||||
ConfirmationDialog *cd_remove;
|
||||
ConfirmationDialog *cd_update;
|
||||
EditorFileDialog *file;
|
||||
bool apply_xforms;
|
||||
int to_erase;
|
||||
|
||||
enum {
|
||||
|
@ -50,15 +52,17 @@ class MeshLibraryEditor : public Control {
|
|||
MENU_OPTION_ADD_ITEM,
|
||||
MENU_OPTION_REMOVE_ITEM,
|
||||
MENU_OPTION_UPDATE_FROM_SCENE,
|
||||
MENU_OPTION_IMPORT_FROM_SCENE
|
||||
MENU_OPTION_IMPORT_FROM_SCENE,
|
||||
MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS
|
||||
};
|
||||
|
||||
int option;
|
||||
void _import_scene_cbk(const String &p_str);
|
||||
void _menu_cbk(int p_option);
|
||||
void _menu_confirm();
|
||||
void _menu_remove_confirm();
|
||||
void _menu_update_confirm(bool p_apply_xforms);
|
||||
|
||||
static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge);
|
||||
static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -67,7 +71,7 @@ public:
|
|||
MenuButton *get_menu_button() const { return menu; }
|
||||
|
||||
void edit(const Ref<MeshLibrary> &p_mesh_library);
|
||||
static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true);
|
||||
static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true, bool p_apply_xforms = false);
|
||||
|
||||
MeshLibraryEditor(EditorNode *p_editor);
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ PY_FILES=$(find \( -path "./.git" \
|
|||
\) -print)
|
||||
black -l 120 $PY_FILES
|
||||
|
||||
git diff > patch.patch
|
||||
git diff --color > patch.patch
|
||||
|
||||
# If no patch has been generated all is OK, clean up, and exit.
|
||||
if [ ! -s patch.patch ] ; then
|
||||
|
|
|
@ -40,7 +40,7 @@ while IFS= read -rd '' f; do
|
|||
done
|
||||
done
|
||||
|
||||
git diff > patch.patch
|
||||
git diff --color > patch.patch
|
||||
|
||||
# If no patch has been generated all is OK, clean up, and exit.
|
||||
if [ ! -s patch.patch ] ; then
|
||||
|
|
|
@ -44,7 +44,7 @@ while IFS= read -rd '' f; do
|
|||
perl -i -pe 's/\x20== true//g' "$f"
|
||||
done
|
||||
|
||||
git diff > patch.patch
|
||||
git diff --color > patch.patch
|
||||
|
||||
# If no patch has been generated all is OK, clean up, and exit.
|
||||
if [ ! -s patch.patch ] ; then
|
||||
|
|
|
@ -460,7 +460,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
|
|||
}
|
||||
|
||||
Pair<Transform, IndexKey> p;
|
||||
p.first = xform;
|
||||
p.first = xform * mesh_library->get_item_mesh_transform(c.item);
|
||||
p.second = E->get();
|
||||
multimesh_items[c.item].push_back(p);
|
||||
}
|
||||
|
|
|
@ -261,6 +261,12 @@ void GridMapEditor::_update_cursor_transform() {
|
|||
cursor_transform.basis *= node->get_cell_scale();
|
||||
cursor_transform = node->get_global_transform() * cursor_transform;
|
||||
|
||||
if (selected_palette >= 0) {
|
||||
if (node && !node->get_mesh_library().is_null()) {
|
||||
cursor_transform *= node->get_mesh_library()->get_item_mesh_transform(selected_palette);
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor_instance.is_valid()) {
|
||||
VisualServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform);
|
||||
VisualServer::get_singleton()->instance_set_visible(cursor_instance, cursor_visible);
|
||||
|
|
|
@ -1630,6 +1630,28 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *
|
|||
return Variant::NIL;
|
||||
}
|
||||
|
||||
void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
|
||||
if (!script->is_valid() || !script->script_class)
|
||||
return;
|
||||
|
||||
GD_MONO_SCOPE_THREAD_ATTACH;
|
||||
|
||||
// TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
|
||||
GDMonoClass *top = script->script_class;
|
||||
|
||||
while (top && top != script->native) {
|
||||
const Vector<GDMonoMethod *> &methods = top->get_all_methods();
|
||||
for (int i = 0; i < methods.size(); ++i) {
|
||||
MethodInfo minfo = methods[i]->get_method_info();
|
||||
if (minfo.name != CACHED_STRING_NAME(dotctor)) {
|
||||
p_list->push_back(minfo);
|
||||
}
|
||||
}
|
||||
|
||||
top = top->get_parent_class();
|
||||
}
|
||||
}
|
||||
|
||||
bool CSharpInstance::has_method(const StringName &p_method) const {
|
||||
if (!script.is_valid())
|
||||
return false;
|
||||
|
@ -3034,10 +3056,19 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
|
|||
|
||||
GD_MONO_SCOPE_THREAD_ATTACH;
|
||||
|
||||
// TODO: Filter out things unsuitable for explicit calls, like constructors.
|
||||
const Vector<GDMonoMethod *> &methods = script_class->get_all_methods();
|
||||
for (int i = 0; i < methods.size(); ++i) {
|
||||
p_list->push_back(methods[i]->get_method_info());
|
||||
// TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
|
||||
GDMonoClass *top = script_class;
|
||||
|
||||
while (top && top != native) {
|
||||
const Vector<GDMonoMethod *> &methods = top->get_all_methods();
|
||||
for (int i = 0; i < methods.size(); ++i) {
|
||||
MethodInfo minfo = methods[i]->get_method_info();
|
||||
if (minfo.name != CACHED_STRING_NAME(dotctor)) {
|
||||
p_list->push_back(methods[i]->get_method_info());
|
||||
}
|
||||
}
|
||||
|
||||
top = top->get_parent_class();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ public:
|
|||
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
|
||||
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const;
|
||||
|
||||
/* TODO */ virtual void get_method_list(List<MethodInfo> *p_list) const {}
|
||||
virtual void get_method_list(List<MethodInfo> *p_list) const;
|
||||
virtual bool has_method(const StringName &p_method) const;
|
||||
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
|
||||
|
|
|
@ -1126,6 +1126,17 @@ namespace Godot
|
|||
return 2.0f * inter / sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a simplified canonical path.
|
||||
/// </summary>
|
||||
public static string SimplifyPath(this string instance)
|
||||
{
|
||||
return godot_icall_String_simplify_path(instance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static string godot_icall_String_simplify_path(string str);
|
||||
|
||||
/// <summary>
|
||||
/// Split the string by a divisor string, return an array of the substrings.
|
||||
/// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
|
||||
|
|
|
@ -67,6 +67,11 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
|
|||
return GDMonoMarshal::mono_string_from_godot(ret);
|
||||
}
|
||||
|
||||
MonoString *godot_icall_String_simplify_path(MonoString *p_str) {
|
||||
String ret = GDMonoMarshal::mono_string_to_godot(p_str).simplify_path();
|
||||
return GDMonoMarshal::mono_string_from_godot(ret);
|
||||
}
|
||||
|
||||
void godot_register_string_icalls() {
|
||||
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer);
|
||||
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text);
|
||||
|
@ -74,6 +79,7 @@ void godot_register_string_icalls() {
|
|||
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn);
|
||||
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer);
|
||||
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text);
|
||||
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path);
|
||||
}
|
||||
|
||||
#endif // MONO_GLUE_ENABLED
|
||||
|
|
|
@ -161,22 +161,28 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
|
|||
ERR_FAIL_COND_V(p_path.empty(), ERR_INVALID_PARAMETER);
|
||||
|
||||
_peer = Ref<WSLPeer>(memnew(WSLPeer));
|
||||
IP_Address addr;
|
||||
|
||||
if (!p_host.is_valid_ip_address()) {
|
||||
addr = IP::get_singleton()->resolve_hostname(p_host);
|
||||
if (p_host.is_valid_ip_address()) {
|
||||
ip_candidates.clear();
|
||||
ip_candidates.push_back(IP_Address(p_host));
|
||||
} else {
|
||||
addr = p_host;
|
||||
ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(ip_candidates.empty(), ERR_INVALID_PARAMETER);
|
||||
|
||||
String port = "";
|
||||
if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
|
||||
port = ":" + itos(p_port);
|
||||
}
|
||||
|
||||
Error err = _tcp->connect_to_host(addr, p_port);
|
||||
Error err = ERR_BUG; // Should be at least one entry.
|
||||
while (ip_candidates.size() > 0) {
|
||||
err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port);
|
||||
if (err == OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (err != OK) {
|
||||
_tcp->disconnect_from_host();
|
||||
_on_error();
|
||||
|
@ -185,6 +191,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
|
|||
_connection = _tcp;
|
||||
_use_ssl = p_ssl;
|
||||
_host = p_host;
|
||||
_port = p_port;
|
||||
// Strip edges from protocols.
|
||||
_protocols.resize(p_protocols.size());
|
||||
String *pw = _protocols.ptrw();
|
||||
|
@ -244,6 +251,7 @@ void WSLClient::poll() {
|
|||
_on_error();
|
||||
break;
|
||||
case StreamPeerTCP::STATUS_CONNECTED: {
|
||||
ip_candidates.clear();
|
||||
Ref<StreamPeerSSL> ssl;
|
||||
if (_use_ssl) {
|
||||
if (_connection == _tcp) {
|
||||
|
@ -274,6 +282,12 @@ void WSLClient::poll() {
|
|||
_do_handshake();
|
||||
} break;
|
||||
case StreamPeerTCP::STATUS_ERROR:
|
||||
while (ip_candidates.size() > 0) {
|
||||
_tcp->disconnect_from_host();
|
||||
if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disconnect_from_host();
|
||||
_on_error();
|
||||
break;
|
||||
|
@ -315,6 +329,8 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
|
|||
|
||||
memset(_resp_buf, 0, sizeof(_resp_buf));
|
||||
_resp_pos = 0;
|
||||
|
||||
ip_candidates.clear();
|
||||
}
|
||||
|
||||
IP_Address WSLClient::get_connected_host() const {
|
||||
|
|
|
@ -63,6 +63,8 @@ private:
|
|||
|
||||
String _key;
|
||||
String _host;
|
||||
int _port;
|
||||
Array ip_candidates;
|
||||
Vector<String> _protocols;
|
||||
bool _use_ssl;
|
||||
|
||||
|
|
|
@ -419,7 +419,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
|
|||
@SuppressLint("MissingPermission")
|
||||
@Keep
|
||||
private void vibrate(int durationMs) {
|
||||
if (requestPermission("VIBRATE")) {
|
||||
if (durationMs > 0 && requestPermission("VIBRATE")) {
|
||||
Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (v != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/version.h"
|
||||
#include "core/version_hash.gen.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -85,11 +87,18 @@ static void handle_crash(int sig) {
|
|||
}
|
||||
|
||||
// Dump the backtrace to stderr with a message to the user
|
||||
fprintf(stderr, "\n================================================================\n");
|
||||
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
|
||||
|
||||
if (OS::get_singleton()->get_main_loop())
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
|
||||
|
||||
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
|
||||
if (String(VERSION_HASH).length() != 0) {
|
||||
fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
|
||||
} else {
|
||||
fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
|
||||
}
|
||||
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
|
||||
char **strings = backtrace_symbols(bt_buffer, size);
|
||||
if (strings) {
|
||||
|
@ -148,6 +157,7 @@ static void handle_crash(int sig) {
|
|||
free(strings);
|
||||
}
|
||||
fprintf(stderr, "-- END OF BACKTRACE --\n");
|
||||
fprintf(stderr, "================================================================\n");
|
||||
|
||||
// Abort to pass the error to the OS
|
||||
abort();
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/version.h"
|
||||
#include "core/version_hash.gen.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#ifdef CRASH_HANDLER_EXCEPTION
|
||||
|
@ -127,6 +129,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
|
|||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n================================================================\n");
|
||||
fprintf(stderr, "%s: Program crashed\n", __FUNCTION__);
|
||||
|
||||
if (OS::get_singleton()->get_main_loop())
|
||||
|
@ -175,6 +178,12 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
|
|||
msg = proj_settings->get("debug/settings/crash_handler/message");
|
||||
}
|
||||
|
||||
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
|
||||
if (String(VERSION_HASH).length() != 0) {
|
||||
fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
|
||||
} else {
|
||||
fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
|
||||
}
|
||||
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
|
||||
|
||||
int n = 0;
|
||||
|
@ -200,6 +209,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
|
|||
} while (frame.AddrReturn.Offset != 0 && n < 256);
|
||||
|
||||
fprintf(stderr, "-- END OF BACKTRACE --\n");
|
||||
fprintf(stderr, "================================================================\n");
|
||||
|
||||
SymCleanup(process);
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/version.h"
|
||||
#include "core/version_hash.gen.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
@ -61,12 +63,19 @@ static void handle_crash(int sig) {
|
|||
}
|
||||
|
||||
// Dump the backtrace to stderr with a message to the user
|
||||
fprintf(stderr, "\n================================================================\n");
|
||||
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
|
||||
}
|
||||
|
||||
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
|
||||
if (String(VERSION_HASH).length() != 0) {
|
||||
fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
|
||||
} else {
|
||||
fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
|
||||
}
|
||||
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
|
||||
char **strings = backtrace_symbols(bt_buffer, size);
|
||||
if (strings) {
|
||||
|
@ -115,6 +124,7 @@ static void handle_crash(int sig) {
|
|||
free(strings);
|
||||
}
|
||||
fprintf(stderr, "-- END OF BACKTRACE --\n");
|
||||
fprintf(stderr, "================================================================\n");
|
||||
|
||||
// Abort to pass the error to the OS
|
||||
abort();
|
||||
|
|
|
@ -1131,7 +1131,7 @@ void AnimatedSprite3D::stop() {
|
|||
}
|
||||
|
||||
bool AnimatedSprite3D::is_playing() const {
|
||||
return is_processing();
|
||||
return playing;
|
||||
}
|
||||
|
||||
void AnimatedSprite3D::_reset_timeout() {
|
||||
|
|
|
@ -549,7 +549,7 @@ Transform SkeletonIK::_get_target_transform() {
|
|||
target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override));
|
||||
}
|
||||
|
||||
if (target_node_override) {
|
||||
if (target_node_override && target_node_override->is_inside_tree()) {
|
||||
return target_node_override->get_global_transform();
|
||||
} else {
|
||||
return target;
|
||||
|
|
|
@ -520,6 +520,7 @@ void GraphEdit::_notification(int p_what) {
|
|||
|
||||
bool GraphEdit::_filter_input(const Point2 &p_point) {
|
||||
Ref<Texture> port = get_icon("port", "GraphNode");
|
||||
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
|
||||
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
|
||||
|
@ -529,14 +530,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
|
|||
|
||||
for (int j = 0; j < gn->get_connection_output_count(); j++) {
|
||||
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
|
||||
if (is_in_hot_zone(pos / zoom, p_point / zoom)) {
|
||||
if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < gn->get_connection_input_count(); j++) {
|
||||
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
|
||||
if (is_in_hot_zone(pos / zoom, p_point / zoom)) {
|
||||
if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -548,8 +549,10 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
|
|||
void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
|
||||
Ref<InputEventMouseButton> mb = p_ev;
|
||||
if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
|
||||
connecting_valid = false;
|
||||
Ref<Texture> port = get_icon("port", "GraphNode");
|
||||
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
|
||||
|
||||
connecting_valid = false;
|
||||
click_pos = mb->get_position() / zoom;
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
|
||||
|
@ -559,7 +562,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
|
||||
for (int j = 0; j < gn->get_connection_output_count(); j++) {
|
||||
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
|
||||
if (is_in_hot_zone(pos / zoom, click_pos)) {
|
||||
if (is_in_hot_zone(pos / zoom, click_pos, port_size, false)) {
|
||||
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
|
||||
//check disconnect
|
||||
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
|
||||
|
@ -601,7 +604,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
|
||||
for (int j = 0; j < gn->get_connection_input_count(); j++) {
|
||||
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
|
||||
if (is_in_hot_zone(pos / zoom, click_pos)) {
|
||||
if (is_in_hot_zone(pos / zoom, click_pos, port_size, true)) {
|
||||
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
|
||||
//check disconnect
|
||||
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
|
||||
|
@ -650,10 +653,12 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
connecting_target = false;
|
||||
top_layer->update();
|
||||
minimap->update();
|
||||
connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0 * zoom;
|
||||
connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0;
|
||||
|
||||
if (connecting_valid) {
|
||||
Ref<Texture> port = get_icon("port", "GraphNode");
|
||||
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
|
||||
|
||||
Vector2 mpos = mm->get_position() / zoom;
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
|
||||
|
@ -665,7 +670,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
for (int j = 0; j < gn->get_connection_output_count(); j++) {
|
||||
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
|
||||
int type = gn->get_connection_output_type(j);
|
||||
if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) {
|
||||
if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, false)) {
|
||||
connecting_target = true;
|
||||
connecting_to = pos;
|
||||
connecting_target_to = gn->get_name();
|
||||
|
@ -677,7 +682,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
for (int j = 0; j < gn->get_connection_input_count(); j++) {
|
||||
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
|
||||
int type = gn->get_connection_input_type(j);
|
||||
if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) {
|
||||
if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, true)) {
|
||||
connecting_target = true;
|
||||
connecting_to = pos;
|
||||
connecting_target_to = gn->get_name();
|
||||
|
@ -748,9 +753,25 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos)
|
|||
}
|
||||
}
|
||||
|
||||
bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos) {
|
||||
if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) {
|
||||
return false;
|
||||
bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
|
||||
if (p_left) {
|
||||
if (!Rect2(
|
||||
pos.x - p_port_size.x / 2 - port_grab_distance_horizontal,
|
||||
pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
|
||||
p_port_size.x + port_grab_distance_horizontal,
|
||||
p_port_size.y + port_grab_distance_vertical)
|
||||
.has_point(p_mouse_pos)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!Rect2(
|
||||
pos.x - p_port_size.x / 2,
|
||||
pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
|
||||
p_port_size.x + port_grab_distance_horizontal,
|
||||
p_port_size.y + port_grab_distance_vertical)
|
||||
.has_point(p_mouse_pos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
|
|
|
@ -181,7 +181,7 @@ private:
|
|||
GraphEditMinimap *minimap;
|
||||
void _top_layer_input(const Ref<InputEvent> &p_ev);
|
||||
|
||||
bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos);
|
||||
bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
|
||||
|
||||
void _top_layer_draw();
|
||||
void _connections_layer_draw();
|
||||
|
|
|
@ -1090,7 +1090,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
|
|||
|
||||
Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
|
||||
if (!underline_meta) {
|
||||
return CURSOR_ARROW;
|
||||
return get_default_cursor_shape();
|
||||
}
|
||||
|
||||
if (selection.click) {
|
||||
|
@ -1098,10 +1098,11 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
|
|||
}
|
||||
|
||||
if (main->first_invalid_line < main->lines.size()) {
|
||||
return CURSOR_ARROW; //invalid
|
||||
return get_default_cursor_shape(); //invalid
|
||||
}
|
||||
|
||||
int line = 0;
|
||||
|
||||
Item *item = nullptr;
|
||||
bool outside;
|
||||
((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line, &outside);
|
||||
|
@ -1110,7 +1111,7 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
|
|||
return CURSOR_POINTING_HAND;
|
||||
}
|
||||
|
||||
return CURSOR_ARROW;
|
||||
return get_default_cursor_shape();
|
||||
}
|
||||
|
||||
void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
|
||||
|
|
|
@ -888,7 +888,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
|
|||
theme->set_constant("bezier_len_neg", "GraphEdit", 160 * scale);
|
||||
|
||||
// Visual Node Ports
|
||||
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale);
|
||||
|
||||
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale);
|
||||
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale);
|
||||
|
||||
theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
|
||||
|
|
|
@ -43,6 +43,8 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
|
|||
set_item_name(idx, p_value);
|
||||
} else if (what == "mesh") {
|
||||
set_item_mesh(idx, p_value);
|
||||
} else if (what == "mesh_transform") {
|
||||
set_item_mesh_transform(idx, p_value);
|
||||
} else if (what == "shape") {
|
||||
Vector<ShapeData> shapes;
|
||||
ShapeData sd;
|
||||
|
@ -77,6 +79,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
r_ret = get_item_name(idx);
|
||||
} else if (what == "mesh") {
|
||||
r_ret = get_item_mesh(idx);
|
||||
} else if (what == "mesh_transform") {
|
||||
r_ret = get_item_mesh_transform(idx);
|
||||
} else if (what == "shapes") {
|
||||
r_ret = _get_item_shapes(idx);
|
||||
} else if (what == "navmesh") {
|
||||
|
@ -127,6 +131,14 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
|
|||
_change_notify();
|
||||
}
|
||||
|
||||
void MeshLibrary::set_item_mesh_transform(int p_item, const Transform &p_transform) {
|
||||
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
|
||||
item_map[p_item].mesh_transform = p_transform;
|
||||
notify_change_to_owners();
|
||||
emit_changed();
|
||||
_change_notify();
|
||||
}
|
||||
|
||||
void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) {
|
||||
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
|
||||
item_map[p_item].shapes = p_shapes;
|
||||
|
@ -170,6 +182,11 @@ Ref<Mesh> MeshLibrary::get_item_mesh(int p_item) const {
|
|||
return item_map[p_item].mesh;
|
||||
}
|
||||
|
||||
Transform MeshLibrary::get_item_mesh_transform(int p_item) const {
|
||||
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
|
||||
return item_map[p_item].mesh_transform;
|
||||
}
|
||||
|
||||
Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const {
|
||||
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector<ShapeData>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
|
||||
return item_map[p_item].shapes;
|
||||
|
@ -267,12 +284,14 @@ void MeshLibrary::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item);
|
||||
ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);
|
||||
ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh);
|
||||
ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform);
|
||||
ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh);
|
||||
ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform);
|
||||
ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes);
|
||||
ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview);
|
||||
ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name);
|
||||
ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh);
|
||||
ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes);
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
Vector<ShapeData> shapes;
|
||||
Ref<Texture> preview;
|
||||
Transform navmesh_transform;
|
||||
Transform mesh_transform;
|
||||
Ref<NavigationMesh> navmesh;
|
||||
};
|
||||
|
||||
|
@ -71,12 +72,14 @@ public:
|
|||
void create_item(int p_item);
|
||||
void set_item_name(int p_item, const String &p_name);
|
||||
void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh);
|
||||
void set_item_mesh_transform(int p_item, const Transform &p_transform);
|
||||
void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh);
|
||||
void set_item_navmesh_transform(int p_item, const Transform &p_transform);
|
||||
void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes);
|
||||
void set_item_preview(int p_item, const Ref<Texture> &p_preview);
|
||||
String get_item_name(int p_item) const;
|
||||
Ref<Mesh> get_item_mesh(int p_item) const;
|
||||
Transform get_item_mesh_transform(int p_item) const;
|
||||
Ref<NavigationMesh> get_item_navmesh(int p_item) const;
|
||||
Transform get_item_navmesh_transform(int p_item) const;
|
||||
Vector<ShapeData> get_item_shapes(int p_item) const;
|
||||
|
|
|
@ -377,10 +377,17 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
|
|||
return OK;
|
||||
}
|
||||
|
||||
// save the child instanced scenes that are chosen as editable, so they can be restored
|
||||
bool is_editable_instance = false;
|
||||
|
||||
// save the child instantiated scenes that are chosen as editable, so they can be restored
|
||||
// upon load back
|
||||
if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) {
|
||||
editable_instances.push_back(p_owner->get_path_to(p_node));
|
||||
// Node is the root of an editable instance.
|
||||
is_editable_instance = true;
|
||||
} else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
|
||||
// Node is part of an editable instance.
|
||||
is_editable_instance = true;
|
||||
}
|
||||
|
||||
NodeData nd;
|
||||
|
@ -610,7 +617,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
|
|||
|
||||
// Save the right type. If this node was created by an instance
|
||||
// then flag that the node should not be created but reused
|
||||
if (pack_state_stack.empty()) {
|
||||
if (pack_state_stack.empty() && !is_editable_instance) {
|
||||
//this node is not part of an instancing process, so save the type
|
||||
nd.type = _nm_get_string(p_node->get_class(), name_map);
|
||||
} else {
|
||||
|
|
|
@ -1456,6 +1456,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
|
|||
int i, j, prevrow, thisrow, point;
|
||||
float x, y, z;
|
||||
|
||||
float scale = height * (is_hemisphere ? 1.0 : 0.5);
|
||||
|
||||
// set our bounding box
|
||||
|
||||
PoolVector<Vector3> points;
|
||||
|
@ -1479,7 +1481,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
|
|||
|
||||
v /= (rings + 1);
|
||||
w = sin(Math_PI * v);
|
||||
y = height * (is_hemisphere ? 1.0 : 0.5) * cos(Math_PI * v);
|
||||
y = scale * cos(Math_PI * v);
|
||||
|
||||
for (i = 0; i <= radial_segments; i++) {
|
||||
float u = i;
|
||||
|
@ -1494,7 +1496,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
|
|||
} else {
|
||||
Vector3 p = Vector3(x * radius * w, y, z * radius * w);
|
||||
points.push_back(p);
|
||||
normals.push_back(p.normalized());
|
||||
Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale);
|
||||
normals.push_back(normal.normalized());
|
||||
};
|
||||
ADD_TANGENT(z, 0.0, -x, 1.0)
|
||||
uvs.push_back(Vector2(u, v));
|
||||
|
|
Loading…
Reference in New Issue